From f00d59c556ea88a114434e6bf60541988d954d82 Mon Sep 17 00:00:00 2001 From: Zach Bray Date: Fri, 2 Jun 2023 15:10:27 +0100 Subject: [PATCH 01/50] [Java] Add disabled tests around checking field access order. SBE does not support arbitrary access to fields when encoding and decoding. In particular, accessing groups and variable length fields advances a `limit`, which marks where the start of the next group/variable length field is expected. We aim to introduce some checking (in debug mode - when bounds checks are enabled) that verifies field access order is consistent enough with the order defined in the schema to encode or decode a valid message. --- build.gradle | 3 +- .../real_logic/sbe/FieldOrderCheckTest.java | 843 ++++++++++++++++++ .../resources/field-order-check-schema.xml | 74 ++ 3 files changed, 919 insertions(+), 1 deletion(-) create mode 100644 sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java create mode 100644 sbe-tool/src/test/resources/field-order-check-schema.xml diff --git a/build.gradle b/build.gradle index 2761b537e4..786ec9c07b 100644 --- a/build.gradle +++ b/build.gradle @@ -281,7 +281,8 @@ project(':sbe-tool') { 'sbe.validation.stop.on.error': 'true', 'sbe.validation.xsd': validationXsdPath) args = ['src/test/resources/json-printer-test-schema.xml', - 'src/test/resources/composite-elements-schema.xml'] + 'src/test/resources/composite-elements-schema.xml', + 'src/test/resources/field-order-check-schema.xml'] } jar { diff --git a/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java b/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java new file mode 100644 index 0000000000..5b8e868380 --- /dev/null +++ b/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java @@ -0,0 +1,843 @@ +/* + * 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; + +import order.check.*; +import org.agrona.ExpandableArrayBuffer; +import org.agrona.MutableDirectBuffer; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assumptions.assumeFalse; + +@Disabled +public class FieldOrderCheckTest +{ + private static final Class INCORRECT_ORDER_EXCEPTION_CLASS = IllegalStateException.class; + private static final int OFFSET = 0; + private final MutableDirectBuffer buffer = new ExpandableArrayBuffer(); + private final MessageHeaderEncoder messageHeaderEncoder = new MessageHeaderEncoder(); + private final MessageHeaderDecoder messageHeaderDecoder = new MessageHeaderDecoder(); + + @BeforeAll + static void assumeDebugMode() + { + final boolean productionMode = Boolean.getBoolean("agrona.disable.bounds.checks"); + assumeFalse(productionMode); + } + + @Test + void allowsEncodingAndDecodingVariableLengthFieldsInSchemaDefinedOrder() + { + final MultipleVarLengthEncoder encoder = new MultipleVarLengthEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + encoder.b("abc"); + encoder.c("def"); + + final MultipleVarLengthDecoder decoder = new MultipleVarLengthDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(42)); + assertThat(decoder.b(), equalTo("abc")); + assertThat(decoder.c(), equalTo("def")); + } + + @Test + void allowsReEncodingTopLevelPrimitiveFields() + { + final MultipleVarLengthEncoder encoder = new MultipleVarLengthEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + encoder.b("abc"); + encoder.c("def"); + encoder.a(43); + + final MultipleVarLengthDecoder decoder = new MultipleVarLengthDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(43)); + assertThat(decoder.b(), equalTo("abc")); + assertThat(decoder.c(), equalTo("def")); + } + + @Test + void disallowsSkippingEncodingOfVariableLengthField() + { + final MultipleVarLengthEncoder encoder = new MultipleVarLengthEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.c("def")); + } + + @Test + void disallowsReEncodingEarlierVariableLengthFields() + { + final MultipleVarLengthEncoder encoder = new MultipleVarLengthEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + encoder.b("abc"); + encoder.c("def"); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.b("ghi")); + } + + @Test + void disallowsReEncodingLatestVariableLengthField() + { + final MultipleVarLengthEncoder encoder = new MultipleVarLengthEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + encoder.b("abc"); + encoder.c("def"); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.c("ghi")); + } + + @Test + void disallowsSkippingDecodingOfVariableLengthField() + { + final MultipleVarLengthEncoder encoder = new MultipleVarLengthEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + encoder.b("abc"); + encoder.c("def"); + + final MultipleVarLengthDecoder decoder = new MultipleVarLengthDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(42)); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::c); + } + + @Test + void disallowsReDecodingEarlierVariableLengthField() + { + final MultipleVarLengthEncoder encoder = new MultipleVarLengthEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + encoder.b("abc"); + encoder.c("def"); + + final MultipleVarLengthDecoder decoder = new MultipleVarLengthDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(42)); + assertThat(decoder.b(), equalTo("abc")); + assertThat(decoder.c(), equalTo("def")); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::b); + } + + @Test + void disallowsReDecodingLatestVariableLengthField() + { + final MultipleVarLengthEncoder encoder = new MultipleVarLengthEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + encoder.b("abc"); + encoder.c("def"); + + final MultipleVarLengthDecoder decoder = new MultipleVarLengthDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(42)); + assertThat(decoder.b(), equalTo("abc")); + assertThat(decoder.c(), equalTo("def")); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::c); + } + + @Test + void allowsEncodingAndDecodingGroupAndVariableLengthFieldsInSchemaDefinedOrder() + { + final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + encoder.bCount(2) + .next() + .c(1) + .next() + .c(2); + encoder.d("abc"); + + final GroupAndVarLengthDecoder decoder = new GroupAndVarLengthDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(42)); + final GroupAndVarLengthDecoder.BDecoder bs = decoder.b(); + assertThat(bs.count(), equalTo(2)); + assertThat(bs.next().c(), equalTo(1)); + assertThat(bs.next().c(), equalTo(2)); + assertThat(decoder.d(), equalTo("abc")); + } + + @Test + void allowsEncodingAndDecodingEmptyGroupAndVariableLengthFieldsInSchemaDefinedOrder() + { + final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + encoder.bCount(0); + encoder.d("abc"); + + final GroupAndVarLengthDecoder decoder = new GroupAndVarLengthDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(42)); + final GroupAndVarLengthDecoder.BDecoder bs = decoder.b(); + assertThat(bs.count(), equalTo(0)); + assertThat(decoder.d(), equalTo("abc")); + } + + @Test + void allowsReEncodingPrimitiveFieldInGroupElementAfterTopLevelVariableLengthField() + { + final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + final GroupAndVarLengthEncoder.BEncoder bEncoder = encoder.bCount(2); + bEncoder + .next() + .c(1) + .next() + .c(2); + encoder.d("abc"); + bEncoder.c(3); + + final GroupAndVarLengthDecoder decoder = new GroupAndVarLengthDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(42)); + final GroupAndVarLengthDecoder.BDecoder bs = decoder.b(); + assertThat(bs.count(), equalTo(2)); + assertThat(bs.next().c(), equalTo(1)); + assertThat(bs.next().c(), equalTo(3)); + assertThat(decoder.d(), equalTo("abc")); + } + + @Test + void disallowsEncodingGroupElementWithoutCallingNext() + { + final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.bCount(1).c(1)); + } + + @Test + void disallowsSkippingEncodingOfGroup() + { + final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.d("abc")); + } + + @Test + void disallowsReEncodingVariableLengthFieldAfterGroup() + { + final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + encoder.bCount(2) + .next() + .c(1) + .next() + .c(2); + encoder.d("abc"); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.d("def")); + } + + @Test + void disallowsReEncodingGroupLength() + { + final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + encoder.bCount(2) + .next() + .c(1) + .next() + .c(2); + encoder.d("abc"); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.bCount(1)); + } + + @Test + void allowsReEncodingGroupElementBlockFieldAfterTopLevelVariableLengthField() + { + final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + final GroupAndVarLengthEncoder.BEncoder b = encoder.bCount(2) + .next() + .c(1) + .next() + .c(2); + encoder.d("abc"); + b.c(3); + + final GroupAndVarLengthDecoder decoder = new GroupAndVarLengthDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(42)); + final GroupAndVarLengthDecoder.BDecoder bs = decoder.b(); + assertThat(bs.count(), equalTo(2)); + assertThat(bs.next().c(), equalTo(1)); + assertThat(bs.next().c(), equalTo(3)); + assertThat(decoder.d(), equalTo("abc")); + } + + @Test + void disallowsSkippingDecodingOfGroupBeforeVariableLengthField() + { + final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + encoder.bCount(2) + .next() + .c(1) + .next() + .c(2); + encoder.d("abc"); + + final GroupAndVarLengthDecoder decoder = new GroupAndVarLengthDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(42)); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::d); + } + + @Test + void disallowsReDecodingVariableLengthFieldAfterGroup() + { + final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + encoder.bCount(2) + .next() + .c(1) + .next() + .c(2); + encoder.d("abc"); + + final GroupAndVarLengthDecoder decoder = new GroupAndVarLengthDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(42)); + final GroupAndVarLengthDecoder.BDecoder bs = decoder.b(); + assertThat(bs.count(), equalTo(2)); + assertThat(bs.next().c(), equalTo(1)); + assertThat(bs.next().c(), equalTo(2)); + assertThat(decoder.d(), equalTo("abc")); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::d); + } + + @Test + void disallowsReDecodingGroupBeforeVariableLengthField() + { + final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + encoder.bCount(2) + .next() + .c(1) + .next() + .c(2); + encoder.d("abc"); + + final GroupAndVarLengthDecoder decoder = new GroupAndVarLengthDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(42)); + final GroupAndVarLengthDecoder.BDecoder bs = decoder.b(); + assertThat(bs.count(), equalTo(2)); + assertThat(bs.next().c(), equalTo(1)); + assertThat(bs.next().c(), equalTo(2)); + assertThat(decoder.d(), equalTo("abc")); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::b); + } + + @Test + void allowsEncodingAndDecodingVariableLengthFieldInsideGroupInSchemaDefinedOrder() + { + final VarLengthInsideGroupEncoder encoder = new VarLengthInsideGroupEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + encoder.bCount(2) + .next() + .c(1) + .d("abc") + .next() + .c(2) + .d("def"); + encoder.e("ghi"); + + final VarLengthInsideGroupDecoder decoder = new VarLengthInsideGroupDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(42)); + final VarLengthInsideGroupDecoder.BDecoder bs = decoder.b(); + assertThat(bs.count(), equalTo(2)); + assertThat(bs.next().c(), equalTo(1)); + assertThat(bs.d(), equalTo("abc")); + assertThat(bs.next().c(), equalTo(2)); + assertThat(bs.d(), equalTo("def")); + assertThat(decoder.e(), equalTo("ghi")); + } + + @Test + void allowsReEncodingGroupElementPrimitiveFieldAfterElementVariableLengthField() + { + final VarLengthInsideGroupEncoder encoder = new VarLengthInsideGroupEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + final VarLengthInsideGroupEncoder.BEncoder bEncoder = encoder.bCount(1); + bEncoder + .next() + .c(1) + .d("abc"); + bEncoder.c(2); + encoder.e("ghi"); + + final VarLengthInsideGroupDecoder decoder = new VarLengthInsideGroupDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(42)); + final VarLengthInsideGroupDecoder.BDecoder bs = decoder.b(); + assertThat(bs.count(), equalTo(1)); + assertThat(bs.next().c(), equalTo(2)); + assertThat(bs.d(), equalTo("abc")); + assertThat(decoder.e(), equalTo("ghi")); + } + + @Test + void disallowsSkippingGroupElementVariableLengthFieldToEncodeAtTopLevel() + { + final VarLengthInsideGroupEncoder encoder = new VarLengthInsideGroupEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + encoder.bCount(1).next().c(1); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.e("abc")); + } + + @Test + void disallowsSkippingGroupElementVariableLengthFieldToEncodeNextElement() + { + final VarLengthInsideGroupEncoder encoder = new VarLengthInsideGroupEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + final VarLengthInsideGroupEncoder.BEncoder b = encoder.bCount(2) + .next(); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, b::next); + } + + @Test + void disallowsSkippingGroupElementEncoding() + { + final VarLengthInsideGroupEncoder encoder = new VarLengthInsideGroupEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + encoder.bCount(2) + .next() + .c(1) + .d("abc"); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.e("abc")); + } + + @Test + void disallowsReEncodingGroupElementVariableLengthField() + { + final VarLengthInsideGroupEncoder encoder = new VarLengthInsideGroupEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + final VarLengthInsideGroupEncoder.BEncoder b = encoder.bCount(1) + .next() + .c(1) + .d("abc"); + encoder.e("def"); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> b.d("ghi")); + } + + @Test + void disallowsReDecodingGroupElementVariableLengthField() + { + final VarLengthInsideGroupEncoder encoder = new VarLengthInsideGroupEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + encoder.bCount(2) + .next() + .c(1) + .d("abc") + .next() + .c(2) + .d("def"); + encoder.e("ghi"); + + final VarLengthInsideGroupDecoder decoder = new VarLengthInsideGroupDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(42)); + final VarLengthInsideGroupDecoder.BDecoder bs = decoder.b(); + assertThat(bs.count(), equalTo(2)); + assertThat(bs.next().c(), equalTo("abc")); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, bs::d); + } + + @Test + void disallowsSkippingDecodingOfGroupElementVariableLengthFieldToNextElement() + { + final VarLengthInsideGroupEncoder encoder = new VarLengthInsideGroupEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + encoder.bCount(2) + .next() + .c(1) + .d("abc") + .next() + .c(2) + .d("def"); + encoder.e("ghi"); + + final VarLengthInsideGroupDecoder decoder = new VarLengthInsideGroupDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(42)); + final VarLengthInsideGroupDecoder.BDecoder bs = decoder.b(); + assertThat(bs.count(), equalTo(2)); + assertThat(bs.next().c(), equalTo(1)); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, bs::next); + } + + @Test + void disallowsSkippingDecodingOfGroupElementVariableLengthFieldToTopLevel() + { + final VarLengthInsideGroupEncoder encoder = new VarLengthInsideGroupEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + encoder.bCount(1) + .next() + .c(1) + .d("abc"); + encoder.e("ghi"); + + final VarLengthInsideGroupDecoder decoder = new VarLengthInsideGroupDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(42)); + final VarLengthInsideGroupDecoder.BDecoder bs = decoder.b(); + assertThat(bs.count(), equalTo(2)); + assertThat(bs.next().c(), equalTo(1)); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::e); + } + + @Test + void disallowsSkippingDecodingOfGroupElement() + { + final VarLengthInsideGroupEncoder encoder = new VarLengthInsideGroupEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + encoder.bCount(2) + .next() + .c(1) + .d("abc") + .next() + .c(2) + .d("def"); + encoder.e("ghi"); + + final VarLengthInsideGroupDecoder decoder = new VarLengthInsideGroupDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(42)); + final VarLengthInsideGroupDecoder.BDecoder bs = decoder.b(); + assertThat(bs.count(), equalTo(2)); + assertThat(bs.next().c(), equalTo("abc")); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::e); + } + + @Test + void allowsEncodingNestedGroupsInSchemaDefinedOrder() + { + final NestedGroupsEncoder encoder = new NestedGroupsEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + final NestedGroupsEncoder.BEncoder b = encoder.bCount(2) + .next(); + b.c(1); + b.dCount(2) + .next() + .e(2) + .next() + .e(3); + b.fCount(1) + .next() + .g(4); + b.next(); + b.c(5); + b.dCount(1) + .next() + .e(6); + b.fCount(1) + .next() + .g(7); + encoder.hCount(1) + .next() + .i(8); + + final NestedGroupsDecoder decoder = new NestedGroupsDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(42)); + final NestedGroupsDecoder.BDecoder bs = decoder.b(); + assertThat(bs.count(), equalTo(2)); + final NestedGroupsDecoder.BDecoder b0 = bs.next(); + assertThat(b0.c(), equalTo(1)); + final NestedGroupsDecoder.BDecoder.DDecoder b0ds = b0.d(); + assertThat(b0ds.count(), equalTo(2)); + assertThat(b0ds.next().e(), equalTo(2)); + assertThat(b0ds.next().e(), equalTo(3)); + final NestedGroupsDecoder.BDecoder.FDecoder b0fs = b0.f(); + assertThat(b0fs.count(), equalTo(1)); + assertThat(b0fs.next().g(), equalTo(4)); + final NestedGroupsDecoder.BDecoder b1 = bs.next(); + assertThat(b1.c(), equalTo(5)); + final NestedGroupsDecoder.BDecoder.DDecoder b1ds = b1.d(); + assertThat(b1ds.count(), equalTo(1)); + assertThat(b1ds.next().e(), equalTo(6)); + final NestedGroupsDecoder.BDecoder.FDecoder b1fs = b1.f(); + assertThat(b1fs.count(), equalTo(1)); + assertThat(b1fs.next().g(), equalTo(7)); + final NestedGroupsDecoder.HDecoder hs = decoder.h(); + assertThat(hs.count(), equalTo(1)); + assertThat(hs.next().i(), equalTo(8)); + } + + @Test + void allowsEncodingEmptyNestedGroupsInSchemaDefinedOrder() + { + final NestedGroupsEncoder encoder = new NestedGroupsEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + encoder.bCount(0); + encoder.hCount(0); + + final NestedGroupsDecoder decoder = new NestedGroupsDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(42)); + final NestedGroupsDecoder.BDecoder bs = decoder.b(); + assertThat(bs.count(), equalTo(0)); + final NestedGroupsDecoder.HDecoder hs = decoder.h(); + assertThat(hs.count(), equalTo(0)); + } + + @Test + void disallowsSkippingEncodingOfNestedGroup() + { + final NestedGroupsEncoder encoder = new NestedGroupsEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + final NestedGroupsEncoder.BEncoder b = encoder.bCount(1) + .next() + .c(1); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> b.fCount(1)); + } + + @Test + void allowsEncodingAndDecodingCompositeInsideGroupInSchemaDefinedOrder() + { + final CompositeInsideGroupEncoder encoder = new CompositeInsideGroupEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a().x(1).y(2); + encoder.bCount(1) + .next() + .c().x(3).y(4); + + final CompositeInsideGroupDecoder decoder = new CompositeInsideGroupDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + final PointDecoder a = decoder.a(); + assertThat(a.x(), equalTo(1)); + assertThat(a.y(), equalTo(2)); + final CompositeInsideGroupDecoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + final PointDecoder c = b.next().c(); + assertThat(c.x(), equalTo(3)); + assertThat(c.y(), equalTo(4)); + } + + @Test + void allowsReEncodingTopLevelCompositeViaReWrap() + { + final CompositeInsideGroupEncoder encoder = new CompositeInsideGroupEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a().x(1).y(2); + encoder.bCount(1) + .next() + .c().x(3).y(4); + encoder.a().x(5).y(6); + + final CompositeInsideGroupDecoder decoder = new CompositeInsideGroupDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + final PointDecoder a = decoder.a(); + assertThat(a.x(), equalTo(5)); + assertThat(a.y(), equalTo(6)); + final CompositeInsideGroupDecoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + final PointDecoder c = b.next().c(); + assertThat(c.x(), equalTo(3)); + assertThat(c.y(), equalTo(4)); + } + + @Test + void allowsReEncodingTopLevelCompositeViaEncoderReference() + { + final CompositeInsideGroupEncoder encoder = new CompositeInsideGroupEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + final PointEncoder aEncoder = encoder.a(); + aEncoder.x(1).y(2); + encoder.bCount(1) + .next() + .c().x(3).y(4); + aEncoder.x(5).y(6); + + final CompositeInsideGroupDecoder decoder = new CompositeInsideGroupDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + final PointDecoder a = decoder.a(); + assertThat(a.x(), equalTo(5)); + assertThat(a.y(), equalTo(6)); + final CompositeInsideGroupDecoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + final PointDecoder c = b.next().c(); + assertThat(c.x(), equalTo(3)); + assertThat(c.y(), equalTo(4)); + } + + @Test + void allowsReEncodingGroupElementCompositeViaReWrap() + { + final CompositeInsideGroupEncoder encoder = new CompositeInsideGroupEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a().x(1).y(2); + final CompositeInsideGroupEncoder.BEncoder bEncoder = encoder.bCount(1).next(); + bEncoder.c().x(3).y(4); + bEncoder.c().x(5).y(6); + + final CompositeInsideGroupDecoder decoder = new CompositeInsideGroupDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + final PointDecoder a = decoder.a(); + assertThat(a.x(), equalTo(1)); + assertThat(a.y(), equalTo(2)); + final CompositeInsideGroupDecoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + final PointDecoder c = b.next().c(); + assertThat(c.x(), equalTo(5)); + assertThat(c.y(), equalTo(6)); + } + + @Test + void allowsReEncodingGroupElementCompositeViaEncoderReference() + { + final CompositeInsideGroupEncoder encoder = new CompositeInsideGroupEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a().x(1).y(2); + final CompositeInsideGroupEncoder.BEncoder bEncoder = encoder.bCount(1).next(); + final PointEncoder cEncoder = bEncoder.c(); + cEncoder.x(3).y(4); + cEncoder.x(5).y(6); + + final CompositeInsideGroupDecoder decoder = new CompositeInsideGroupDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + final PointDecoder a = decoder.a(); + assertThat(a.x(), equalTo(1)); + assertThat(a.y(), equalTo(2)); + final CompositeInsideGroupDecoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + final PointDecoder c = b.next().c(); + assertThat(c.x(), equalTo(5)); + assertThat(c.y(), equalTo(6)); + } + + @Test + void allowsReDecodingTopLevelCompositeViaReWrap() + { + final CompositeInsideGroupEncoder encoder = new CompositeInsideGroupEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a().x(1).y(2); + encoder.bCount(1) + .next() + .c().x(3).y(4); + + final CompositeInsideGroupDecoder decoder = new CompositeInsideGroupDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + final PointDecoder a1 = decoder.a(); + assertThat(a1.x(), equalTo(1)); + assertThat(a1.y(), equalTo(2)); + final PointDecoder a2 = decoder.a(); + assertThat(a2.x(), equalTo(1)); + assertThat(a2.y(), equalTo(2)); + } + + @Test + void allowsReDecodingTopLevelCompositeViaEncoderReference() + { + final CompositeInsideGroupEncoder encoder = new CompositeInsideGroupEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a().x(1).y(2); + encoder.bCount(1) + .next() + .c().x(3).y(4); + + final CompositeInsideGroupDecoder decoder = new CompositeInsideGroupDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + final PointDecoder a = decoder.a(); + assertThat(a.x(), equalTo(1)); + assertThat(a.y(), equalTo(2)); + assertThat(a.x(), equalTo(1)); + assertThat(a.y(), equalTo(2)); + } + + @Test + void allowsReDecodingGroupElementCompositeViaReWrap() + { + final CompositeInsideGroupEncoder encoder = new CompositeInsideGroupEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a().x(1).y(2); + encoder.bCount(1) + .next() + .c().x(3).y(4); + + final CompositeInsideGroupDecoder decoder = new CompositeInsideGroupDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + final PointDecoder a = decoder.a(); + assertThat(a.x(), equalTo(1)); + assertThat(a.y(), equalTo(2)); + final CompositeInsideGroupDecoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + final PointDecoder c1 = b.next().c(); + assertThat(c1.x(), equalTo(3)); + assertThat(c1.y(), equalTo(4)); + final PointDecoder c2 = b.c(); + assertThat(c2.x(), equalTo(3)); + assertThat(c2.y(), equalTo(4)); + } + + @Test + void allowsReDecodingGroupElementCompositeViaEncoderReference() + { + final CompositeInsideGroupEncoder encoder = new CompositeInsideGroupEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a().x(1).y(2); + encoder.bCount(1) + .next() + .c().x(3).y(4); + + final CompositeInsideGroupDecoder decoder = new CompositeInsideGroupDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + final PointDecoder a = decoder.a(); + assertThat(a.x(), equalTo(1)); + assertThat(a.y(), equalTo(2)); + final CompositeInsideGroupDecoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + final PointDecoder c = b.next().c(); + assertThat(c.x(), equalTo(3)); + assertThat(c.y(), equalTo(4)); + assertThat(c.x(), equalTo(3)); + assertThat(c.y(), equalTo(4)); + } +} diff --git a/sbe-tool/src/test/resources/field-order-check-schema.xml b/sbe-tool/src/test/resources/field-order-check-schema.xml new file mode 100644 index 0000000000..817ec4dc48 --- /dev/null +++ b/sbe-tool/src/test/resources/field-order-check-schema.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From ae404dfb35a8255ca7a2e4a36ef47711fa2c513d Mon Sep 17 00:00:00 2001 From: Zach Bray Date: Mon, 5 Jun 2023 19:42:03 +0100 Subject: [PATCH 02/50] [Java] Spike an approach for checking field encode order. Also, enable some tests around field order checking. Outline of the approach: - Using a FSM, model the states of an encoder during encoding. - Hopefully, this model will be language independent. - Generate code to perform state transitions upon field accesses. - And to throw when transitions are not in the model. Assumptions: - JIT compilation will remove `if (DEBUG_MODE) { ... }` blocks when the static boolean constant, `DEBUG_MODE`, is `false`. Questions: - Is the approach reasonable? - Is the newly disallowed behaviour reasonable? - For example, it prevents encoding block fields after a group or variable length field has been encoded. Further work: - Decode path - More tests (including tests for decoding old messages where some fields may not be present) --- .../sbe/generation/java/FieldOrderModel.java | 358 +++++++++++ .../sbe/generation/java/JavaGenerator.java | 378 ++++++++++-- .../sbe/ir/generated/FrameCodecEncoder.java | 173 ++++++ .../sbe/ir/generated/TokenCodecEncoder.java | 561 ++++++++++++++++++ .../real_logic/sbe/FieldOrderCheckTest.java | 24 +- .../generation/java/JavaGeneratorTest.java | 3 + 6 files changed, 1443 insertions(+), 54 deletions(-) create mode 100644 sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java new file mode 100644 index 0000000000..2eacd02095 --- /dev/null +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java @@ -0,0 +1,358 @@ +/* + * 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.java; + +import uk.co.real_logic.sbe.generation.Generators; +import uk.co.real_logic.sbe.ir.Signal; +import uk.co.real_logic.sbe.ir.Token; +import org.agrona.collections.Int2ObjectHashMap; +import org.agrona.collections.MutableReference; + +import java.util.*; +import java.util.function.Consumer; + +import static uk.co.real_logic.sbe.ir.GenerationUtil.collectFields; +import static uk.co.real_logic.sbe.ir.GenerationUtil.collectGroups; +import static uk.co.real_logic.sbe.ir.GenerationUtil.collectVarData; + +final class FieldOrderModel +{ + private static final boolean BLOCK_SKIP_CHECK_ENABLED = Boolean.getBoolean("sbe.block.skip.check.enabled"); + private final Int2ObjectHashMap states = new Int2ObjectHashMap<>(); + private final Map transitions = new LinkedHashMap<>(); + private final Set reservedNames = new HashSet<>(); + private final State notWrappedState = allocateState("NOT_WRAPPED"); + private final State wrappedState = allocateState("WRAPPED"); + private int transitionNumber; + + public State notWrappedState() + { + return notWrappedState; + } + + public State wrappedState() + { + return wrappedState; + } + + public void forEachState(final Consumer consumer) + { + states.values() + .stream() + .sorted(Comparator.comparingInt(s -> s.number)) + .forEach(consumer); + } + + public List getTransitions(final TransitionContext context, final Token token) + { + final TransitionGroup transitionGroup = transitions.get(token); + if (transitionGroup == null) + { + return Collections.emptyList(); + } + + return transitionGroup.transitions.get(context); + } + + public void findTransitions( + final List fields, + final List groups, + final List varData) + { + findTransitions( + Collections.singletonList(wrappedState), + BLOCK_SKIP_CHECK_ENABLED ? null : wrappedState, + "", + fields, + groups, + varData + ); + } + + @SuppressWarnings("checkstyle:MethodLength") + private List findTransitions( + final List entryStates, + final State blockStateOrNull, + final String prefix, + final List fields, + final List groups, + final List varData + ) + { + final MutableReference blockState = new MutableReference<>(blockStateOrNull); + + final List fromStates = new ArrayList<>(entryStates); + + Generators.forEachField(fields, (token, ignored) -> + { + if (null == blockState.get()) + { + blockState.set(allocateState(prefix.isEmpty() ? "BLOCK" : prefix + "_BLOCK")); + fromStates.add(blockState.get()); + } + + allocateTransition( + TransitionContext.NONE, + "FILL_" + prefix + token.name().toUpperCase(), + token, + fromStates, + blockState.get()); + }); + + if (blockState.get() != null) + { + fromStates.clear(); + fromStates.add(blockState.get()); + } + + for (int i = 0; i < groups.size(); i++) + { + final Token token = groups.get(i); + if (token.signal() != Signal.BEGIN_GROUP) + { + throw new IllegalStateException("tokens must begin with BEGIN_GROUP: token=" + token); + } + + ++i; + final int groupHeaderTokenCount = groups.get(i).componentTokenCount(); + i += groupHeaderTokenCount; + + final ArrayList groupFields = new ArrayList<>(); + i = collectFields(groups, i, groupFields); + final ArrayList groupGroups = new ArrayList<>(); + i = collectGroups(groups, i, groupGroups); + final ArrayList groupVarData = new ArrayList<>(); + i = collectVarData(groups, i, groupVarData); + + final String groupName = token.name().toUpperCase(); + final String groupPrefix = prefix.isEmpty() ? groupName : prefix + "_" + groupName; + + final List beginGroupStates = new ArrayList<>(fromStates); + + final State nRemainingGroup = allocateState(groupPrefix + "_N"); + final State nRemainingGroupElement = allocateState(groupPrefix + "_N_ELEMENT"); + final State oneRemainingGroupElement = allocateState(groupPrefix + "_1_ELEMENT"); + final State emptyGroup = allocateState(groupPrefix + "_0"); + + // fooCount(0) + allocateTransition( + TransitionContext.SELECT_EMPTY_GROUP, + "ZERO_" + groupPrefix, + token, + beginGroupStates, + emptyGroup); + + // fooCount(N) where N > 0 + allocateTransition( + TransitionContext.SELECT_MULTI_ELEMENT_GROUP, + "MANY_" + groupPrefix, + token, + beginGroupStates, + nRemainingGroup); + + fromStates.clear(); + fromStates.add(nRemainingGroupElement); + final List nRemainingExitStates = findTransitions( + fromStates, + BLOCK_SKIP_CHECK_ENABLED ? null : nRemainingGroupElement, + groupPrefix + "_N", + groupFields, + groupGroups, + groupVarData); + + fromStates.clear(); + fromStates.add(nRemainingGroup); + fromStates.addAll(nRemainingExitStates); + + // where more than one element remains in the group + allocateTransition( + TransitionContext.NEXT_ELEMENT_IN_GROUP, + "NEXT_" + groupPrefix, + token, + fromStates, + nRemainingGroupElement); + + fromStates.clear(); + fromStates.add(nRemainingGroup); + fromStates.addAll(nRemainingExitStates); + + // where only one element remains in the group + allocateTransition( + TransitionContext.LAST_ELEMENT_IN_GROUP, + "LAST_" + groupPrefix, + token, + fromStates, + oneRemainingGroupElement); + + fromStates.clear(); + fromStates.add(oneRemainingGroupElement); + + final List oneRemainingExitStates = findTransitions( + fromStates, + BLOCK_SKIP_CHECK_ENABLED ? null : oneRemainingGroupElement, + groupPrefix + "_1", + groupFields, + groupGroups, + groupVarData); + + fromStates.clear(); + fromStates.add(emptyGroup); + fromStates.addAll(oneRemainingExitStates); + } + + for (int i = 0; i < varData.size(); ) + { + final Token token = varData.get(i); + if (token.signal() != Signal.BEGIN_VAR_DATA) + { + throw new IllegalStateException("tokens must begin with BEGIN_VAR_DATA: token=" + token); + } + i += token.componentTokenCount(); + + final State state = allocateState("FILLED_" + prefix + "_" + token.name().toUpperCase()); + allocateTransition( + TransitionContext.NONE, + "FILL_" + prefix + "_" + token.name().toUpperCase(), + token, + fromStates, + state); + fromStates.clear(); + fromStates.add(state); + } + + return fromStates; + } + + private State allocateState(final String name) + { + if (!reservedNames.add(name)) + { + throw new IllegalStateException("Name is already reserved: " + name); + } + + final State state = new State(states.size() + 1, name); + states.put(state.number, state); + return state; + } + + private void allocateTransition( + final TransitionContext context, + final String name, + final Token token, + final List from, + final State to) + { + if (!reservedNames.add(name)) + { + throw new IllegalStateException("Name is already reserved: " + name); + } + + final TransitionGroup transitionGroup = transitions.computeIfAbsent(token, ignored -> new TransitionGroup()); + final Transition transition = new Transition(nextTransitionNumber(), name, from, to); + transitionGroup.add(context, transition); + } + + private int nextTransitionNumber() + { + return transitionNumber++; + } + + static final class State + { + private final int number; + private final String name; + + private State(final int number, final String name) + { + this.number = number; + this.name = name; + } + + public int number() + { + return number; + } + + public String name() + { + return name; + } + } + + static final class Transition + { + private final int number; + private final String name; + private final Set from; + private final State to; + + private Transition(final int number, final String name, final List from, final State to) + { + this.number = number; + this.name = name; + this.from = new HashSet<>(from); + this.to = to; + } + + void forEachStartState(final Consumer consumer) + { + from.forEach(consumer); + } + + State endState() + { + return to; + } + } + + enum TransitionContext + { + NONE, + SELECT_EMPTY_GROUP, + SELECT_MULTI_ELEMENT_GROUP, + NEXT_ELEMENT_IN_GROUP, + LAST_ELEMENT_IN_GROUP + } + + private static final class TransitionGroup + { + private final Map> transitions = new LinkedHashMap<>(); + + public void add(final TransitionContext context, final Transition transition) + { + final List transitionsForContext = + transitions.computeIfAbsent(context, ignored -> new ArrayList<>()); + + final boolean duplicateEndState = + transitionsForContext.stream().anyMatch(t -> t.to.number == transition.to.number); + + if (duplicateEndState) + { + throw new IllegalStateException("Duplicate end state: " + transition.to.name); + } + + final boolean conflictingStartState = + transitionsForContext.stream().anyMatch(t -> t.from.stream().anyMatch(transition.from::contains)); + + if (conflictingStartState) + { + throw new IllegalStateException("Conflicting start states: " + transition.from); + } + + transitionsForContext.add(transition); + } + } +} diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java index e40a02c6a1..ddb1d88006 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java @@ -15,25 +15,20 @@ */ package uk.co.real_logic.sbe.generation.java; +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.*; import org.agrona.DirectBuffer; import org.agrona.MutableDirectBuffer; import org.agrona.Strings; import org.agrona.Verify; import org.agrona.generation.DynamicPackageOutputManager; import org.agrona.sbe.*; -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.*; import java.io.IOException; import java.io.Writer; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Formatter; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; import java.util.function.Function; import static uk.co.real_logic.sbe.SbeTool.JAVA_INTERFACE_PACKAGE; @@ -255,12 +250,15 @@ private void generateEncoder( generateAnnotations(BASE_INDENT, className, groups, out, this::encoderName); } out.append(generateDeclaration(className, implementsString, msgToken)); - out.append(generateEncoderFlyweightCode(className, msgToken)); + final FieldOrderModel fieldOrderModel = new FieldOrderModel(); + fieldOrderModel.findTransitions(fields, groups, varData); + out.append(generateFieldOrderStates(fieldOrderModel)); + out.append(generateEncoderFlyweightCode(className, fieldOrderModel, msgToken)); final StringBuilder sb = new StringBuilder(); - generateEncoderFields(sb, className, fields, BASE_INDENT); - generateEncoderGroups(sb, className, groups, BASE_INDENT, false); - generateEncoderVarData(sb, className, varData, BASE_INDENT); + generateEncoderFields(sb, className, fieldOrderModel, fields, BASE_INDENT); + generateEncoderGroups(sb, className, fieldOrderModel, groups, BASE_INDENT, false); + generateEncoderVarData(sb, className, fieldOrderModel, varData, BASE_INDENT); generateEncoderDisplay(sb, decoderName(msgToken.name())); @@ -269,6 +267,171 @@ private void generateEncoder( } } + private CharSequence stateFieldName(final FieldOrderModel.State state) + { + return "ACCESS_STATE_" + state.name(); + } + + private CharSequence generateFieldOrderStates(final FieldOrderModel fieldOrderModel) + { + final StringBuilder sb = new StringBuilder(); + + sb.append(" private static final boolean DEBUG_MODE = ") + .append("!Boolean.getBoolean(\"agrona.disable.bounds.checks\");\n\n"); + + fieldOrderModel.forEachState(state -> + { + sb.append(" private static final int ").append(stateFieldName(state)).append(" = ") + .append(state.number()).append(";\n\n"); + }); + + sb.append(" private int fieldOrderState = ") + .append(stateFieldName(fieldOrderModel.notWrappedState())) + .append(";\n\n"); + + sb.append(" private int fieldOrderState()\n") + .append(" {\n") + .append(" return fieldOrderState;\n") + .append(" }\n\n"); + + sb.append(" private void fieldOrderState(int newState)\n") + .append(" {\n") + .append(" fieldOrderState = newState;\n") + .append(" }\n\n"); + + return sb; + } + + private CharSequence generateFieldOrderStateTransitions( + final FieldOrderModel fieldOrderModel, + final String indent, + final Token token) + { + if (fieldOrderModel == null) + { + return ""; + } + + final StringBuilder sb = new StringBuilder(); + + sb.append(indent).append("if (DEBUG_MODE)\n") + .append(indent).append("{\n"); + + generateFieldOrderStateTransitions( + sb, + indent + " ", + fieldOrderModel, + FieldOrderModel.TransitionContext.NONE, + token); + + sb.append(indent).append("}\n\n"); + + return sb; + } + + private CharSequence generateFieldOrderStateTransitions( + final FieldOrderModel fieldOrderModel, + final String indent, + final Token token, + final String remainingExpression) + { + final StringBuilder sb = new StringBuilder(); + + sb.append(indent).append("if (DEBUG_MODE)\n") + .append(indent).append("{\n") + .append(indent).append(" final int remaining = ").append(remainingExpression).append(";\n") + .append(indent).append(" if (remaining == 0)\n") + .append(indent).append(" {\n"); + + generateFieldOrderStateTransitions( + sb, + indent + " ", + fieldOrderModel, + FieldOrderModel.TransitionContext.SELECT_EMPTY_GROUP, + token); + + sb.append(indent).append(" }\n") + .append(indent).append(" else\n") + .append(indent).append(" {\n"); + + generateFieldOrderStateTransitions( + sb, + indent + " ", + fieldOrderModel, + FieldOrderModel.TransitionContext.SELECT_MULTI_ELEMENT_GROUP, + token); + + sb.append(indent).append(" }\n") + .append(indent).append("}\n\n"); + + return sb; + } + + private void generateFieldOrderStateTransitions( + final StringBuilder sb, + final String indent, + final FieldOrderModel fieldOrderModel, + final FieldOrderModel.TransitionContext context, + final Token token) + { + sb.append(indent).append("switch (fieldOrderState())\n") + .append(indent).append("{\n"); + + final List transitions = fieldOrderModel.getTransitions(context, token); + + transitions.forEach(transition -> + { + transition.forEachStartState(startState -> + sb.append(indent).append(" case ").append(stateFieldName(startState)).append(":\n")); + sb.append(indent).append(" fieldOrderState(") + .append(stateFieldName(transition.endState())).append(");\n") + .append(indent).append(" break;\n"); + }); + + sb.append(indent).append(" default:\n") + .append(indent).append(" throw new IllegalStateException(") + .append("\"Unexpected state: \" + fieldOrderState());\n") + .append(indent).append("}\n"); + } + + private CharSequence generateFieldOrderStateTransitionsForNextGroupElement( + final FieldOrderModel fieldOrderModel, + final String indent, + final Token token, + final String remainingExpression) + { + final StringBuilder sb = new StringBuilder(); + + sb.append(indent).append("if (DEBUG_MODE)\n") + .append(indent).append("{\n") + .append(indent).append(" final int remaining = ").append(remainingExpression).append(";\n") + .append(indent).append(" if (remaining > 1)\n") + .append(indent).append(" {\n"); + + generateFieldOrderStateTransitions( + sb, + indent + " ", + fieldOrderModel, + FieldOrderModel.TransitionContext.NEXT_ELEMENT_IN_GROUP, + token); + + sb.append(indent).append(" }\n") + .append(indent).append(" else if (remaining == 1)\n") + .append(indent).append(" {\n"); + + generateFieldOrderStateTransitions( + sb, + indent + " ", + fieldOrderModel, + FieldOrderModel.TransitionContext.LAST_ELEMENT_IN_GROUP, + token); + + sb.append(indent).append(" }\n") + .append(indent).append("}\n\n"); + + return sb; + } + private void generateDecoder( final Token msgToken, final List fields, @@ -358,6 +521,7 @@ private void generateDecoderGroups( private void generateEncoderGroups( final StringBuilder sb, final String outerClassName, + final FieldOrderModel fieldOrderModel, final List tokens, final String indent, final boolean isSubGroup) throws IOException @@ -387,18 +551,19 @@ private void generateEncoderGroups( final List varData = new ArrayList<>(); i = collectVarData(tokens, i, varData); - generateGroupEncoderProperty(sb, groupName, groupToken, indent, isSubGroup); + generateGroupEncoderProperty(sb, groupName, fieldOrderModel, groupToken, indent, isSubGroup); generateTypeJavadoc(sb, indent + INDENT, groupToken); if (shouldGenerateGroupOrderAnnotation) { generateAnnotations(indent + INDENT, groupClassName, groups, sb, this::encoderName); } - generateGroupEncoderClassHeader(sb, groupName, outerClassName, tokens, groups, index, indent + INDENT); + generateGroupEncoderClassHeader( + sb, groupName, outerClassName, fieldOrderModel, groupToken, tokens, groups, index, indent + INDENT); - generateEncoderFields(sb, groupClassName, fields, indent + INDENT); - generateEncoderGroups(sb, outerClassName, groups, indent + INDENT, true); - generateEncoderVarData(sb, groupClassName, varData, indent + INDENT); + generateEncoderFields(sb, groupClassName, fieldOrderModel, fields, indent + INDENT); + generateEncoderGroups(sb, outerClassName, fieldOrderModel, groups, indent + INDENT, true); + generateEncoderVarData(sb, groupClassName, fieldOrderModel, varData, indent + INDENT); sb.append(indent).append(" }\n"); } @@ -514,6 +679,8 @@ private void generateGroupEncoderClassHeader( final StringBuilder sb, final String groupName, final String parentMessageClassName, + final FieldOrderModel fieldOrderModel, + final Token groupToken, final List tokens, final List subGroupTokens, final int index, @@ -577,6 +744,8 @@ private void generateGroupEncoderClassHeader( sb.append("\n") .append(ind).append(" public ").append(encoderName(groupName)).append(" next()\n") .append(ind).append(" {\n") + .append(generateFieldOrderStateTransitionsForNextGroupElement( + fieldOrderModel, ind + " ", groupToken, "count - index")) .append(ind).append(" if (index >= count)\n") .append(ind).append(" {\n") .append(ind).append(" throw new java.util.NoSuchElementException();\n") @@ -618,6 +787,18 @@ private void generateGroupEncoderClassHeader( .append(ind).append(" {\n") .append(ind).append(" return ").append(blockLength).append(";\n") .append(ind).append(" }\n"); + + sb.append("\n") + .append(ind).append(" private int fieldOrderState()\n") + .append(ind).append(" {\n") + .append(ind).append(" return parentMessage.fieldOrderState();\n") + .append(ind).append(" }\n"); + + sb.append("\n") + .append(ind).append(" private void fieldOrderState(final int newState)\n") + .append(ind).append(" {\n") + .append(ind).append(" parentMessage.fieldOrderState(newState);\n") + .append(ind).append(" }\n"); } private static String primitiveTypeName(final Token token) @@ -788,6 +969,7 @@ private static void generateGroupDecoderProperty( private void generateGroupEncoderProperty( final StringBuilder sb, final String groupName, + final FieldOrderModel fieldOrderModel, final Token token, final String indent, final boolean isSubGroup) @@ -816,6 +998,7 @@ private void generateGroupEncoderProperty( new Formatter(sb).format("\n" + indent + " public %1$s %2$sCount(final int count)\n" + indent + " {\n" + + generateFieldOrderStateTransitions(fieldOrderModel, indent + " ", token, "count") + indent + " %2$s.wrap(buffer, count);\n" + indent + " return %2$s;\n" + indent + " }\n", @@ -872,7 +1055,11 @@ private void generateDecoderVarData( } private void generateEncoderVarData( - final StringBuilder sb, final String className, final List tokens, final String indent) + final StringBuilder sb, + final String className, + final FieldOrderModel fieldOrderModel, + final List tokens, + final String indent) { for (int i = 0, size = tokens.size(); i < size;) { @@ -906,6 +1093,8 @@ private void generateEncoderVarData( generateDataEncodeMethods( sb, propertyName, + fieldOrderModel, + token, sizeOfLengthField, maxLengthValue, lengthEncoding.primitiveType(), @@ -1045,6 +1234,8 @@ private void generateVarDataWrapDecoder( private void generateDataEncodeMethods( final StringBuilder sb, final String propertyName, + final FieldOrderModel fieldOrderModel, + final Token fieldToken, final int sizeOfLengthField, final int maxLengthValue, final PrimitiveType lengthType, @@ -1057,6 +1248,8 @@ private void generateDataEncodeMethods( sb, className, propertyName, + fieldOrderModel, + fieldToken, sizeOfLengthField, maxLengthValue, readOnlyBuffer, @@ -1068,6 +1261,8 @@ private void generateDataEncodeMethods( sb, className, propertyName, + fieldOrderModel, + fieldToken, sizeOfLengthField, maxLengthValue, "byte[]", @@ -1080,6 +1275,8 @@ private void generateDataEncodeMethods( generateCharArrayEncodeMethods( sb, propertyName, + fieldOrderModel, + fieldToken, sizeOfLengthField, maxLengthValue, lengthType, @@ -1093,6 +1290,8 @@ private void generateDataEncodeMethods( private void generateCharArrayEncodeMethods( final StringBuilder sb, final String propertyName, + final FieldOrderModel fieldOrderModel, + final Token fieldToken, final int sizeOfLengthField, final int maxLengthValue, final PrimitiveType lengthType, @@ -1113,6 +1312,7 @@ private void generateCharArrayEncodeMethods( indent + " {\n" + indent + " throw new IllegalStateException(\"length > maxValue for type: \" + length);\n" + indent + " }\n\n" + + generateFieldOrderStateTransitions(fieldOrderModel, indent + " ", fieldToken) + indent + " final int headerLength = %4$d;\n" + indent + " final int limit = parentMessage.limit();\n" + indent + " parentMessage.limit(limit + headerLength + length);\n" + @@ -1134,6 +1334,7 @@ private void generateCharArrayEncodeMethods( indent + " {\n" + indent + " throw new IllegalStateException(\"length > maxValue for type: \" + length);\n" + indent + " }\n\n" + + generateFieldOrderStateTransitions(fieldOrderModel, indent + " ", fieldToken) + indent + " final int headerLength = %4$d;\n" + indent + " final int limit = parentMessage.limit();\n" + indent + " parentMessage.limit(limit + headerLength + length);\n" + @@ -1159,6 +1360,7 @@ private void generateCharArrayEncodeMethods( indent + " {\n" + indent + " throw new IllegalStateException(\"length > maxValue for type: \" + length);\n" + indent + " }\n\n" + + generateFieldOrderStateTransitions(fieldOrderModel, indent + " ", fieldToken) + indent + " final int headerLength = %5$d;\n" + indent + " final int limit = parentMessage.limit();\n" + indent + " parentMessage.limit(limit + headerLength + length);\n" + @@ -1209,6 +1411,8 @@ private void generateDataTypedEncoder( final StringBuilder sb, final String className, final String propertyName, + final FieldOrderModel fieldOrderModel, + final Token fieldToken, final int sizeOfLengthField, final int maxLengthValue, final String exchangeType, @@ -1225,6 +1429,7 @@ private void generateDataTypedEncoder( indent + " {\n" + indent + " throw new IllegalStateException(\"length > maxValue for type: \" + length);\n" + indent + " }\n\n" + + generateFieldOrderStateTransitions(fieldOrderModel, indent + " ", fieldToken) + indent + " final int headerLength = %5$d;\n" + indent + " final int limit = parentMessage.limit();\n" + indent + " parentMessage.limit(limit + headerLength + length);\n" + @@ -1373,12 +1578,12 @@ private void generateComposite(final List tokens) throws IOException case BEGIN_SET: generateBitSetProperty( - sb, true, DECODER, propertyName, encodingToken, encodingToken, BASE_INDENT, typeName); + sb, true, DECODER, propertyName, null, encodingToken, encodingToken, BASE_INDENT, typeName); break; case BEGIN_COMPOSITE: generateCompositeProperty( - sb, true, DECODER, propertyName, encodingToken, encodingToken, BASE_INDENT, typeName); + sb, true, DECODER, propertyName, null, encodingToken, encodingToken, BASE_INDENT, typeName); break; default: @@ -1414,21 +1619,23 @@ private void generateComposite(final List tokens) throws IOException switch (encodingToken.signal()) { case ENCODING: - generatePrimitiveEncoder(sb, encoderName, encodingToken.name(), encodingToken, BASE_INDENT); + generatePrimitiveEncoder(sb, encoderName, encodingToken.name(), + null, null, encodingToken, BASE_INDENT); break; case BEGIN_ENUM: - generateEnumEncoder(sb, encoderName, encodingToken, propertyName, encodingToken, BASE_INDENT); + generateEnumEncoder(sb, encoderName, null, + encodingToken, propertyName, encodingToken, BASE_INDENT); break; case BEGIN_SET: generateBitSetProperty( - sb, true, ENCODER, propertyName, encodingToken, encodingToken, BASE_INDENT, typeName); + sb, true, ENCODER, propertyName, null, encodingToken, encodingToken, BASE_INDENT, typeName); break; case BEGIN_COMPOSITE: generateCompositeProperty( - sb, true, ENCODER, propertyName, encodingToken, encodingToken, BASE_INDENT, typeName); + sb, true, ENCODER, propertyName, null, encodingToken, encodingToken, BASE_INDENT, typeName); break; default: @@ -1878,21 +2085,23 @@ private void generatePrimitiveEncoder( final StringBuilder sb, final String containingClassName, final String propertyName, - final Token token, + final FieldOrderModel fieldOrderModel, + final Token fieldToken, + final Token typeToken, final String indent) { final String formattedPropertyName = formatPropertyName(propertyName); - generatePrimitiveFieldMetaMethod(sb, formattedPropertyName, token, indent); + generatePrimitiveFieldMetaMethod(sb, formattedPropertyName, typeToken, indent); - if (!token.isConstantEncoding()) + if (!typeToken.isConstantEncoding()) { sb.append(generatePrimitivePropertyEncodeMethods( - containingClassName, formattedPropertyName, token, indent)); + containingClassName, formattedPropertyName, fieldOrderModel, fieldToken, typeToken, indent)); } else { - generateConstPropertyMethods(sb, formattedPropertyName, token, indent); + generateConstPropertyMethods(sb, formattedPropertyName, typeToken, indent); } } @@ -1910,11 +2119,18 @@ private CharSequence generatePrimitivePropertyDecodeMethods( } private CharSequence generatePrimitivePropertyEncodeMethods( - final String containingClassName, final String propertyName, final Token token, final String indent) + final String containingClassName, + final String propertyName, + final FieldOrderModel fieldOrderModel, + final Token fieldToken, + final Token typeToken, + final String indent) { - return token.matchOnLength( - () -> generatePrimitivePropertyEncode(containingClassName, propertyName, token, indent), - () -> generatePrimitiveArrayPropertyEncode(containingClassName, propertyName, token, indent)); + return typeToken.matchOnLength( + () -> generatePrimitivePropertyEncode( + containingClassName, propertyName, fieldOrderModel, fieldToken, typeToken, indent), + () -> generatePrimitiveArrayPropertyEncode( + containingClassName, propertyName, fieldOrderModel, fieldToken, typeToken, indent)); } private void generatePrimitiveFieldMetaMethod( @@ -1977,17 +2193,23 @@ private CharSequence generatePrimitivePropertyDecode( } private CharSequence generatePrimitivePropertyEncode( - final String containingClassName, final String propertyName, final Token token, final String indent) + final String containingClassName, + final String propertyName, + final FieldOrderModel fieldOrderModel, + final Token fieldToken, + final Token typeToken, + final String indent) { - final Encoding encoding = token.encoding(); + final Encoding encoding = typeToken.encoding(); final String javaTypeName = javaTypeName(encoding.primitiveType()); - final int offset = token.offset(); + final int offset = typeToken.offset(); final String byteOrderStr = byteOrderString(encoding); return String.format( "\n" + indent + " public %s %s(final %s value)\n" + indent + " {\n" + + generateFieldOrderStateTransitions(fieldOrderModel, indent + " ", fieldToken) + indent + " %s;\n" + indent + " return this;\n" + indent + " }\n\n", @@ -2260,14 +2482,19 @@ private String byteOrderString(final Encoding encoding) } private CharSequence generatePrimitiveArrayPropertyEncode( - final String containingClassName, final String propertyName, final Token token, final String indent) + final String containingClassName, + final String propertyName, + final FieldOrderModel fieldOrderModel, + final Token fieldToken, + final Token typeToken, + final String indent) { - final Encoding encoding = token.encoding(); + final Encoding encoding = typeToken.encoding(); final PrimitiveType primitiveType = encoding.primitiveType(); final String javaTypeName = javaTypeName(primitiveType); - final int offset = token.offset(); + final int offset = typeToken.offset(); final String byteOrderStr = byteOrderString(encoding); - final int arrayLength = token.arrayLength(); + final int arrayLength = typeToken.arrayLength(); final int typeSize = sizeOfPrimitive(encoding); final StringBuilder sb = new StringBuilder(); @@ -2278,6 +2505,7 @@ private CharSequence generatePrimitiveArrayPropertyEncode( new Formatter(sb).format("\n" + indent + " public %s %s(final int index, final %s value)\n" + indent + " {\n" + + generateFieldOrderStateTransitions(fieldOrderModel, indent + " ", fieldToken) + indent + " if (index < 0 || index >= %d)\n" + indent + " {\n" + indent + " throw new IndexOutOfBoundsException(\"index out of range: index=\" + index);\n" + @@ -2310,6 +2538,8 @@ private CharSequence generatePrimitiveArrayPropertyEncode( sb.append(")\n"); sb.append(indent).append(" {\n"); + sb.append(generateFieldOrderStateTransitions(fieldOrderModel, indent + " ", fieldToken)); + for (int i = 0; i < arrayLength; i++) { final String indexStr = "offset + " + (offset + (typeSize * i)); @@ -2327,12 +2557,27 @@ private CharSequence generatePrimitiveArrayPropertyEncode( if (primitiveType == PrimitiveType.CHAR) { generateCharArrayEncodeMethods( - containingClassName, propertyName, indent, encoding, offset, arrayLength, sb); + containingClassName, + propertyName, + indent, + fieldOrderModel, + fieldToken, + encoding, + offset, + arrayLength, + sb); } else if (primitiveType == PrimitiveType.UINT8) { generateByteArrayEncodeMethods( - containingClassName, propertyName, indent, offset, arrayLength, sb); + containingClassName, + propertyName, + indent, + fieldOrderModel, + fieldToken, + offset, + arrayLength, + sb); } return sb; @@ -2342,6 +2587,8 @@ private void generateCharArrayEncodeMethods( final String containingClassName, final String propertyName, final String indent, + final FieldOrderModel fieldOrderModel, + final Token fieldToken, final Encoding encoding, final int offset, final int fieldLength, @@ -2352,6 +2599,7 @@ private void generateCharArrayEncodeMethods( new Formatter(sb).format("\n" + indent + " public %s put%s(final byte[] src, final int srcOffset)\n" + indent + " {\n" + + generateFieldOrderStateTransitions(fieldOrderModel, indent + " ", fieldToken) + indent + " final int length = %d;\n" + indent + " if (srcOffset < 0 || srcOffset > (src.length - length))\n" + indent + " {\n" + @@ -2371,6 +2619,7 @@ private void generateCharArrayEncodeMethods( new Formatter(sb).format("\n" + indent + " public %1$s %2$s(final String src)\n" + indent + " {\n" + + generateFieldOrderStateTransitions(fieldOrderModel, indent + " ", fieldToken) + indent + " final int length = %3$d;\n" + indent + " final int srcLength = null == src ? 0 : src.length();\n" + indent + " if (srcLength > length)\n" + @@ -2393,6 +2642,7 @@ private void generateCharArrayEncodeMethods( new Formatter(sb).format("\n" + indent + " public %1$s %2$s(final CharSequence src)\n" + indent + " {\n" + + generateFieldOrderStateTransitions(fieldOrderModel, indent + " ", fieldToken) + indent + " final int length = %3$d;\n" + indent + " final int srcLength = null == src ? 0 : src.length();\n" + indent + " if (srcLength > length)\n" + @@ -2417,6 +2667,7 @@ private void generateCharArrayEncodeMethods( new Formatter(sb).format("\n" + indent + " public %s %s(final String src)\n" + indent + " {\n" + + generateFieldOrderStateTransitions(fieldOrderModel, indent + " ", fieldToken) + indent + " final int length = %d;\n" + indent + " final byte[] bytes = (null == src || src.isEmpty()) ?" + " org.agrona.collections.ArrayUtil.EMPTY_BYTE_ARRAY : src.getBytes(%s);\n" + @@ -2445,6 +2696,8 @@ private void generateByteArrayEncodeMethods( final String containingClassName, final String propertyName, final String indent, + final FieldOrderModel fieldOrderModel, + final Token fieldToken, final int offset, final int fieldLength, final StringBuilder sb) @@ -2452,6 +2705,7 @@ private void generateByteArrayEncodeMethods( new Formatter(sb).format("\n" + indent + " public %s put%s(final byte[] src, final int srcOffset, final int length)\n" + indent + " {\n" + + generateFieldOrderStateTransitions(fieldOrderModel, indent + " ", fieldToken) + indent + " if (length > %d)\n" + indent + " {\n" + indent + " throw new IllegalStateException(" + @@ -2474,6 +2728,7 @@ private void generateByteArrayEncodeMethods( new Formatter(sb).format("\n" + indent + " public %s put%s(final %s src, final int srcOffset, final int length)\n" + indent + " {\n" + + generateFieldOrderStateTransitions(fieldOrderModel, indent + " ", fieldToken) + indent + " if (length > %d)\n" + indent + " {\n" + indent + " throw new IllegalStateException(" + @@ -2829,7 +3084,10 @@ private CharSequence generateFlyweightCode( semanticVersion); } - private CharSequence generateEncoderFlyweightCode(final String className, final Token token) + private CharSequence generateEncoderFlyweightCode( + final String className, + final FieldOrderModel fieldOrderModel, + final Token token) { final String wrapMethod = " public " + className + " wrap(final " + mutableBuffer + " buffer, final int offset)\n" + @@ -2841,6 +3099,10 @@ private CharSequence generateEncoderFlyweightCode(final String className, final " this.initialOffset = offset;\n" + " this.offset = offset;\n" + " limit(offset + BLOCK_LENGTH);\n\n" + + " if (DEBUG_MODE)\n" + + " {\n" + + " fieldOrderState(" + stateFieldName(fieldOrderModel.wrappedState()) + ");\n" + + " }\n\n" + " return this;\n" + " }\n\n"; @@ -2889,7 +3151,11 @@ private CharSequence generateEncoderFlyweightCode(final String className, final } private void generateEncoderFields( - final StringBuilder sb, final String containingClassName, final List tokens, final String indent) + final StringBuilder sb, + final String containingClassName, + final FieldOrderModel fieldOrderModel, + final List tokens, + final String indent) { Generators.forEachField( tokens, @@ -2907,21 +3173,23 @@ private void generateEncoderFields( switch (typeToken.signal()) { case ENCODING: - generatePrimitiveEncoder(sb, containingClassName, propertyName, typeToken, indent); + generatePrimitiveEncoder(sb, containingClassName, propertyName, + fieldOrderModel, fieldToken, typeToken, indent); break; case BEGIN_ENUM: - generateEnumEncoder(sb, containingClassName, fieldToken, propertyName, typeToken, indent); + generateEnumEncoder(sb, containingClassName, + fieldOrderModel, fieldToken, propertyName, typeToken, indent); break; case BEGIN_SET: generateBitSetProperty( - sb, false, ENCODER, propertyName, fieldToken, typeToken, indent, typeName); + sb, false, ENCODER, propertyName, fieldOrderModel, fieldToken, typeToken, indent, typeName); break; case BEGIN_COMPOSITE: generateCompositeProperty( - sb, false, ENCODER, propertyName, fieldToken, typeToken, indent, typeName); + sb, false, ENCODER, propertyName, fieldOrderModel, fieldToken, typeToken, indent, typeName); break; default: @@ -2957,12 +3225,12 @@ private void generateDecoderFields(final StringBuilder sb, final List tok case BEGIN_SET: generateBitSetProperty( - sb, false, DECODER, propertyName, fieldToken, typeToken, indent, typeName); + sb, false, DECODER, propertyName, /* TODO */ null, fieldToken, typeToken, indent, typeName); break; case BEGIN_COMPOSITE: generateCompositeProperty( - sb, false, DECODER, propertyName, fieldToken, typeToken, indent, typeName); + sb, false, DECODER, propertyName, /* TODO */ null, fieldToken, typeToken, indent, typeName); break; default: @@ -3131,6 +3399,7 @@ private void generateEnumDecoder( private void generateEnumEncoder( final StringBuilder sb, final String containingClassName, + final FieldOrderModel fieldOrderModel, final Token fieldToken, final String propertyName, final Token typeToken, @@ -3146,6 +3415,7 @@ private void generateEnumEncoder( new Formatter(sb).format("\n" + indent + " public %s %s(final %s value)\n" + indent + " {\n" + + generateFieldOrderStateTransitions(fieldOrderModel, indent + " ", fieldToken) + indent + " %s;\n" + indent + " return this;\n" + indent + " }\n", @@ -3161,6 +3431,7 @@ private void generateBitSetProperty( final boolean inComposite, final CodecType codecType, final String propertyName, + final FieldOrderModel fieldOrderModel, final Token propertyToken, final Token bitsetToken, final String indent, @@ -3176,6 +3447,7 @@ private void generateBitSetProperty( new Formatter(sb).format("\n" + indent + " public %s %s()\n" + indent + " {\n" + + generateFieldOrderStateTransitions(fieldOrderModel, indent + " ", propertyToken) + "%s" + indent + " %s.wrap(buffer, offset + %d);\n" + indent + " return %s;\n" + @@ -3193,6 +3465,7 @@ private void generateCompositeProperty( final boolean inComposite, final CodecType codecType, final String propertyName, + final FieldOrderModel fieldOrderModel, final Token propertyToken, final Token compositeToken, final String indent, @@ -3208,6 +3481,7 @@ private void generateCompositeProperty( new Formatter(sb).format("\n" + indent + " public %s %s()\n" + indent + " {\n" + + generateFieldOrderStateTransitions(fieldOrderModel, indent + " ", propertyToken) + "%s" + indent + " %s.wrap(buffer, offset + %d);\n" + indent + " return %s;\n" + diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java index 0fd115d556..91bde3796e 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java @@ -11,6 +11,30 @@ @SuppressWarnings("all") public final class FrameCodecEncoder { + private static final boolean DEBUG_MODE = !Boolean.getBoolean("agrona.disable.bounds.checks"); + + private static final int ACCESS_STATE_NOT_WRAPPED = 1; + + private static final int ACCESS_STATE_WRAPPED = 2; + + private static final int ACCESS_STATE_FILLED__PACKAGENAME = 3; + + private static final int ACCESS_STATE_FILLED__NAMESPACENAME = 4; + + private static final int ACCESS_STATE_FILLED__SEMANTICVERSION = 5; + + private int fieldOrderState = ACCESS_STATE_NOT_WRAPPED; + + private int fieldOrderState() + { + return fieldOrderState; + } + + private void fieldOrderState(int newState) + { + fieldOrderState = newState; + } + public static final int BLOCK_LENGTH = 12; public static final int TEMPLATE_ID = 1; public static final int SCHEMA_ID = 1; @@ -74,6 +98,11 @@ public FrameCodecEncoder wrap(final MutableDirectBuffer buffer, final int offset this.offset = offset; limit(offset + BLOCK_LENGTH); + if (DEBUG_MODE) + { + fieldOrderState(ACCESS_STATE_WRAPPED); + } + return this; } @@ -152,6 +181,18 @@ public static int irIdMaxValue() public FrameCodecEncoder irId(final int value) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_WRAPPED: + fieldOrderState(ACCESS_STATE_WRAPPED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + buffer.putInt(offset + 0, value, java.nio.ByteOrder.LITTLE_ENDIAN); return this; } @@ -204,6 +245,18 @@ public static int irVersionMaxValue() public FrameCodecEncoder irVersion(final int value) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_WRAPPED: + fieldOrderState(ACCESS_STATE_WRAPPED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + buffer.putInt(offset + 4, value, java.nio.ByteOrder.LITTLE_ENDIAN); return this; } @@ -256,6 +309,18 @@ public static int schemaVersionMaxValue() public FrameCodecEncoder schemaVersion(final int value) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_WRAPPED: + fieldOrderState(ACCESS_STATE_WRAPPED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + buffer.putInt(offset + 8, value, java.nio.ByteOrder.LITTLE_ENDIAN); return this; } @@ -293,6 +358,18 @@ public FrameCodecEncoder putPackageName(final DirectBuffer src, final int srcOff throw new IllegalStateException("length > maxValue for type: " + length); } + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_WRAPPED: + fieldOrderState(ACCESS_STATE_FILLED__PACKAGENAME); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); parentMessage.limit(limit + headerLength + length); @@ -309,6 +386,18 @@ public FrameCodecEncoder putPackageName(final byte[] src, final int srcOffset, f throw new IllegalStateException("length > maxValue for type: " + length); } + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_WRAPPED: + fieldOrderState(ACCESS_STATE_FILLED__PACKAGENAME); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); parentMessage.limit(limit + headerLength + length); @@ -328,6 +417,18 @@ public FrameCodecEncoder packageName(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_WRAPPED: + fieldOrderState(ACCESS_STATE_FILLED__PACKAGENAME); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); parentMessage.limit(limit + headerLength + length); @@ -369,6 +470,18 @@ public FrameCodecEncoder putNamespaceName(final DirectBuffer src, final int srcO throw new IllegalStateException("length > maxValue for type: " + length); } + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_FILLED__PACKAGENAME: + fieldOrderState(ACCESS_STATE_FILLED__NAMESPACENAME); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); parentMessage.limit(limit + headerLength + length); @@ -385,6 +498,18 @@ public FrameCodecEncoder putNamespaceName(final byte[] src, final int srcOffset, throw new IllegalStateException("length > maxValue for type: " + length); } + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_FILLED__PACKAGENAME: + fieldOrderState(ACCESS_STATE_FILLED__NAMESPACENAME); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); parentMessage.limit(limit + headerLength + length); @@ -404,6 +529,18 @@ public FrameCodecEncoder namespaceName(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_FILLED__PACKAGENAME: + fieldOrderState(ACCESS_STATE_FILLED__NAMESPACENAME); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); parentMessage.limit(limit + headerLength + length); @@ -445,6 +582,18 @@ public FrameCodecEncoder putSemanticVersion(final DirectBuffer src, final int sr throw new IllegalStateException("length > maxValue for type: " + length); } + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_FILLED__NAMESPACENAME: + fieldOrderState(ACCESS_STATE_FILLED__SEMANTICVERSION); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); parentMessage.limit(limit + headerLength + length); @@ -461,6 +610,18 @@ public FrameCodecEncoder putSemanticVersion(final byte[] src, final int srcOffse throw new IllegalStateException("length > maxValue for type: " + length); } + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_FILLED__NAMESPACENAME: + fieldOrderState(ACCESS_STATE_FILLED__SEMANTICVERSION); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); parentMessage.limit(limit + headerLength + length); @@ -480,6 +641,18 @@ public FrameCodecEncoder semanticVersion(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_FILLED__NAMESPACENAME: + fieldOrderState(ACCESS_STATE_FILLED__SEMANTICVERSION); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); parentMessage.limit(limit + headerLength + length); diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java index 793f10756f..1a01973ae7 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java @@ -11,6 +11,46 @@ @SuppressWarnings("all") public final class TokenCodecEncoder { + private static final boolean DEBUG_MODE = !Boolean.getBoolean("agrona.disable.bounds.checks"); + + private static final int ACCESS_STATE_NOT_WRAPPED = 1; + + private static final int ACCESS_STATE_WRAPPED = 2; + + private static final int ACCESS_STATE_FILLED__NAME = 3; + + private static final int ACCESS_STATE_FILLED__CONSTVALUE = 4; + + private static final int ACCESS_STATE_FILLED__MINVALUE = 5; + + private static final int ACCESS_STATE_FILLED__MAXVALUE = 6; + + private static final int ACCESS_STATE_FILLED__NULLVALUE = 7; + + private static final int ACCESS_STATE_FILLED__CHARACTERENCODING = 8; + + private static final int ACCESS_STATE_FILLED__EPOCH = 9; + + private static final int ACCESS_STATE_FILLED__TIMEUNIT = 10; + + private static final int ACCESS_STATE_FILLED__SEMANTICTYPE = 11; + + private static final int ACCESS_STATE_FILLED__DESCRIPTION = 12; + + private static final int ACCESS_STATE_FILLED__REFERENCEDNAME = 13; + + private int fieldOrderState = ACCESS_STATE_NOT_WRAPPED; + + private int fieldOrderState() + { + return fieldOrderState; + } + + private void fieldOrderState(int newState) + { + fieldOrderState = newState; + } + public static final int BLOCK_LENGTH = 28; public static final int TEMPLATE_ID = 2; public static final int SCHEMA_ID = 1; @@ -74,6 +114,11 @@ public TokenCodecEncoder wrap(final MutableDirectBuffer buffer, final int offset this.offset = offset; limit(offset + BLOCK_LENGTH); + if (DEBUG_MODE) + { + fieldOrderState(ACCESS_STATE_WRAPPED); + } + return this; } @@ -152,6 +197,18 @@ public static int tokenOffsetMaxValue() public TokenCodecEncoder tokenOffset(final int value) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_WRAPPED: + fieldOrderState(ACCESS_STATE_WRAPPED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + buffer.putInt(offset + 0, value, java.nio.ByteOrder.LITTLE_ENDIAN); return this; } @@ -204,6 +261,18 @@ public static int tokenSizeMaxValue() public TokenCodecEncoder tokenSize(final int value) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_WRAPPED: + fieldOrderState(ACCESS_STATE_WRAPPED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + buffer.putInt(offset + 4, value, java.nio.ByteOrder.LITTLE_ENDIAN); return this; } @@ -256,6 +325,18 @@ public static int fieldIdMaxValue() public TokenCodecEncoder fieldId(final int value) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_WRAPPED: + fieldOrderState(ACCESS_STATE_WRAPPED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + buffer.putInt(offset + 8, value, java.nio.ByteOrder.LITTLE_ENDIAN); return this; } @@ -308,6 +389,18 @@ public static int tokenVersionMaxValue() public TokenCodecEncoder tokenVersion(final int value) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_WRAPPED: + fieldOrderState(ACCESS_STATE_WRAPPED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + buffer.putInt(offset + 12, value, java.nio.ByteOrder.LITTLE_ENDIAN); return this; } @@ -360,6 +453,18 @@ public static int componentTokenCountMaxValue() public TokenCodecEncoder componentTokenCount(final int value) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_WRAPPED: + fieldOrderState(ACCESS_STATE_WRAPPED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + buffer.putInt(offset + 16, value, java.nio.ByteOrder.LITTLE_ENDIAN); return this; } @@ -397,6 +502,18 @@ public static String signalMetaAttribute(final MetaAttribute metaAttribute) public TokenCodecEncoder signal(final SignalCodec value) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_WRAPPED: + fieldOrderState(ACCESS_STATE_WRAPPED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + buffer.putByte(offset + 20, (byte)value.value()); return this; } @@ -433,6 +550,18 @@ public static String primitiveTypeMetaAttribute(final MetaAttribute metaAttribut public TokenCodecEncoder primitiveType(final PrimitiveTypeCodec value) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_WRAPPED: + fieldOrderState(ACCESS_STATE_WRAPPED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + buffer.putByte(offset + 21, (byte)value.value()); return this; } @@ -469,6 +598,18 @@ public static String byteOrderMetaAttribute(final MetaAttribute metaAttribute) public TokenCodecEncoder byteOrder(final ByteOrderCodec value) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_WRAPPED: + fieldOrderState(ACCESS_STATE_WRAPPED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + buffer.putByte(offset + 22, (byte)value.value()); return this; } @@ -505,6 +646,18 @@ public static String presenceMetaAttribute(final MetaAttribute metaAttribute) public TokenCodecEncoder presence(final PresenceCodec value) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_WRAPPED: + fieldOrderState(ACCESS_STATE_WRAPPED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + buffer.putByte(offset + 23, (byte)value.value()); return this; } @@ -556,6 +709,18 @@ public static int deprecatedMaxValue() public TokenCodecEncoder deprecated(final int value) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_WRAPPED: + fieldOrderState(ACCESS_STATE_WRAPPED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + buffer.putInt(offset + 24, value, java.nio.ByteOrder.LITTLE_ENDIAN); return this; } @@ -593,6 +758,18 @@ public TokenCodecEncoder putName(final DirectBuffer src, final int srcOffset, fi throw new IllegalStateException("length > maxValue for type: " + length); } + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_WRAPPED: + fieldOrderState(ACCESS_STATE_FILLED__NAME); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); parentMessage.limit(limit + headerLength + length); @@ -609,6 +786,18 @@ public TokenCodecEncoder putName(final byte[] src, final int srcOffset, final in throw new IllegalStateException("length > maxValue for type: " + length); } + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_WRAPPED: + fieldOrderState(ACCESS_STATE_FILLED__NAME); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); parentMessage.limit(limit + headerLength + length); @@ -628,6 +817,18 @@ public TokenCodecEncoder name(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_WRAPPED: + fieldOrderState(ACCESS_STATE_FILLED__NAME); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); parentMessage.limit(limit + headerLength + length); @@ -669,6 +870,18 @@ public TokenCodecEncoder putConstValue(final DirectBuffer src, final int srcOffs throw new IllegalStateException("length > maxValue for type: " + length); } + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_FILLED__NAME: + fieldOrderState(ACCESS_STATE_FILLED__CONSTVALUE); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); parentMessage.limit(limit + headerLength + length); @@ -685,6 +898,18 @@ public TokenCodecEncoder putConstValue(final byte[] src, final int srcOffset, fi throw new IllegalStateException("length > maxValue for type: " + length); } + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_FILLED__NAME: + fieldOrderState(ACCESS_STATE_FILLED__CONSTVALUE); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); parentMessage.limit(limit + headerLength + length); @@ -704,6 +929,18 @@ public TokenCodecEncoder constValue(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_FILLED__NAME: + fieldOrderState(ACCESS_STATE_FILLED__CONSTVALUE); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); parentMessage.limit(limit + headerLength + length); @@ -745,6 +982,18 @@ public TokenCodecEncoder putMinValue(final DirectBuffer src, final int srcOffset throw new IllegalStateException("length > maxValue for type: " + length); } + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_FILLED__CONSTVALUE: + fieldOrderState(ACCESS_STATE_FILLED__MINVALUE); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); parentMessage.limit(limit + headerLength + length); @@ -761,6 +1010,18 @@ public TokenCodecEncoder putMinValue(final byte[] src, final int srcOffset, fina throw new IllegalStateException("length > maxValue for type: " + length); } + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_FILLED__CONSTVALUE: + fieldOrderState(ACCESS_STATE_FILLED__MINVALUE); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); parentMessage.limit(limit + headerLength + length); @@ -780,6 +1041,18 @@ public TokenCodecEncoder minValue(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_FILLED__CONSTVALUE: + fieldOrderState(ACCESS_STATE_FILLED__MINVALUE); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); parentMessage.limit(limit + headerLength + length); @@ -821,6 +1094,18 @@ public TokenCodecEncoder putMaxValue(final DirectBuffer src, final int srcOffset throw new IllegalStateException("length > maxValue for type: " + length); } + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_FILLED__MINVALUE: + fieldOrderState(ACCESS_STATE_FILLED__MAXVALUE); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); parentMessage.limit(limit + headerLength + length); @@ -837,6 +1122,18 @@ public TokenCodecEncoder putMaxValue(final byte[] src, final int srcOffset, fina throw new IllegalStateException("length > maxValue for type: " + length); } + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_FILLED__MINVALUE: + fieldOrderState(ACCESS_STATE_FILLED__MAXVALUE); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); parentMessage.limit(limit + headerLength + length); @@ -856,6 +1153,18 @@ public TokenCodecEncoder maxValue(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_FILLED__MINVALUE: + fieldOrderState(ACCESS_STATE_FILLED__MAXVALUE); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); parentMessage.limit(limit + headerLength + length); @@ -897,6 +1206,18 @@ public TokenCodecEncoder putNullValue(final DirectBuffer src, final int srcOffse throw new IllegalStateException("length > maxValue for type: " + length); } + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_FILLED__MAXVALUE: + fieldOrderState(ACCESS_STATE_FILLED__NULLVALUE); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); parentMessage.limit(limit + headerLength + length); @@ -913,6 +1234,18 @@ public TokenCodecEncoder putNullValue(final byte[] src, final int srcOffset, fin throw new IllegalStateException("length > maxValue for type: " + length); } + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_FILLED__MAXVALUE: + fieldOrderState(ACCESS_STATE_FILLED__NULLVALUE); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); parentMessage.limit(limit + headerLength + length); @@ -932,6 +1265,18 @@ public TokenCodecEncoder nullValue(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_FILLED__MAXVALUE: + fieldOrderState(ACCESS_STATE_FILLED__NULLVALUE); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); parentMessage.limit(limit + headerLength + length); @@ -973,6 +1318,18 @@ public TokenCodecEncoder putCharacterEncoding(final DirectBuffer src, final int throw new IllegalStateException("length > maxValue for type: " + length); } + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_FILLED__NULLVALUE: + fieldOrderState(ACCESS_STATE_FILLED__CHARACTERENCODING); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); parentMessage.limit(limit + headerLength + length); @@ -989,6 +1346,18 @@ public TokenCodecEncoder putCharacterEncoding(final byte[] src, final int srcOff throw new IllegalStateException("length > maxValue for type: " + length); } + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_FILLED__NULLVALUE: + fieldOrderState(ACCESS_STATE_FILLED__CHARACTERENCODING); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); parentMessage.limit(limit + headerLength + length); @@ -1008,6 +1377,18 @@ public TokenCodecEncoder characterEncoding(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_FILLED__NULLVALUE: + fieldOrderState(ACCESS_STATE_FILLED__CHARACTERENCODING); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); parentMessage.limit(limit + headerLength + length); @@ -1049,6 +1430,18 @@ public TokenCodecEncoder putEpoch(final DirectBuffer src, final int srcOffset, f throw new IllegalStateException("length > maxValue for type: " + length); } + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_FILLED__CHARACTERENCODING: + fieldOrderState(ACCESS_STATE_FILLED__EPOCH); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); parentMessage.limit(limit + headerLength + length); @@ -1065,6 +1458,18 @@ public TokenCodecEncoder putEpoch(final byte[] src, final int srcOffset, final i throw new IllegalStateException("length > maxValue for type: " + length); } + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_FILLED__CHARACTERENCODING: + fieldOrderState(ACCESS_STATE_FILLED__EPOCH); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); parentMessage.limit(limit + headerLength + length); @@ -1084,6 +1489,18 @@ public TokenCodecEncoder epoch(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_FILLED__CHARACTERENCODING: + fieldOrderState(ACCESS_STATE_FILLED__EPOCH); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); parentMessage.limit(limit + headerLength + length); @@ -1125,6 +1542,18 @@ public TokenCodecEncoder putTimeUnit(final DirectBuffer src, final int srcOffset throw new IllegalStateException("length > maxValue for type: " + length); } + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_FILLED__EPOCH: + fieldOrderState(ACCESS_STATE_FILLED__TIMEUNIT); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); parentMessage.limit(limit + headerLength + length); @@ -1141,6 +1570,18 @@ public TokenCodecEncoder putTimeUnit(final byte[] src, final int srcOffset, fina throw new IllegalStateException("length > maxValue for type: " + length); } + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_FILLED__EPOCH: + fieldOrderState(ACCESS_STATE_FILLED__TIMEUNIT); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); parentMessage.limit(limit + headerLength + length); @@ -1160,6 +1601,18 @@ public TokenCodecEncoder timeUnit(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_FILLED__EPOCH: + fieldOrderState(ACCESS_STATE_FILLED__TIMEUNIT); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); parentMessage.limit(limit + headerLength + length); @@ -1201,6 +1654,18 @@ public TokenCodecEncoder putSemanticType(final DirectBuffer src, final int srcOf throw new IllegalStateException("length > maxValue for type: " + length); } + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_FILLED__TIMEUNIT: + fieldOrderState(ACCESS_STATE_FILLED__SEMANTICTYPE); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); parentMessage.limit(limit + headerLength + length); @@ -1217,6 +1682,18 @@ public TokenCodecEncoder putSemanticType(final byte[] src, final int srcOffset, throw new IllegalStateException("length > maxValue for type: " + length); } + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_FILLED__TIMEUNIT: + fieldOrderState(ACCESS_STATE_FILLED__SEMANTICTYPE); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); parentMessage.limit(limit + headerLength + length); @@ -1236,6 +1713,18 @@ public TokenCodecEncoder semanticType(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_FILLED__TIMEUNIT: + fieldOrderState(ACCESS_STATE_FILLED__SEMANTICTYPE); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); parentMessage.limit(limit + headerLength + length); @@ -1277,6 +1766,18 @@ public TokenCodecEncoder putDescription(final DirectBuffer src, final int srcOff throw new IllegalStateException("length > maxValue for type: " + length); } + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_FILLED__SEMANTICTYPE: + fieldOrderState(ACCESS_STATE_FILLED__DESCRIPTION); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); parentMessage.limit(limit + headerLength + length); @@ -1293,6 +1794,18 @@ public TokenCodecEncoder putDescription(final byte[] src, final int srcOffset, f throw new IllegalStateException("length > maxValue for type: " + length); } + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_FILLED__SEMANTICTYPE: + fieldOrderState(ACCESS_STATE_FILLED__DESCRIPTION); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); parentMessage.limit(limit + headerLength + length); @@ -1312,6 +1825,18 @@ public TokenCodecEncoder description(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_FILLED__SEMANTICTYPE: + fieldOrderState(ACCESS_STATE_FILLED__DESCRIPTION); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); parentMessage.limit(limit + headerLength + length); @@ -1353,6 +1878,18 @@ public TokenCodecEncoder putReferencedName(final DirectBuffer src, final int src throw new IllegalStateException("length > maxValue for type: " + length); } + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_FILLED__DESCRIPTION: + fieldOrderState(ACCESS_STATE_FILLED__REFERENCEDNAME); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); parentMessage.limit(limit + headerLength + length); @@ -1369,6 +1906,18 @@ public TokenCodecEncoder putReferencedName(final byte[] src, final int srcOffset throw new IllegalStateException("length > maxValue for type: " + length); } + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_FILLED__DESCRIPTION: + fieldOrderState(ACCESS_STATE_FILLED__REFERENCEDNAME); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); parentMessage.limit(limit + headerLength + length); @@ -1388,6 +1937,18 @@ public TokenCodecEncoder referencedName(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case ACCESS_STATE_FILLED__DESCRIPTION: + fieldOrderState(ACCESS_STATE_FILLED__REFERENCEDNAME); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); parentMessage.limit(limit + headerLength + length); diff --git a/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java b/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java index 5b8e868380..b231949b2a 100644 --- a/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java +++ b/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java @@ -28,7 +28,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assumptions.assumeFalse; -@Disabled public class FieldOrderCheckTest { private static final Class INCORRECT_ORDER_EXCEPTION_CLASS = IllegalStateException.class; @@ -61,6 +60,7 @@ void allowsEncodingAndDecodingVariableLengthFieldsInSchemaDefinedOrder() } @Test + @Disabled("Our access checks are too strict to allow the behaviour in this test.") void allowsReEncodingTopLevelPrimitiveFields() { final MultipleVarLengthEncoder encoder = new MultipleVarLengthEncoder() @@ -109,6 +109,7 @@ void disallowsReEncodingLatestVariableLengthField() } @Test + @Disabled("Decoding checks not implemented yet.") void disallowsSkippingDecodingOfVariableLengthField() { final MultipleVarLengthEncoder encoder = new MultipleVarLengthEncoder() @@ -124,6 +125,7 @@ void disallowsSkippingDecodingOfVariableLengthField() } @Test + @Disabled("Decoding checks not implemented yet.") void disallowsReDecodingEarlierVariableLengthField() { final MultipleVarLengthEncoder encoder = new MultipleVarLengthEncoder() @@ -141,6 +143,7 @@ void disallowsReDecodingEarlierVariableLengthField() } @Test + @Disabled("Decoding checks not implemented yet.") void disallowsReDecodingLatestVariableLengthField() { final MultipleVarLengthEncoder encoder = new MultipleVarLengthEncoder() @@ -198,6 +201,7 @@ void allowsEncodingAndDecodingEmptyGroupAndVariableLengthFieldsInSchemaDefinedOr } @Test + @Disabled("Our access checks are too strict to allow the behaviour in this test.") void allowsReEncodingPrimitiveFieldInGroupElementAfterTopLevelVariableLengthField() { final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder() @@ -271,6 +275,7 @@ void disallowsReEncodingGroupLength() } @Test + @Disabled("Our access checks are too strict to allow the behaviour in this test.") void allowsReEncodingGroupElementBlockFieldAfterTopLevelVariableLengthField() { final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder() @@ -295,6 +300,7 @@ void allowsReEncodingGroupElementBlockFieldAfterTopLevelVariableLengthField() } @Test + @Disabled("Decoding checks not implemented yet.") void disallowsSkippingDecodingOfGroupBeforeVariableLengthField() { final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder() @@ -314,6 +320,7 @@ void disallowsSkippingDecodingOfGroupBeforeVariableLengthField() } @Test + @Disabled("Decoding checks not implemented yet.") void disallowsReDecodingVariableLengthFieldAfterGroup() { final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder() @@ -338,6 +345,7 @@ void disallowsReDecodingVariableLengthFieldAfterGroup() } @Test + @Disabled("Decoding checks not implemented yet.") void disallowsReDecodingGroupBeforeVariableLengthField() { final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder() @@ -389,6 +397,7 @@ void allowsEncodingAndDecodingVariableLengthFieldInsideGroupInSchemaDefinedOrder } @Test + @Disabled("Our access checks are too strict to allow the behaviour in this test.") void allowsReEncodingGroupElementPrimitiveFieldAfterElementVariableLengthField() { final VarLengthInsideGroupEncoder encoder = new VarLengthInsideGroupEncoder() @@ -461,6 +470,7 @@ void disallowsReEncodingGroupElementVariableLengthField() } @Test + @Disabled("Decoding checks not implemented yet.") void disallowsReDecodingGroupElementVariableLengthField() { final VarLengthInsideGroupEncoder encoder = new VarLengthInsideGroupEncoder() @@ -485,6 +495,7 @@ void disallowsReDecodingGroupElementVariableLengthField() } @Test + @Disabled("Decoding checks not implemented yet.") void disallowsSkippingDecodingOfGroupElementVariableLengthFieldToNextElement() { final VarLengthInsideGroupEncoder encoder = new VarLengthInsideGroupEncoder() @@ -509,6 +520,7 @@ void disallowsSkippingDecodingOfGroupElementVariableLengthFieldToNextElement() } @Test + @Disabled("Decoding checks not implemented yet.") void disallowsSkippingDecodingOfGroupElementVariableLengthFieldToTopLevel() { final VarLengthInsideGroupEncoder encoder = new VarLengthInsideGroupEncoder() @@ -524,12 +536,13 @@ void disallowsSkippingDecodingOfGroupElementVariableLengthFieldToTopLevel() .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); assertThat(decoder.a(), equalTo(42)); final VarLengthInsideGroupDecoder.BDecoder bs = decoder.b(); - assertThat(bs.count(), equalTo(2)); + assertThat(bs.count(), equalTo(1)); assertThat(bs.next().c(), equalTo(1)); assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::e); } @Test + @Disabled("Decoding checks not implemented yet.") void disallowsSkippingDecodingOfGroupElement() { final VarLengthInsideGroupEncoder encoder = new VarLengthInsideGroupEncoder() @@ -662,6 +675,7 @@ void allowsEncodingAndDecodingCompositeInsideGroupInSchemaDefinedOrder() } @Test + @Disabled("Our access checks are too strict to allow the behaviour in this test.") void allowsReEncodingTopLevelCompositeViaReWrap() { final CompositeInsideGroupEncoder encoder = new CompositeInsideGroupEncoder() @@ -840,4 +854,10 @@ void allowsReDecodingGroupElementCompositeViaEncoderReference() assertThat(c.x(), equalTo(3)); assertThat(c.y(), equalTo(4)); } + + // TODO test more of SBE: + // - array setters + // - alternative varData setters + // - bitset setters + // - versioning } diff --git a/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/JavaGeneratorTest.java b/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/JavaGeneratorTest.java index f12fef3751..93f6af13e4 100644 --- a/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/JavaGeneratorTest.java +++ b/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/JavaGeneratorTest.java @@ -21,6 +21,7 @@ import org.agrona.generation.CompilerUtil; import org.agrona.generation.StringWriterOutputManager; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import uk.co.real_logic.sbe.Tests; import uk.co.real_logic.sbe.ir.Ir; @@ -288,6 +289,7 @@ void shouldGenerateReadOnlyMessage() throws Exception } @Test + @Disabled("TODO: invalid field encode order") void shouldGenerateVarDataCodecs() throws Exception { final String expectedManufacturer = "Ford"; @@ -403,6 +405,7 @@ void shouldGenerateGetFixedLengthStringUsingAppendable() throws Exception } @Test + @Disabled("TODO: invalid field encode order") void shouldGenerateGetVariableStringUsingAppendable() throws Exception { final UnsafeBuffer buffer = new UnsafeBuffer(new byte[4096]); From 9f9de4a1c7ccb0d83ed095a73698b926cdfdfd0f Mon Sep 17 00:00:00 2001 From: Zach Bray Date: Mon, 5 Jun 2023 20:18:45 +0100 Subject: [PATCH 03/50] [Java] Support printing field order access state machine. Use `-Dsbe.print.state.machine=$NAME_OF_MSG`. --- .../sbe/generation/java/FieldOrderModel.java | 75 +++++++++++++------ .../sbe/generation/java/JavaGenerator.java | 2 +- 2 files changed, 52 insertions(+), 25 deletions(-) diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java index 2eacd02095..ea9f7e5b57 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java @@ -31,12 +31,23 @@ final class FieldOrderModel { private static final boolean BLOCK_SKIP_CHECK_ENABLED = Boolean.getBoolean("sbe.block.skip.check.enabled"); + private static final String PRINT_STATE_MACHINE = System.getProperty("sbe.print.state.machine"); private final Int2ObjectHashMap states = new Int2ObjectHashMap<>(); private final Map transitions = new LinkedHashMap<>(); private final Set reservedNames = new HashSet<>(); private final State notWrappedState = allocateState("NOT_WRAPPED"); private final State wrappedState = allocateState("WRAPPED"); - private int transitionNumber; + + FieldOrderModel() + { + allocateTransition( + TransitionContext.NONE, + ".wrap(...)", + null, + Collections.singletonList(notWrappedState), + wrappedState + ); + } public State notWrappedState() { @@ -68,6 +79,7 @@ public List getTransitions(final TransitionContext context, final To } public void findTransitions( + final String msgName, final List fields, final List groups, final List varData) @@ -80,6 +92,33 @@ public void findTransitions( groups, varData ); + + if (Objects.equals(PRINT_STATE_MACHINE, msgName)) + { + final StringBuilder sb = new StringBuilder(); + sb.append("digraph G {\n"); + writeTransitions(sb); + sb.append("}\n"); + System.out.println(sb); + } + } + + @SuppressWarnings("CodeBlock2Expr") // lambdas without braces tend to conflict with checkstyle + private void writeTransitions(final StringBuilder sb) + { + transitions.values().forEach(transitionGroup -> + { + transitionGroup.transitions.forEach((context, transitions) -> + { + transitions.forEach(transition -> + transition.forEachStartState(startState -> + { + sb.append(" ").append(startState.name).append(" -> ").append(transition.endState().name); + sb.append(" [label=\"").append(transition.description).append("\"];\n"); + }) + ); + }); + }); } @SuppressWarnings("checkstyle:MethodLength") @@ -106,7 +145,7 @@ private List findTransitions( allocateTransition( TransitionContext.NONE, - "FILL_" + prefix + token.name().toUpperCase(), + " ." + token.name() + "(value) ", token, fromStates, blockState.get()); @@ -150,7 +189,7 @@ private List findTransitions( // fooCount(0) allocateTransition( TransitionContext.SELECT_EMPTY_GROUP, - "ZERO_" + groupPrefix, + "." + token.name() + "Length(0)", token, beginGroupStates, emptyGroup); @@ -158,7 +197,7 @@ private List findTransitions( // fooCount(N) where N > 0 allocateTransition( TransitionContext.SELECT_MULTI_ELEMENT_GROUP, - "MANY_" + groupPrefix, + "." + token.name() + "Length(N) where N > 0", token, beginGroupStates, nRemainingGroup); @@ -180,7 +219,7 @@ private List findTransitions( // where more than one element remains in the group allocateTransition( TransitionContext.NEXT_ELEMENT_IN_GROUP, - "NEXT_" + groupPrefix, + token.name() + ".next()\\n&& count - index > 1", token, fromStates, nRemainingGroupElement); @@ -192,7 +231,7 @@ private List findTransitions( // where only one element remains in the group allocateTransition( TransitionContext.LAST_ELEMENT_IN_GROUP, - "LAST_" + groupPrefix, + token.name() + ".next()\\n&& count - index == 1", token, fromStates, oneRemainingGroupElement); @@ -225,7 +264,7 @@ private List findTransitions( final State state = allocateState("FILLED_" + prefix + "_" + token.name().toUpperCase()); allocateTransition( TransitionContext.NONE, - "FILL_" + prefix + "_" + token.name().toUpperCase(), + "." + token.name() + "(value)", token, fromStates, state); @@ -250,26 +289,16 @@ private State allocateState(final String name) private void allocateTransition( final TransitionContext context, - final String name, + final String description, final Token token, final List from, final State to) { - if (!reservedNames.add(name)) - { - throw new IllegalStateException("Name is already reserved: " + name); - } - final TransitionGroup transitionGroup = transitions.computeIfAbsent(token, ignored -> new TransitionGroup()); - final Transition transition = new Transition(nextTransitionNumber(), name, from, to); + final Transition transition = new Transition(description, from, to); transitionGroup.add(context, transition); } - private int nextTransitionNumber() - { - return transitionNumber++; - } - static final class State { private final int number; @@ -294,15 +323,13 @@ public String name() static final class Transition { - private final int number; - private final String name; + private final String description; private final Set from; private final State to; - private Transition(final int number, final String name, final List from, final State to) + private Transition(final String description, final List from, final State to) { - this.number = number; - this.name = name; + this.description = description; this.from = new HashSet<>(from); this.to = to; } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java index ddb1d88006..85ff16cdca 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java @@ -251,7 +251,7 @@ private void generateEncoder( } out.append(generateDeclaration(className, implementsString, msgToken)); final FieldOrderModel fieldOrderModel = new FieldOrderModel(); - fieldOrderModel.findTransitions(fields, groups, varData); + fieldOrderModel.findTransitions(msgToken.name(), fields, groups, varData); out.append(generateFieldOrderStates(fieldOrderModel)); out.append(generateEncoderFlyweightCode(className, fieldOrderModel, msgToken)); From ec53b7db173a047495261746b41b5b3b0f23d533 Mon Sep 17 00:00:00 2001 From: Zach Bray Date: Fri, 9 Jun 2023 12:47:59 +0100 Subject: [PATCH 04/50] [Java] Add checking around decoding order. This change builds upon the work to check field encoding order. It is a slightly more-complex problem than with encoding, as when decoding it is necessary to deal with multiple versions of encoded data. To handle this, we fuse together the state machines for decoding each possible version and branch on "decoder.wrap(...)". Further work: - More tests - Better error messages --- .../sbe/generation/java/FieldOrderModel.java | 218 +++-- .../sbe/generation/java/JavaGenerator.java | 164 +++- .../sbe/ir/generated/FrameCodecDecoder.java | 216 +++++ .../sbe/ir/generated/FrameCodecEncoder.java | 62 +- .../sbe/ir/generated/TokenCodecDecoder.java | 748 ++++++++++++++++++ .../sbe/ir/generated/TokenCodecEncoder.java | 202 ++--- .../real_logic/sbe/FieldOrderCheckTest.java | 26 +- .../generation/java/SchemaExtensionTest.java | 2 + .../java/SkipAndDecodedLengthTest.java | 2 + 9 files changed, 1404 insertions(+), 236 deletions(-) diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java index ea9f7e5b57..0752d436ef 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java @@ -19,10 +19,13 @@ import uk.co.real_logic.sbe.ir.Signal; import uk.co.real_logic.sbe.ir.Token; import org.agrona.collections.Int2ObjectHashMap; +import org.agrona.collections.IntHashSet; +import org.agrona.collections.IntObjConsumer; import org.agrona.collections.MutableReference; import java.util.*; import java.util.function.Consumer; +import java.util.function.Predicate; import static uk.co.real_logic.sbe.ir.GenerationUtil.collectFields; import static uk.co.real_logic.sbe.ir.GenerationUtil.collectGroups; @@ -30,33 +33,32 @@ final class FieldOrderModel { - private static final boolean BLOCK_SKIP_CHECK_ENABLED = Boolean.getBoolean("sbe.block.skip.check.enabled"); private static final String PRINT_STATE_MACHINE = System.getProperty("sbe.print.state.machine"); private final Int2ObjectHashMap states = new Int2ObjectHashMap<>(); private final Map transitions = new LinkedHashMap<>(); + private final Int2ObjectHashMap versionWrappedStates = new Int2ObjectHashMap<>(); private final Set reservedNames = new HashSet<>(); private final State notWrappedState = allocateState("NOT_WRAPPED"); - private final State wrappedState = allocateState("WRAPPED"); + private State encoderWrappedState; - FieldOrderModel() + public State notWrappedState() { - allocateTransition( - TransitionContext.NONE, - ".wrap(...)", - null, - Collections.singletonList(notWrappedState), - wrappedState - ); + return notWrappedState; } - public State notWrappedState() + public State latestVersionWrappedState() { - return notWrappedState; + return encoderWrappedState; } - public State wrappedState() + public void forEachDecoderWrappedState(final IntObjConsumer consumer) { - return wrappedState; + final Int2ObjectHashMap.EntryIterator iterator = versionWrappedStates.entrySet().iterator(); + while (iterator.hasNext()) + { + iterator.next(); + consumer.accept(iterator.getIntKey(), iterator.getValue()); + } } public void forEachState(final Consumer consumer) @@ -78,29 +80,100 @@ public List getTransitions(final TransitionContext context, final To return transitionGroup.transitions.get(context); } - public void findTransitions( - final String msgName, + public static FieldOrderModel findTransitions( + final Token msgToken, final List fields, final List groups, final List varData) { - findTransitions( - Collections.singletonList(wrappedState), - BLOCK_SKIP_CHECK_ENABLED ? null : wrappedState, - "", - fields, - groups, - varData - ); - - if (Objects.equals(PRINT_STATE_MACHINE, msgName)) + final FieldOrderModel model = new FieldOrderModel(); + + final IntHashSet versions = new IntHashSet(); + versions.add(msgToken.version()); + findVersions(versions, fields, groups, varData); + + versions.stream().sorted().forEach(version -> + { + final State versionWrappedState = model.allocateState("V" + version + "_BLOCK"); + + model.versionWrappedStates.put(version, versionWrappedState); + + model.encoderWrappedState = versionWrappedState; + + model.allocateTransition( + version, + ".wrap(...)", + null, + Collections.singletonList(model.notWrappedState), + versionWrappedState + ); + + model.findTransitions( + Collections.singletonList(versionWrappedState), + versionWrappedState, + "V" + version + "_", + fields, + groups, + varData, + token -> token.version() <= version + ); + }); + + if (Objects.equals(PRINT_STATE_MACHINE, msgToken.name())) { final StringBuilder sb = new StringBuilder(); sb.append("digraph G {\n"); - writeTransitions(sb); - sb.append("}\n"); + model.writeTransitions(sb); + sb.append("}\n\n"); System.out.println(sb); } + + return model; + } + + private static void findVersions( + final IntHashSet versions, + final List fields, + final List groups, + final List varData) + { + Generators.forEachField(fields, (token, ignored) -> versions.add(token.version())); + + for (int i = 0; i < groups.size(); i++) + { + final Token token = groups.get(i); + if (token.signal() != Signal.BEGIN_GROUP) + { + throw new IllegalStateException("tokens must begin with BEGIN_GROUP: token=" + token); + } + + versions.add(token.version()); + + ++i; + final int groupHeaderTokenCount = groups.get(i).componentTokenCount(); + i += groupHeaderTokenCount; + + final ArrayList groupFields = new ArrayList<>(); + i = collectFields(groups, i, groupFields); + final ArrayList groupGroups = new ArrayList<>(); + i = collectGroups(groups, i, groupGroups); + final ArrayList groupVarData = new ArrayList<>(); + i = collectVarData(groups, i, groupVarData); + + findVersions(versions, groupFields, groupGroups, groupVarData); + } + + for (int i = 0; i < varData.size(); ) + { + final Token token = varData.get(i); + if (token.signal() != Signal.BEGIN_VAR_DATA) + { + throw new IllegalStateException("tokens must begin with BEGIN_VAR_DATA: token=" + token); + } + i += token.componentTokenCount(); + + versions.add(token.version()); + } } @SuppressWarnings("CodeBlock2Expr") // lambdas without braces tend to conflict with checkstyle @@ -111,12 +184,13 @@ private void writeTransitions(final StringBuilder sb) transitionGroup.transitions.forEach((context, transitions) -> { transitions.forEach(transition -> + { transition.forEachStartState(startState -> { sb.append(" ").append(startState.name).append(" -> ").append(transition.endState().name); sb.append(" [label=\"").append(transition.description).append("\"];\n"); - }) - ); + }); + }); }); }); } @@ -128,8 +202,8 @@ private List findTransitions( final String prefix, final List fields, final List groups, - final List varData - ) + final List varData, + final Predicate filter) { final MutableReference blockState = new MutableReference<>(blockStateOrNull); @@ -137,9 +211,14 @@ private List findTransitions( Generators.forEachField(fields, (token, ignored) -> { + if (!filter.test(token)) + { + return; + } + if (null == blockState.get()) { - blockState.set(allocateState(prefix.isEmpty() ? "BLOCK" : prefix + "_BLOCK")); + blockState.set(allocateState(prefix + "BLOCK")); fromStates.add(blockState.get()); } @@ -176,15 +255,20 @@ private List findTransitions( final ArrayList groupVarData = new ArrayList<>(); i = collectVarData(groups, i, groupVarData); + if (!filter.test(token)) + { + continue; + } + final String groupName = token.name().toUpperCase(); - final String groupPrefix = prefix.isEmpty() ? groupName : prefix + "_" + groupName; + final String groupPrefix = prefix + groupName + "_"; final List beginGroupStates = new ArrayList<>(fromStates); - final State nRemainingGroup = allocateState(groupPrefix + "_N"); - final State nRemainingGroupElement = allocateState(groupPrefix + "_N_ELEMENT"); - final State oneRemainingGroupElement = allocateState(groupPrefix + "_1_ELEMENT"); - final State emptyGroup = allocateState(groupPrefix + "_0"); + final State nRemainingGroup = allocateState(groupPrefix + "N"); + final State nRemainingGroupElement = allocateState(groupPrefix + "N_BLOCK"); + final State oneRemainingGroupElement = allocateState(groupPrefix + "1_BLOCK"); + final State emptyGroup = allocateState(groupPrefix + "0"); // fooCount(0) allocateTransition( @@ -206,11 +290,12 @@ private List findTransitions( fromStates.add(nRemainingGroupElement); final List nRemainingExitStates = findTransitions( fromStates, - BLOCK_SKIP_CHECK_ENABLED ? null : nRemainingGroupElement, - groupPrefix + "_N", + nRemainingGroupElement, + groupPrefix + "N_", groupFields, groupGroups, - groupVarData); + groupVarData, + filter); fromStates.clear(); fromStates.add(nRemainingGroup); @@ -241,11 +326,12 @@ private List findTransitions( final List oneRemainingExitStates = findTransitions( fromStates, - BLOCK_SKIP_CHECK_ENABLED ? null : oneRemainingGroupElement, - groupPrefix + "_1", + oneRemainingGroupElement, + groupPrefix + "1_", groupFields, groupGroups, - groupVarData); + groupVarData, + filter); fromStates.clear(); fromStates.add(emptyGroup); @@ -261,7 +347,12 @@ private List findTransitions( } i += token.componentTokenCount(); - final State state = allocateState("FILLED_" + prefix + "_" + token.name().toUpperCase()); + if (!filter.test(token)) + { + continue; + } + + final State state = allocateState(prefix + token.name().toUpperCase() + "_FILLED"); allocateTransition( TransitionContext.NONE, "." + token.name() + "(value)", @@ -288,7 +379,7 @@ private State allocateState(final String name) } private void allocateTransition( - final TransitionContext context, + final Object firingContext, final String description, final Token token, final List from, @@ -296,7 +387,7 @@ private void allocateTransition( { final TransitionGroup transitionGroup = transitions.computeIfAbsent(token, ignored -> new TransitionGroup()); final Transition transition = new Transition(description, from, to); - transitionGroup.add(context, transition); + transitionGroup.add(firingContext, transition); } static final class State @@ -319,6 +410,15 @@ public String name() { return name; } + + @Override + public String toString() + { + return "State{" + + "number=" + number + + ", name='" + name + '\'' + + '}'; + } } static final class Transition @@ -343,6 +443,16 @@ State endState() { return to; } + + @Override + public String toString() + { + return "Transition{" + + "description='" + description + '\'' + + ", from=" + from + + ", to=" + to + + '}'; + } } enum TransitionContext @@ -356,9 +466,9 @@ enum TransitionContext private static final class TransitionGroup { - private final Map> transitions = new LinkedHashMap<>(); + private final Map> transitions = new LinkedHashMap<>(); - public void add(final TransitionContext context, final Transition transition) + public void add(final Object context, final Transition transition) { final List transitionsForContext = transitions.computeIfAbsent(context, ignored -> new ArrayList<>()); @@ -371,12 +481,14 @@ public void add(final TransitionContext context, final Transition transition) throw new IllegalStateException("Duplicate end state: " + transition.to.name); } - final boolean conflictingStartState = - transitionsForContext.stream().anyMatch(t -> t.from.stream().anyMatch(transition.from::contains)); + final Optional conflictingTransition = transitionsForContext.stream() + .filter(t -> t.from.stream().anyMatch(transition.from::contains)) + .findAny(); - if (conflictingStartState) + if (conflictingTransition.isPresent()) { - throw new IllegalStateException("Conflicting start states: " + transition.from); + throw new IllegalStateException( + "Conflicting transition: " + transition + " conflicts with " + conflictingTransition.get()); } transitionsForContext.add(transition); diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java index 85ff16cdca..ca38f6cd5b 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java @@ -250,8 +250,8 @@ private void generateEncoder( generateAnnotations(BASE_INDENT, className, groups, out, this::encoderName); } out.append(generateDeclaration(className, implementsString, msgToken)); - final FieldOrderModel fieldOrderModel = new FieldOrderModel(); - fieldOrderModel.findTransitions(msgToken.name(), fields, groups, varData); + final FieldOrderModel fieldOrderModel = FieldOrderModel.findTransitions( + msgToken, fields, groups, varData); out.append(generateFieldOrderStates(fieldOrderModel)); out.append(generateEncoderFlyweightCode(className, fieldOrderModel, msgToken)); @@ -267,12 +267,12 @@ private void generateEncoder( } } - private CharSequence stateFieldName(final FieldOrderModel.State state) + private static CharSequence stateFieldName(final FieldOrderModel.State state) { - return "ACCESS_STATE_" + state.name(); + return "STATE_" + state.name(); } - private CharSequence generateFieldOrderStates(final FieldOrderModel fieldOrderModel) + private static CharSequence generateFieldOrderStates(final FieldOrderModel fieldOrderModel) { final StringBuilder sb = new StringBuilder(); @@ -302,7 +302,7 @@ private CharSequence generateFieldOrderStates(final FieldOrderModel fieldOrderMo return sb; } - private CharSequence generateFieldOrderStateTransitions( + private static CharSequence generateFieldOrderStateTransitions( final FieldOrderModel fieldOrderModel, final String indent, final Token token) @@ -329,7 +329,7 @@ private CharSequence generateFieldOrderStateTransitions( return sb; } - private CharSequence generateFieldOrderStateTransitions( + private static CharSequence generateFieldOrderStateTransitions( final FieldOrderModel fieldOrderModel, final String indent, final Token token, @@ -367,7 +367,7 @@ private CharSequence generateFieldOrderStateTransitions( return sb; } - private void generateFieldOrderStateTransitions( + private static void generateFieldOrderStateTransitions( final StringBuilder sb, final String indent, final FieldOrderModel fieldOrderModel, @@ -394,7 +394,7 @@ private void generateFieldOrderStateTransitions( .append(indent).append("}\n"); } - private CharSequence generateFieldOrderStateTransitionsForNextGroupElement( + private static CharSequence generateFieldOrderStateTransitionsForNextGroupElement( final FieldOrderModel fieldOrderModel, final String indent, final Token token, @@ -432,6 +432,36 @@ private CharSequence generateFieldOrderStateTransitionsForNextGroupElement( return sb; } + private static CharSequence generateFieldOrderStateTransitionForDecoderWrap( + final FieldOrderModel fieldOrderModel, + final String indent, + final String actingVersion) + { + final StringBuilder sb = new StringBuilder(); + sb.append(indent).append("if (DEBUG_MODE)\n") + .append(indent).append("{\n") + .append(indent).append(" switch(" + actingVersion + ")") + .append(indent).append(" {\n"); + + fieldOrderModel.forEachDecoderWrappedState((version, state) -> + { + sb.append(indent).append(" case ").append(version).append(":\n") + .append(indent).append(" fieldOrderState(") + .append(stateFieldName(state)).append(");\n") + .append(indent).append(" break;\n"); + }); + + sb.append(indent).append(" default:\n") + .append(indent).append(" fieldOrderState(") + .append(stateFieldName(fieldOrderModel.latestVersionWrappedState())).append(");\n") + .append(indent).append(" break;\n") + .append(indent).append(" }\n") + .append(indent).append("}\n\n"); + + return sb; + } + + private void generateDecoder( final Token msgToken, final List fields, @@ -452,12 +482,15 @@ private void generateDecoder( generateAnnotations(BASE_INDENT, className, groups, out, this::decoderName); } out.append(generateDeclaration(className, implementsString, msgToken)); - out.append(generateDecoderFlyweightCode(className, msgToken)); + final FieldOrderModel fieldOrderModel = FieldOrderModel.findTransitions( + msgToken, fields, groups, varData); + out.append(generateFieldOrderStates(fieldOrderModel)); + out.append(generateDecoderFlyweightCode(fieldOrderModel, className, msgToken)); final StringBuilder sb = new StringBuilder(); - generateDecoderFields(sb, fields, BASE_INDENT); - generateDecoderGroups(sb, className, groups, BASE_INDENT, false); - generateDecoderVarData(sb, varData, BASE_INDENT); + generateDecoderFields(sb, fieldOrderModel, fields, BASE_INDENT); + generateDecoderGroups(sb, fieldOrderModel, className, groups, BASE_INDENT, false); + generateDecoderVarData(sb, fieldOrderModel, varData, BASE_INDENT); generateDecoderDisplay(sb, msgToken.name(), fields, groups, varData); generateMessageLength(sb, className, true, groups, varData, BASE_INDENT); @@ -469,7 +502,7 @@ private void generateDecoder( private void generateDecoderGroups( final StringBuilder sb, - final String outerClassName, + final FieldOrderModel fieldOrderModel, final String outerClassName, final List tokens, final String indent, final boolean isSubGroup) throws IOException @@ -498,18 +531,19 @@ private void generateDecoderGroups( final List varData = new ArrayList<>(); i = collectVarData(tokens, i, varData); - generateGroupDecoderProperty(sb, groupName, groupToken, indent, isSubGroup); + generateGroupDecoderProperty(sb, groupName, fieldOrderModel, groupToken, indent, isSubGroup); generateTypeJavadoc(sb, indent + INDENT, groupToken); if (shouldGenerateGroupOrderAnnotation) { generateAnnotations(indent + INDENT, groupName, groups, sb, this::decoderName); } - generateGroupDecoderClassHeader(sb, groupName, outerClassName, tokens, groups, index, indent + INDENT); + generateGroupDecoderClassHeader(sb, groupName, outerClassName, fieldOrderModel, groupToken, + tokens, groups, index, indent + INDENT); - generateDecoderFields(sb, fields, indent + INDENT); - generateDecoderGroups(sb, outerClassName, groups, indent + INDENT, true); - generateDecoderVarData(sb, varData, indent + INDENT); + generateDecoderFields(sb, fieldOrderModel, fields, indent + INDENT); + generateDecoderGroups(sb, fieldOrderModel, outerClassName, groups, indent + INDENT, true); + generateDecoderVarData(sb, fieldOrderModel, varData, indent + INDENT); appendGroupInstanceDecoderDisplay(sb, fields, groups, varData, indent + INDENT); generateMessageLength(sb, groupName, false, groups, varData, indent + INDENT); @@ -573,6 +607,8 @@ private void generateGroupDecoderClassHeader( final StringBuilder sb, final String groupName, final String parentMessageClassName, + final FieldOrderModel fieldOrderModel, + final Token groupToken, final List tokens, final List subGroupTokens, final int index, @@ -626,6 +662,8 @@ private void generateGroupDecoderClassHeader( .append(indent).append(" {\n") .append(indent).append(" throw new java.util.NoSuchElementException();\n") .append(indent).append(" }\n\n") + .append(generateFieldOrderStateTransitionsForNextGroupElement( + fieldOrderModel, indent + " ", groupToken, "count - index")) .append(indent).append(" offset = parentMessage.limit();\n") .append(indent).append(" parentMessage.limit(offset + blockLength);\n") .append(indent).append(" ++index;\n\n") @@ -673,6 +711,18 @@ private void generateGroupDecoderClassHeader( .append(indent).append(" {\n") .append(indent).append(" return index < count;\n") .append(indent).append(" }\n"); + + sb.append("\n") + .append(indent).append(" private int fieldOrderState()\n") + .append(indent).append(" {\n") + .append(indent).append(" return parentMessage.fieldOrderState();\n") + .append(indent).append(" }\n"); + + sb.append("\n") + .append(indent).append(" private void fieldOrderState(final int newState)\n") + .append(indent).append(" {\n") + .append(indent).append(" parentMessage.fieldOrderState(newState);\n") + .append(indent).append(" }\n"); } private void generateGroupEncoderClassHeader( @@ -912,7 +962,7 @@ private void generateGroupEncoderClassDeclaration( private static void generateGroupDecoderProperty( final StringBuilder sb, final String groupName, - final Token token, + final FieldOrderModel fieldOrderModel, final Token token, final String indent, final boolean isSubGroup) { @@ -959,6 +1009,7 @@ private static void generateGroupDecoderProperty( indent + " {\n" + "%3$s" + indent + " %2$s.wrap(buffer);\n" + + generateFieldOrderStateTransitions(fieldOrderModel, indent + " ", token, propertyName + ".count") + indent + " return %2$s;\n" + indent + " }\n", className, @@ -1007,7 +1058,10 @@ private void generateGroupEncoderProperty( } private void generateDecoderVarData( - final StringBuilder sb, final List tokens, final String indent) + final StringBuilder sb, + final FieldOrderModel fieldOrderModel, + final List tokens, + final String indent) { for (int i = 0, size = tokens.size(); i < size;) { @@ -1047,8 +1101,11 @@ private void generateDecoderVarData( .append(generateGet(lengthType, "limit", byteOrderStr)).append(";\n") .append(indent).append(" }\n"); - generateDataDecodeMethods( - sb, token, propertyName, sizeOfLengthField, lengthType, byteOrderStr, characterEncoding, indent); + final CharSequence stateTransition = + generateFieldOrderStateTransitions(fieldOrderModel, indent + " ", token); + + generateDataDecodeMethods(sb, token, propertyName, sizeOfLengthField, lengthType, + byteOrderStr, characterEncoding, stateTransition, indent); i += token.componentTokenCount(); } @@ -1115,12 +1172,14 @@ private void generateDataDecodeMethods( final PrimitiveType lengthType, final String byteOrderStr, final String characterEncoding, + final CharSequence stateTransition, final String indent) { new Formatter(sb).format("\n" + indent + " public int skip%1$s()\n" + indent + " {\n" + "%2$s" + + stateTransition + indent + " final int headerLength = %3$d;\n" + indent + " final int limit = parentMessage.limit();\n" + indent + " final int dataLength = %4$s%5$s;\n" + @@ -1142,6 +1201,7 @@ private void generateDataDecodeMethods( mutableBuffer, lengthType, byteOrderStr, + stateTransition, indent); generateVarDataTypedDecoder( @@ -1152,6 +1212,7 @@ private void generateDataDecodeMethods( "byte[]", lengthType, byteOrderStr, + stateTransition, indent); generateVarDataWrapDecoder(sb, token, propertyName, sizeOfLengthField, lengthType, byteOrderStr, indent); @@ -1162,6 +1223,7 @@ private void generateDataDecodeMethods( indent + " public String %1$s()\n" + indent + " {\n" + "%2$s" + + stateTransition + indent + " final int headerLength = %3$d;\n" + indent + " final int limit = parentMessage.limit();\n" + indent + " final int dataLength = %4$s%5$s;\n" + @@ -1187,6 +1249,7 @@ private void generateDataDecodeMethods( indent + " public int get%1$s(final Appendable appendable)\n" + indent + " {\n" + "%2$s" + + stateTransition + indent + " final int headerLength = %3$d;\n" + indent + " final int limit = parentMessage.limit();\n" + indent + " final int dataLength = %4$s%5$s;\n" + @@ -1385,12 +1448,14 @@ private void generateVarDataTypedDecoder( final String exchangeType, final PrimitiveType lengthType, final String byteOrderStr, + final CharSequence stateTransition, final String indent) { new Formatter(sb).format("\n" + indent + " public int get%s(final %s dst, final int dstOffset, final int length)\n" + indent + " {\n" + "%s" + + stateTransition + indent + " final int headerLength = %d;\n" + indent + " final int limit = parentMessage.limit();\n" + indent + " final int dataLength = %s%s;\n" + @@ -1569,11 +1634,11 @@ private void generateComposite(final List tokens) throws IOException { case ENCODING: generatePrimitiveDecoder( - sb, true, encodingToken.name(), encodingToken, encodingToken, BASE_INDENT); + sb, true, encodingToken.name(), "", encodingToken, encodingToken, BASE_INDENT); break; case BEGIN_ENUM: - generateEnumDecoder(sb, true, encodingToken, propertyName, encodingToken, BASE_INDENT); + generateEnumDecoder(sb, true, "", encodingToken, propertyName, encodingToken, BASE_INDENT); break; case BEGIN_SET: @@ -2062,6 +2127,7 @@ private void generatePrimitiveDecoder( final StringBuilder sb, final boolean inComposite, final String propertyName, + final CharSequence stateTransition, final Token propertyToken, final Token encodingToken, final String indent) @@ -2077,7 +2143,7 @@ private void generatePrimitiveDecoder( else { sb.append(generatePrimitivePropertyDecodeMethods( - inComposite, formattedPropertyName, propertyToken, encodingToken, indent)); + inComposite, formattedPropertyName, stateTransition, propertyToken, encodingToken, indent)); } } @@ -2108,14 +2174,16 @@ private void generatePrimitiveEncoder( private CharSequence generatePrimitivePropertyDecodeMethods( final boolean inComposite, final String propertyName, + final CharSequence stateTransition, final Token propertyToken, final Token encodingToken, final String indent) { return encodingToken.matchOnLength( - () -> generatePrimitivePropertyDecode(inComposite, propertyName, propertyToken, encodingToken, indent), + () -> generatePrimitivePropertyDecode( + inComposite, propertyName, stateTransition, propertyToken, encodingToken, indent), () -> generatePrimitiveArrayPropertyDecode( - inComposite, propertyName, propertyToken, encodingToken, indent)); + inComposite, propertyName, stateTransition, propertyToken, encodingToken, indent)); } private CharSequence generatePrimitivePropertyEncodeMethods( @@ -2169,6 +2237,7 @@ private void generatePrimitiveFieldMetaMethod( private CharSequence generatePrimitivePropertyDecode( final boolean inComposite, final String propertyName, + final CharSequence stateTransition, final Token propertyToken, final Token encodingToken, final String indent) @@ -2183,6 +2252,7 @@ private CharSequence generatePrimitivePropertyDecode( "\n" + indent + " public %s %s()\n" + indent + " {\n" + + stateTransition + "%s" + indent + " return %s;\n" + indent + " }\n\n", @@ -2316,6 +2386,7 @@ private static CharSequence generatePropertyNotPresentCondition( private CharSequence generatePrimitiveArrayPropertyDecode( final boolean inComposite, final String propertyName, + final CharSequence stateTransition, final Token propertyToken, final Token encodingToken, final String indent) @@ -2334,6 +2405,7 @@ private CharSequence generatePrimitiveArrayPropertyDecode( new Formatter(sb).format("\n" + indent + " public %s %s(final int index)\n" + indent + " {\n" + + stateTransition + indent + " if (index < 0 || index >= %d)\n" + indent + " {\n" + indent + " throw new IndexOutOfBoundsException(\"index out of range: index=\" + index);\n" + @@ -2357,6 +2429,7 @@ private CharSequence generatePrimitiveArrayPropertyDecode( new Formatter(sb).format("\n" + indent + " public int get%s(final byte[] dst, final int dstOffset)\n" + indent + " {\n" + + stateTransition + indent + " final int length = %d;\n" + indent + " if (dstOffset < 0 || dstOffset > (dst.length - length))\n" + indent + " {\n" + @@ -2375,6 +2448,7 @@ private CharSequence generatePrimitiveArrayPropertyDecode( new Formatter(sb).format("\n" + indent + " public String %s()\n" + indent + " {\n" + + stateTransition + "%s" + indent + " final byte[] dst = new byte[%d];\n" + indent + " buffer.getBytes(offset + %d, dst, 0, %d);\n\n" + @@ -2395,6 +2469,7 @@ private CharSequence generatePrimitiveArrayPropertyDecode( new Formatter(sb).format("\n" + indent + " public int get%1$s(final Appendable value)\n" + indent + " {\n" + + stateTransition + "%2$s" + indent + " for (int i = 0; i < %3$d; ++i)\n" + indent + " {\n" + @@ -2425,6 +2500,7 @@ else if (encoding.primitiveType() == PrimitiveType.UINT8) new Formatter(sb).format("\n" + indent + " public int get%s(final byte[] dst, final int dstOffset, final int length)\n" + indent + " {\n" + + stateTransition + "%s" + indent + " final int bytesCopied = Math.min(length, %d);\n" + indent + " buffer.getBytes(offset + %d, dst, dstOffset, bytesCopied);\n\n" + @@ -2438,6 +2514,7 @@ else if (encoding.primitiveType() == PrimitiveType.UINT8) new Formatter(sb).format("\n" + indent + " public int get%s(final %s dst, final int dstOffset, final int length)\n" + indent + " {\n" + + stateTransition + "%s" + indent + " final int bytesCopied = Math.min(length, %d);\n" + indent + " buffer.getBytes(offset + %d, dst, dstOffset, bytesCopied);\n\n" + @@ -2452,6 +2529,7 @@ else if (encoding.primitiveType() == PrimitiveType.UINT8) new Formatter(sb).format("\n" + indent + " public void wrap%s(final %s wrapBuffer)\n" + indent + " {\n" + + stateTransition + "%s" + indent + " wrapBuffer.wrap(buffer, offset + %d, %d);\n" + indent + " }\n", @@ -2918,7 +2996,10 @@ private CharSequence generateFixedFlyweightCode( semanticVersion); } - private CharSequence generateDecoderFlyweightCode(final String className, final Token token) + private CharSequence generateDecoderFlyweightCode( + final FieldOrderModel fieldOrderModel, + final String className, + final Token token) { final String headerClassName = formatClassName(ir.headerStructure().tokens().get(0).applicableTypeName()); @@ -2938,6 +3019,7 @@ private CharSequence generateDecoderFlyweightCode(final String className, final " this.actingBlockLength = actingBlockLength;\n" + " this.actingVersion = actingVersion;\n" + " limit(offset + actingBlockLength);\n\n" + + generateFieldOrderStateTransitionForDecoderWrap(fieldOrderModel, " ", "actingVersion") + " return this;\n" + " }\n\n" + @@ -3101,7 +3183,7 @@ private CharSequence generateEncoderFlyweightCode( " limit(offset + BLOCK_LENGTH);\n\n" + " if (DEBUG_MODE)\n" + " {\n" + - " fieldOrderState(" + stateFieldName(fieldOrderModel.wrappedState()) + ");\n" + + " fieldOrderState(" + stateFieldName(fieldOrderModel.latestVersionWrappedState()) + ");\n" + " }\n\n" + " return this;\n" + " }\n\n"; @@ -3198,7 +3280,11 @@ private void generateEncoderFields( }); } - private void generateDecoderFields(final StringBuilder sb, final List tokens, final String indent) + private void generateDecoderFields( + final StringBuilder sb, + final FieldOrderModel fieldOrderModel, + final List tokens, + final String indent) { Generators.forEachField( tokens, @@ -3213,24 +3299,29 @@ private void generateDecoderFields(final StringBuilder sb, final List tok generateEncodingLengthMethod(sb, propertyName, typeToken.encodedLength(), indent); generateFieldMetaAttributeMethod(sb, fieldToken, indent); + final CharSequence stateTransition = generateFieldOrderStateTransitions( + fieldOrderModel, indent + " ", fieldToken); + switch (typeToken.signal()) { case ENCODING: - generatePrimitiveDecoder(sb, false, propertyName, fieldToken, typeToken, indent); + generatePrimitiveDecoder( + sb, false, propertyName, stateTransition, fieldToken, typeToken, indent); break; case BEGIN_ENUM: - generateEnumDecoder(sb, false, fieldToken, propertyName, typeToken, indent); + generateEnumDecoder( + sb, false, stateTransition, fieldToken, propertyName, typeToken, indent); break; case BEGIN_SET: generateBitSetProperty( - sb, false, DECODER, propertyName, /* TODO */ null, fieldToken, typeToken, indent, typeName); + sb, false, DECODER, propertyName, fieldOrderModel, fieldToken, typeToken, indent, typeName); break; case BEGIN_COMPOSITE: generateCompositeProperty( - sb, false, DECODER, propertyName, /* TODO */ null, fieldToken, typeToken, indent, typeName); + sb, false, DECODER, propertyName, fieldOrderModel, fieldToken, typeToken, indent, typeName); break; default: @@ -3331,6 +3422,7 @@ private static void generateFieldMetaAttributeMethod(final StringBuilder sb, fin private void generateEnumDecoder( final StringBuilder sb, final boolean inComposite, + final CharSequence stateTransition, final Token fieldToken, final String propertyName, final Token typeToken, @@ -3373,6 +3465,7 @@ private void generateEnumDecoder( "\n" + indent + " public %s %sRaw()\n" + indent + " {\n" + + stateTransition + "%s" + indent + " return %s;\n" + indent + " }\n", @@ -3385,6 +3478,7 @@ private void generateEnumDecoder( "\n" + indent + " public %s %s()\n" + indent + " {\n" + + stateTransition + "%s" + indent + " return %s.get(%s);\n" + indent + " }\n\n", diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java index 8cbdc11954..dbc245540e 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java @@ -11,6 +11,30 @@ @SuppressWarnings("all") public final class FrameCodecDecoder { + private static final boolean DEBUG_MODE = !Boolean.getBoolean("agrona.disable.bounds.checks"); + + private static final int STATE_NOT_WRAPPED = 1; + + private static final int STATE_V0_BLOCK = 2; + + private static final int STATE_V0_PACKAGENAME_FILLED = 3; + + private static final int STATE_V0_NAMESPACENAME_FILLED = 4; + + private static final int STATE_V0_SEMANTICVERSION_FILLED = 5; + + private int fieldOrderState = STATE_NOT_WRAPPED; + + private int fieldOrderState() + { + return fieldOrderState; + } + + private void fieldOrderState(int newState) + { + fieldOrderState = newState; + } + public static final int BLOCK_LENGTH = 12; public static final int TEMPLATE_ID = 1; public static final int SCHEMA_ID = 1; @@ -82,6 +106,18 @@ public FrameCodecDecoder wrap( this.actingVersion = actingVersion; limit(offset + actingBlockLength); + if (DEBUG_MODE) + { + switch(actingVersion) { + case 0: + fieldOrderState(STATE_V0_BLOCK); + break; + default: + fieldOrderState(STATE_V0_BLOCK); + break; + } + } + return this; } @@ -187,6 +223,18 @@ public static int irIdMaxValue() public int irId() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_BLOCK: + fieldOrderState(STATE_V0_BLOCK); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + return buffer.getInt(offset + 0, java.nio.ByteOrder.LITTLE_ENDIAN); } @@ -238,6 +286,18 @@ public static int irVersionMaxValue() public int irVersion() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_BLOCK: + fieldOrderState(STATE_V0_BLOCK); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + return buffer.getInt(offset + 4, java.nio.ByteOrder.LITTLE_ENDIAN); } @@ -289,6 +349,18 @@ public static int schemaVersionMaxValue() public int schemaVersion() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_BLOCK: + fieldOrderState(STATE_V0_BLOCK); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + return buffer.getInt(offset + 8, java.nio.ByteOrder.LITTLE_ENDIAN); } @@ -331,6 +403,18 @@ public int packageNameLength() public int skipPackageName() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_BLOCK: + fieldOrderState(STATE_V0_PACKAGENAME_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -342,6 +426,18 @@ public int skipPackageName() public int getPackageName(final MutableDirectBuffer dst, final int dstOffset, final int length) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_BLOCK: + fieldOrderState(STATE_V0_PACKAGENAME_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -354,6 +450,18 @@ public int getPackageName(final MutableDirectBuffer dst, final int dstOffset, fi public int getPackageName(final byte[] dst, final int dstOffset, final int length) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_BLOCK: + fieldOrderState(STATE_V0_PACKAGENAME_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -375,6 +483,18 @@ public void wrapPackageName(final DirectBuffer wrapBuffer) public String packageName() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_BLOCK: + fieldOrderState(STATE_V0_PACKAGENAME_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -429,6 +549,18 @@ public int namespaceNameLength() public int skipNamespaceName() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_PACKAGENAME_FILLED: + fieldOrderState(STATE_V0_NAMESPACENAME_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -440,6 +572,18 @@ public int skipNamespaceName() public int getNamespaceName(final MutableDirectBuffer dst, final int dstOffset, final int length) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_PACKAGENAME_FILLED: + fieldOrderState(STATE_V0_NAMESPACENAME_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -452,6 +596,18 @@ public int getNamespaceName(final MutableDirectBuffer dst, final int dstOffset, public int getNamespaceName(final byte[] dst, final int dstOffset, final int length) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_PACKAGENAME_FILLED: + fieldOrderState(STATE_V0_NAMESPACENAME_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -473,6 +629,18 @@ public void wrapNamespaceName(final DirectBuffer wrapBuffer) public String namespaceName() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_PACKAGENAME_FILLED: + fieldOrderState(STATE_V0_NAMESPACENAME_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -527,6 +695,18 @@ public int semanticVersionLength() public int skipSemanticVersion() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_NAMESPACENAME_FILLED: + fieldOrderState(STATE_V0_SEMANTICVERSION_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -538,6 +718,18 @@ public int skipSemanticVersion() public int getSemanticVersion(final MutableDirectBuffer dst, final int dstOffset, final int length) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_NAMESPACENAME_FILLED: + fieldOrderState(STATE_V0_SEMANTICVERSION_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -550,6 +742,18 @@ public int getSemanticVersion(final MutableDirectBuffer dst, final int dstOffset public int getSemanticVersion(final byte[] dst, final int dstOffset, final int length) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_NAMESPACENAME_FILLED: + fieldOrderState(STATE_V0_SEMANTICVERSION_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -571,6 +775,18 @@ public void wrapSemanticVersion(final DirectBuffer wrapBuffer) public String semanticVersion() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_NAMESPACENAME_FILLED: + fieldOrderState(STATE_V0_SEMANTICVERSION_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java index 91bde3796e..af8fb02bbc 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java @@ -13,17 +13,17 @@ public final class FrameCodecEncoder { private static final boolean DEBUG_MODE = !Boolean.getBoolean("agrona.disable.bounds.checks"); - private static final int ACCESS_STATE_NOT_WRAPPED = 1; + private static final int STATE_NOT_WRAPPED = 1; - private static final int ACCESS_STATE_WRAPPED = 2; + private static final int STATE_V0_BLOCK = 2; - private static final int ACCESS_STATE_FILLED__PACKAGENAME = 3; + private static final int STATE_V0_PACKAGENAME_FILLED = 3; - private static final int ACCESS_STATE_FILLED__NAMESPACENAME = 4; + private static final int STATE_V0_NAMESPACENAME_FILLED = 4; - private static final int ACCESS_STATE_FILLED__SEMANTICVERSION = 5; + private static final int STATE_V0_SEMANTICVERSION_FILLED = 5; - private int fieldOrderState = ACCESS_STATE_NOT_WRAPPED; + private int fieldOrderState = STATE_NOT_WRAPPED; private int fieldOrderState() { @@ -100,7 +100,7 @@ public FrameCodecEncoder wrap(final MutableDirectBuffer buffer, final int offset if (DEBUG_MODE) { - fieldOrderState(ACCESS_STATE_WRAPPED); + fieldOrderState(STATE_V0_BLOCK); } return this; @@ -185,8 +185,8 @@ public FrameCodecEncoder irId(final int value) { switch (fieldOrderState()) { - case ACCESS_STATE_WRAPPED: - fieldOrderState(ACCESS_STATE_WRAPPED); + case STATE_V0_BLOCK: + fieldOrderState(STATE_V0_BLOCK); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -249,8 +249,8 @@ public FrameCodecEncoder irVersion(final int value) { switch (fieldOrderState()) { - case ACCESS_STATE_WRAPPED: - fieldOrderState(ACCESS_STATE_WRAPPED); + case STATE_V0_BLOCK: + fieldOrderState(STATE_V0_BLOCK); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -313,8 +313,8 @@ public FrameCodecEncoder schemaVersion(final int value) { switch (fieldOrderState()) { - case ACCESS_STATE_WRAPPED: - fieldOrderState(ACCESS_STATE_WRAPPED); + case STATE_V0_BLOCK: + fieldOrderState(STATE_V0_BLOCK); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -362,8 +362,8 @@ public FrameCodecEncoder putPackageName(final DirectBuffer src, final int srcOff { switch (fieldOrderState()) { - case ACCESS_STATE_WRAPPED: - fieldOrderState(ACCESS_STATE_FILLED__PACKAGENAME); + case STATE_V0_BLOCK: + fieldOrderState(STATE_V0_PACKAGENAME_FILLED); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -390,8 +390,8 @@ public FrameCodecEncoder putPackageName(final byte[] src, final int srcOffset, f { switch (fieldOrderState()) { - case ACCESS_STATE_WRAPPED: - fieldOrderState(ACCESS_STATE_FILLED__PACKAGENAME); + case STATE_V0_BLOCK: + fieldOrderState(STATE_V0_PACKAGENAME_FILLED); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -421,8 +421,8 @@ public FrameCodecEncoder packageName(final String value) { switch (fieldOrderState()) { - case ACCESS_STATE_WRAPPED: - fieldOrderState(ACCESS_STATE_FILLED__PACKAGENAME); + case STATE_V0_BLOCK: + fieldOrderState(STATE_V0_PACKAGENAME_FILLED); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -474,8 +474,8 @@ public FrameCodecEncoder putNamespaceName(final DirectBuffer src, final int srcO { switch (fieldOrderState()) { - case ACCESS_STATE_FILLED__PACKAGENAME: - fieldOrderState(ACCESS_STATE_FILLED__NAMESPACENAME); + case STATE_V0_PACKAGENAME_FILLED: + fieldOrderState(STATE_V0_NAMESPACENAME_FILLED); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -502,8 +502,8 @@ public FrameCodecEncoder putNamespaceName(final byte[] src, final int srcOffset, { switch (fieldOrderState()) { - case ACCESS_STATE_FILLED__PACKAGENAME: - fieldOrderState(ACCESS_STATE_FILLED__NAMESPACENAME); + case STATE_V0_PACKAGENAME_FILLED: + fieldOrderState(STATE_V0_NAMESPACENAME_FILLED); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -533,8 +533,8 @@ public FrameCodecEncoder namespaceName(final String value) { switch (fieldOrderState()) { - case ACCESS_STATE_FILLED__PACKAGENAME: - fieldOrderState(ACCESS_STATE_FILLED__NAMESPACENAME); + case STATE_V0_PACKAGENAME_FILLED: + fieldOrderState(STATE_V0_NAMESPACENAME_FILLED); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -586,8 +586,8 @@ public FrameCodecEncoder putSemanticVersion(final DirectBuffer src, final int sr { switch (fieldOrderState()) { - case ACCESS_STATE_FILLED__NAMESPACENAME: - fieldOrderState(ACCESS_STATE_FILLED__SEMANTICVERSION); + case STATE_V0_NAMESPACENAME_FILLED: + fieldOrderState(STATE_V0_SEMANTICVERSION_FILLED); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -614,8 +614,8 @@ public FrameCodecEncoder putSemanticVersion(final byte[] src, final int srcOffse { switch (fieldOrderState()) { - case ACCESS_STATE_FILLED__NAMESPACENAME: - fieldOrderState(ACCESS_STATE_FILLED__SEMANTICVERSION); + case STATE_V0_NAMESPACENAME_FILLED: + fieldOrderState(STATE_V0_SEMANTICVERSION_FILLED); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -645,8 +645,8 @@ public FrameCodecEncoder semanticVersion(final String value) { switch (fieldOrderState()) { - case ACCESS_STATE_FILLED__NAMESPACENAME: - fieldOrderState(ACCESS_STATE_FILLED__SEMANTICVERSION); + case STATE_V0_NAMESPACENAME_FILLED: + fieldOrderState(STATE_V0_SEMANTICVERSION_FILLED); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java index 8c7a7a1ee6..da989d7527 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java @@ -11,6 +11,46 @@ @SuppressWarnings("all") public final class TokenCodecDecoder { + private static final boolean DEBUG_MODE = !Boolean.getBoolean("agrona.disable.bounds.checks"); + + private static final int STATE_NOT_WRAPPED = 1; + + private static final int STATE_V0_BLOCK = 2; + + private static final int STATE_V0_NAME_FILLED = 3; + + private static final int STATE_V0_CONSTVALUE_FILLED = 4; + + private static final int STATE_V0_MINVALUE_FILLED = 5; + + private static final int STATE_V0_MAXVALUE_FILLED = 6; + + private static final int STATE_V0_NULLVALUE_FILLED = 7; + + private static final int STATE_V0_CHARACTERENCODING_FILLED = 8; + + private static final int STATE_V0_EPOCH_FILLED = 9; + + private static final int STATE_V0_TIMEUNIT_FILLED = 10; + + private static final int STATE_V0_SEMANTICTYPE_FILLED = 11; + + private static final int STATE_V0_DESCRIPTION_FILLED = 12; + + private static final int STATE_V0_REFERENCEDNAME_FILLED = 13; + + private int fieldOrderState = STATE_NOT_WRAPPED; + + private int fieldOrderState() + { + return fieldOrderState; + } + + private void fieldOrderState(int newState) + { + fieldOrderState = newState; + } + public static final int BLOCK_LENGTH = 28; public static final int TEMPLATE_ID = 2; public static final int SCHEMA_ID = 1; @@ -82,6 +122,18 @@ public TokenCodecDecoder wrap( this.actingVersion = actingVersion; limit(offset + actingBlockLength); + if (DEBUG_MODE) + { + switch(actingVersion) { + case 0: + fieldOrderState(STATE_V0_BLOCK); + break; + default: + fieldOrderState(STATE_V0_BLOCK); + break; + } + } + return this; } @@ -187,6 +239,18 @@ public static int tokenOffsetMaxValue() public int tokenOffset() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_BLOCK: + fieldOrderState(STATE_V0_BLOCK); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + return buffer.getInt(offset + 0, java.nio.ByteOrder.LITTLE_ENDIAN); } @@ -238,6 +302,18 @@ public static int tokenSizeMaxValue() public int tokenSize() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_BLOCK: + fieldOrderState(STATE_V0_BLOCK); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + return buffer.getInt(offset + 4, java.nio.ByteOrder.LITTLE_ENDIAN); } @@ -289,6 +365,18 @@ public static int fieldIdMaxValue() public int fieldId() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_BLOCK: + fieldOrderState(STATE_V0_BLOCK); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + return buffer.getInt(offset + 8, java.nio.ByteOrder.LITTLE_ENDIAN); } @@ -340,6 +428,18 @@ public static int tokenVersionMaxValue() public int tokenVersion() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_BLOCK: + fieldOrderState(STATE_V0_BLOCK); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + return buffer.getInt(offset + 12, java.nio.ByteOrder.LITTLE_ENDIAN); } @@ -391,6 +491,18 @@ public static int componentTokenCountMaxValue() public int componentTokenCount() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_BLOCK: + fieldOrderState(STATE_V0_BLOCK); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + return buffer.getInt(offset + 16, java.nio.ByteOrder.LITTLE_ENDIAN); } @@ -427,11 +539,35 @@ public static String signalMetaAttribute(final MetaAttribute metaAttribute) public short signalRaw() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_BLOCK: + fieldOrderState(STATE_V0_BLOCK); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + return ((short)(buffer.getByte(offset + 20) & 0xFF)); } public SignalCodec signal() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_BLOCK: + fieldOrderState(STATE_V0_BLOCK); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + return SignalCodec.get(((short)(buffer.getByte(offset + 20) & 0xFF))); } @@ -468,11 +604,35 @@ public static String primitiveTypeMetaAttribute(final MetaAttribute metaAttribut public short primitiveTypeRaw() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_BLOCK: + fieldOrderState(STATE_V0_BLOCK); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + return ((short)(buffer.getByte(offset + 21) & 0xFF)); } public PrimitiveTypeCodec primitiveType() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_BLOCK: + fieldOrderState(STATE_V0_BLOCK); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + return PrimitiveTypeCodec.get(((short)(buffer.getByte(offset + 21) & 0xFF))); } @@ -509,11 +669,35 @@ public static String byteOrderMetaAttribute(final MetaAttribute metaAttribute) public short byteOrderRaw() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_BLOCK: + fieldOrderState(STATE_V0_BLOCK); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + return ((short)(buffer.getByte(offset + 22) & 0xFF)); } public ByteOrderCodec byteOrder() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_BLOCK: + fieldOrderState(STATE_V0_BLOCK); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + return ByteOrderCodec.get(((short)(buffer.getByte(offset + 22) & 0xFF))); } @@ -550,11 +734,35 @@ public static String presenceMetaAttribute(final MetaAttribute metaAttribute) public short presenceRaw() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_BLOCK: + fieldOrderState(STATE_V0_BLOCK); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + return ((short)(buffer.getByte(offset + 23) & 0xFF)); } public PresenceCodec presence() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_BLOCK: + fieldOrderState(STATE_V0_BLOCK); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + return PresenceCodec.get(((short)(buffer.getByte(offset + 23) & 0xFF))); } @@ -606,6 +814,18 @@ public static int deprecatedMaxValue() public int deprecated() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_BLOCK: + fieldOrderState(STATE_V0_BLOCK); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + return buffer.getInt(offset + 24, java.nio.ByteOrder.LITTLE_ENDIAN); } @@ -648,6 +868,18 @@ public int nameLength() public int skipName() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_BLOCK: + fieldOrderState(STATE_V0_NAME_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -659,6 +891,18 @@ public int skipName() public int getName(final MutableDirectBuffer dst, final int dstOffset, final int length) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_BLOCK: + fieldOrderState(STATE_V0_NAME_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -671,6 +915,18 @@ public int getName(final MutableDirectBuffer dst, final int dstOffset, final int public int getName(final byte[] dst, final int dstOffset, final int length) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_BLOCK: + fieldOrderState(STATE_V0_NAME_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -692,6 +948,18 @@ public void wrapName(final DirectBuffer wrapBuffer) public String name() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_BLOCK: + fieldOrderState(STATE_V0_NAME_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -746,6 +1014,18 @@ public int constValueLength() public int skipConstValue() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_NAME_FILLED: + fieldOrderState(STATE_V0_CONSTVALUE_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -757,6 +1037,18 @@ public int skipConstValue() public int getConstValue(final MutableDirectBuffer dst, final int dstOffset, final int length) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_NAME_FILLED: + fieldOrderState(STATE_V0_CONSTVALUE_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -769,6 +1061,18 @@ public int getConstValue(final MutableDirectBuffer dst, final int dstOffset, fin public int getConstValue(final byte[] dst, final int dstOffset, final int length) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_NAME_FILLED: + fieldOrderState(STATE_V0_CONSTVALUE_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -790,6 +1094,18 @@ public void wrapConstValue(final DirectBuffer wrapBuffer) public String constValue() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_NAME_FILLED: + fieldOrderState(STATE_V0_CONSTVALUE_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -844,6 +1160,18 @@ public int minValueLength() public int skipMinValue() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_CONSTVALUE_FILLED: + fieldOrderState(STATE_V0_MINVALUE_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -855,6 +1183,18 @@ public int skipMinValue() public int getMinValue(final MutableDirectBuffer dst, final int dstOffset, final int length) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_CONSTVALUE_FILLED: + fieldOrderState(STATE_V0_MINVALUE_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -867,6 +1207,18 @@ public int getMinValue(final MutableDirectBuffer dst, final int dstOffset, final public int getMinValue(final byte[] dst, final int dstOffset, final int length) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_CONSTVALUE_FILLED: + fieldOrderState(STATE_V0_MINVALUE_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -888,6 +1240,18 @@ public void wrapMinValue(final DirectBuffer wrapBuffer) public String minValue() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_CONSTVALUE_FILLED: + fieldOrderState(STATE_V0_MINVALUE_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -942,6 +1306,18 @@ public int maxValueLength() public int skipMaxValue() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_MINVALUE_FILLED: + fieldOrderState(STATE_V0_MAXVALUE_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -953,6 +1329,18 @@ public int skipMaxValue() public int getMaxValue(final MutableDirectBuffer dst, final int dstOffset, final int length) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_MINVALUE_FILLED: + fieldOrderState(STATE_V0_MAXVALUE_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -965,6 +1353,18 @@ public int getMaxValue(final MutableDirectBuffer dst, final int dstOffset, final public int getMaxValue(final byte[] dst, final int dstOffset, final int length) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_MINVALUE_FILLED: + fieldOrderState(STATE_V0_MAXVALUE_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -986,6 +1386,18 @@ public void wrapMaxValue(final DirectBuffer wrapBuffer) public String maxValue() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_MINVALUE_FILLED: + fieldOrderState(STATE_V0_MAXVALUE_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -1040,6 +1452,18 @@ public int nullValueLength() public int skipNullValue() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_MAXVALUE_FILLED: + fieldOrderState(STATE_V0_NULLVALUE_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -1051,6 +1475,18 @@ public int skipNullValue() public int getNullValue(final MutableDirectBuffer dst, final int dstOffset, final int length) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_MAXVALUE_FILLED: + fieldOrderState(STATE_V0_NULLVALUE_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -1063,6 +1499,18 @@ public int getNullValue(final MutableDirectBuffer dst, final int dstOffset, fina public int getNullValue(final byte[] dst, final int dstOffset, final int length) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_MAXVALUE_FILLED: + fieldOrderState(STATE_V0_NULLVALUE_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -1084,6 +1532,18 @@ public void wrapNullValue(final DirectBuffer wrapBuffer) public String nullValue() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_MAXVALUE_FILLED: + fieldOrderState(STATE_V0_NULLVALUE_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -1138,6 +1598,18 @@ public int characterEncodingLength() public int skipCharacterEncoding() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_NULLVALUE_FILLED: + fieldOrderState(STATE_V0_CHARACTERENCODING_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -1149,6 +1621,18 @@ public int skipCharacterEncoding() public int getCharacterEncoding(final MutableDirectBuffer dst, final int dstOffset, final int length) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_NULLVALUE_FILLED: + fieldOrderState(STATE_V0_CHARACTERENCODING_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -1161,6 +1645,18 @@ public int getCharacterEncoding(final MutableDirectBuffer dst, final int dstOffs public int getCharacterEncoding(final byte[] dst, final int dstOffset, final int length) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_NULLVALUE_FILLED: + fieldOrderState(STATE_V0_CHARACTERENCODING_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -1182,6 +1678,18 @@ public void wrapCharacterEncoding(final DirectBuffer wrapBuffer) public String characterEncoding() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_NULLVALUE_FILLED: + fieldOrderState(STATE_V0_CHARACTERENCODING_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -1236,6 +1744,18 @@ public int epochLength() public int skipEpoch() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_CHARACTERENCODING_FILLED: + fieldOrderState(STATE_V0_EPOCH_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -1247,6 +1767,18 @@ public int skipEpoch() public int getEpoch(final MutableDirectBuffer dst, final int dstOffset, final int length) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_CHARACTERENCODING_FILLED: + fieldOrderState(STATE_V0_EPOCH_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -1259,6 +1791,18 @@ public int getEpoch(final MutableDirectBuffer dst, final int dstOffset, final in public int getEpoch(final byte[] dst, final int dstOffset, final int length) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_CHARACTERENCODING_FILLED: + fieldOrderState(STATE_V0_EPOCH_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -1280,6 +1824,18 @@ public void wrapEpoch(final DirectBuffer wrapBuffer) public String epoch() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_CHARACTERENCODING_FILLED: + fieldOrderState(STATE_V0_EPOCH_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -1334,6 +1890,18 @@ public int timeUnitLength() public int skipTimeUnit() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_EPOCH_FILLED: + fieldOrderState(STATE_V0_TIMEUNIT_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -1345,6 +1913,18 @@ public int skipTimeUnit() public int getTimeUnit(final MutableDirectBuffer dst, final int dstOffset, final int length) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_EPOCH_FILLED: + fieldOrderState(STATE_V0_TIMEUNIT_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -1357,6 +1937,18 @@ public int getTimeUnit(final MutableDirectBuffer dst, final int dstOffset, final public int getTimeUnit(final byte[] dst, final int dstOffset, final int length) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_EPOCH_FILLED: + fieldOrderState(STATE_V0_TIMEUNIT_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -1378,6 +1970,18 @@ public void wrapTimeUnit(final DirectBuffer wrapBuffer) public String timeUnit() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_EPOCH_FILLED: + fieldOrderState(STATE_V0_TIMEUNIT_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -1432,6 +2036,18 @@ public int semanticTypeLength() public int skipSemanticType() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_TIMEUNIT_FILLED: + fieldOrderState(STATE_V0_SEMANTICTYPE_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -1443,6 +2059,18 @@ public int skipSemanticType() public int getSemanticType(final MutableDirectBuffer dst, final int dstOffset, final int length) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_TIMEUNIT_FILLED: + fieldOrderState(STATE_V0_SEMANTICTYPE_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -1455,6 +2083,18 @@ public int getSemanticType(final MutableDirectBuffer dst, final int dstOffset, f public int getSemanticType(final byte[] dst, final int dstOffset, final int length) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_TIMEUNIT_FILLED: + fieldOrderState(STATE_V0_SEMANTICTYPE_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -1476,6 +2116,18 @@ public void wrapSemanticType(final DirectBuffer wrapBuffer) public String semanticType() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_TIMEUNIT_FILLED: + fieldOrderState(STATE_V0_SEMANTICTYPE_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -1530,6 +2182,18 @@ public int descriptionLength() public int skipDescription() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_SEMANTICTYPE_FILLED: + fieldOrderState(STATE_V0_DESCRIPTION_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -1541,6 +2205,18 @@ public int skipDescription() public int getDescription(final MutableDirectBuffer dst, final int dstOffset, final int length) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_SEMANTICTYPE_FILLED: + fieldOrderState(STATE_V0_DESCRIPTION_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -1553,6 +2229,18 @@ public int getDescription(final MutableDirectBuffer dst, final int dstOffset, fi public int getDescription(final byte[] dst, final int dstOffset, final int length) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_SEMANTICTYPE_FILLED: + fieldOrderState(STATE_V0_DESCRIPTION_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -1574,6 +2262,18 @@ public void wrapDescription(final DirectBuffer wrapBuffer) public String description() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_SEMANTICTYPE_FILLED: + fieldOrderState(STATE_V0_DESCRIPTION_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -1628,6 +2328,18 @@ public int referencedNameLength() public int skipReferencedName() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_DESCRIPTION_FILLED: + fieldOrderState(STATE_V0_REFERENCEDNAME_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -1639,6 +2351,18 @@ public int skipReferencedName() public int getReferencedName(final MutableDirectBuffer dst, final int dstOffset, final int length) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_DESCRIPTION_FILLED: + fieldOrderState(STATE_V0_REFERENCEDNAME_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -1651,6 +2375,18 @@ public int getReferencedName(final MutableDirectBuffer dst, final int dstOffset, public int getReferencedName(final byte[] dst, final int dstOffset, final int length) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_DESCRIPTION_FILLED: + fieldOrderState(STATE_V0_REFERENCEDNAME_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -1672,6 +2408,18 @@ public void wrapReferencedName(final DirectBuffer wrapBuffer) public String referencedName() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_DESCRIPTION_FILLED: + fieldOrderState(STATE_V0_REFERENCEDNAME_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java index 1a01973ae7..53dad48cb5 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java @@ -13,33 +13,33 @@ public final class TokenCodecEncoder { private static final boolean DEBUG_MODE = !Boolean.getBoolean("agrona.disable.bounds.checks"); - private static final int ACCESS_STATE_NOT_WRAPPED = 1; + private static final int STATE_NOT_WRAPPED = 1; - private static final int ACCESS_STATE_WRAPPED = 2; + private static final int STATE_V0_BLOCK = 2; - private static final int ACCESS_STATE_FILLED__NAME = 3; + private static final int STATE_V0_NAME_FILLED = 3; - private static final int ACCESS_STATE_FILLED__CONSTVALUE = 4; + private static final int STATE_V0_CONSTVALUE_FILLED = 4; - private static final int ACCESS_STATE_FILLED__MINVALUE = 5; + private static final int STATE_V0_MINVALUE_FILLED = 5; - private static final int ACCESS_STATE_FILLED__MAXVALUE = 6; + private static final int STATE_V0_MAXVALUE_FILLED = 6; - private static final int ACCESS_STATE_FILLED__NULLVALUE = 7; + private static final int STATE_V0_NULLVALUE_FILLED = 7; - private static final int ACCESS_STATE_FILLED__CHARACTERENCODING = 8; + private static final int STATE_V0_CHARACTERENCODING_FILLED = 8; - private static final int ACCESS_STATE_FILLED__EPOCH = 9; + private static final int STATE_V0_EPOCH_FILLED = 9; - private static final int ACCESS_STATE_FILLED__TIMEUNIT = 10; + private static final int STATE_V0_TIMEUNIT_FILLED = 10; - private static final int ACCESS_STATE_FILLED__SEMANTICTYPE = 11; + private static final int STATE_V0_SEMANTICTYPE_FILLED = 11; - private static final int ACCESS_STATE_FILLED__DESCRIPTION = 12; + private static final int STATE_V0_DESCRIPTION_FILLED = 12; - private static final int ACCESS_STATE_FILLED__REFERENCEDNAME = 13; + private static final int STATE_V0_REFERENCEDNAME_FILLED = 13; - private int fieldOrderState = ACCESS_STATE_NOT_WRAPPED; + private int fieldOrderState = STATE_NOT_WRAPPED; private int fieldOrderState() { @@ -116,7 +116,7 @@ public TokenCodecEncoder wrap(final MutableDirectBuffer buffer, final int offset if (DEBUG_MODE) { - fieldOrderState(ACCESS_STATE_WRAPPED); + fieldOrderState(STATE_V0_BLOCK); } return this; @@ -201,8 +201,8 @@ public TokenCodecEncoder tokenOffset(final int value) { switch (fieldOrderState()) { - case ACCESS_STATE_WRAPPED: - fieldOrderState(ACCESS_STATE_WRAPPED); + case STATE_V0_BLOCK: + fieldOrderState(STATE_V0_BLOCK); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -265,8 +265,8 @@ public TokenCodecEncoder tokenSize(final int value) { switch (fieldOrderState()) { - case ACCESS_STATE_WRAPPED: - fieldOrderState(ACCESS_STATE_WRAPPED); + case STATE_V0_BLOCK: + fieldOrderState(STATE_V0_BLOCK); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -329,8 +329,8 @@ public TokenCodecEncoder fieldId(final int value) { switch (fieldOrderState()) { - case ACCESS_STATE_WRAPPED: - fieldOrderState(ACCESS_STATE_WRAPPED); + case STATE_V0_BLOCK: + fieldOrderState(STATE_V0_BLOCK); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -393,8 +393,8 @@ public TokenCodecEncoder tokenVersion(final int value) { switch (fieldOrderState()) { - case ACCESS_STATE_WRAPPED: - fieldOrderState(ACCESS_STATE_WRAPPED); + case STATE_V0_BLOCK: + fieldOrderState(STATE_V0_BLOCK); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -457,8 +457,8 @@ public TokenCodecEncoder componentTokenCount(final int value) { switch (fieldOrderState()) { - case ACCESS_STATE_WRAPPED: - fieldOrderState(ACCESS_STATE_WRAPPED); + case STATE_V0_BLOCK: + fieldOrderState(STATE_V0_BLOCK); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -506,8 +506,8 @@ public TokenCodecEncoder signal(final SignalCodec value) { switch (fieldOrderState()) { - case ACCESS_STATE_WRAPPED: - fieldOrderState(ACCESS_STATE_WRAPPED); + case STATE_V0_BLOCK: + fieldOrderState(STATE_V0_BLOCK); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -554,8 +554,8 @@ public TokenCodecEncoder primitiveType(final PrimitiveTypeCodec value) { switch (fieldOrderState()) { - case ACCESS_STATE_WRAPPED: - fieldOrderState(ACCESS_STATE_WRAPPED); + case STATE_V0_BLOCK: + fieldOrderState(STATE_V0_BLOCK); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -602,8 +602,8 @@ public TokenCodecEncoder byteOrder(final ByteOrderCodec value) { switch (fieldOrderState()) { - case ACCESS_STATE_WRAPPED: - fieldOrderState(ACCESS_STATE_WRAPPED); + case STATE_V0_BLOCK: + fieldOrderState(STATE_V0_BLOCK); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -650,8 +650,8 @@ public TokenCodecEncoder presence(final PresenceCodec value) { switch (fieldOrderState()) { - case ACCESS_STATE_WRAPPED: - fieldOrderState(ACCESS_STATE_WRAPPED); + case STATE_V0_BLOCK: + fieldOrderState(STATE_V0_BLOCK); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -713,8 +713,8 @@ public TokenCodecEncoder deprecated(final int value) { switch (fieldOrderState()) { - case ACCESS_STATE_WRAPPED: - fieldOrderState(ACCESS_STATE_WRAPPED); + case STATE_V0_BLOCK: + fieldOrderState(STATE_V0_BLOCK); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -762,8 +762,8 @@ public TokenCodecEncoder putName(final DirectBuffer src, final int srcOffset, fi { switch (fieldOrderState()) { - case ACCESS_STATE_WRAPPED: - fieldOrderState(ACCESS_STATE_FILLED__NAME); + case STATE_V0_BLOCK: + fieldOrderState(STATE_V0_NAME_FILLED); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -790,8 +790,8 @@ public TokenCodecEncoder putName(final byte[] src, final int srcOffset, final in { switch (fieldOrderState()) { - case ACCESS_STATE_WRAPPED: - fieldOrderState(ACCESS_STATE_FILLED__NAME); + case STATE_V0_BLOCK: + fieldOrderState(STATE_V0_NAME_FILLED); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -821,8 +821,8 @@ public TokenCodecEncoder name(final String value) { switch (fieldOrderState()) { - case ACCESS_STATE_WRAPPED: - fieldOrderState(ACCESS_STATE_FILLED__NAME); + case STATE_V0_BLOCK: + fieldOrderState(STATE_V0_NAME_FILLED); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -874,8 +874,8 @@ public TokenCodecEncoder putConstValue(final DirectBuffer src, final int srcOffs { switch (fieldOrderState()) { - case ACCESS_STATE_FILLED__NAME: - fieldOrderState(ACCESS_STATE_FILLED__CONSTVALUE); + case STATE_V0_NAME_FILLED: + fieldOrderState(STATE_V0_CONSTVALUE_FILLED); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -902,8 +902,8 @@ public TokenCodecEncoder putConstValue(final byte[] src, final int srcOffset, fi { switch (fieldOrderState()) { - case ACCESS_STATE_FILLED__NAME: - fieldOrderState(ACCESS_STATE_FILLED__CONSTVALUE); + case STATE_V0_NAME_FILLED: + fieldOrderState(STATE_V0_CONSTVALUE_FILLED); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -933,8 +933,8 @@ public TokenCodecEncoder constValue(final String value) { switch (fieldOrderState()) { - case ACCESS_STATE_FILLED__NAME: - fieldOrderState(ACCESS_STATE_FILLED__CONSTVALUE); + case STATE_V0_NAME_FILLED: + fieldOrderState(STATE_V0_CONSTVALUE_FILLED); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -986,8 +986,8 @@ public TokenCodecEncoder putMinValue(final DirectBuffer src, final int srcOffset { switch (fieldOrderState()) { - case ACCESS_STATE_FILLED__CONSTVALUE: - fieldOrderState(ACCESS_STATE_FILLED__MINVALUE); + case STATE_V0_CONSTVALUE_FILLED: + fieldOrderState(STATE_V0_MINVALUE_FILLED); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -1014,8 +1014,8 @@ public TokenCodecEncoder putMinValue(final byte[] src, final int srcOffset, fina { switch (fieldOrderState()) { - case ACCESS_STATE_FILLED__CONSTVALUE: - fieldOrderState(ACCESS_STATE_FILLED__MINVALUE); + case STATE_V0_CONSTVALUE_FILLED: + fieldOrderState(STATE_V0_MINVALUE_FILLED); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -1045,8 +1045,8 @@ public TokenCodecEncoder minValue(final String value) { switch (fieldOrderState()) { - case ACCESS_STATE_FILLED__CONSTVALUE: - fieldOrderState(ACCESS_STATE_FILLED__MINVALUE); + case STATE_V0_CONSTVALUE_FILLED: + fieldOrderState(STATE_V0_MINVALUE_FILLED); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -1098,8 +1098,8 @@ public TokenCodecEncoder putMaxValue(final DirectBuffer src, final int srcOffset { switch (fieldOrderState()) { - case ACCESS_STATE_FILLED__MINVALUE: - fieldOrderState(ACCESS_STATE_FILLED__MAXVALUE); + case STATE_V0_MINVALUE_FILLED: + fieldOrderState(STATE_V0_MAXVALUE_FILLED); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -1126,8 +1126,8 @@ public TokenCodecEncoder putMaxValue(final byte[] src, final int srcOffset, fina { switch (fieldOrderState()) { - case ACCESS_STATE_FILLED__MINVALUE: - fieldOrderState(ACCESS_STATE_FILLED__MAXVALUE); + case STATE_V0_MINVALUE_FILLED: + fieldOrderState(STATE_V0_MAXVALUE_FILLED); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -1157,8 +1157,8 @@ public TokenCodecEncoder maxValue(final String value) { switch (fieldOrderState()) { - case ACCESS_STATE_FILLED__MINVALUE: - fieldOrderState(ACCESS_STATE_FILLED__MAXVALUE); + case STATE_V0_MINVALUE_FILLED: + fieldOrderState(STATE_V0_MAXVALUE_FILLED); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -1210,8 +1210,8 @@ public TokenCodecEncoder putNullValue(final DirectBuffer src, final int srcOffse { switch (fieldOrderState()) { - case ACCESS_STATE_FILLED__MAXVALUE: - fieldOrderState(ACCESS_STATE_FILLED__NULLVALUE); + case STATE_V0_MAXVALUE_FILLED: + fieldOrderState(STATE_V0_NULLVALUE_FILLED); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -1238,8 +1238,8 @@ public TokenCodecEncoder putNullValue(final byte[] src, final int srcOffset, fin { switch (fieldOrderState()) { - case ACCESS_STATE_FILLED__MAXVALUE: - fieldOrderState(ACCESS_STATE_FILLED__NULLVALUE); + case STATE_V0_MAXVALUE_FILLED: + fieldOrderState(STATE_V0_NULLVALUE_FILLED); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -1269,8 +1269,8 @@ public TokenCodecEncoder nullValue(final String value) { switch (fieldOrderState()) { - case ACCESS_STATE_FILLED__MAXVALUE: - fieldOrderState(ACCESS_STATE_FILLED__NULLVALUE); + case STATE_V0_MAXVALUE_FILLED: + fieldOrderState(STATE_V0_NULLVALUE_FILLED); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -1322,8 +1322,8 @@ public TokenCodecEncoder putCharacterEncoding(final DirectBuffer src, final int { switch (fieldOrderState()) { - case ACCESS_STATE_FILLED__NULLVALUE: - fieldOrderState(ACCESS_STATE_FILLED__CHARACTERENCODING); + case STATE_V0_NULLVALUE_FILLED: + fieldOrderState(STATE_V0_CHARACTERENCODING_FILLED); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -1350,8 +1350,8 @@ public TokenCodecEncoder putCharacterEncoding(final byte[] src, final int srcOff { switch (fieldOrderState()) { - case ACCESS_STATE_FILLED__NULLVALUE: - fieldOrderState(ACCESS_STATE_FILLED__CHARACTERENCODING); + case STATE_V0_NULLVALUE_FILLED: + fieldOrderState(STATE_V0_CHARACTERENCODING_FILLED); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -1381,8 +1381,8 @@ public TokenCodecEncoder characterEncoding(final String value) { switch (fieldOrderState()) { - case ACCESS_STATE_FILLED__NULLVALUE: - fieldOrderState(ACCESS_STATE_FILLED__CHARACTERENCODING); + case STATE_V0_NULLVALUE_FILLED: + fieldOrderState(STATE_V0_CHARACTERENCODING_FILLED); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -1434,8 +1434,8 @@ public TokenCodecEncoder putEpoch(final DirectBuffer src, final int srcOffset, f { switch (fieldOrderState()) { - case ACCESS_STATE_FILLED__CHARACTERENCODING: - fieldOrderState(ACCESS_STATE_FILLED__EPOCH); + case STATE_V0_CHARACTERENCODING_FILLED: + fieldOrderState(STATE_V0_EPOCH_FILLED); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -1462,8 +1462,8 @@ public TokenCodecEncoder putEpoch(final byte[] src, final int srcOffset, final i { switch (fieldOrderState()) { - case ACCESS_STATE_FILLED__CHARACTERENCODING: - fieldOrderState(ACCESS_STATE_FILLED__EPOCH); + case STATE_V0_CHARACTERENCODING_FILLED: + fieldOrderState(STATE_V0_EPOCH_FILLED); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -1493,8 +1493,8 @@ public TokenCodecEncoder epoch(final String value) { switch (fieldOrderState()) { - case ACCESS_STATE_FILLED__CHARACTERENCODING: - fieldOrderState(ACCESS_STATE_FILLED__EPOCH); + case STATE_V0_CHARACTERENCODING_FILLED: + fieldOrderState(STATE_V0_EPOCH_FILLED); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -1546,8 +1546,8 @@ public TokenCodecEncoder putTimeUnit(final DirectBuffer src, final int srcOffset { switch (fieldOrderState()) { - case ACCESS_STATE_FILLED__EPOCH: - fieldOrderState(ACCESS_STATE_FILLED__TIMEUNIT); + case STATE_V0_EPOCH_FILLED: + fieldOrderState(STATE_V0_TIMEUNIT_FILLED); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -1574,8 +1574,8 @@ public TokenCodecEncoder putTimeUnit(final byte[] src, final int srcOffset, fina { switch (fieldOrderState()) { - case ACCESS_STATE_FILLED__EPOCH: - fieldOrderState(ACCESS_STATE_FILLED__TIMEUNIT); + case STATE_V0_EPOCH_FILLED: + fieldOrderState(STATE_V0_TIMEUNIT_FILLED); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -1605,8 +1605,8 @@ public TokenCodecEncoder timeUnit(final String value) { switch (fieldOrderState()) { - case ACCESS_STATE_FILLED__EPOCH: - fieldOrderState(ACCESS_STATE_FILLED__TIMEUNIT); + case STATE_V0_EPOCH_FILLED: + fieldOrderState(STATE_V0_TIMEUNIT_FILLED); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -1658,8 +1658,8 @@ public TokenCodecEncoder putSemanticType(final DirectBuffer src, final int srcOf { switch (fieldOrderState()) { - case ACCESS_STATE_FILLED__TIMEUNIT: - fieldOrderState(ACCESS_STATE_FILLED__SEMANTICTYPE); + case STATE_V0_TIMEUNIT_FILLED: + fieldOrderState(STATE_V0_SEMANTICTYPE_FILLED); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -1686,8 +1686,8 @@ public TokenCodecEncoder putSemanticType(final byte[] src, final int srcOffset, { switch (fieldOrderState()) { - case ACCESS_STATE_FILLED__TIMEUNIT: - fieldOrderState(ACCESS_STATE_FILLED__SEMANTICTYPE); + case STATE_V0_TIMEUNIT_FILLED: + fieldOrderState(STATE_V0_SEMANTICTYPE_FILLED); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -1717,8 +1717,8 @@ public TokenCodecEncoder semanticType(final String value) { switch (fieldOrderState()) { - case ACCESS_STATE_FILLED__TIMEUNIT: - fieldOrderState(ACCESS_STATE_FILLED__SEMANTICTYPE); + case STATE_V0_TIMEUNIT_FILLED: + fieldOrderState(STATE_V0_SEMANTICTYPE_FILLED); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -1770,8 +1770,8 @@ public TokenCodecEncoder putDescription(final DirectBuffer src, final int srcOff { switch (fieldOrderState()) { - case ACCESS_STATE_FILLED__SEMANTICTYPE: - fieldOrderState(ACCESS_STATE_FILLED__DESCRIPTION); + case STATE_V0_SEMANTICTYPE_FILLED: + fieldOrderState(STATE_V0_DESCRIPTION_FILLED); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -1798,8 +1798,8 @@ public TokenCodecEncoder putDescription(final byte[] src, final int srcOffset, f { switch (fieldOrderState()) { - case ACCESS_STATE_FILLED__SEMANTICTYPE: - fieldOrderState(ACCESS_STATE_FILLED__DESCRIPTION); + case STATE_V0_SEMANTICTYPE_FILLED: + fieldOrderState(STATE_V0_DESCRIPTION_FILLED); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -1829,8 +1829,8 @@ public TokenCodecEncoder description(final String value) { switch (fieldOrderState()) { - case ACCESS_STATE_FILLED__SEMANTICTYPE: - fieldOrderState(ACCESS_STATE_FILLED__DESCRIPTION); + case STATE_V0_SEMANTICTYPE_FILLED: + fieldOrderState(STATE_V0_DESCRIPTION_FILLED); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -1882,8 +1882,8 @@ public TokenCodecEncoder putReferencedName(final DirectBuffer src, final int src { switch (fieldOrderState()) { - case ACCESS_STATE_FILLED__DESCRIPTION: - fieldOrderState(ACCESS_STATE_FILLED__REFERENCEDNAME); + case STATE_V0_DESCRIPTION_FILLED: + fieldOrderState(STATE_V0_REFERENCEDNAME_FILLED); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -1910,8 +1910,8 @@ public TokenCodecEncoder putReferencedName(final byte[] src, final int srcOffset { switch (fieldOrderState()) { - case ACCESS_STATE_FILLED__DESCRIPTION: - fieldOrderState(ACCESS_STATE_FILLED__REFERENCEDNAME); + case STATE_V0_DESCRIPTION_FILLED: + fieldOrderState(STATE_V0_REFERENCEDNAME_FILLED); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); @@ -1941,8 +1941,8 @@ public TokenCodecEncoder referencedName(final String value) { switch (fieldOrderState()) { - case ACCESS_STATE_FILLED__DESCRIPTION: - fieldOrderState(ACCESS_STATE_FILLED__REFERENCEDNAME); + case STATE_V0_DESCRIPTION_FILLED: + fieldOrderState(STATE_V0_REFERENCEDNAME_FILLED); break; default: throw new IllegalStateException("Unexpected state: " + fieldOrderState()); diff --git a/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java b/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java index b231949b2a..b491c202d3 100644 --- a/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java +++ b/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java @@ -109,7 +109,6 @@ void disallowsReEncodingLatestVariableLengthField() } @Test - @Disabled("Decoding checks not implemented yet.") void disallowsSkippingDecodingOfVariableLengthField() { final MultipleVarLengthEncoder encoder = new MultipleVarLengthEncoder() @@ -125,7 +124,6 @@ void disallowsSkippingDecodingOfVariableLengthField() } @Test - @Disabled("Decoding checks not implemented yet.") void disallowsReDecodingEarlierVariableLengthField() { final MultipleVarLengthEncoder encoder = new MultipleVarLengthEncoder() @@ -143,7 +141,6 @@ void disallowsReDecodingEarlierVariableLengthField() } @Test - @Disabled("Decoding checks not implemented yet.") void disallowsReDecodingLatestVariableLengthField() { final MultipleVarLengthEncoder encoder = new MultipleVarLengthEncoder() @@ -300,7 +297,6 @@ void allowsReEncodingGroupElementBlockFieldAfterTopLevelVariableLengthField() } @Test - @Disabled("Decoding checks not implemented yet.") void disallowsSkippingDecodingOfGroupBeforeVariableLengthField() { final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder() @@ -320,7 +316,6 @@ void disallowsSkippingDecodingOfGroupBeforeVariableLengthField() } @Test - @Disabled("Decoding checks not implemented yet.") void disallowsReDecodingVariableLengthFieldAfterGroup() { final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder() @@ -345,7 +340,6 @@ void disallowsReDecodingVariableLengthFieldAfterGroup() } @Test - @Disabled("Decoding checks not implemented yet.") void disallowsReDecodingGroupBeforeVariableLengthField() { final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder() @@ -470,7 +464,6 @@ void disallowsReEncodingGroupElementVariableLengthField() } @Test - @Disabled("Decoding checks not implemented yet.") void disallowsReDecodingGroupElementVariableLengthField() { final VarLengthInsideGroupEncoder encoder = new VarLengthInsideGroupEncoder() @@ -490,12 +483,12 @@ void disallowsReDecodingGroupElementVariableLengthField() assertThat(decoder.a(), equalTo(42)); final VarLengthInsideGroupDecoder.BDecoder bs = decoder.b(); assertThat(bs.count(), equalTo(2)); - assertThat(bs.next().c(), equalTo("abc")); + assertThat(bs.next().c(), equalTo(1)); + assertThat(bs.d(), equalTo("abc")); assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, bs::d); } @Test - @Disabled("Decoding checks not implemented yet.") void disallowsSkippingDecodingOfGroupElementVariableLengthFieldToNextElement() { final VarLengthInsideGroupEncoder encoder = new VarLengthInsideGroupEncoder() @@ -520,7 +513,6 @@ void disallowsSkippingDecodingOfGroupElementVariableLengthFieldToNextElement() } @Test - @Disabled("Decoding checks not implemented yet.") void disallowsSkippingDecodingOfGroupElementVariableLengthFieldToTopLevel() { final VarLengthInsideGroupEncoder encoder = new VarLengthInsideGroupEncoder() @@ -542,7 +534,6 @@ void disallowsSkippingDecodingOfGroupElementVariableLengthFieldToTopLevel() } @Test - @Disabled("Decoding checks not implemented yet.") void disallowsSkippingDecodingOfGroupElement() { final VarLengthInsideGroupEncoder encoder = new VarLengthInsideGroupEncoder() @@ -562,7 +553,7 @@ void disallowsSkippingDecodingOfGroupElement() assertThat(decoder.a(), equalTo(42)); final VarLengthInsideGroupDecoder.BDecoder bs = decoder.b(); assertThat(bs.count(), equalTo(2)); - assertThat(bs.next().c(), equalTo("abc")); + assertThat(bs.next().c(), equalTo(1)); assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::e); } @@ -855,9 +846,12 @@ void allowsReDecodingGroupElementCompositeViaEncoderReference() assertThat(c.y(), equalTo(4)); } - // TODO test more of SBE: - // - array setters - // - alternative varData setters - // - bitset setters + // TODO + // test more of SBE: + // - array getters/setters + // - alternative varData getters/setters + // - bitset getters/setters // - versioning + // improve and test error message + // - should include description of problem } diff --git a/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/SchemaExtensionTest.java b/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/SchemaExtensionTest.java index 1774666139..78bcc0e9c7 100644 --- a/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/SchemaExtensionTest.java +++ b/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/SchemaExtensionTest.java @@ -21,6 +21,7 @@ import org.agrona.generation.CompilerUtil; import org.agrona.generation.StringWriterOutputManager; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import uk.co.real_logic.sbe.Tests; import uk.co.real_logic.sbe.ir.Ir; @@ -42,6 +43,7 @@ import static uk.co.real_logic.sbe.generation.java.ReflectionUtil.*; @SuppressWarnings("MethodLength") +@Disabled // TODO class SchemaExtensionTest { private static final Class BUFFER_CLASS = MutableDirectBuffer.class; diff --git a/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/SkipAndDecodedLengthTest.java b/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/SkipAndDecodedLengthTest.java index 8d8fcf3d4d..101bf6ccc7 100644 --- a/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/SkipAndDecodedLengthTest.java +++ b/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/SkipAndDecodedLengthTest.java @@ -18,6 +18,7 @@ import baseline.CarDecoder; import baseline.MessageHeaderDecoder; import org.agrona.concurrent.UnsafeBuffer; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import uk.co.real_logic.sbe.EncodedCarTestBase; @@ -30,6 +31,7 @@ class SkipAndDecodedLengthTest extends EncodedCarTestBase private static final int MSG_BUFFER_CAPACITY = 4 * 1024; @Test + @Disabled // TODO void shouldRewindAfterReadingFullMessage() { final ByteBuffer encodedMsgBuffer = ByteBuffer.allocate(MSG_BUFFER_CAPACITY); From 2331fb190f44a49137484fcc17296fa177dd0373 Mon Sep 17 00:00:00 2001 From: Zach Bray Date: Wed, 14 Jun 2023 12:26:20 +0100 Subject: [PATCH 05/50] [Java] Test field order checks across versions. "New" decoders (in Java at least) read null-like values for fields that are not present in "old" messages. There are no restrictions on the order in which these "new" fields are accessed when decoding "old" data. In Java, a version check returns a null-like value when a field is not present in the acting version. This behaviour matches the wiki documentation. C#, currently, has different behaviour: the application code needs to interrogate a boolean property, e.g., `IsFooInActingVersion`, before accessing the data property, e.g., `Foo`. The property itself assumes that the field is present. It will attempt to read it from the buffer and possibly advance a `limit` field. In this commit, I have moved the state transition beneath the existing version check in Java decoders. This change allows these fields to be accessed when decoding "old" messages without changing the state of decoding, which seems appropriate as there are no side-effects on internal state, e.g., the `limit` field. Also, in this commit, I have expanded the test coverage to include the getter and setter variants for fixed-size arrays and variable-length data. --- .../sbe/generation/java/JavaGenerator.java | 32 +- .../sbe/ir/generated/FrameCodecDecoder.java | 72 + .../sbe/ir/generated/TokenCodecDecoder.java | 264 ++++ .../real_logic/sbe/FieldOrderCheckTest.java | 1285 ++++++++++++++++- .../resources/field-order-check-schema.xml | 167 ++- 5 files changed, 1768 insertions(+), 52 deletions(-) diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java index ca38f6cd5b..932374a5a1 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java @@ -1092,18 +1092,19 @@ private void generateDecoderVarData( .append(indent).append(" return ").append(sizeOfLengthField).append(";\n") .append(indent).append(" }\n"); + final CharSequence stateTransition = + generateFieldOrderStateTransitions(fieldOrderModel, indent + " ", token); + sb.append("\n") .append(indent).append(" public int ").append(methodPropName).append("Length()\n") .append(indent).append(" {\n") .append(generateArrayFieldNotPresentCondition(token.version(), indent)) + .append(stateTransition) .append(indent).append(" final int limit = parentMessage.limit();\n") .append(indent).append(" return ").append(PrimitiveType.UINT32 == lengthType ? "(int)" : "") .append(generateGet(lengthType, "limit", byteOrderStr)).append(";\n") .append(indent).append(" }\n"); - final CharSequence stateTransition = - generateFieldOrderStateTransitions(fieldOrderModel, indent + " ", token); - generateDataDecodeMethods(sb, token, propertyName, sizeOfLengthField, lengthType, byteOrderStr, characterEncoding, stateTransition, indent); @@ -1215,7 +1216,15 @@ private void generateDataDecodeMethods( stateTransition, indent); - generateVarDataWrapDecoder(sb, token, propertyName, sizeOfLengthField, lengthType, byteOrderStr, indent); + generateVarDataWrapDecoder( + sb, + token, + propertyName, + sizeOfLengthField, + lengthType, + byteOrderStr, + stateTransition, + indent); if (null != characterEncoding) { @@ -1274,12 +1283,13 @@ private void generateVarDataWrapDecoder( final int sizeOfLengthField, final PrimitiveType lengthType, final String byteOrderStr, - final String indent) + final CharSequence stateTransition, final String indent) { new Formatter(sb).format("\n" + indent + " public void wrap%s(final %s wrapBuffer)\n" + indent + " {\n" + "%s" + + stateTransition + indent + " final int headerLength = %d;\n" + indent + " final int limit = parentMessage.limit();\n" + indent + " final int dataLength = %s%s;\n" + @@ -2252,8 +2262,8 @@ private CharSequence generatePrimitivePropertyDecode( "\n" + indent + " public %s %s()\n" + indent + " {\n" + - stateTransition + "%s" + + stateTransition + indent + " return %s;\n" + indent + " }\n\n", javaTypeName, @@ -2405,12 +2415,12 @@ private CharSequence generatePrimitiveArrayPropertyDecode( new Formatter(sb).format("\n" + indent + " public %s %s(final int index)\n" + indent + " {\n" + - stateTransition + indent + " if (index < 0 || index >= %d)\n" + indent + " {\n" + indent + " throw new IndexOutOfBoundsException(\"index out of range: index=\" + index);\n" + indent + " }\n\n" + "%s" + + stateTransition + indent + " final int pos = offset + %d + (index * %d);\n\n" + indent + " return %s;\n" + indent + " }\n\n", @@ -3465,8 +3475,8 @@ private void generateEnumDecoder( "\n" + indent + " public %s %sRaw()\n" + indent + " {\n" + - stateTransition + "%s" + + stateTransition + indent + " return %s;\n" + indent + " }\n", javaTypeName, @@ -3478,8 +3488,8 @@ private void generateEnumDecoder( "\n" + indent + " public %s %s()\n" + indent + " {\n" + - stateTransition + "%s" + + stateTransition + indent + " return %s.get(%s);\n" + indent + " }\n\n", enumName, @@ -3541,8 +3551,8 @@ private void generateBitSetProperty( new Formatter(sb).format("\n" + indent + " public %s %s()\n" + indent + " {\n" + - generateFieldOrderStateTransitions(fieldOrderModel, indent + " ", propertyToken) + "%s" + + generateFieldOrderStateTransitions(fieldOrderModel, indent + " ", propertyToken) + indent + " %s.wrap(buffer, offset + %d);\n" + indent + " return %s;\n" + indent + " }\n", @@ -3575,8 +3585,8 @@ private void generateCompositeProperty( new Formatter(sb).format("\n" + indent + " public %s %s()\n" + indent + " {\n" + - generateFieldOrderStateTransitions(fieldOrderModel, indent + " ", propertyToken) + "%s" + + generateFieldOrderStateTransitions(fieldOrderModel, indent + " ", propertyToken) + indent + " %s.wrap(buffer, offset + %d);\n" + indent + " return %s;\n" + indent + " }\n", diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java index dbc245540e..e6cc47e5cd 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java @@ -397,6 +397,18 @@ public static int packageNameHeaderLength() public int packageNameLength() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_BLOCK: + fieldOrderState(STATE_V0_PACKAGENAME_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int limit = parentMessage.limit(); return (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); } @@ -474,6 +486,18 @@ public int getPackageName(final byte[] dst, final int dstOffset, final int lengt public void wrapPackageName(final DirectBuffer wrapBuffer) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_BLOCK: + fieldOrderState(STATE_V0_PACKAGENAME_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -543,6 +567,18 @@ public static int namespaceNameHeaderLength() public int namespaceNameLength() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_PACKAGENAME_FILLED: + fieldOrderState(STATE_V0_NAMESPACENAME_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int limit = parentMessage.limit(); return (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); } @@ -620,6 +656,18 @@ public int getNamespaceName(final byte[] dst, final int dstOffset, final int len public void wrapNamespaceName(final DirectBuffer wrapBuffer) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_PACKAGENAME_FILLED: + fieldOrderState(STATE_V0_NAMESPACENAME_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -689,6 +737,18 @@ public static int semanticVersionHeaderLength() public int semanticVersionLength() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_NAMESPACENAME_FILLED: + fieldOrderState(STATE_V0_SEMANTICVERSION_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int limit = parentMessage.limit(); return (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); } @@ -766,6 +826,18 @@ public int getSemanticVersion(final byte[] dst, final int dstOffset, final int l public void wrapSemanticVersion(final DirectBuffer wrapBuffer) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_NAMESPACENAME_FILLED: + fieldOrderState(STATE_V0_SEMANTICVERSION_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java index da989d7527..f12a44f20d 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java @@ -862,6 +862,18 @@ public static int nameHeaderLength() public int nameLength() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_BLOCK: + fieldOrderState(STATE_V0_NAME_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int limit = parentMessage.limit(); return (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); } @@ -939,6 +951,18 @@ public int getName(final byte[] dst, final int dstOffset, final int length) public void wrapName(final DirectBuffer wrapBuffer) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_BLOCK: + fieldOrderState(STATE_V0_NAME_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -1008,6 +1032,18 @@ public static int constValueHeaderLength() public int constValueLength() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_NAME_FILLED: + fieldOrderState(STATE_V0_CONSTVALUE_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int limit = parentMessage.limit(); return (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); } @@ -1085,6 +1121,18 @@ public int getConstValue(final byte[] dst, final int dstOffset, final int length public void wrapConstValue(final DirectBuffer wrapBuffer) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_NAME_FILLED: + fieldOrderState(STATE_V0_CONSTVALUE_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -1154,6 +1202,18 @@ public static int minValueHeaderLength() public int minValueLength() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_CONSTVALUE_FILLED: + fieldOrderState(STATE_V0_MINVALUE_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int limit = parentMessage.limit(); return (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); } @@ -1231,6 +1291,18 @@ public int getMinValue(final byte[] dst, final int dstOffset, final int length) public void wrapMinValue(final DirectBuffer wrapBuffer) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_CONSTVALUE_FILLED: + fieldOrderState(STATE_V0_MINVALUE_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -1300,6 +1372,18 @@ public static int maxValueHeaderLength() public int maxValueLength() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_MINVALUE_FILLED: + fieldOrderState(STATE_V0_MAXVALUE_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int limit = parentMessage.limit(); return (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); } @@ -1377,6 +1461,18 @@ public int getMaxValue(final byte[] dst, final int dstOffset, final int length) public void wrapMaxValue(final DirectBuffer wrapBuffer) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_MINVALUE_FILLED: + fieldOrderState(STATE_V0_MAXVALUE_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -1446,6 +1542,18 @@ public static int nullValueHeaderLength() public int nullValueLength() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_MAXVALUE_FILLED: + fieldOrderState(STATE_V0_NULLVALUE_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int limit = parentMessage.limit(); return (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); } @@ -1523,6 +1631,18 @@ public int getNullValue(final byte[] dst, final int dstOffset, final int length) public void wrapNullValue(final DirectBuffer wrapBuffer) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_MAXVALUE_FILLED: + fieldOrderState(STATE_V0_NULLVALUE_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -1592,6 +1712,18 @@ public static int characterEncodingHeaderLength() public int characterEncodingLength() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_NULLVALUE_FILLED: + fieldOrderState(STATE_V0_CHARACTERENCODING_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int limit = parentMessage.limit(); return (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); } @@ -1669,6 +1801,18 @@ public int getCharacterEncoding(final byte[] dst, final int dstOffset, final int public void wrapCharacterEncoding(final DirectBuffer wrapBuffer) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_NULLVALUE_FILLED: + fieldOrderState(STATE_V0_CHARACTERENCODING_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -1738,6 +1882,18 @@ public static int epochHeaderLength() public int epochLength() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_CHARACTERENCODING_FILLED: + fieldOrderState(STATE_V0_EPOCH_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int limit = parentMessage.limit(); return (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); } @@ -1815,6 +1971,18 @@ public int getEpoch(final byte[] dst, final int dstOffset, final int length) public void wrapEpoch(final DirectBuffer wrapBuffer) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_CHARACTERENCODING_FILLED: + fieldOrderState(STATE_V0_EPOCH_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -1884,6 +2052,18 @@ public static int timeUnitHeaderLength() public int timeUnitLength() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_EPOCH_FILLED: + fieldOrderState(STATE_V0_TIMEUNIT_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int limit = parentMessage.limit(); return (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); } @@ -1961,6 +2141,18 @@ public int getTimeUnit(final byte[] dst, final int dstOffset, final int length) public void wrapTimeUnit(final DirectBuffer wrapBuffer) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_EPOCH_FILLED: + fieldOrderState(STATE_V0_TIMEUNIT_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -2030,6 +2222,18 @@ public static int semanticTypeHeaderLength() public int semanticTypeLength() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_TIMEUNIT_FILLED: + fieldOrderState(STATE_V0_SEMANTICTYPE_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int limit = parentMessage.limit(); return (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); } @@ -2107,6 +2311,18 @@ public int getSemanticType(final byte[] dst, final int dstOffset, final int leng public void wrapSemanticType(final DirectBuffer wrapBuffer) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_TIMEUNIT_FILLED: + fieldOrderState(STATE_V0_SEMANTICTYPE_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -2176,6 +2392,18 @@ public static int descriptionHeaderLength() public int descriptionLength() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_SEMANTICTYPE_FILLED: + fieldOrderState(STATE_V0_DESCRIPTION_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int limit = parentMessage.limit(); return (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); } @@ -2253,6 +2481,18 @@ public int getDescription(final byte[] dst, final int dstOffset, final int lengt public void wrapDescription(final DirectBuffer wrapBuffer) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_SEMANTICTYPE_FILLED: + fieldOrderState(STATE_V0_DESCRIPTION_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); @@ -2322,6 +2562,18 @@ public static int referencedNameHeaderLength() public int referencedNameLength() { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_DESCRIPTION_FILLED: + fieldOrderState(STATE_V0_REFERENCEDNAME_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int limit = parentMessage.limit(); return (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); } @@ -2399,6 +2651,18 @@ public int getReferencedName(final byte[] dst, final int dstOffset, final int le public void wrapReferencedName(final DirectBuffer wrapBuffer) { + if (DEBUG_MODE) + { + switch (fieldOrderState()) + { + case STATE_V0_DESCRIPTION_FILLED: + fieldOrderState(STATE_V0_REFERENCEDNAME_FILLED); + break; + default: + throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + } + } + final int headerLength = 2; final int limit = parentMessage.limit(); final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF); diff --git a/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java b/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java index b491c202d3..d6f8a2f1de 100644 --- a/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java +++ b/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java @@ -19,12 +19,15 @@ import order.check.*; import org.agrona.ExpandableArrayBuffer; import org.agrona.MutableDirectBuffer; +import org.agrona.concurrent.UnsafeBuffer; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assumptions.assumeFalse; @@ -78,7 +81,7 @@ void allowsReEncodingTopLevelPrimitiveFields() } @Test - void disallowsSkippingEncodingOfVariableLengthField() + void disallowsSkippingEncodingOfVariableLengthField1() { final MultipleVarLengthEncoder encoder = new MultipleVarLengthEncoder() .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); @@ -87,54 +90,104 @@ void disallowsSkippingEncodingOfVariableLengthField() } @Test - void disallowsReEncodingEarlierVariableLengthFields() + void disallowsSkippingEncodingOfVariableLengthField2() { final MultipleVarLengthEncoder encoder = new MultipleVarLengthEncoder() .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); encoder.a(42); - encoder.b("abc"); - encoder.c("def"); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.b("ghi")); + final CharSequence def = new StringBuilder("def"); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.c(def)); } @Test - void disallowsReEncodingLatestVariableLengthField() + void disallowsSkippingEncodingOfVariableLengthField3() { final MultipleVarLengthEncoder encoder = new MultipleVarLengthEncoder() .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); encoder.a(42); - encoder.b("abc"); - encoder.c("def"); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.c("ghi")); + final byte[] value = "def".getBytes(); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.putC(value, 0, value.length)); } @Test - void disallowsSkippingDecodingOfVariableLengthField() + void disallowsSkippingEncodingOfVariableLengthField4() + { + final MultipleVarLengthEncoder encoder = new MultipleVarLengthEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + final byte[] value = "def".getBytes(); + final UnsafeBuffer buffer = new UnsafeBuffer(value); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.putC(buffer, 0, buffer.capacity())); + } + + @Test + void disallowsReEncodingEarlierVariableLengthFields() { final MultipleVarLengthEncoder encoder = new MultipleVarLengthEncoder() .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); encoder.a(42); encoder.b("abc"); encoder.c("def"); - - final MultipleVarLengthDecoder decoder = new MultipleVarLengthDecoder() - .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); - assertThat(decoder.a(), equalTo(42)); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::c); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.b("ghi")); } @Test - void disallowsReDecodingEarlierVariableLengthField() + void disallowsReEncodingLatestVariableLengthField() { final MultipleVarLengthEncoder encoder = new MultipleVarLengthEncoder() .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); encoder.a(42); encoder.b("abc"); encoder.c("def"); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.c("ghi")); + } - final MultipleVarLengthDecoder decoder = new MultipleVarLengthDecoder() - .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); - assertThat(decoder.a(), equalTo(42)); + @Test + void disallowsSkippingDecodingOfVariableLengthField1() + { + final MultipleVarLengthDecoder decoder = decodeUntilVarLengthFields(); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::c); + } + + @Test + void disallowsSkippingDecodingOfVariableLengthField2() + { + final MultipleVarLengthDecoder decoder = decodeUntilVarLengthFields(); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> decoder.wrapC(new UnsafeBuffer())); + } + + @Test + void disallowsSkippingDecodingOfVariableLengthField3() + { + final MultipleVarLengthDecoder decoder = decodeUntilVarLengthFields(); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> decoder.getC(new StringBuilder())); + } + + @Test + void disallowsSkippingDecodingOfVariableLengthField4() + { + final MultipleVarLengthDecoder decoder = decodeUntilVarLengthFields(); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> decoder.getC(new byte[3], 0, 3)); + } + + @Test + void disallowsSkippingDecodingOfVariableLengthField5() + { + final MultipleVarLengthDecoder decoder = decodeUntilVarLengthFields(); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::cLength); + } + + @Test + void disallowsSkippingDecodingOfVariableLengthField6() + { + final MultipleVarLengthDecoder decoder = decodeUntilVarLengthFields(); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> decoder.getC(new ExpandableArrayBuffer(), 0, 3)); + } + + @Test + void disallowsReDecodingEarlierVariableLengthField() + { + final MultipleVarLengthDecoder decoder = decodeUntilVarLengthFields(); assertThat(decoder.b(), equalTo("abc")); assertThat(decoder.c(), equalTo("def")); assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::b); @@ -142,6 +195,14 @@ void disallowsReDecodingEarlierVariableLengthField() @Test void disallowsReDecodingLatestVariableLengthField() + { + final MultipleVarLengthDecoder decoder = decodeUntilVarLengthFields(); + assertThat(decoder.b(), equalTo("abc")); + assertThat(decoder.c(), equalTo("def")); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::c); + } + + private MultipleVarLengthDecoder decodeUntilVarLengthFields() { final MultipleVarLengthEncoder encoder = new MultipleVarLengthEncoder() .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); @@ -152,9 +213,7 @@ void disallowsReDecodingLatestVariableLengthField() final MultipleVarLengthDecoder decoder = new MultipleVarLengthDecoder() .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); assertThat(decoder.a(), equalTo(42)); - assertThat(decoder.b(), equalTo("abc")); - assertThat(decoder.c(), equalTo("def")); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::c); + return decoder; } @Test @@ -224,12 +283,59 @@ void allowsReEncodingPrimitiveFieldInGroupElementAfterTopLevelVariableLengthFiel } @Test - void disallowsEncodingGroupElementWithoutCallingNext() + @Disabled("Our access checks are too strict to allow the behaviour in this test.") + void allowsReWrappingGroupDecoderAfterAccessingLength() + { + final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + final GroupAndVarLengthEncoder.BEncoder bEncoder = encoder.bCount(2); + bEncoder + .next() + .c(1) + .next() + .c(2); + encoder.d("abc"); + + final GroupAndVarLengthDecoder decoder = new GroupAndVarLengthDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(42)); + assertThat(decoder.b().count(), equalTo(2)); + final GroupAndVarLengthDecoder.BDecoder b = decoder.b(); + assertThat(b.next().c(), equalTo(1)); + assertThat(b.next().c(), equalTo(3)); + assertThat(decoder.d(), equalTo("abc")); + } + + @Test + void disallowsEncodingGroupElementBeforeCallingNext() + { + final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + final GroupAndVarLengthEncoder.BEncoder bEncoder = encoder.bCount(1); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.c(1)); + } + + @Test + void disallowsDecodingGroupElementBeforeCallingNext() { final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder() .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); encoder.a(42); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.bCount(1).c(1)); + encoder.bCount(2) + .next() + .c(1) + .next() + .c(2); + encoder.d("abc"); + + final GroupAndVarLengthDecoder decoder = new GroupAndVarLengthDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(42)); + final GroupAndVarLengthDecoder.BDecoder bs = decoder.b(); + assertThat(bs.count(), equalTo(2)); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, bs::c); } @Test @@ -297,7 +403,7 @@ void allowsReEncodingGroupElementBlockFieldAfterTopLevelVariableLengthField() } @Test - void disallowsSkippingDecodingOfGroupBeforeVariableLengthField() + void disallowsMissedDecodingOfGroupBeforeVariableLengthField() { final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder() .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); @@ -416,7 +522,7 @@ void allowsReEncodingGroupElementPrimitiveFieldAfterElementVariableLengthField() } @Test - void disallowsSkippingGroupElementVariableLengthFieldToEncodeAtTopLevel() + void disallowsMissedGroupElementVariableLengthFieldToEncodeAtTopLevel() { final VarLengthInsideGroupEncoder encoder = new VarLengthInsideGroupEncoder() .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); @@ -426,7 +532,7 @@ void disallowsSkippingGroupElementVariableLengthFieldToEncodeAtTopLevel() } @Test - void disallowsSkippingGroupElementVariableLengthFieldToEncodeNextElement() + void disallowsMissedGroupElementVariableLengthFieldToEncodeNextElement() { final VarLengthInsideGroupEncoder encoder = new VarLengthInsideGroupEncoder() .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); @@ -437,7 +543,7 @@ void disallowsSkippingGroupElementVariableLengthFieldToEncodeNextElement() } @Test - void disallowsSkippingGroupElementEncoding() + void disallowsMissedGroupElementEncoding() { final VarLengthInsideGroupEncoder encoder = new VarLengthInsideGroupEncoder() .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); @@ -489,7 +595,7 @@ void disallowsReDecodingGroupElementVariableLengthField() } @Test - void disallowsSkippingDecodingOfGroupElementVariableLengthFieldToNextElement() + void disallowsMissedDecodingOfGroupElementVariableLengthFieldToNextElement() { final VarLengthInsideGroupEncoder encoder = new VarLengthInsideGroupEncoder() .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); @@ -513,7 +619,7 @@ void disallowsSkippingDecodingOfGroupElementVariableLengthFieldToNextElement() } @Test - void disallowsSkippingDecodingOfGroupElementVariableLengthFieldToTopLevel() + void disallowsMissedDecodingOfGroupElementVariableLengthFieldToTopLevel() { final VarLengthInsideGroupEncoder encoder = new VarLengthInsideGroupEncoder() .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); @@ -534,7 +640,7 @@ void disallowsSkippingDecodingOfGroupElementVariableLengthFieldToTopLevel() } @Test - void disallowsSkippingDecodingOfGroupElement() + void disallowsMissedDecodingOfGroupElement() { final VarLengthInsideGroupEncoder encoder = new VarLengthInsideGroupEncoder() .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); @@ -632,7 +738,7 @@ void allowsEncodingEmptyNestedGroupsInSchemaDefinedOrder() } @Test - void disallowsSkippingEncodingOfNestedGroup() + void disallowsMissedEncodingOfNestedGroup() { final NestedGroupsEncoder encoder = new NestedGroupsEncoder() .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); @@ -665,6 +771,36 @@ void allowsEncodingAndDecodingCompositeInsideGroupInSchemaDefinedOrder() assertThat(c.y(), equalTo(4)); } + @Test + void disallowsEncodingCompositeInsideGroupBeforeCallingNext() + { + final CompositeInsideGroupEncoder encoder = new CompositeInsideGroupEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a().x(1).y(2); + final CompositeInsideGroupEncoder.BEncoder bEncoder = encoder.bCount(1); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, bEncoder::c); + } + + @Test + void disallowsDecodingCompositeInsideGroupBeforeCallingNext() + { + final CompositeInsideGroupEncoder encoder = new CompositeInsideGroupEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a().x(1).y(2); + encoder.bCount(1) + .next() + .c().x(3).y(4); + + final CompositeInsideGroupDecoder decoder = new CompositeInsideGroupDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + final PointDecoder a = decoder.a(); + assertThat(a.x(), equalTo(1)); + assertThat(a.y(), equalTo(2)); + final CompositeInsideGroupDecoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, b::c); + } + @Test @Disabled("Our access checks are too strict to allow the behaviour in this test.") void allowsReEncodingTopLevelCompositeViaReWrap() @@ -759,6 +895,7 @@ void allowsReEncodingGroupElementCompositeViaEncoderReference() } @Test + @Disabled("Our access checks are too strict to allow the behaviour in this test.") void allowsReDecodingTopLevelCompositeViaReWrap() { final CompositeInsideGroupEncoder encoder = new CompositeInsideGroupEncoder() @@ -773,6 +910,11 @@ void allowsReDecodingTopLevelCompositeViaReWrap() final PointDecoder a1 = decoder.a(); assertThat(a1.x(), equalTo(1)); assertThat(a1.y(), equalTo(2)); + final CompositeInsideGroupDecoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + final PointDecoder c = b.next().c(); + assertThat(c.x(), equalTo(3)); + assertThat(c.y(), equalTo(4)); final PointDecoder a2 = decoder.a(); assertThat(a2.x(), equalTo(1)); assertThat(a2.y(), equalTo(2)); @@ -793,6 +935,11 @@ void allowsReDecodingTopLevelCompositeViaEncoderReference() final PointDecoder a = decoder.a(); assertThat(a.x(), equalTo(1)); assertThat(a.y(), equalTo(2)); + final CompositeInsideGroupDecoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + final PointDecoder c = b.next().c(); + assertThat(c.x(), equalTo(3)); + assertThat(c.y(), equalTo(4)); assertThat(a.x(), equalTo(1)); assertThat(a.y(), equalTo(2)); } @@ -846,12 +993,1070 @@ void allowsReDecodingGroupElementCompositeViaEncoderReference() assertThat(c.y(), equalTo(4)); } - // TODO - // test more of SBE: - // - array getters/setters - // - alternative varData getters/setters - // - bitset getters/setters - // - versioning - // improve and test error message - // - should include description of problem + @Test + void allowsNewDecoderToDecodeAddedPrimitiveField() + { + final AddPrimitiveV1Encoder encoder = new AddPrimitiveV1Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(1).b(2); + + final AddPrimitiveV1Decoder decoder = new AddPrimitiveV1Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(1)); + assertThat(decoder.b(), equalTo(2)); + } + + @Test + void allowsNewDecoderToDecodeMissingPrimitiveFieldAsNullValue() + { + final AddPrimitiveV0Encoder encoder = new AddPrimitiveV0Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(1); + + modifyHeaderToLookLikeVersion0(); + + final AddPrimitiveV1Decoder decoder = new AddPrimitiveV1Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(1)); + assertThat(decoder.b(), equalTo(AddPrimitiveV1Decoder.bNullValue())); + } + + @Test + void allowsNewDecoderToDecodeAddedPrimitiveFieldBeforeGroup() + { + final AddPrimitiveBeforeGroupV1Encoder encoder = new AddPrimitiveBeforeGroupV1Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(1).d(3).bCount(1).next().c(2); + + final AddPrimitiveBeforeGroupV1Decoder decoder = new AddPrimitiveBeforeGroupV1Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(1)); + assertThat(decoder.d(), equalTo(3)); + final AddPrimitiveBeforeGroupV1Decoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + assertThat(b.next().c(), equalTo(2)); + } + + @Test + void allowsNewDecoderToDecodeMissingPrimitiveFieldBeforeGroupAsNullValue() + { + final AddPrimitiveBeforeGroupV0Encoder encoder = new AddPrimitiveBeforeGroupV0Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(1).bCount(1).next().c(2); + + modifyHeaderToLookLikeVersion0(); + + final AddPrimitiveBeforeGroupV1Decoder decoder = new AddPrimitiveBeforeGroupV1Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(1)); + assertThat(decoder.d(), equalTo(AddPrimitiveBeforeGroupV1Decoder.dNullValue())); + final AddPrimitiveBeforeGroupV1Decoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + assertThat(b.next().c(), equalTo(2)); + } + + @Test + void allowsNewDecoderToSkipPresentButAddedPrimitiveFieldBeforeGroup() + { + final AddPrimitiveBeforeGroupV1Encoder encoder = new AddPrimitiveBeforeGroupV1Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(1).d(3).bCount(1).next().c(2); + + final AddPrimitiveBeforeGroupV1Decoder decoder = new AddPrimitiveBeforeGroupV1Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(1)); + final AddPrimitiveBeforeGroupV1Decoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + assertThat(b.next().c(), equalTo(2)); + } + + @Test + void allowsOldDecoderToSkipAddedPrimitiveFieldBeforeGroup() + { + final AddPrimitiveBeforeGroupV1Encoder encoder = new AddPrimitiveBeforeGroupV1Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(1).d(3).bCount(1).next().c(2); + + modifyHeaderToLookLikeVersion1(); + + final AddPrimitiveBeforeGroupV0Decoder decoder = new AddPrimitiveBeforeGroupV0Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(1)); + final AddPrimitiveBeforeGroupV0Decoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + assertThat(b.next().c(), equalTo(2)); + } + + @Test + void allowsNewDecoderToDecodeAddedPrimitiveFieldBeforeVarData() + { + final AddPrimitiveBeforeVarDataV1Encoder encoder = new AddPrimitiveBeforeVarDataV1Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(1).c(3).b("abc"); + + final AddPrimitiveBeforeVarDataV1Decoder decoder = new AddPrimitiveBeforeVarDataV1Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(1)); + assertThat(decoder.c(), equalTo(3)); + assertThat(decoder.b(), equalTo("abc")); + } + + @Test + void allowsNewDecoderToDecodeMissingPrimitiveFieldBeforeVarDataAsNullValue() + { + final AddPrimitiveBeforeVarDataV0Encoder encoder = new AddPrimitiveBeforeVarDataV0Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(1).b("abc"); + + modifyHeaderToLookLikeVersion0(); + + final AddPrimitiveBeforeVarDataV1Decoder decoder = new AddPrimitiveBeforeVarDataV1Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(1)); + assertThat(decoder.c(), equalTo(AddPrimitiveBeforeVarDataV1Decoder.cNullValue())); + assertThat(decoder.b(), equalTo("abc")); + } + + @Test + void allowsNewDecoderToSkipPresentButAddedPrimitiveFieldBeforeVarData() + { + final AddPrimitiveBeforeVarDataV1Encoder encoder = new AddPrimitiveBeforeVarDataV1Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(1).c(3).b("abc"); + + final AddPrimitiveBeforeVarDataV1Decoder decoder = new AddPrimitiveBeforeVarDataV1Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(1)); + assertThat(decoder.b(), equalTo("abc")); + } + + @Test + void allowsOldDecoderToSkipAddedPrimitiveFieldBeforeVarData() + { + final AddPrimitiveBeforeVarDataV1Encoder encoder = new AddPrimitiveBeforeVarDataV1Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(1).c(3).b("abc"); + + modifyHeaderToLookLikeVersion1(); + + final AddPrimitiveBeforeVarDataV0Decoder decoder = new AddPrimitiveBeforeVarDataV0Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(1)); + assertThat(decoder.b(), equalTo("abc")); + } + + @Test + void allowsNewDecoderToDecodeAddedPrimitiveFieldInsideGroup() + { + final AddPrimitiveInsideGroupV1Encoder encoder = new AddPrimitiveInsideGroupV1Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(1).bCount(1).next().c(2).d(3); + + final AddPrimitiveInsideGroupV1Decoder decoder = new AddPrimitiveInsideGroupV1Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(1)); + final AddPrimitiveInsideGroupV1Decoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + assertThat(b.next().c(), equalTo(2)); + assertThat(b.d(), equalTo(3)); + } + + @Test + void allowsNewDecoderToDecodeMissingPrimitiveFieldInsideGroupAsNullValue() + { + final AddPrimitiveInsideGroupV0Encoder encoder = new AddPrimitiveInsideGroupV0Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(1).bCount(1).next().c(2); + + modifyHeaderToLookLikeVersion0(); + + final AddPrimitiveInsideGroupV1Decoder decoder = new AddPrimitiveInsideGroupV1Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(1)); + final AddPrimitiveInsideGroupV1Decoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + assertThat(b.next().c(), equalTo(2)); + assertThat(b.d(), equalTo(AddPrimitiveInsideGroupV1Decoder.BDecoder.dNullValue())); + } + + @Test + void allowsNewDecoderToSkipPresentButAddedPrimitiveFieldInsideGroup() + { + final AddPrimitiveInsideGroupV1Encoder encoder = new AddPrimitiveInsideGroupV1Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(1).bCount(2).next().c(2).d(3).next().c(4).d(5); + + final AddPrimitiveInsideGroupV1Decoder decoder = new AddPrimitiveInsideGroupV1Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(1)); + final AddPrimitiveInsideGroupV1Decoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(2)); + assertThat(b.next().c(), equalTo(2)); + assertThat(b.next().c(), equalTo(4)); + } + + @Test + void allowsOldDecoderToSkipAddedPrimitiveFieldInsideGroup() + { + final AddPrimitiveInsideGroupV1Encoder encoder = new AddPrimitiveInsideGroupV1Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(1).bCount(2).next().c(2).d(3).next().c(4).d(5); + + modifyHeaderToLookLikeVersion1(); + + final AddPrimitiveInsideGroupV0Decoder decoder = new AddPrimitiveInsideGroupV0Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(1)); + final AddPrimitiveInsideGroupV0Decoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(2)); + assertThat(b.next().c(), equalTo(2)); + assertThat(b.next().c(), equalTo(4)); + } + + @Test + void allowsNewDecoderToDecodeAddedGroupBeforeVarData() + { + final AddGroupBeforeVarDataV1Encoder encoder = new AddGroupBeforeVarDataV1Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(1).cCount(1).next().d(2); + encoder.b("abc"); + + final AddGroupBeforeVarDataV1Decoder decoder = new AddGroupBeforeVarDataV1Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(1)); + final AddGroupBeforeVarDataV1Decoder.CDecoder c = decoder.c(); + assertThat(c.count(), equalTo(1)); + assertThat(c.next().d(), equalTo(2)); + assertThat(decoder.b(), equalTo("abc")); + } + + @Test + void allowsNewDecoderToDecodeMissingGroupBeforeVarDataAsNullValue() + { + final AddGroupBeforeVarDataV0Encoder encoder = new AddGroupBeforeVarDataV0Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(1).b("abc"); + + modifyHeaderToLookLikeVersion0(); + + final AddGroupBeforeVarDataV1Decoder decoder = new AddGroupBeforeVarDataV1Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(1)); + final AddGroupBeforeVarDataV1Decoder.CDecoder c = decoder.c(); + assertThat(c.count(), equalTo(0)); + assertThat(decoder.b(), equalTo("abc")); + } + + @Test + void allowsNewDecoderToSkipMissingGroupBeforeVarData() + { + final AddGroupBeforeVarDataV0Encoder encoder = new AddGroupBeforeVarDataV0Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(1).b("abc"); + + modifyHeaderToLookLikeVersion0(); + + final AddGroupBeforeVarDataV1Decoder decoder = new AddGroupBeforeVarDataV1Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(1)); + assertThat(decoder.b(), equalTo("abc")); + } + + @Test + void disallowsNewDecoderToSkipPresentButAddedGroupBeforeVarData() + { + final AddGroupBeforeVarDataV1Encoder encoder = new AddGroupBeforeVarDataV1Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(1).cCount(1).next().d(2); + encoder.b("abc"); + + final AddGroupBeforeVarDataV1Decoder decoder = new AddGroupBeforeVarDataV1Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(1)); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::b); + } + + @Test + void allowsOldDecoderToSkipAddedGroupBeforeVarData() + { + final AddGroupBeforeVarDataV1Encoder encoder = new AddGroupBeforeVarDataV1Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + messageHeaderEncoder.numGroups(1); + encoder.a(1).cCount(1).next().d(2); + encoder.b("abc"); + + modifyHeaderToLookLikeVersion1(); + + final AddGroupBeforeVarDataV0Decoder decoder = new AddGroupBeforeVarDataV0Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(1)); + + for (int i = 0; i < messageHeaderDecoder.numGroups(); i++) + { + skipGroup(decoder); + } + + assertThat(decoder.b(), equalTo("abc")); + } + + private void skipGroup(final AddGroupBeforeVarDataV0Decoder decoder) + { + final GroupSizeEncodingDecoder groupSizeEncodingDecoder = new GroupSizeEncodingDecoder() + .wrap(buffer, decoder.limit()); + final int bytesToSkip = groupSizeEncodingDecoder.encodedLength() + + groupSizeEncodingDecoder.blockLength() * groupSizeEncodingDecoder.numInGroup(); + decoder.limit(decoder.limit() + bytesToSkip); + } + + @Test + void allowsNewDecoderToDecodeAddedEnumFieldBeforeGroup() + { + final AddEnumBeforeGroupV1Encoder encoder = new AddEnumBeforeGroupV1Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(1).d(Direction.BUY).bCount(1).next().c(2); + + final AddEnumBeforeGroupV1Decoder decoder = new AddEnumBeforeGroupV1Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(1)); + assertThat(decoder.d(), equalTo(Direction.BUY)); + final AddEnumBeforeGroupV1Decoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + assertThat(b.next().c(), equalTo(2)); + } + + @Test + void allowsNewDecoderToDecodeMissingEnumFieldBeforeGroupAsNullValue() + { + final AddEnumBeforeGroupV0Encoder encoder = new AddEnumBeforeGroupV0Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(1).bCount(1).next().c(2); + + modifyHeaderToLookLikeVersion0(); + + final AddEnumBeforeGroupV1Decoder decoder = new AddEnumBeforeGroupV1Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(1)); + assertThat(decoder.d(), equalTo(Direction.NULL_VAL)); + final AddEnumBeforeGroupV1Decoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + assertThat(b.next().c(), equalTo(2)); + } + + @Test + void allowsNewDecoderToSkipPresentButAddedEnumFieldBeforeGroup() + { + final AddEnumBeforeGroupV1Encoder encoder = new AddEnumBeforeGroupV1Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(1).d(Direction.SELL).bCount(1).next().c(2); + + final AddEnumBeforeGroupV1Decoder decoder = new AddEnumBeforeGroupV1Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(1)); + final AddEnumBeforeGroupV1Decoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + assertThat(b.next().c(), equalTo(2)); + } + + @Test + void allowsOldDecoderToSkipAddedEnumFieldBeforeGroup() + { + final AddEnumBeforeGroupV1Encoder encoder = new AddEnumBeforeGroupV1Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(1).d(Direction.BUY).bCount(1).next().c(2); + + modifyHeaderToLookLikeVersion1(); + + final AddEnumBeforeGroupV0Decoder decoder = new AddEnumBeforeGroupV0Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(1)); + final AddEnumBeforeGroupV0Decoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + assertThat(b.next().c(), equalTo(2)); + } + + @Test + void allowsNewDecoderToDecodeAddedCompositeFieldBeforeGroup() + { + final AddCompositeBeforeGroupV1Encoder encoder = new AddCompositeBeforeGroupV1Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(1).d().x(-1).y(-2); + encoder.bCount(1).next().c(2); + + final AddCompositeBeforeGroupV1Decoder decoder = new AddCompositeBeforeGroupV1Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(1)); + final PointDecoder d = decoder.d(); + assertThat(d, notNullValue()); + assertThat(d.x(), equalTo(-1)); + assertThat(d.y(), equalTo(-2)); + final AddCompositeBeforeGroupV1Decoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + assertThat(b.next().c(), equalTo(2)); + } + + @Test + void allowsNewDecoderToDecodeMissingCompositeFieldBeforeGroupAsNullValue() + { + final AddCompositeBeforeGroupV0Encoder encoder = new AddCompositeBeforeGroupV0Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(1).bCount(1).next().c(2); + + modifyHeaderToLookLikeVersion0(); + + final AddCompositeBeforeGroupV1Decoder decoder = new AddCompositeBeforeGroupV1Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(1)); + assertThat(decoder.d(), nullValue()); + final AddCompositeBeforeGroupV1Decoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + assertThat(b.next().c(), equalTo(2)); + } + + @Test + void allowsNewDecoderToSkipPresentButAddedCompositeFieldBeforeGroup() + { + final AddCompositeBeforeGroupV1Encoder encoder = new AddCompositeBeforeGroupV1Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(1).d().x(-1).y(-2); + encoder.bCount(1).next().c(2); + + final AddCompositeBeforeGroupV1Decoder decoder = new AddCompositeBeforeGroupV1Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(1)); + final AddCompositeBeforeGroupV1Decoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + assertThat(b.next().c(), equalTo(2)); + } + + @Test + void allowsOldDecoderToSkipAddedCompositeFieldBeforeGroup() + { + final AddCompositeBeforeGroupV1Encoder encoder = new AddCompositeBeforeGroupV1Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(1).d().x(-1).y(-2); + encoder.bCount(1).next().c(2); + + modifyHeaderToLookLikeVersion1(); + + final AddCompositeBeforeGroupV0Decoder decoder = new AddCompositeBeforeGroupV0Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(1)); + final AddCompositeBeforeGroupV0Decoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + assertThat(b.next().c(), equalTo(2)); + } + + @Test + void allowsNewDecoderToDecodeAddedArrayFieldBeforeGroup() + { + final AddArrayBeforeGroupV1Encoder encoder = new AddArrayBeforeGroupV1Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(1) + .putD((short)1, (short)2, (short)3, (short)4) + .bCount(1) + .next().c(2); + + final AddArrayBeforeGroupV1Decoder decoder = new AddArrayBeforeGroupV1Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(1)); + assertThat(decoder.d(0), equalTo((short)1)); + assertThat(decoder.d(1), equalTo((short)2)); + assertThat(decoder.d(2), equalTo((short)3)); + assertThat(decoder.d(3), equalTo((short)4)); + final AddArrayBeforeGroupV1Decoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + assertThat(b.next().c(), equalTo(2)); + } + + @Test + void allowsNewDecoderToDecodeMissingArrayFieldBeforeGroupAsNullValue() + { + final AddArrayBeforeGroupV0Encoder encoder = new AddArrayBeforeGroupV0Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(1).bCount(1).next().c(2); + + modifyHeaderToLookLikeVersion0(); + + final AddArrayBeforeGroupV1Decoder decoder = new AddArrayBeforeGroupV1Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(1)); + assertThat(decoder.d(0), equalTo(AddArrayBeforeGroupV1Decoder.dNullValue())); + assertThat(decoder.d(1), equalTo(AddArrayBeforeGroupV1Decoder.dNullValue())); + assertThat(decoder.d(2), equalTo(AddArrayBeforeGroupV1Decoder.dNullValue())); + assertThat(decoder.d(3), equalTo(AddArrayBeforeGroupV1Decoder.dNullValue())); + final AddArrayBeforeGroupV1Decoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + assertThat(b.next().c(), equalTo(2)); + } + + @Test + void allowsNewDecoderToSkipPresentButAddedArrayFieldBeforeGroup() + { + final AddArrayBeforeGroupV1Encoder encoder = new AddArrayBeforeGroupV1Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(1) + .putD((short)1, (short)2, (short)3, (short)4) + .bCount(1) + .next().c(2); + + final AddArrayBeforeGroupV1Decoder decoder = new AddArrayBeforeGroupV1Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(1)); + final AddArrayBeforeGroupV1Decoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + assertThat(b.next().c(), equalTo(2)); + } + + @Test + void allowsOldDecoderToSkipAddedArrayFieldBeforeGroup() + { + final AddArrayBeforeGroupV1Encoder encoder = new AddArrayBeforeGroupV1Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(1) + .putD((short)1, (short)2, (short)3, (short)4) + .bCount(1) + .next().c(2); + + modifyHeaderToLookLikeVersion1(); + + final AddArrayBeforeGroupV0Decoder decoder = new AddArrayBeforeGroupV0Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(1)); + final AddArrayBeforeGroupV0Decoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + assertThat(b.next().c(), equalTo(2)); + } + + @Test + void allowsNewDecoderToDecodeAddedBitSetFieldBeforeGroup() + { + final AddBitSetBeforeGroupV1Encoder encoder = new AddBitSetBeforeGroupV1Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(1).d().guacamole(true).cheese(true); + encoder.bCount(1).next().c(2); + + final AddBitSetBeforeGroupV1Decoder decoder = new AddBitSetBeforeGroupV1Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(1)); + final FlagsDecoder d = decoder.d(); + assertThat(d, notNullValue()); + assertThat(d.guacamole(), equalTo(true)); + assertThat(d.cheese(), equalTo(true)); + assertThat(d.sourCream(), equalTo(false)); + final AddBitSetBeforeGroupV1Decoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + assertThat(b.next().c(), equalTo(2)); + } + + @Test + void allowsNewDecoderToDecodeMissingBitSetFieldBeforeGroupAsNullValue() + { + final AddBitSetBeforeGroupV0Encoder encoder = new AddBitSetBeforeGroupV0Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(1).bCount(1).next().c(2); + + modifyHeaderToLookLikeVersion0(); + + final AddBitSetBeforeGroupV1Decoder decoder = new AddBitSetBeforeGroupV1Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(1)); + assertThat(decoder.d(), nullValue()); + final AddBitSetBeforeGroupV1Decoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + assertThat(b.next().c(), equalTo(2)); + } + + @Test + void allowsNewDecoderToSkipPresentButAddedBitSetFieldBeforeGroup() + { + final AddBitSetBeforeGroupV1Encoder encoder = new AddBitSetBeforeGroupV1Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(1).d().guacamole(true).cheese(true); + encoder.bCount(1).next().c(2); + + final AddBitSetBeforeGroupV1Decoder decoder = new AddBitSetBeforeGroupV1Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(1)); + final AddBitSetBeforeGroupV1Decoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + assertThat(b.next().c(), equalTo(2)); + } + + @Test + void allowsOldDecoderToSkipAddedBitSetFieldBeforeGroup() + { + final AddBitSetBeforeGroupV1Encoder encoder = new AddBitSetBeforeGroupV1Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(1).d().guacamole(true).cheese(true); + encoder.bCount(1).next().c(2); + + modifyHeaderToLookLikeVersion1(); + + final AddBitSetBeforeGroupV0Decoder decoder = new AddBitSetBeforeGroupV0Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(1)); + final AddBitSetBeforeGroupV0Decoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + assertThat(b.next().c(), equalTo(2)); + } + + @Test + void allowsEncodingAndDecodingEnumInsideGroupInSchemaDefinedOrder() + { + final EnumInsideGroupEncoder encoder = new EnumInsideGroupEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(Direction.BUY) + .bCount(1) + .next() + .c(Direction.SELL); + + final EnumInsideGroupDecoder decoder = new EnumInsideGroupDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(Direction.BUY)); + final EnumInsideGroupDecoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + assertThat(b.next().c(), equalTo(Direction.SELL)); + } + + @Test + void disallowsEncodingEnumInsideGroupBeforeCallingNext() + { + final EnumInsideGroupEncoder encoder = new EnumInsideGroupEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(Direction.BUY); + final EnumInsideGroupEncoder.BEncoder bEncoder = encoder.bCount(1); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.c(Direction.SELL)); + } + + @Test + void disallowsDecodingEnumInsideGroupBeforeCallingNext() + { + final EnumInsideGroupEncoder encoder = new EnumInsideGroupEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(Direction.BUY) + .bCount(1) + .next() + .c(Direction.SELL); + + final EnumInsideGroupDecoder decoder = new EnumInsideGroupDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(Direction.BUY)); + final EnumInsideGroupDecoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, b::c); + } + + @Test + @Disabled("Our access checks are too strict to allow the behaviour in this test.") + void allowsReEncodingTopLevelEnum() + { + final EnumInsideGroupEncoder encoder = new EnumInsideGroupEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(Direction.BUY) + .bCount(1) + .next() + .c(Direction.SELL); + + encoder.a(Direction.SELL); + + + final EnumInsideGroupDecoder decoder = new EnumInsideGroupDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(Direction.SELL)); + final EnumInsideGroupDecoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + assertThat(b.next().c(), equalTo(Direction.SELL)); + } + + @Test + void allowsEncodingAndDecodingBitSetInsideGroupInSchemaDefinedOrder() + { + final BitSetInsideGroupEncoder encoder = new BitSetInsideGroupEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a().cheese(true).guacamole(true); + encoder.bCount(1) + .next() + .c().sourCream(true); + + final BitSetInsideGroupDecoder decoder = new BitSetInsideGroupDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + final FlagsDecoder a = decoder.a(); + assertThat(a.guacamole(), equalTo(true)); + assertThat(a.cheese(), equalTo(true)); + assertThat(a.sourCream(), equalTo(false)); + final BitSetInsideGroupDecoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + final FlagsDecoder c = b.next().c(); + assertThat(c.guacamole(), equalTo(false)); + assertThat(c.cheese(), equalTo(false)); + assertThat(c.sourCream(), equalTo(true)); + } + + @Test + void disallowsEncodingBitSetInsideGroupBeforeCallingNext() + { + final BitSetInsideGroupEncoder encoder = new BitSetInsideGroupEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a().cheese(true).guacamole(true); + final BitSetInsideGroupEncoder.BEncoder bEncoder = encoder.bCount(1); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, bEncoder::c); + } + + @Test + void disallowsDecodingBitSetInsideGroupBeforeCallingNext() + { + final BitSetInsideGroupEncoder encoder = new BitSetInsideGroupEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a().cheese(true).guacamole(true); + encoder.bCount(1) + .next() + .c().sourCream(true); + + final BitSetInsideGroupDecoder decoder = new BitSetInsideGroupDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + final FlagsDecoder a = decoder.a(); + assertThat(a.guacamole(), equalTo(true)); + assertThat(a.cheese(), equalTo(true)); + assertThat(a.sourCream(), equalTo(false)); + final BitSetInsideGroupDecoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, b::c); + } + + @Test + @Disabled("Our access checks are too strict to allow the behaviour in this test.") + void allowsReEncodingTopLevelBitSetViaReWrap() + { + final BitSetInsideGroupEncoder encoder = new BitSetInsideGroupEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a().cheese(true).guacamole(true); + encoder.bCount(1) + .next() + .c().sourCream(true); + + encoder.a().sourCream(true); + + final BitSetInsideGroupDecoder decoder = new BitSetInsideGroupDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + final FlagsDecoder a = decoder.a(); + assertThat(a.guacamole(), equalTo(true)); + assertThat(a.cheese(), equalTo(true)); + assertThat(a.sourCream(), equalTo(true)); + final BitSetInsideGroupDecoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + final FlagsDecoder c = b.next().c(); + assertThat(c.guacamole(), equalTo(false)); + assertThat(c.cheese(), equalTo(false)); + assertThat(c.sourCream(), equalTo(true)); + } + + @Test + void allowsEncodingAndDecodingArrayInsideGroupInSchemaDefinedOrder() + { + final ArrayInsideGroupEncoder encoder = new ArrayInsideGroupEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.putA((short)1, (short)2, (short)3, (short)4); + encoder.bCount(1) + .next() + .putC((short)5, (short)6, (short)7, (short)8); + + final ArrayInsideGroupDecoder decoder = new ArrayInsideGroupDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(0), equalTo((short)1)); + assertThat(decoder.a(1), equalTo((short)2)); + assertThat(decoder.a(2), equalTo((short)3)); + assertThat(decoder.a(3), equalTo((short)4)); + final ArrayInsideGroupDecoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + b.next(); + assertThat(b.c(0), equalTo((short)5)); + assertThat(b.c(1), equalTo((short)6)); + assertThat(b.c(2), equalTo((short)7)); + assertThat(b.c(3), equalTo((short)8)); + } + + @Test + void disallowsEncodingArrayInsideGroupBeforeCallingNext1() + { + final ArrayInsideGroupEncoder.BEncoder bEncoder = encodeUntilGroupWithArrayInside(); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.putC((short)5, (short)6, (short)7, (short)8)); + } + + @Test + void disallowsEncodingArrayInsideGroupBeforeCallingNext2() + { + final ArrayInsideGroupEncoder.BEncoder bEncoder = encodeUntilGroupWithArrayInside(); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.c(0, (short)5)); + } + + @Test + void disallowsEncodingArrayInsideGroupBeforeCallingNext3() + { + final ArrayInsideGroupEncoder.BEncoder bEncoder = encodeUntilGroupWithArrayInside(); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> + bEncoder.putC(new byte[] {5, 0, 6, 0, 7, 0, 8, 0}, 0, 8)); + } + + @Test + void disallowsEncodingArrayInsideGroupBeforeCallingNext4() + { + final ArrayInsideGroupEncoder.BEncoder bEncoder = encodeUntilGroupWithArrayInside(); + final UnsafeBuffer buffer = new UnsafeBuffer(new byte[8]); + buffer.putShort(0, (short)5); + buffer.putShort(2, (short)6); + buffer.putShort(4, (short)7); + buffer.putShort(6, (short)8); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> + bEncoder.putC(buffer, 0, 8)); + } + + private ArrayInsideGroupEncoder.BEncoder encodeUntilGroupWithArrayInside() + { + final ArrayInsideGroupEncoder encoder = new ArrayInsideGroupEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.putA((short)1, (short)2, (short)3, (short)4); + return encoder.bCount(1); + } + + @Test + void disallowsDecodingArrayInsideGroupBeforeCallingNext1() + { + final ArrayInsideGroupDecoder.BDecoder b = decodeUntilGroupWithArrayInside(); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> b.c(0)); + } + + @Test + void disallowsDecodingArrayInsideGroupBeforeCallingNext2() + { + final ArrayInsideGroupDecoder.BDecoder b = decodeUntilGroupWithArrayInside(); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> b.getC(new byte[8], 0, 8)); + } + + @Test + void disallowsDecodingArrayInsideGroupBeforeCallingNext3() + { + final ArrayInsideGroupDecoder.BDecoder b = decodeUntilGroupWithArrayInside(); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> b.getC(new ExpandableArrayBuffer(), 0, 8)); + } + + @Test + void disallowsDecodingArrayInsideGroupBeforeCallingNext4() + { + final ArrayInsideGroupDecoder.BDecoder b = decodeUntilGroupWithArrayInside(); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> b.wrapC(new UnsafeBuffer())); + } + + private ArrayInsideGroupDecoder.BDecoder decodeUntilGroupWithArrayInside() + { + final ArrayInsideGroupEncoder encoder = new ArrayInsideGroupEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.putA((short)1, (short)2, (short)3, (short)4); + encoder.bCount(1) + .next() + .putC((short)5, (short)6, (short)7, (short)8); + + final ArrayInsideGroupDecoder decoder = new ArrayInsideGroupDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(0), equalTo((short)1)); + assertThat(decoder.a(1), equalTo((short)2)); + assertThat(decoder.a(2), equalTo((short)3)); + assertThat(decoder.a(3), equalTo((short)4)); + final ArrayInsideGroupDecoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + return b; + } + + @Test + @Disabled("Our access checks are too strict to allow the behaviour in this test.") + void allowsReEncodingTopLevelArrayViaReWrap() + { + final ArrayInsideGroupEncoder encoder = new ArrayInsideGroupEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.putA((short)1, (short)2, (short)3, (short)4); + encoder.bCount(1) + .next() + .putC((short)5, (short)6, (short)7, (short)8); + + encoder.putA((short)9, (short)10, (short)11, (short)12); + + final ArrayInsideGroupDecoder decoder = new ArrayInsideGroupDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(0), equalTo((short)9)); + assertThat(decoder.a(1), equalTo((short)10)); + assertThat(decoder.a(2), equalTo((short)11)); + assertThat(decoder.a(3), equalTo((short)12)); + final ArrayInsideGroupDecoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + b.next(); + assertThat(b.c(0), equalTo((short)5)); + assertThat(b.c(1), equalTo((short)6)); + assertThat(b.c(2), equalTo((short)7)); + assertThat(b.c(3), equalTo((short)8)); + } + + @Test + void allowsEncodingAndDecodingGroupFieldsInSchemaDefinedOrder1() + { + final MultipleGroupsEncoder encoder = new MultipleGroupsEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + encoder.bCount(0); + encoder.dCount(1).next().e(43); + + final MultipleGroupsDecoder decoder = new MultipleGroupsDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(42)); + assertThat(decoder.b().count(), equalTo(0)); + final MultipleGroupsDecoder.DDecoder d = decoder.d(); + assertThat(d.count(), equalTo(1)); + assertThat(d.next().e(), equalTo(43)); + } + + @Test + void allowsEncodingAndDecodingGroupFieldsInSchemaDefinedOrder2() + { + final MultipleGroupsEncoder encoder = new MultipleGroupsEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(41); + encoder.bCount(1).next().c(42); + encoder.dCount(1).next().e(43); + + final MultipleGroupsDecoder decoder = new MultipleGroupsDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(41)); + final MultipleGroupsDecoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + assertThat(b.next().c(), equalTo(42)); + final MultipleGroupsDecoder.DDecoder d = decoder.d(); + assertThat(d.count(), equalTo(1)); + assertThat(d.next().e(), equalTo(43)); + } + + @Test + @Disabled("Our access checks are too strict to allow the behaviour in this test.") + void allowsReEncodingTopLevelPrimitiveFieldsAfterGroups() + { + final MultipleGroupsEncoder encoder = new MultipleGroupsEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(41); + encoder.bCount(1).next().c(42); + encoder.dCount(1).next().e(43); + encoder.a(44); + + final MultipleGroupsDecoder decoder = new MultipleGroupsDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(44)); + final MultipleGroupsDecoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + assertThat(b.next().c(), equalTo(42)); + final MultipleGroupsDecoder.DDecoder d = decoder.d(); + assertThat(d.count(), equalTo(1)); + assertThat(d.next().e(), equalTo(43)); + } + + @Test + void disallowsMissedEncodingOfGroupField() + { + final MultipleGroupsEncoder encoder = new MultipleGroupsEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(41); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.dCount(0)); + } + + @Test + void disallowsReEncodingEarlierGroupFields() + { + final MultipleGroupsEncoder encoder = new MultipleGroupsEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(41); + encoder.bCount(1).next().c(42); + encoder.dCount(1).next().e(43); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.bCount(1)); + } + + @Test + void disallowsReEncodingLatestGroupField() + { + final MultipleGroupsEncoder encoder = new MultipleGroupsEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(41); + encoder.bCount(1).next().c(42); + encoder.dCount(1).next().e(43); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.dCount(1)); + } + + @Test + void disallowsMissedDecodingOfGroupField() + { + final MultipleGroupsEncoder encoder = new MultipleGroupsEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(41); + encoder.bCount(1).next().c(42); + encoder.dCount(1).next().e(43); + + final MultipleGroupsDecoder decoder = new MultipleGroupsDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(41)); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::d); + } + + @Test + void disallowsReDecodingEarlierGroupField() + { + final MultipleGroupsEncoder encoder = new MultipleGroupsEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(41); + encoder.bCount(1).next().c(42); + encoder.dCount(1).next().e(43); + + final MultipleGroupsDecoder decoder = new MultipleGroupsDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(41)); + final MultipleGroupsDecoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + assertThat(b.next().c(), equalTo(42)); + final MultipleGroupsDecoder.DDecoder d = decoder.d(); + assertThat(d.count(), equalTo(1)); + assertThat(d.next().e(), equalTo(43)); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::b); + } + + @Test + void disallowsReDecodingLatestGroupField() + { + final MultipleGroupsEncoder encoder = new MultipleGroupsEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(41); + encoder.bCount(1).next().c(42); + encoder.dCount(1).next().e(43); + + final MultipleGroupsDecoder decoder = new MultipleGroupsDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(41)); + final MultipleGroupsDecoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + assertThat(b.next().c(), equalTo(42)); + final MultipleGroupsDecoder.DDecoder d = decoder.d(); + assertThat(d.count(), equalTo(1)); + assertThat(d.next().e(), equalTo(43)); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::d); + } + + private void modifyHeaderToLookLikeVersion0() + { + messageHeaderDecoder.wrap(buffer, OFFSET); + final int v1TemplateId = messageHeaderDecoder.templateId() + 1_000; + messageHeaderEncoder.wrap(buffer, OFFSET); + messageHeaderEncoder.templateId(v1TemplateId).version(0); + } + + private void modifyHeaderToLookLikeVersion1() + { + messageHeaderDecoder.wrap(buffer, OFFSET); + assert messageHeaderDecoder.version() == 1; + final int v0TemplateId = messageHeaderDecoder.templateId() - 1_000; + messageHeaderEncoder.wrap(buffer, OFFSET); + messageHeaderEncoder.templateId(v0TemplateId); + } + // TODO improve and test error message } diff --git a/sbe-tool/src/test/resources/field-order-check-schema.xml b/sbe-tool/src/test/resources/field-order-check-schema.xml index 817ec4dc48..75756b4906 100644 --- a/sbe-tool/src/test/resources/field-order-check-schema.xml +++ b/sbe-tool/src/test/resources/field-order-check-schema.xml @@ -4,13 +4,14 @@ id="1" description="Unit Test" byteOrder="littleEndian" - version="0"> + version="1"> + @@ -24,6 +25,16 @@ + + 1 + 2 + + + + 1 + 2 + 4 + @@ -71,4 +82,158 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 60b48032ea63295137d831f32ca311d5ae6e086d Mon Sep 17 00:00:00 2001 From: Zach Bray Date: Wed, 14 Jun 2023 13:43:00 +0100 Subject: [PATCH 06/50] [Java] Generate better error messages. Also, use an enum to represent codec states rather than a state number and generate (dot-based) state machine diagram in Javadoc for states enum. --- .../sbe/generation/java/FieldOrderModel.java | 61 +- .../sbe/generation/java/JavaGenerator.java | 77 +- .../sbe/ir/generated/FrameCodecDecoder.java | 217 ++--- .../sbe/ir/generated/FrameCodecEncoder.java | 143 ++-- .../sbe/ir/generated/TokenCodecDecoder.java | 740 +++++++++--------- .../sbe/ir/generated/TokenCodecEncoder.java | 442 ++++++----- 6 files changed, 888 insertions(+), 792 deletions(-) diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java index 0752d436ef..b28c4d5958 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java @@ -31,9 +31,9 @@ import static uk.co.real_logic.sbe.ir.GenerationUtil.collectGroups; import static uk.co.real_logic.sbe.ir.GenerationUtil.collectVarData; +@SuppressWarnings("DuplicatedCode") // there is no abstraction for visiting fields, groups, and varData final class FieldOrderModel { - private static final String PRINT_STATE_MACHINE = System.getProperty("sbe.print.state.machine"); private final Int2ObjectHashMap states = new Int2ObjectHashMap<>(); private final Map transitions = new LinkedHashMap<>(); private final Int2ObjectHashMap versionWrappedStates = new Int2ObjectHashMap<>(); @@ -119,18 +119,34 @@ public static FieldOrderModel findTransitions( ); }); - if (Objects.equals(PRINT_STATE_MACHINE, msgToken.name())) - { - final StringBuilder sb = new StringBuilder(); - sb.append("digraph G {\n"); - model.writeTransitions(sb); - sb.append("}\n\n"); - System.out.println(sb); - } - return model; } + @SuppressWarnings("CodeBlock2Expr") // lambdas without braces tend to conflict with checkstyle + public void generateGraph( + final StringBuilder sb, + final String indent) + { + sb.append(indent).append("digraph G {\n"); + transitions.values().forEach(transitionGroup -> + transitionGroup.transitions.forEach((context, transitions1) -> + { + transitions1.forEach(transition -> + { + transition.forEachStartState(startState -> + { + sb.append(indent).append(" ") + .append(startState.name) + .append(" -> ") + .append(transition.endState().name) + .append(" [label=\" ").append(transition.description).append(" \"];\n"); + }); + }); + }) + ); + sb.append(indent).append("}\n"); + } + private static void findVersions( final IntHashSet versions, final List fields, @@ -176,25 +192,6 @@ private static void findVersions( } } - @SuppressWarnings("CodeBlock2Expr") // lambdas without braces tend to conflict with checkstyle - private void writeTransitions(final StringBuilder sb) - { - transitions.values().forEach(transitionGroup -> - { - transitionGroup.transitions.forEach((context, transitions) -> - { - transitions.forEach(transition -> - { - transition.forEachStartState(startState -> - { - sb.append(" ").append(startState.name).append(" -> ").append(transition.endState().name); - sb.append(" [label=\"").append(transition.description).append("\"];\n"); - }); - }); - }); - }); - } - @SuppressWarnings("checkstyle:MethodLength") private List findTransitions( final List entryStates, @@ -224,7 +221,7 @@ private List findTransitions( allocateTransition( TransitionContext.NONE, - " ." + token.name() + "(value) ", + "." + token.name() + "(value)", token, fromStates, blockState.get()); @@ -352,7 +349,7 @@ private List findTransitions( continue; } - final State state = allocateState(prefix + token.name().toUpperCase() + "_FILLED"); + final State state = allocateState(prefix + token.name().toUpperCase() + "_DONE"); allocateTransition( TransitionContext.NONE, "." + token.name() + "(value)", @@ -373,7 +370,7 @@ private State allocateState(final String name) throw new IllegalStateException("Name is already reserved: " + name); } - final State state = new State(states.size() + 1, name); + final State state = new State(states.size(), name); states.put(state.number, state); return state; } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java index 932374a5a1..84ee6f6ed9 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java @@ -267,9 +267,14 @@ private void generateEncoder( } } - private static CharSequence stateFieldName(final FieldOrderModel.State state) + private static CharSequence qualifiedStateCase(final FieldOrderModel.State state) { - return "STATE_" + state.name(); + return "CodecState." + state.name(); + } + + private static CharSequence unqualifiedStateCase(final FieldOrderModel.State state) + { + return state.name(); } private static CharSequence generateFieldOrderStates(final FieldOrderModel fieldOrderModel) @@ -279,24 +284,35 @@ private static CharSequence generateFieldOrderStates(final FieldOrderModel field sb.append(" private static final boolean DEBUG_MODE = ") .append("!Boolean.getBoolean(\"agrona.disable.bounds.checks\");\n\n"); + sb.append(" /**\n"); + sb.append(" * The states in which a encoder/decoder/codec can live.\n"); + sb.append(" *\n"); + sb.append(" *

The state machine diagram below describes the valid state transitions\n"); + sb.append(" * according to the order in which fields may be accessed safely.\n"); + sb.append(" *\n"); + sb.append(" *

{@code\n");
+        fieldOrderModel.generateGraph(sb, "     *   ");
+        sb.append("     * }
\n"); + sb.append(" */\n"); + sb.append(" private enum CodecState\n") + .append(" {\n"); fieldOrderModel.forEachState(state -> - { - sb.append(" private static final int ").append(stateFieldName(state)).append(" = ") - .append(state.number()).append(";\n\n"); - }); + sb.append(" ").append(unqualifiedStateCase(state)).append(",\n")); + + sb.append(" }\n\n"); - sb.append(" private int fieldOrderState = ") - .append(stateFieldName(fieldOrderModel.notWrappedState())) + sb.append(" private CodecState codecState = ") + .append(qualifiedStateCase(fieldOrderModel.notWrappedState())) .append(";\n\n"); - sb.append(" private int fieldOrderState()\n") + sb.append(" private CodecState codecState()\n") .append(" {\n") - .append(" return fieldOrderState;\n") + .append(" return codecState;\n") .append(" }\n\n"); - sb.append(" private void fieldOrderState(int newState)\n") + sb.append(" private void codecState(CodecState newState)\n") .append(" {\n") - .append(" fieldOrderState = newState;\n") + .append(" codecState = newState;\n") .append(" }\n\n"); return sb; @@ -374,7 +390,7 @@ private static void generateFieldOrderStateTransitions( final FieldOrderModel.TransitionContext context, final Token token) { - sb.append(indent).append("switch (fieldOrderState())\n") + sb.append(indent).append("switch (codecState())\n") .append(indent).append("{\n"); final List transitions = fieldOrderModel.getTransitions(context, token); @@ -382,15 +398,16 @@ private static void generateFieldOrderStateTransitions( transitions.forEach(transition -> { transition.forEachStartState(startState -> - sb.append(indent).append(" case ").append(stateFieldName(startState)).append(":\n")); - sb.append(indent).append(" fieldOrderState(") - .append(stateFieldName(transition.endState())).append(");\n") + sb.append(indent).append(" case ").append(unqualifiedStateCase(startState)).append(":\n")); + sb.append(indent).append(" codecState(") + .append(qualifiedStateCase(transition.endState())).append(");\n") .append(indent).append(" break;\n"); }); sb.append(indent).append(" default:\n") .append(indent).append(" throw new IllegalStateException(") - .append("\"Unexpected state: \" + fieldOrderState());\n") + .append("\"Cannot access field \\\"").append(token.name()) + .append("\\\" in state: \" + codecState());\n") .append(indent).append("}\n"); } @@ -446,14 +463,14 @@ private static CharSequence generateFieldOrderStateTransitionForDecoderWrap( fieldOrderModel.forEachDecoderWrappedState((version, state) -> { sb.append(indent).append(" case ").append(version).append(":\n") - .append(indent).append(" fieldOrderState(") - .append(stateFieldName(state)).append(");\n") + .append(indent).append(" codecState(") + .append(qualifiedStateCase(state)).append(");\n") .append(indent).append(" break;\n"); }); sb.append(indent).append(" default:\n") - .append(indent).append(" fieldOrderState(") - .append(stateFieldName(fieldOrderModel.latestVersionWrappedState())).append(");\n") + .append(indent).append(" codecState(") + .append(qualifiedStateCase(fieldOrderModel.latestVersionWrappedState())).append(");\n") .append(indent).append(" break;\n") .append(indent).append(" }\n") .append(indent).append("}\n\n"); @@ -713,15 +730,15 @@ private void generateGroupDecoderClassHeader( .append(indent).append(" }\n"); sb.append("\n") - .append(indent).append(" private int fieldOrderState()\n") + .append(indent).append(" private CodecState codecState()\n") .append(indent).append(" {\n") - .append(indent).append(" return parentMessage.fieldOrderState();\n") + .append(indent).append(" return parentMessage.codecState();\n") .append(indent).append(" }\n"); sb.append("\n") - .append(indent).append(" private void fieldOrderState(final int newState)\n") + .append(indent).append(" private void codecState(final CodecState newState)\n") .append(indent).append(" {\n") - .append(indent).append(" parentMessage.fieldOrderState(newState);\n") + .append(indent).append(" parentMessage.codecState(newState);\n") .append(indent).append(" }\n"); } @@ -839,15 +856,15 @@ private void generateGroupEncoderClassHeader( .append(ind).append(" }\n"); sb.append("\n") - .append(ind).append(" private int fieldOrderState()\n") + .append(ind).append(" private CodecState codecState()\n") .append(ind).append(" {\n") - .append(ind).append(" return parentMessage.fieldOrderState();\n") + .append(ind).append(" return parentMessage.codecState();\n") .append(ind).append(" }\n"); sb.append("\n") - .append(ind).append(" private void fieldOrderState(final int newState)\n") + .append(ind).append(" private void codecState(final CodecState newState)\n") .append(ind).append(" {\n") - .append(ind).append(" parentMessage.fieldOrderState(newState);\n") + .append(ind).append(" parentMessage.codecState(newState);\n") .append(ind).append(" }\n"); } @@ -3193,7 +3210,7 @@ private CharSequence generateEncoderFlyweightCode( " limit(offset + BLOCK_LENGTH);\n\n" + " if (DEBUG_MODE)\n" + " {\n" + - " fieldOrderState(" + stateFieldName(fieldOrderModel.latestVersionWrappedState()) + ");\n" + + " codecState(" + qualifiedStateCase(fieldOrderModel.latestVersionWrappedState()) + ");\n" + " }\n\n" + " return this;\n" + " }\n\n"; diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java index e6cc47e5cd..957aab7896 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java @@ -13,26 +13,43 @@ public final class FrameCodecDecoder { private static final boolean DEBUG_MODE = !Boolean.getBoolean("agrona.disable.bounds.checks"); - private static final int STATE_NOT_WRAPPED = 1; - - private static final int STATE_V0_BLOCK = 2; - - private static final int STATE_V0_PACKAGENAME_FILLED = 3; - - private static final int STATE_V0_NAMESPACENAME_FILLED = 4; - - private static final int STATE_V0_SEMANTICVERSION_FILLED = 5; + /** + * The states in which a encoder/decoder/codec can live. + * + *

The state machine diagram below describes the valid state transitions + * according to the order in which fields may be accessed safely. + * + *

{@code
+     *   digraph G {
+     *       NOT_WRAPPED -> V0_BLOCK [label="  .wrap(...)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  .irId(value)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  .irVersion(value)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  .schemaVersion(value)  "];
+     *       V0_BLOCK -> V0_PACKAGENAME_DONE [label="  .packageName(value)  "];
+     *       V0_PACKAGENAME_DONE -> V0_NAMESPACENAME_DONE [label="  .namespaceName(value)  "];
+     *       V0_NAMESPACENAME_DONE -> V0_SEMANTICVERSION_DONE [label="  .semanticVersion(value)  "];
+     *   }
+     * }
+ */ + private enum CodecState + { + NOT_WRAPPED, + V0_BLOCK, + V0_PACKAGENAME_DONE, + V0_NAMESPACENAME_DONE, + V0_SEMANTICVERSION_DONE, + } - private int fieldOrderState = STATE_NOT_WRAPPED; + private CodecState codecState = CodecState.NOT_WRAPPED; - private int fieldOrderState() + private CodecState codecState() { - return fieldOrderState; + return codecState; } - private void fieldOrderState(int newState) + private void codecState(CodecState newState) { - fieldOrderState = newState; + codecState = newState; } public static final int BLOCK_LENGTH = 12; @@ -110,10 +127,10 @@ public FrameCodecDecoder wrap( { switch(actingVersion) { case 0: - fieldOrderState(STATE_V0_BLOCK); + codecState(CodecState.V0_BLOCK); break; default: - fieldOrderState(STATE_V0_BLOCK); + codecState(CodecState.V0_BLOCK); break; } } @@ -225,13 +242,13 @@ public int irId() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_BLOCK: - fieldOrderState(STATE_V0_BLOCK); + case V0_BLOCK: + codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"irId\" in state: " + codecState()); } } @@ -288,13 +305,13 @@ public int irVersion() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_BLOCK: - fieldOrderState(STATE_V0_BLOCK); + case V0_BLOCK: + codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"irVersion\" in state: " + codecState()); } } @@ -351,13 +368,13 @@ public int schemaVersion() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_BLOCK: - fieldOrderState(STATE_V0_BLOCK); + case V0_BLOCK: + codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"schemaVersion\" in state: " + codecState()); } } @@ -399,13 +416,13 @@ public int packageNameLength() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_BLOCK: - fieldOrderState(STATE_V0_PACKAGENAME_FILLED); + case V0_BLOCK: + codecState(CodecState.V0_PACKAGENAME_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"packageName\" in state: " + codecState()); } } @@ -417,13 +434,13 @@ public int skipPackageName() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_BLOCK: - fieldOrderState(STATE_V0_PACKAGENAME_FILLED); + case V0_BLOCK: + codecState(CodecState.V0_PACKAGENAME_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"packageName\" in state: " + codecState()); } } @@ -440,13 +457,13 @@ public int getPackageName(final MutableDirectBuffer dst, final int dstOffset, fi { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_BLOCK: - fieldOrderState(STATE_V0_PACKAGENAME_FILLED); + case V0_BLOCK: + codecState(CodecState.V0_PACKAGENAME_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"packageName\" in state: " + codecState()); } } @@ -464,13 +481,13 @@ public int getPackageName(final byte[] dst, final int dstOffset, final int lengt { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_BLOCK: - fieldOrderState(STATE_V0_PACKAGENAME_FILLED); + case V0_BLOCK: + codecState(CodecState.V0_PACKAGENAME_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"packageName\" in state: " + codecState()); } } @@ -488,13 +505,13 @@ public void wrapPackageName(final DirectBuffer wrapBuffer) { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_BLOCK: - fieldOrderState(STATE_V0_PACKAGENAME_FILLED); + case V0_BLOCK: + codecState(CodecState.V0_PACKAGENAME_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"packageName\" in state: " + codecState()); } } @@ -509,13 +526,13 @@ public String packageName() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_BLOCK: - fieldOrderState(STATE_V0_PACKAGENAME_FILLED); + case V0_BLOCK: + codecState(CodecState.V0_PACKAGENAME_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"packageName\" in state: " + codecState()); } } @@ -569,13 +586,13 @@ public int namespaceNameLength() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_PACKAGENAME_FILLED: - fieldOrderState(STATE_V0_NAMESPACENAME_FILLED); + case V0_PACKAGENAME_DONE: + codecState(CodecState.V0_NAMESPACENAME_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"namespaceName\" in state: " + codecState()); } } @@ -587,13 +604,13 @@ public int skipNamespaceName() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_PACKAGENAME_FILLED: - fieldOrderState(STATE_V0_NAMESPACENAME_FILLED); + case V0_PACKAGENAME_DONE: + codecState(CodecState.V0_NAMESPACENAME_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"namespaceName\" in state: " + codecState()); } } @@ -610,13 +627,13 @@ public int getNamespaceName(final MutableDirectBuffer dst, final int dstOffset, { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_PACKAGENAME_FILLED: - fieldOrderState(STATE_V0_NAMESPACENAME_FILLED); + case V0_PACKAGENAME_DONE: + codecState(CodecState.V0_NAMESPACENAME_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"namespaceName\" in state: " + codecState()); } } @@ -634,13 +651,13 @@ public int getNamespaceName(final byte[] dst, final int dstOffset, final int len { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_PACKAGENAME_FILLED: - fieldOrderState(STATE_V0_NAMESPACENAME_FILLED); + case V0_PACKAGENAME_DONE: + codecState(CodecState.V0_NAMESPACENAME_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"namespaceName\" in state: " + codecState()); } } @@ -658,13 +675,13 @@ public void wrapNamespaceName(final DirectBuffer wrapBuffer) { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_PACKAGENAME_FILLED: - fieldOrderState(STATE_V0_NAMESPACENAME_FILLED); + case V0_PACKAGENAME_DONE: + codecState(CodecState.V0_NAMESPACENAME_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"namespaceName\" in state: " + codecState()); } } @@ -679,13 +696,13 @@ public String namespaceName() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_PACKAGENAME_FILLED: - fieldOrderState(STATE_V0_NAMESPACENAME_FILLED); + case V0_PACKAGENAME_DONE: + codecState(CodecState.V0_NAMESPACENAME_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"namespaceName\" in state: " + codecState()); } } @@ -739,13 +756,13 @@ public int semanticVersionLength() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_NAMESPACENAME_FILLED: - fieldOrderState(STATE_V0_SEMANTICVERSION_FILLED); + case V0_NAMESPACENAME_DONE: + codecState(CodecState.V0_SEMANTICVERSION_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"semanticVersion\" in state: " + codecState()); } } @@ -757,13 +774,13 @@ public int skipSemanticVersion() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_NAMESPACENAME_FILLED: - fieldOrderState(STATE_V0_SEMANTICVERSION_FILLED); + case V0_NAMESPACENAME_DONE: + codecState(CodecState.V0_SEMANTICVERSION_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"semanticVersion\" in state: " + codecState()); } } @@ -780,13 +797,13 @@ public int getSemanticVersion(final MutableDirectBuffer dst, final int dstOffset { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_NAMESPACENAME_FILLED: - fieldOrderState(STATE_V0_SEMANTICVERSION_FILLED); + case V0_NAMESPACENAME_DONE: + codecState(CodecState.V0_SEMANTICVERSION_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"semanticVersion\" in state: " + codecState()); } } @@ -804,13 +821,13 @@ public int getSemanticVersion(final byte[] dst, final int dstOffset, final int l { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_NAMESPACENAME_FILLED: - fieldOrderState(STATE_V0_SEMANTICVERSION_FILLED); + case V0_NAMESPACENAME_DONE: + codecState(CodecState.V0_SEMANTICVERSION_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"semanticVersion\" in state: " + codecState()); } } @@ -828,13 +845,13 @@ public void wrapSemanticVersion(final DirectBuffer wrapBuffer) { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_NAMESPACENAME_FILLED: - fieldOrderState(STATE_V0_SEMANTICVERSION_FILLED); + case V0_NAMESPACENAME_DONE: + codecState(CodecState.V0_SEMANTICVERSION_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"semanticVersion\" in state: " + codecState()); } } @@ -849,13 +866,13 @@ public String semanticVersion() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_NAMESPACENAME_FILLED: - fieldOrderState(STATE_V0_SEMANTICVERSION_FILLED); + case V0_NAMESPACENAME_DONE: + codecState(CodecState.V0_SEMANTICVERSION_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"semanticVersion\" in state: " + codecState()); } } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java index af8fb02bbc..5de82c0b5c 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java @@ -13,26 +13,43 @@ public final class FrameCodecEncoder { private static final boolean DEBUG_MODE = !Boolean.getBoolean("agrona.disable.bounds.checks"); - private static final int STATE_NOT_WRAPPED = 1; - - private static final int STATE_V0_BLOCK = 2; - - private static final int STATE_V0_PACKAGENAME_FILLED = 3; - - private static final int STATE_V0_NAMESPACENAME_FILLED = 4; - - private static final int STATE_V0_SEMANTICVERSION_FILLED = 5; + /** + * The states in which a encoder/decoder/codec can live. + * + *

The state machine diagram below describes the valid state transitions + * according to the order in which fields may be accessed safely. + * + *

{@code
+     *   digraph G {
+     *       NOT_WRAPPED -> V0_BLOCK [label="  .wrap(...)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  .irId(value)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  .irVersion(value)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  .schemaVersion(value)  "];
+     *       V0_BLOCK -> V0_PACKAGENAME_DONE [label="  .packageName(value)  "];
+     *       V0_PACKAGENAME_DONE -> V0_NAMESPACENAME_DONE [label="  .namespaceName(value)  "];
+     *       V0_NAMESPACENAME_DONE -> V0_SEMANTICVERSION_DONE [label="  .semanticVersion(value)  "];
+     *   }
+     * }
+ */ + private enum CodecState + { + NOT_WRAPPED, + V0_BLOCK, + V0_PACKAGENAME_DONE, + V0_NAMESPACENAME_DONE, + V0_SEMANTICVERSION_DONE, + } - private int fieldOrderState = STATE_NOT_WRAPPED; + private CodecState codecState = CodecState.NOT_WRAPPED; - private int fieldOrderState() + private CodecState codecState() { - return fieldOrderState; + return codecState; } - private void fieldOrderState(int newState) + private void codecState(CodecState newState) { - fieldOrderState = newState; + codecState = newState; } public static final int BLOCK_LENGTH = 12; @@ -100,7 +117,7 @@ public FrameCodecEncoder wrap(final MutableDirectBuffer buffer, final int offset if (DEBUG_MODE) { - fieldOrderState(STATE_V0_BLOCK); + codecState(CodecState.V0_BLOCK); } return this; @@ -183,13 +200,13 @@ public FrameCodecEncoder irId(final int value) { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_BLOCK: - fieldOrderState(STATE_V0_BLOCK); + case V0_BLOCK: + codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"irId\" in state: " + codecState()); } } @@ -247,13 +264,13 @@ public FrameCodecEncoder irVersion(final int value) { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_BLOCK: - fieldOrderState(STATE_V0_BLOCK); + case V0_BLOCK: + codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"irVersion\" in state: " + codecState()); } } @@ -311,13 +328,13 @@ public FrameCodecEncoder schemaVersion(final int value) { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_BLOCK: - fieldOrderState(STATE_V0_BLOCK); + case V0_BLOCK: + codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"schemaVersion\" in state: " + codecState()); } } @@ -360,13 +377,13 @@ public FrameCodecEncoder putPackageName(final DirectBuffer src, final int srcOff if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_BLOCK: - fieldOrderState(STATE_V0_PACKAGENAME_FILLED); + case V0_BLOCK: + codecState(CodecState.V0_PACKAGENAME_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"packageName\" in state: " + codecState()); } } @@ -388,13 +405,13 @@ public FrameCodecEncoder putPackageName(final byte[] src, final int srcOffset, f if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_BLOCK: - fieldOrderState(STATE_V0_PACKAGENAME_FILLED); + case V0_BLOCK: + codecState(CodecState.V0_PACKAGENAME_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"packageName\" in state: " + codecState()); } } @@ -419,13 +436,13 @@ public FrameCodecEncoder packageName(final String value) if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_BLOCK: - fieldOrderState(STATE_V0_PACKAGENAME_FILLED); + case V0_BLOCK: + codecState(CodecState.V0_PACKAGENAME_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"packageName\" in state: " + codecState()); } } @@ -472,13 +489,13 @@ public FrameCodecEncoder putNamespaceName(final DirectBuffer src, final int srcO if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_PACKAGENAME_FILLED: - fieldOrderState(STATE_V0_NAMESPACENAME_FILLED); + case V0_PACKAGENAME_DONE: + codecState(CodecState.V0_NAMESPACENAME_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"namespaceName\" in state: " + codecState()); } } @@ -500,13 +517,13 @@ public FrameCodecEncoder putNamespaceName(final byte[] src, final int srcOffset, if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_PACKAGENAME_FILLED: - fieldOrderState(STATE_V0_NAMESPACENAME_FILLED); + case V0_PACKAGENAME_DONE: + codecState(CodecState.V0_NAMESPACENAME_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"namespaceName\" in state: " + codecState()); } } @@ -531,13 +548,13 @@ public FrameCodecEncoder namespaceName(final String value) if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_PACKAGENAME_FILLED: - fieldOrderState(STATE_V0_NAMESPACENAME_FILLED); + case V0_PACKAGENAME_DONE: + codecState(CodecState.V0_NAMESPACENAME_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"namespaceName\" in state: " + codecState()); } } @@ -584,13 +601,13 @@ public FrameCodecEncoder putSemanticVersion(final DirectBuffer src, final int sr if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_NAMESPACENAME_FILLED: - fieldOrderState(STATE_V0_SEMANTICVERSION_FILLED); + case V0_NAMESPACENAME_DONE: + codecState(CodecState.V0_SEMANTICVERSION_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"semanticVersion\" in state: " + codecState()); } } @@ -612,13 +629,13 @@ public FrameCodecEncoder putSemanticVersion(final byte[] src, final int srcOffse if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_NAMESPACENAME_FILLED: - fieldOrderState(STATE_V0_SEMANTICVERSION_FILLED); + case V0_NAMESPACENAME_DONE: + codecState(CodecState.V0_SEMANTICVERSION_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"semanticVersion\" in state: " + codecState()); } } @@ -643,13 +660,13 @@ public FrameCodecEncoder semanticVersion(final String value) if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_NAMESPACENAME_FILLED: - fieldOrderState(STATE_V0_SEMANTICVERSION_FILLED); + case V0_NAMESPACENAME_DONE: + codecState(CodecState.V0_SEMANTICVERSION_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"semanticVersion\" in state: " + codecState()); } } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java index f12a44f20d..6f0ace424b 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java @@ -13,42 +13,66 @@ public final class TokenCodecDecoder { private static final boolean DEBUG_MODE = !Boolean.getBoolean("agrona.disable.bounds.checks"); - private static final int STATE_NOT_WRAPPED = 1; - - private static final int STATE_V0_BLOCK = 2; - - private static final int STATE_V0_NAME_FILLED = 3; - - private static final int STATE_V0_CONSTVALUE_FILLED = 4; - - private static final int STATE_V0_MINVALUE_FILLED = 5; - - private static final int STATE_V0_MAXVALUE_FILLED = 6; - - private static final int STATE_V0_NULLVALUE_FILLED = 7; - - private static final int STATE_V0_CHARACTERENCODING_FILLED = 8; - - private static final int STATE_V0_EPOCH_FILLED = 9; - - private static final int STATE_V0_TIMEUNIT_FILLED = 10; - - private static final int STATE_V0_SEMANTICTYPE_FILLED = 11; - - private static final int STATE_V0_DESCRIPTION_FILLED = 12; - - private static final int STATE_V0_REFERENCEDNAME_FILLED = 13; - - private int fieldOrderState = STATE_NOT_WRAPPED; - - private int fieldOrderState() - { - return fieldOrderState; - } - - private void fieldOrderState(int newState) - { - fieldOrderState = newState; + /** + * The states in which a encoder/decoder/codec can live. + * + *

The state machine diagram below describes the valid state transitions + * according to the order in which fields may be accessed safely. + * + *

{@code
+     *   digraph G {
+     *       NOT_WRAPPED -> V0_BLOCK [label="  .wrap(...)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  .tokenOffset(value)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  .tokenSize(value)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  .fieldId(value)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  .tokenVersion(value)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  .componentTokenCount(value)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  .signal(value)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  .primitiveType(value)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  .byteOrder(value)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  .presence(value)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  .deprecated(value)  "];
+     *       V0_BLOCK -> V0_NAME_DONE [label="  .name(value)  "];
+     *       V0_NAME_DONE -> V0_CONSTVALUE_DONE [label="  .constValue(value)  "];
+     *       V0_CONSTVALUE_DONE -> V0_MINVALUE_DONE [label="  .minValue(value)  "];
+     *       V0_MINVALUE_DONE -> V0_MAXVALUE_DONE [label="  .maxValue(value)  "];
+     *       V0_MAXVALUE_DONE -> V0_NULLVALUE_DONE [label="  .nullValue(value)  "];
+     *       V0_NULLVALUE_DONE -> V0_CHARACTERENCODING_DONE [label="  .characterEncoding(value)  "];
+     *       V0_CHARACTERENCODING_DONE -> V0_EPOCH_DONE [label="  .epoch(value)  "];
+     *       V0_EPOCH_DONE -> V0_TIMEUNIT_DONE [label="  .timeUnit(value)  "];
+     *       V0_TIMEUNIT_DONE -> V0_SEMANTICTYPE_DONE [label="  .semanticType(value)  "];
+     *       V0_SEMANTICTYPE_DONE -> V0_DESCRIPTION_DONE [label="  .description(value)  "];
+     *       V0_DESCRIPTION_DONE -> V0_REFERENCEDNAME_DONE [label="  .referencedName(value)  "];
+     *   }
+     * }
+ */ + private enum CodecState + { + NOT_WRAPPED, + V0_BLOCK, + V0_NAME_DONE, + V0_CONSTVALUE_DONE, + V0_MINVALUE_DONE, + V0_MAXVALUE_DONE, + V0_NULLVALUE_DONE, + V0_CHARACTERENCODING_DONE, + V0_EPOCH_DONE, + V0_TIMEUNIT_DONE, + V0_SEMANTICTYPE_DONE, + V0_DESCRIPTION_DONE, + V0_REFERENCEDNAME_DONE, + } + + private CodecState codecState = CodecState.NOT_WRAPPED; + + private CodecState codecState() + { + return codecState; + } + + private void codecState(CodecState newState) + { + codecState = newState; } public static final int BLOCK_LENGTH = 28; @@ -126,10 +150,10 @@ public TokenCodecDecoder wrap( { switch(actingVersion) { case 0: - fieldOrderState(STATE_V0_BLOCK); + codecState(CodecState.V0_BLOCK); break; default: - fieldOrderState(STATE_V0_BLOCK); + codecState(CodecState.V0_BLOCK); break; } } @@ -241,13 +265,13 @@ public int tokenOffset() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_BLOCK: - fieldOrderState(STATE_V0_BLOCK); + case V0_BLOCK: + codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"tokenOffset\" in state: " + codecState()); } } @@ -304,13 +328,13 @@ public int tokenSize() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_BLOCK: - fieldOrderState(STATE_V0_BLOCK); + case V0_BLOCK: + codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"tokenSize\" in state: " + codecState()); } } @@ -367,13 +391,13 @@ public int fieldId() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_BLOCK: - fieldOrderState(STATE_V0_BLOCK); + case V0_BLOCK: + codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"fieldId\" in state: " + codecState()); } } @@ -430,13 +454,13 @@ public int tokenVersion() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_BLOCK: - fieldOrderState(STATE_V0_BLOCK); + case V0_BLOCK: + codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"tokenVersion\" in state: " + codecState()); } } @@ -493,13 +517,13 @@ public int componentTokenCount() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_BLOCK: - fieldOrderState(STATE_V0_BLOCK); + case V0_BLOCK: + codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"componentTokenCount\" in state: " + codecState()); } } @@ -541,13 +565,13 @@ public short signalRaw() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_BLOCK: - fieldOrderState(STATE_V0_BLOCK); + case V0_BLOCK: + codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"signal\" in state: " + codecState()); } } @@ -558,13 +582,13 @@ public SignalCodec signal() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_BLOCK: - fieldOrderState(STATE_V0_BLOCK); + case V0_BLOCK: + codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"signal\" in state: " + codecState()); } } @@ -606,13 +630,13 @@ public short primitiveTypeRaw() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_BLOCK: - fieldOrderState(STATE_V0_BLOCK); + case V0_BLOCK: + codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"primitiveType\" in state: " + codecState()); } } @@ -623,13 +647,13 @@ public PrimitiveTypeCodec primitiveType() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_BLOCK: - fieldOrderState(STATE_V0_BLOCK); + case V0_BLOCK: + codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"primitiveType\" in state: " + codecState()); } } @@ -671,13 +695,13 @@ public short byteOrderRaw() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_BLOCK: - fieldOrderState(STATE_V0_BLOCK); + case V0_BLOCK: + codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"byteOrder\" in state: " + codecState()); } } @@ -688,13 +712,13 @@ public ByteOrderCodec byteOrder() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_BLOCK: - fieldOrderState(STATE_V0_BLOCK); + case V0_BLOCK: + codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"byteOrder\" in state: " + codecState()); } } @@ -736,13 +760,13 @@ public short presenceRaw() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_BLOCK: - fieldOrderState(STATE_V0_BLOCK); + case V0_BLOCK: + codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"presence\" in state: " + codecState()); } } @@ -753,13 +777,13 @@ public PresenceCodec presence() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_BLOCK: - fieldOrderState(STATE_V0_BLOCK); + case V0_BLOCK: + codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"presence\" in state: " + codecState()); } } @@ -816,13 +840,13 @@ public int deprecated() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_BLOCK: - fieldOrderState(STATE_V0_BLOCK); + case V0_BLOCK: + codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"deprecated\" in state: " + codecState()); } } @@ -864,13 +888,13 @@ public int nameLength() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_BLOCK: - fieldOrderState(STATE_V0_NAME_FILLED); + case V0_BLOCK: + codecState(CodecState.V0_NAME_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"name\" in state: " + codecState()); } } @@ -882,13 +906,13 @@ public int skipName() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_BLOCK: - fieldOrderState(STATE_V0_NAME_FILLED); + case V0_BLOCK: + codecState(CodecState.V0_NAME_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"name\" in state: " + codecState()); } } @@ -905,13 +929,13 @@ public int getName(final MutableDirectBuffer dst, final int dstOffset, final int { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_BLOCK: - fieldOrderState(STATE_V0_NAME_FILLED); + case V0_BLOCK: + codecState(CodecState.V0_NAME_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"name\" in state: " + codecState()); } } @@ -929,13 +953,13 @@ public int getName(final byte[] dst, final int dstOffset, final int length) { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_BLOCK: - fieldOrderState(STATE_V0_NAME_FILLED); + case V0_BLOCK: + codecState(CodecState.V0_NAME_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"name\" in state: " + codecState()); } } @@ -953,13 +977,13 @@ public void wrapName(final DirectBuffer wrapBuffer) { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_BLOCK: - fieldOrderState(STATE_V0_NAME_FILLED); + case V0_BLOCK: + codecState(CodecState.V0_NAME_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"name\" in state: " + codecState()); } } @@ -974,13 +998,13 @@ public String name() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_BLOCK: - fieldOrderState(STATE_V0_NAME_FILLED); + case V0_BLOCK: + codecState(CodecState.V0_NAME_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"name\" in state: " + codecState()); } } @@ -1034,13 +1058,13 @@ public int constValueLength() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_NAME_FILLED: - fieldOrderState(STATE_V0_CONSTVALUE_FILLED); + case V0_NAME_DONE: + codecState(CodecState.V0_CONSTVALUE_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"constValue\" in state: " + codecState()); } } @@ -1052,13 +1076,13 @@ public int skipConstValue() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_NAME_FILLED: - fieldOrderState(STATE_V0_CONSTVALUE_FILLED); + case V0_NAME_DONE: + codecState(CodecState.V0_CONSTVALUE_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"constValue\" in state: " + codecState()); } } @@ -1075,13 +1099,13 @@ public int getConstValue(final MutableDirectBuffer dst, final int dstOffset, fin { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_NAME_FILLED: - fieldOrderState(STATE_V0_CONSTVALUE_FILLED); + case V0_NAME_DONE: + codecState(CodecState.V0_CONSTVALUE_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"constValue\" in state: " + codecState()); } } @@ -1099,13 +1123,13 @@ public int getConstValue(final byte[] dst, final int dstOffset, final int length { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_NAME_FILLED: - fieldOrderState(STATE_V0_CONSTVALUE_FILLED); + case V0_NAME_DONE: + codecState(CodecState.V0_CONSTVALUE_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"constValue\" in state: " + codecState()); } } @@ -1123,13 +1147,13 @@ public void wrapConstValue(final DirectBuffer wrapBuffer) { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_NAME_FILLED: - fieldOrderState(STATE_V0_CONSTVALUE_FILLED); + case V0_NAME_DONE: + codecState(CodecState.V0_CONSTVALUE_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"constValue\" in state: " + codecState()); } } @@ -1144,13 +1168,13 @@ public String constValue() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_NAME_FILLED: - fieldOrderState(STATE_V0_CONSTVALUE_FILLED); + case V0_NAME_DONE: + codecState(CodecState.V0_CONSTVALUE_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"constValue\" in state: " + codecState()); } } @@ -1204,13 +1228,13 @@ public int minValueLength() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_CONSTVALUE_FILLED: - fieldOrderState(STATE_V0_MINVALUE_FILLED); + case V0_CONSTVALUE_DONE: + codecState(CodecState.V0_MINVALUE_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"minValue\" in state: " + codecState()); } } @@ -1222,13 +1246,13 @@ public int skipMinValue() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_CONSTVALUE_FILLED: - fieldOrderState(STATE_V0_MINVALUE_FILLED); + case V0_CONSTVALUE_DONE: + codecState(CodecState.V0_MINVALUE_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"minValue\" in state: " + codecState()); } } @@ -1245,13 +1269,13 @@ public int getMinValue(final MutableDirectBuffer dst, final int dstOffset, final { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_CONSTVALUE_FILLED: - fieldOrderState(STATE_V0_MINVALUE_FILLED); + case V0_CONSTVALUE_DONE: + codecState(CodecState.V0_MINVALUE_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"minValue\" in state: " + codecState()); } } @@ -1269,13 +1293,13 @@ public int getMinValue(final byte[] dst, final int dstOffset, final int length) { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_CONSTVALUE_FILLED: - fieldOrderState(STATE_V0_MINVALUE_FILLED); + case V0_CONSTVALUE_DONE: + codecState(CodecState.V0_MINVALUE_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"minValue\" in state: " + codecState()); } } @@ -1293,13 +1317,13 @@ public void wrapMinValue(final DirectBuffer wrapBuffer) { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_CONSTVALUE_FILLED: - fieldOrderState(STATE_V0_MINVALUE_FILLED); + case V0_CONSTVALUE_DONE: + codecState(CodecState.V0_MINVALUE_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"minValue\" in state: " + codecState()); } } @@ -1314,13 +1338,13 @@ public String minValue() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_CONSTVALUE_FILLED: - fieldOrderState(STATE_V0_MINVALUE_FILLED); + case V0_CONSTVALUE_DONE: + codecState(CodecState.V0_MINVALUE_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"minValue\" in state: " + codecState()); } } @@ -1374,13 +1398,13 @@ public int maxValueLength() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_MINVALUE_FILLED: - fieldOrderState(STATE_V0_MAXVALUE_FILLED); + case V0_MINVALUE_DONE: + codecState(CodecState.V0_MAXVALUE_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"maxValue\" in state: " + codecState()); } } @@ -1392,13 +1416,13 @@ public int skipMaxValue() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_MINVALUE_FILLED: - fieldOrderState(STATE_V0_MAXVALUE_FILLED); + case V0_MINVALUE_DONE: + codecState(CodecState.V0_MAXVALUE_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"maxValue\" in state: " + codecState()); } } @@ -1415,13 +1439,13 @@ public int getMaxValue(final MutableDirectBuffer dst, final int dstOffset, final { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_MINVALUE_FILLED: - fieldOrderState(STATE_V0_MAXVALUE_FILLED); + case V0_MINVALUE_DONE: + codecState(CodecState.V0_MAXVALUE_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"maxValue\" in state: " + codecState()); } } @@ -1439,13 +1463,13 @@ public int getMaxValue(final byte[] dst, final int dstOffset, final int length) { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_MINVALUE_FILLED: - fieldOrderState(STATE_V0_MAXVALUE_FILLED); + case V0_MINVALUE_DONE: + codecState(CodecState.V0_MAXVALUE_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"maxValue\" in state: " + codecState()); } } @@ -1463,13 +1487,13 @@ public void wrapMaxValue(final DirectBuffer wrapBuffer) { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_MINVALUE_FILLED: - fieldOrderState(STATE_V0_MAXVALUE_FILLED); + case V0_MINVALUE_DONE: + codecState(CodecState.V0_MAXVALUE_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"maxValue\" in state: " + codecState()); } } @@ -1484,13 +1508,13 @@ public String maxValue() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_MINVALUE_FILLED: - fieldOrderState(STATE_V0_MAXVALUE_FILLED); + case V0_MINVALUE_DONE: + codecState(CodecState.V0_MAXVALUE_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"maxValue\" in state: " + codecState()); } } @@ -1544,13 +1568,13 @@ public int nullValueLength() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_MAXVALUE_FILLED: - fieldOrderState(STATE_V0_NULLVALUE_FILLED); + case V0_MAXVALUE_DONE: + codecState(CodecState.V0_NULLVALUE_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"nullValue\" in state: " + codecState()); } } @@ -1562,13 +1586,13 @@ public int skipNullValue() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_MAXVALUE_FILLED: - fieldOrderState(STATE_V0_NULLVALUE_FILLED); + case V0_MAXVALUE_DONE: + codecState(CodecState.V0_NULLVALUE_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"nullValue\" in state: " + codecState()); } } @@ -1585,13 +1609,13 @@ public int getNullValue(final MutableDirectBuffer dst, final int dstOffset, fina { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_MAXVALUE_FILLED: - fieldOrderState(STATE_V0_NULLVALUE_FILLED); + case V0_MAXVALUE_DONE: + codecState(CodecState.V0_NULLVALUE_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"nullValue\" in state: " + codecState()); } } @@ -1609,13 +1633,13 @@ public int getNullValue(final byte[] dst, final int dstOffset, final int length) { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_MAXVALUE_FILLED: - fieldOrderState(STATE_V0_NULLVALUE_FILLED); + case V0_MAXVALUE_DONE: + codecState(CodecState.V0_NULLVALUE_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"nullValue\" in state: " + codecState()); } } @@ -1633,13 +1657,13 @@ public void wrapNullValue(final DirectBuffer wrapBuffer) { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_MAXVALUE_FILLED: - fieldOrderState(STATE_V0_NULLVALUE_FILLED); + case V0_MAXVALUE_DONE: + codecState(CodecState.V0_NULLVALUE_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"nullValue\" in state: " + codecState()); } } @@ -1654,13 +1678,13 @@ public String nullValue() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_MAXVALUE_FILLED: - fieldOrderState(STATE_V0_NULLVALUE_FILLED); + case V0_MAXVALUE_DONE: + codecState(CodecState.V0_NULLVALUE_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"nullValue\" in state: " + codecState()); } } @@ -1714,13 +1738,13 @@ public int characterEncodingLength() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_NULLVALUE_FILLED: - fieldOrderState(STATE_V0_CHARACTERENCODING_FILLED); + case V0_NULLVALUE_DONE: + codecState(CodecState.V0_CHARACTERENCODING_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"characterEncoding\" in state: " + codecState()); } } @@ -1732,13 +1756,13 @@ public int skipCharacterEncoding() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_NULLVALUE_FILLED: - fieldOrderState(STATE_V0_CHARACTERENCODING_FILLED); + case V0_NULLVALUE_DONE: + codecState(CodecState.V0_CHARACTERENCODING_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"characterEncoding\" in state: " + codecState()); } } @@ -1755,13 +1779,13 @@ public int getCharacterEncoding(final MutableDirectBuffer dst, final int dstOffs { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_NULLVALUE_FILLED: - fieldOrderState(STATE_V0_CHARACTERENCODING_FILLED); + case V0_NULLVALUE_DONE: + codecState(CodecState.V0_CHARACTERENCODING_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"characterEncoding\" in state: " + codecState()); } } @@ -1779,13 +1803,13 @@ public int getCharacterEncoding(final byte[] dst, final int dstOffset, final int { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_NULLVALUE_FILLED: - fieldOrderState(STATE_V0_CHARACTERENCODING_FILLED); + case V0_NULLVALUE_DONE: + codecState(CodecState.V0_CHARACTERENCODING_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"characterEncoding\" in state: " + codecState()); } } @@ -1803,13 +1827,13 @@ public void wrapCharacterEncoding(final DirectBuffer wrapBuffer) { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_NULLVALUE_FILLED: - fieldOrderState(STATE_V0_CHARACTERENCODING_FILLED); + case V0_NULLVALUE_DONE: + codecState(CodecState.V0_CHARACTERENCODING_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"characterEncoding\" in state: " + codecState()); } } @@ -1824,13 +1848,13 @@ public String characterEncoding() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_NULLVALUE_FILLED: - fieldOrderState(STATE_V0_CHARACTERENCODING_FILLED); + case V0_NULLVALUE_DONE: + codecState(CodecState.V0_CHARACTERENCODING_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"characterEncoding\" in state: " + codecState()); } } @@ -1884,13 +1908,13 @@ public int epochLength() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_CHARACTERENCODING_FILLED: - fieldOrderState(STATE_V0_EPOCH_FILLED); + case V0_CHARACTERENCODING_DONE: + codecState(CodecState.V0_EPOCH_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"epoch\" in state: " + codecState()); } } @@ -1902,13 +1926,13 @@ public int skipEpoch() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_CHARACTERENCODING_FILLED: - fieldOrderState(STATE_V0_EPOCH_FILLED); + case V0_CHARACTERENCODING_DONE: + codecState(CodecState.V0_EPOCH_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"epoch\" in state: " + codecState()); } } @@ -1925,13 +1949,13 @@ public int getEpoch(final MutableDirectBuffer dst, final int dstOffset, final in { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_CHARACTERENCODING_FILLED: - fieldOrderState(STATE_V0_EPOCH_FILLED); + case V0_CHARACTERENCODING_DONE: + codecState(CodecState.V0_EPOCH_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"epoch\" in state: " + codecState()); } } @@ -1949,13 +1973,13 @@ public int getEpoch(final byte[] dst, final int dstOffset, final int length) { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_CHARACTERENCODING_FILLED: - fieldOrderState(STATE_V0_EPOCH_FILLED); + case V0_CHARACTERENCODING_DONE: + codecState(CodecState.V0_EPOCH_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"epoch\" in state: " + codecState()); } } @@ -1973,13 +1997,13 @@ public void wrapEpoch(final DirectBuffer wrapBuffer) { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_CHARACTERENCODING_FILLED: - fieldOrderState(STATE_V0_EPOCH_FILLED); + case V0_CHARACTERENCODING_DONE: + codecState(CodecState.V0_EPOCH_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"epoch\" in state: " + codecState()); } } @@ -1994,13 +2018,13 @@ public String epoch() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_CHARACTERENCODING_FILLED: - fieldOrderState(STATE_V0_EPOCH_FILLED); + case V0_CHARACTERENCODING_DONE: + codecState(CodecState.V0_EPOCH_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"epoch\" in state: " + codecState()); } } @@ -2054,13 +2078,13 @@ public int timeUnitLength() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_EPOCH_FILLED: - fieldOrderState(STATE_V0_TIMEUNIT_FILLED); + case V0_EPOCH_DONE: + codecState(CodecState.V0_TIMEUNIT_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"timeUnit\" in state: " + codecState()); } } @@ -2072,13 +2096,13 @@ public int skipTimeUnit() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_EPOCH_FILLED: - fieldOrderState(STATE_V0_TIMEUNIT_FILLED); + case V0_EPOCH_DONE: + codecState(CodecState.V0_TIMEUNIT_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"timeUnit\" in state: " + codecState()); } } @@ -2095,13 +2119,13 @@ public int getTimeUnit(final MutableDirectBuffer dst, final int dstOffset, final { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_EPOCH_FILLED: - fieldOrderState(STATE_V0_TIMEUNIT_FILLED); + case V0_EPOCH_DONE: + codecState(CodecState.V0_TIMEUNIT_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"timeUnit\" in state: " + codecState()); } } @@ -2119,13 +2143,13 @@ public int getTimeUnit(final byte[] dst, final int dstOffset, final int length) { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_EPOCH_FILLED: - fieldOrderState(STATE_V0_TIMEUNIT_FILLED); + case V0_EPOCH_DONE: + codecState(CodecState.V0_TIMEUNIT_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"timeUnit\" in state: " + codecState()); } } @@ -2143,13 +2167,13 @@ public void wrapTimeUnit(final DirectBuffer wrapBuffer) { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_EPOCH_FILLED: - fieldOrderState(STATE_V0_TIMEUNIT_FILLED); + case V0_EPOCH_DONE: + codecState(CodecState.V0_TIMEUNIT_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"timeUnit\" in state: " + codecState()); } } @@ -2164,13 +2188,13 @@ public String timeUnit() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_EPOCH_FILLED: - fieldOrderState(STATE_V0_TIMEUNIT_FILLED); + case V0_EPOCH_DONE: + codecState(CodecState.V0_TIMEUNIT_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"timeUnit\" in state: " + codecState()); } } @@ -2224,13 +2248,13 @@ public int semanticTypeLength() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_TIMEUNIT_FILLED: - fieldOrderState(STATE_V0_SEMANTICTYPE_FILLED); + case V0_TIMEUNIT_DONE: + codecState(CodecState.V0_SEMANTICTYPE_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"semanticType\" in state: " + codecState()); } } @@ -2242,13 +2266,13 @@ public int skipSemanticType() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_TIMEUNIT_FILLED: - fieldOrderState(STATE_V0_SEMANTICTYPE_FILLED); + case V0_TIMEUNIT_DONE: + codecState(CodecState.V0_SEMANTICTYPE_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"semanticType\" in state: " + codecState()); } } @@ -2265,13 +2289,13 @@ public int getSemanticType(final MutableDirectBuffer dst, final int dstOffset, f { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_TIMEUNIT_FILLED: - fieldOrderState(STATE_V0_SEMANTICTYPE_FILLED); + case V0_TIMEUNIT_DONE: + codecState(CodecState.V0_SEMANTICTYPE_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"semanticType\" in state: " + codecState()); } } @@ -2289,13 +2313,13 @@ public int getSemanticType(final byte[] dst, final int dstOffset, final int leng { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_TIMEUNIT_FILLED: - fieldOrderState(STATE_V0_SEMANTICTYPE_FILLED); + case V0_TIMEUNIT_DONE: + codecState(CodecState.V0_SEMANTICTYPE_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"semanticType\" in state: " + codecState()); } } @@ -2313,13 +2337,13 @@ public void wrapSemanticType(final DirectBuffer wrapBuffer) { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_TIMEUNIT_FILLED: - fieldOrderState(STATE_V0_SEMANTICTYPE_FILLED); + case V0_TIMEUNIT_DONE: + codecState(CodecState.V0_SEMANTICTYPE_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"semanticType\" in state: " + codecState()); } } @@ -2334,13 +2358,13 @@ public String semanticType() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_TIMEUNIT_FILLED: - fieldOrderState(STATE_V0_SEMANTICTYPE_FILLED); + case V0_TIMEUNIT_DONE: + codecState(CodecState.V0_SEMANTICTYPE_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"semanticType\" in state: " + codecState()); } } @@ -2394,13 +2418,13 @@ public int descriptionLength() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_SEMANTICTYPE_FILLED: - fieldOrderState(STATE_V0_DESCRIPTION_FILLED); + case V0_SEMANTICTYPE_DONE: + codecState(CodecState.V0_DESCRIPTION_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"description\" in state: " + codecState()); } } @@ -2412,13 +2436,13 @@ public int skipDescription() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_SEMANTICTYPE_FILLED: - fieldOrderState(STATE_V0_DESCRIPTION_FILLED); + case V0_SEMANTICTYPE_DONE: + codecState(CodecState.V0_DESCRIPTION_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"description\" in state: " + codecState()); } } @@ -2435,13 +2459,13 @@ public int getDescription(final MutableDirectBuffer dst, final int dstOffset, fi { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_SEMANTICTYPE_FILLED: - fieldOrderState(STATE_V0_DESCRIPTION_FILLED); + case V0_SEMANTICTYPE_DONE: + codecState(CodecState.V0_DESCRIPTION_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"description\" in state: " + codecState()); } } @@ -2459,13 +2483,13 @@ public int getDescription(final byte[] dst, final int dstOffset, final int lengt { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_SEMANTICTYPE_FILLED: - fieldOrderState(STATE_V0_DESCRIPTION_FILLED); + case V0_SEMANTICTYPE_DONE: + codecState(CodecState.V0_DESCRIPTION_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"description\" in state: " + codecState()); } } @@ -2483,13 +2507,13 @@ public void wrapDescription(final DirectBuffer wrapBuffer) { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_SEMANTICTYPE_FILLED: - fieldOrderState(STATE_V0_DESCRIPTION_FILLED); + case V0_SEMANTICTYPE_DONE: + codecState(CodecState.V0_DESCRIPTION_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"description\" in state: " + codecState()); } } @@ -2504,13 +2528,13 @@ public String description() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_SEMANTICTYPE_FILLED: - fieldOrderState(STATE_V0_DESCRIPTION_FILLED); + case V0_SEMANTICTYPE_DONE: + codecState(CodecState.V0_DESCRIPTION_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"description\" in state: " + codecState()); } } @@ -2564,13 +2588,13 @@ public int referencedNameLength() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_DESCRIPTION_FILLED: - fieldOrderState(STATE_V0_REFERENCEDNAME_FILLED); + case V0_DESCRIPTION_DONE: + codecState(CodecState.V0_REFERENCEDNAME_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"referencedName\" in state: " + codecState()); } } @@ -2582,13 +2606,13 @@ public int skipReferencedName() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_DESCRIPTION_FILLED: - fieldOrderState(STATE_V0_REFERENCEDNAME_FILLED); + case V0_DESCRIPTION_DONE: + codecState(CodecState.V0_REFERENCEDNAME_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"referencedName\" in state: " + codecState()); } } @@ -2605,13 +2629,13 @@ public int getReferencedName(final MutableDirectBuffer dst, final int dstOffset, { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_DESCRIPTION_FILLED: - fieldOrderState(STATE_V0_REFERENCEDNAME_FILLED); + case V0_DESCRIPTION_DONE: + codecState(CodecState.V0_REFERENCEDNAME_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"referencedName\" in state: " + codecState()); } } @@ -2629,13 +2653,13 @@ public int getReferencedName(final byte[] dst, final int dstOffset, final int le { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_DESCRIPTION_FILLED: - fieldOrderState(STATE_V0_REFERENCEDNAME_FILLED); + case V0_DESCRIPTION_DONE: + codecState(CodecState.V0_REFERENCEDNAME_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"referencedName\" in state: " + codecState()); } } @@ -2653,13 +2677,13 @@ public void wrapReferencedName(final DirectBuffer wrapBuffer) { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_DESCRIPTION_FILLED: - fieldOrderState(STATE_V0_REFERENCEDNAME_FILLED); + case V0_DESCRIPTION_DONE: + codecState(CodecState.V0_REFERENCEDNAME_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"referencedName\" in state: " + codecState()); } } @@ -2674,13 +2698,13 @@ public String referencedName() { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_DESCRIPTION_FILLED: - fieldOrderState(STATE_V0_REFERENCEDNAME_FILLED); + case V0_DESCRIPTION_DONE: + codecState(CodecState.V0_REFERENCEDNAME_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"referencedName\" in state: " + codecState()); } } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java index 53dad48cb5..54dc2633e3 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java @@ -13,42 +13,66 @@ public final class TokenCodecEncoder { private static final boolean DEBUG_MODE = !Boolean.getBoolean("agrona.disable.bounds.checks"); - private static final int STATE_NOT_WRAPPED = 1; - - private static final int STATE_V0_BLOCK = 2; - - private static final int STATE_V0_NAME_FILLED = 3; - - private static final int STATE_V0_CONSTVALUE_FILLED = 4; - - private static final int STATE_V0_MINVALUE_FILLED = 5; - - private static final int STATE_V0_MAXVALUE_FILLED = 6; - - private static final int STATE_V0_NULLVALUE_FILLED = 7; - - private static final int STATE_V0_CHARACTERENCODING_FILLED = 8; - - private static final int STATE_V0_EPOCH_FILLED = 9; - - private static final int STATE_V0_TIMEUNIT_FILLED = 10; - - private static final int STATE_V0_SEMANTICTYPE_FILLED = 11; - - private static final int STATE_V0_DESCRIPTION_FILLED = 12; - - private static final int STATE_V0_REFERENCEDNAME_FILLED = 13; - - private int fieldOrderState = STATE_NOT_WRAPPED; - - private int fieldOrderState() - { - return fieldOrderState; - } - - private void fieldOrderState(int newState) - { - fieldOrderState = newState; + /** + * The states in which a encoder/decoder/codec can live. + * + *

The state machine diagram below describes the valid state transitions + * according to the order in which fields may be accessed safely. + * + *

{@code
+     *   digraph G {
+     *       NOT_WRAPPED -> V0_BLOCK [label="  .wrap(...)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  .tokenOffset(value)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  .tokenSize(value)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  .fieldId(value)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  .tokenVersion(value)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  .componentTokenCount(value)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  .signal(value)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  .primitiveType(value)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  .byteOrder(value)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  .presence(value)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  .deprecated(value)  "];
+     *       V0_BLOCK -> V0_NAME_DONE [label="  .name(value)  "];
+     *       V0_NAME_DONE -> V0_CONSTVALUE_DONE [label="  .constValue(value)  "];
+     *       V0_CONSTVALUE_DONE -> V0_MINVALUE_DONE [label="  .minValue(value)  "];
+     *       V0_MINVALUE_DONE -> V0_MAXVALUE_DONE [label="  .maxValue(value)  "];
+     *       V0_MAXVALUE_DONE -> V0_NULLVALUE_DONE [label="  .nullValue(value)  "];
+     *       V0_NULLVALUE_DONE -> V0_CHARACTERENCODING_DONE [label="  .characterEncoding(value)  "];
+     *       V0_CHARACTERENCODING_DONE -> V0_EPOCH_DONE [label="  .epoch(value)  "];
+     *       V0_EPOCH_DONE -> V0_TIMEUNIT_DONE [label="  .timeUnit(value)  "];
+     *       V0_TIMEUNIT_DONE -> V0_SEMANTICTYPE_DONE [label="  .semanticType(value)  "];
+     *       V0_SEMANTICTYPE_DONE -> V0_DESCRIPTION_DONE [label="  .description(value)  "];
+     *       V0_DESCRIPTION_DONE -> V0_REFERENCEDNAME_DONE [label="  .referencedName(value)  "];
+     *   }
+     * }
+ */ + private enum CodecState + { + NOT_WRAPPED, + V0_BLOCK, + V0_NAME_DONE, + V0_CONSTVALUE_DONE, + V0_MINVALUE_DONE, + V0_MAXVALUE_DONE, + V0_NULLVALUE_DONE, + V0_CHARACTERENCODING_DONE, + V0_EPOCH_DONE, + V0_TIMEUNIT_DONE, + V0_SEMANTICTYPE_DONE, + V0_DESCRIPTION_DONE, + V0_REFERENCEDNAME_DONE, + } + + private CodecState codecState = CodecState.NOT_WRAPPED; + + private CodecState codecState() + { + return codecState; + } + + private void codecState(CodecState newState) + { + codecState = newState; } public static final int BLOCK_LENGTH = 28; @@ -116,7 +140,7 @@ public TokenCodecEncoder wrap(final MutableDirectBuffer buffer, final int offset if (DEBUG_MODE) { - fieldOrderState(STATE_V0_BLOCK); + codecState(CodecState.V0_BLOCK); } return this; @@ -199,13 +223,13 @@ public TokenCodecEncoder tokenOffset(final int value) { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_BLOCK: - fieldOrderState(STATE_V0_BLOCK); + case V0_BLOCK: + codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"tokenOffset\" in state: " + codecState()); } } @@ -263,13 +287,13 @@ public TokenCodecEncoder tokenSize(final int value) { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_BLOCK: - fieldOrderState(STATE_V0_BLOCK); + case V0_BLOCK: + codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"tokenSize\" in state: " + codecState()); } } @@ -327,13 +351,13 @@ public TokenCodecEncoder fieldId(final int value) { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_BLOCK: - fieldOrderState(STATE_V0_BLOCK); + case V0_BLOCK: + codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"fieldId\" in state: " + codecState()); } } @@ -391,13 +415,13 @@ public TokenCodecEncoder tokenVersion(final int value) { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_BLOCK: - fieldOrderState(STATE_V0_BLOCK); + case V0_BLOCK: + codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"tokenVersion\" in state: " + codecState()); } } @@ -455,13 +479,13 @@ public TokenCodecEncoder componentTokenCount(final int value) { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_BLOCK: - fieldOrderState(STATE_V0_BLOCK); + case V0_BLOCK: + codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"componentTokenCount\" in state: " + codecState()); } } @@ -504,13 +528,13 @@ public TokenCodecEncoder signal(final SignalCodec value) { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_BLOCK: - fieldOrderState(STATE_V0_BLOCK); + case V0_BLOCK: + codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"signal\" in state: " + codecState()); } } @@ -552,13 +576,13 @@ public TokenCodecEncoder primitiveType(final PrimitiveTypeCodec value) { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_BLOCK: - fieldOrderState(STATE_V0_BLOCK); + case V0_BLOCK: + codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"primitiveType\" in state: " + codecState()); } } @@ -600,13 +624,13 @@ public TokenCodecEncoder byteOrder(final ByteOrderCodec value) { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_BLOCK: - fieldOrderState(STATE_V0_BLOCK); + case V0_BLOCK: + codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"byteOrder\" in state: " + codecState()); } } @@ -648,13 +672,13 @@ public TokenCodecEncoder presence(final PresenceCodec value) { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_BLOCK: - fieldOrderState(STATE_V0_BLOCK); + case V0_BLOCK: + codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"presence\" in state: " + codecState()); } } @@ -711,13 +735,13 @@ public TokenCodecEncoder deprecated(final int value) { if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_BLOCK: - fieldOrderState(STATE_V0_BLOCK); + case V0_BLOCK: + codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"deprecated\" in state: " + codecState()); } } @@ -760,13 +784,13 @@ public TokenCodecEncoder putName(final DirectBuffer src, final int srcOffset, fi if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_BLOCK: - fieldOrderState(STATE_V0_NAME_FILLED); + case V0_BLOCK: + codecState(CodecState.V0_NAME_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"name\" in state: " + codecState()); } } @@ -788,13 +812,13 @@ public TokenCodecEncoder putName(final byte[] src, final int srcOffset, final in if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_BLOCK: - fieldOrderState(STATE_V0_NAME_FILLED); + case V0_BLOCK: + codecState(CodecState.V0_NAME_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"name\" in state: " + codecState()); } } @@ -819,13 +843,13 @@ public TokenCodecEncoder name(final String value) if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_BLOCK: - fieldOrderState(STATE_V0_NAME_FILLED); + case V0_BLOCK: + codecState(CodecState.V0_NAME_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"name\" in state: " + codecState()); } } @@ -872,13 +896,13 @@ public TokenCodecEncoder putConstValue(final DirectBuffer src, final int srcOffs if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_NAME_FILLED: - fieldOrderState(STATE_V0_CONSTVALUE_FILLED); + case V0_NAME_DONE: + codecState(CodecState.V0_CONSTVALUE_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"constValue\" in state: " + codecState()); } } @@ -900,13 +924,13 @@ public TokenCodecEncoder putConstValue(final byte[] src, final int srcOffset, fi if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_NAME_FILLED: - fieldOrderState(STATE_V0_CONSTVALUE_FILLED); + case V0_NAME_DONE: + codecState(CodecState.V0_CONSTVALUE_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"constValue\" in state: " + codecState()); } } @@ -931,13 +955,13 @@ public TokenCodecEncoder constValue(final String value) if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_NAME_FILLED: - fieldOrderState(STATE_V0_CONSTVALUE_FILLED); + case V0_NAME_DONE: + codecState(CodecState.V0_CONSTVALUE_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"constValue\" in state: " + codecState()); } } @@ -984,13 +1008,13 @@ public TokenCodecEncoder putMinValue(final DirectBuffer src, final int srcOffset if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_CONSTVALUE_FILLED: - fieldOrderState(STATE_V0_MINVALUE_FILLED); + case V0_CONSTVALUE_DONE: + codecState(CodecState.V0_MINVALUE_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"minValue\" in state: " + codecState()); } } @@ -1012,13 +1036,13 @@ public TokenCodecEncoder putMinValue(final byte[] src, final int srcOffset, fina if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_CONSTVALUE_FILLED: - fieldOrderState(STATE_V0_MINVALUE_FILLED); + case V0_CONSTVALUE_DONE: + codecState(CodecState.V0_MINVALUE_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"minValue\" in state: " + codecState()); } } @@ -1043,13 +1067,13 @@ public TokenCodecEncoder minValue(final String value) if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_CONSTVALUE_FILLED: - fieldOrderState(STATE_V0_MINVALUE_FILLED); + case V0_CONSTVALUE_DONE: + codecState(CodecState.V0_MINVALUE_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"minValue\" in state: " + codecState()); } } @@ -1096,13 +1120,13 @@ public TokenCodecEncoder putMaxValue(final DirectBuffer src, final int srcOffset if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_MINVALUE_FILLED: - fieldOrderState(STATE_V0_MAXVALUE_FILLED); + case V0_MINVALUE_DONE: + codecState(CodecState.V0_MAXVALUE_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"maxValue\" in state: " + codecState()); } } @@ -1124,13 +1148,13 @@ public TokenCodecEncoder putMaxValue(final byte[] src, final int srcOffset, fina if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_MINVALUE_FILLED: - fieldOrderState(STATE_V0_MAXVALUE_FILLED); + case V0_MINVALUE_DONE: + codecState(CodecState.V0_MAXVALUE_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"maxValue\" in state: " + codecState()); } } @@ -1155,13 +1179,13 @@ public TokenCodecEncoder maxValue(final String value) if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_MINVALUE_FILLED: - fieldOrderState(STATE_V0_MAXVALUE_FILLED); + case V0_MINVALUE_DONE: + codecState(CodecState.V0_MAXVALUE_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"maxValue\" in state: " + codecState()); } } @@ -1208,13 +1232,13 @@ public TokenCodecEncoder putNullValue(final DirectBuffer src, final int srcOffse if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_MAXVALUE_FILLED: - fieldOrderState(STATE_V0_NULLVALUE_FILLED); + case V0_MAXVALUE_DONE: + codecState(CodecState.V0_NULLVALUE_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"nullValue\" in state: " + codecState()); } } @@ -1236,13 +1260,13 @@ public TokenCodecEncoder putNullValue(final byte[] src, final int srcOffset, fin if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_MAXVALUE_FILLED: - fieldOrderState(STATE_V0_NULLVALUE_FILLED); + case V0_MAXVALUE_DONE: + codecState(CodecState.V0_NULLVALUE_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"nullValue\" in state: " + codecState()); } } @@ -1267,13 +1291,13 @@ public TokenCodecEncoder nullValue(final String value) if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_MAXVALUE_FILLED: - fieldOrderState(STATE_V0_NULLVALUE_FILLED); + case V0_MAXVALUE_DONE: + codecState(CodecState.V0_NULLVALUE_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"nullValue\" in state: " + codecState()); } } @@ -1320,13 +1344,13 @@ public TokenCodecEncoder putCharacterEncoding(final DirectBuffer src, final int if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_NULLVALUE_FILLED: - fieldOrderState(STATE_V0_CHARACTERENCODING_FILLED); + case V0_NULLVALUE_DONE: + codecState(CodecState.V0_CHARACTERENCODING_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"characterEncoding\" in state: " + codecState()); } } @@ -1348,13 +1372,13 @@ public TokenCodecEncoder putCharacterEncoding(final byte[] src, final int srcOff if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_NULLVALUE_FILLED: - fieldOrderState(STATE_V0_CHARACTERENCODING_FILLED); + case V0_NULLVALUE_DONE: + codecState(CodecState.V0_CHARACTERENCODING_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"characterEncoding\" in state: " + codecState()); } } @@ -1379,13 +1403,13 @@ public TokenCodecEncoder characterEncoding(final String value) if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_NULLVALUE_FILLED: - fieldOrderState(STATE_V0_CHARACTERENCODING_FILLED); + case V0_NULLVALUE_DONE: + codecState(CodecState.V0_CHARACTERENCODING_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"characterEncoding\" in state: " + codecState()); } } @@ -1432,13 +1456,13 @@ public TokenCodecEncoder putEpoch(final DirectBuffer src, final int srcOffset, f if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_CHARACTERENCODING_FILLED: - fieldOrderState(STATE_V0_EPOCH_FILLED); + case V0_CHARACTERENCODING_DONE: + codecState(CodecState.V0_EPOCH_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"epoch\" in state: " + codecState()); } } @@ -1460,13 +1484,13 @@ public TokenCodecEncoder putEpoch(final byte[] src, final int srcOffset, final i if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_CHARACTERENCODING_FILLED: - fieldOrderState(STATE_V0_EPOCH_FILLED); + case V0_CHARACTERENCODING_DONE: + codecState(CodecState.V0_EPOCH_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"epoch\" in state: " + codecState()); } } @@ -1491,13 +1515,13 @@ public TokenCodecEncoder epoch(final String value) if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_CHARACTERENCODING_FILLED: - fieldOrderState(STATE_V0_EPOCH_FILLED); + case V0_CHARACTERENCODING_DONE: + codecState(CodecState.V0_EPOCH_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"epoch\" in state: " + codecState()); } } @@ -1544,13 +1568,13 @@ public TokenCodecEncoder putTimeUnit(final DirectBuffer src, final int srcOffset if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_EPOCH_FILLED: - fieldOrderState(STATE_V0_TIMEUNIT_FILLED); + case V0_EPOCH_DONE: + codecState(CodecState.V0_TIMEUNIT_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"timeUnit\" in state: " + codecState()); } } @@ -1572,13 +1596,13 @@ public TokenCodecEncoder putTimeUnit(final byte[] src, final int srcOffset, fina if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_EPOCH_FILLED: - fieldOrderState(STATE_V0_TIMEUNIT_FILLED); + case V0_EPOCH_DONE: + codecState(CodecState.V0_TIMEUNIT_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"timeUnit\" in state: " + codecState()); } } @@ -1603,13 +1627,13 @@ public TokenCodecEncoder timeUnit(final String value) if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_EPOCH_FILLED: - fieldOrderState(STATE_V0_TIMEUNIT_FILLED); + case V0_EPOCH_DONE: + codecState(CodecState.V0_TIMEUNIT_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"timeUnit\" in state: " + codecState()); } } @@ -1656,13 +1680,13 @@ public TokenCodecEncoder putSemanticType(final DirectBuffer src, final int srcOf if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_TIMEUNIT_FILLED: - fieldOrderState(STATE_V0_SEMANTICTYPE_FILLED); + case V0_TIMEUNIT_DONE: + codecState(CodecState.V0_SEMANTICTYPE_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"semanticType\" in state: " + codecState()); } } @@ -1684,13 +1708,13 @@ public TokenCodecEncoder putSemanticType(final byte[] src, final int srcOffset, if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_TIMEUNIT_FILLED: - fieldOrderState(STATE_V0_SEMANTICTYPE_FILLED); + case V0_TIMEUNIT_DONE: + codecState(CodecState.V0_SEMANTICTYPE_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"semanticType\" in state: " + codecState()); } } @@ -1715,13 +1739,13 @@ public TokenCodecEncoder semanticType(final String value) if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_TIMEUNIT_FILLED: - fieldOrderState(STATE_V0_SEMANTICTYPE_FILLED); + case V0_TIMEUNIT_DONE: + codecState(CodecState.V0_SEMANTICTYPE_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"semanticType\" in state: " + codecState()); } } @@ -1768,13 +1792,13 @@ public TokenCodecEncoder putDescription(final DirectBuffer src, final int srcOff if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_SEMANTICTYPE_FILLED: - fieldOrderState(STATE_V0_DESCRIPTION_FILLED); + case V0_SEMANTICTYPE_DONE: + codecState(CodecState.V0_DESCRIPTION_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"description\" in state: " + codecState()); } } @@ -1796,13 +1820,13 @@ public TokenCodecEncoder putDescription(final byte[] src, final int srcOffset, f if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_SEMANTICTYPE_FILLED: - fieldOrderState(STATE_V0_DESCRIPTION_FILLED); + case V0_SEMANTICTYPE_DONE: + codecState(CodecState.V0_DESCRIPTION_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"description\" in state: " + codecState()); } } @@ -1827,13 +1851,13 @@ public TokenCodecEncoder description(final String value) if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_SEMANTICTYPE_FILLED: - fieldOrderState(STATE_V0_DESCRIPTION_FILLED); + case V0_SEMANTICTYPE_DONE: + codecState(CodecState.V0_DESCRIPTION_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"description\" in state: " + codecState()); } } @@ -1880,13 +1904,13 @@ public TokenCodecEncoder putReferencedName(final DirectBuffer src, final int src if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_DESCRIPTION_FILLED: - fieldOrderState(STATE_V0_REFERENCEDNAME_FILLED); + case V0_DESCRIPTION_DONE: + codecState(CodecState.V0_REFERENCEDNAME_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"referencedName\" in state: " + codecState()); } } @@ -1908,13 +1932,13 @@ public TokenCodecEncoder putReferencedName(final byte[] src, final int srcOffset if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_DESCRIPTION_FILLED: - fieldOrderState(STATE_V0_REFERENCEDNAME_FILLED); + case V0_DESCRIPTION_DONE: + codecState(CodecState.V0_REFERENCEDNAME_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"referencedName\" in state: " + codecState()); } } @@ -1939,13 +1963,13 @@ public TokenCodecEncoder referencedName(final String value) if (DEBUG_MODE) { - switch (fieldOrderState()) + switch (codecState()) { - case STATE_V0_DESCRIPTION_FILLED: - fieldOrderState(STATE_V0_REFERENCEDNAME_FILLED); + case V0_DESCRIPTION_DONE: + codecState(CodecState.V0_REFERENCEDNAME_DONE); break; default: - throw new IllegalStateException("Unexpected state: " + fieldOrderState()); + throw new IllegalStateException("Cannot access field \"referencedName\" in state: " + codecState()); } } From e06462aa5174765cb6c68757f1941ddd9132b69f Mon Sep 17 00:00:00 2001 From: Zach Bray Date: Thu, 15 Jun 2023 08:48:15 +0100 Subject: [PATCH 07/50] [Java] Test field order checks with fixed-length strings. Also, fix some spots where I had forgotten to move the state transition below the version check in decoders. --- .../sbe/generation/java/JavaGenerator.java | 12 +- .../real_logic/sbe/FieldOrderCheckTest.java | 514 +++++++++++++++++- .../resources/field-order-check-schema.xml | 32 ++ 3 files changed, 548 insertions(+), 10 deletions(-) diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java index 84ee6f6ed9..af0e4dff84 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java @@ -2456,7 +2456,6 @@ private CharSequence generatePrimitiveArrayPropertyDecode( new Formatter(sb).format("\n" + indent + " public int get%s(final byte[] dst, final int dstOffset)\n" + indent + " {\n" + - stateTransition + indent + " final int length = %d;\n" + indent + " if (dstOffset < 0 || dstOffset > (dst.length - length))\n" + indent + " {\n" + @@ -2464,6 +2463,7 @@ private CharSequence generatePrimitiveArrayPropertyDecode( "\"Copy will go out of range: offset=\" + dstOffset);\n" + indent + " }\n\n" + "%s" + + stateTransition + indent + " buffer.getBytes(offset + %d, dst, dstOffset, length);\n\n" + indent + " return length;\n" + indent + " }\n", @@ -2475,8 +2475,8 @@ private CharSequence generatePrimitiveArrayPropertyDecode( new Formatter(sb).format("\n" + indent + " public String %s()\n" + indent + " {\n" + - stateTransition + "%s" + + stateTransition + indent + " final byte[] dst = new byte[%d];\n" + indent + " buffer.getBytes(offset + %d, dst, 0, %d);\n\n" + indent + " int end = 0;\n" + @@ -2496,8 +2496,8 @@ private CharSequence generatePrimitiveArrayPropertyDecode( new Formatter(sb).format("\n" + indent + " public int get%1$s(final Appendable value)\n" + indent + " {\n" + - stateTransition + "%2$s" + + stateTransition + indent + " for (int i = 0; i < %3$d; ++i)\n" + indent + " {\n" + indent + " final int c = buffer.getByte(offset + %4$d + i) & 0xFF;\n" + @@ -2527,8 +2527,8 @@ else if (encoding.primitiveType() == PrimitiveType.UINT8) new Formatter(sb).format("\n" + indent + " public int get%s(final byte[] dst, final int dstOffset, final int length)\n" + indent + " {\n" + - stateTransition + "%s" + + stateTransition + indent + " final int bytesCopied = Math.min(length, %d);\n" + indent + " buffer.getBytes(offset + %d, dst, dstOffset, bytesCopied);\n\n" + indent + " return bytesCopied;\n" + @@ -2541,8 +2541,8 @@ else if (encoding.primitiveType() == PrimitiveType.UINT8) new Formatter(sb).format("\n" + indent + " public int get%s(final %s dst, final int dstOffset, final int length)\n" + indent + " {\n" + - stateTransition + "%s" + + stateTransition + indent + " final int bytesCopied = Math.min(length, %d);\n" + indent + " buffer.getBytes(offset + %d, dst, dstOffset, bytesCopied);\n\n" + indent + " return bytesCopied;\n" + @@ -2556,8 +2556,8 @@ else if (encoding.primitiveType() == PrimitiveType.UINT8) new Formatter(sb).format("\n" + indent + " public void wrap%s(final %s wrapBuffer)\n" + indent + " {\n" + - stateTransition + "%s" + + stateTransition + indent + " wrapBuffer.wrap(buffer, offset + %d, %d);\n" + indent + " }\n", Generators.toUpperFirstChar(propertyName), diff --git a/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java b/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java index d6f8a2f1de..6f2f26f2fc 100644 --- a/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java +++ b/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java @@ -24,6 +24,8 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import java.nio.charset.StandardCharsets; + import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.notNullValue; @@ -105,7 +107,7 @@ void disallowsSkippingEncodingOfVariableLengthField3() final MultipleVarLengthEncoder encoder = new MultipleVarLengthEncoder() .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); encoder.a(42); - final byte[] value = "def".getBytes(); + final byte[] value = "def".getBytes(StandardCharsets.US_ASCII); assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.putC(value, 0, value.length)); } @@ -115,7 +117,7 @@ void disallowsSkippingEncodingOfVariableLengthField4() final MultipleVarLengthEncoder encoder = new MultipleVarLengthEncoder() .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); encoder.a(42); - final byte[] value = "def".getBytes(); + final byte[] value = "def".getBytes(StandardCharsets.US_ASCII); final UnsafeBuffer buffer = new UnsafeBuffer(value); assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.putC(buffer, 0, buffer.capacity())); } @@ -1469,7 +1471,7 @@ void allowsNewDecoderToDecodeAddedArrayFieldBeforeGroup() } @Test - void allowsNewDecoderToDecodeMissingArrayFieldBeforeGroupAsNullValue() + void allowsNewDecoderToDecodeMissingArrayFieldBeforeGroupAsNullValue1() { final AddArrayBeforeGroupV0Encoder encoder = new AddArrayBeforeGroupV0Encoder() .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); @@ -1489,6 +1491,62 @@ void allowsNewDecoderToDecodeMissingArrayFieldBeforeGroupAsNullValue() assertThat(b.next().c(), equalTo(2)); } + @Test + void allowsNewDecoderToDecodeMissingArrayFieldBeforeGroupAsNullValue2() + { + final AddArrayBeforeGroupV0Encoder encoder = new AddArrayBeforeGroupV0Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(1).bCount(1).next().c(2); + + modifyHeaderToLookLikeVersion0(); + + final AddArrayBeforeGroupV1Decoder decoder = new AddArrayBeforeGroupV1Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(1)); + assertThat(decoder.getD(new byte[8], 0, 8), equalTo(0)); + final AddArrayBeforeGroupV1Decoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + assertThat(b.next().c(), equalTo(2)); + } + + @Test + void allowsNewDecoderToDecodeMissingArrayFieldBeforeGroupAsNullValue3() + { + final AddArrayBeforeGroupV0Encoder encoder = new AddArrayBeforeGroupV0Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(1).bCount(1).next().c(2); + + modifyHeaderToLookLikeVersion0(); + + final AddArrayBeforeGroupV1Decoder decoder = new AddArrayBeforeGroupV1Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(1)); + assertThat(decoder.getD(new ExpandableArrayBuffer(), 0, 8), equalTo(0)); + final AddArrayBeforeGroupV1Decoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + assertThat(b.next().c(), equalTo(2)); + } + + @Test + void allowsNewDecoderToDecodeMissingArrayFieldBeforeGroupAsNullValue4() + { + final AddArrayBeforeGroupV0Encoder encoder = new AddArrayBeforeGroupV0Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(1).bCount(1).next().c(2); + + modifyHeaderToLookLikeVersion0(); + + final AddArrayBeforeGroupV1Decoder decoder = new AddArrayBeforeGroupV1Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(1)); + final UnsafeBuffer buffer = new UnsafeBuffer(); + decoder.wrapD(buffer); + assertThat(buffer.capacity(), equalTo(0)); + final AddArrayBeforeGroupV1Decoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + assertThat(b.next().c(), equalTo(2)); + } + @Test void allowsNewDecoderToSkipPresentButAddedArrayFieldBeforeGroup() { @@ -2042,6 +2100,453 @@ void disallowsReDecodingLatestGroupField() assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::d); } + @Test + void allowsNewDecoderToDecodeAddedVarData() + { + final AddVarDataV1Encoder encoder = new AddVarDataV1Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + encoder.b("abc"); + + final AddVarDataV1Decoder decoder = new AddVarDataV1Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(42)); + assertThat(decoder.b(), equalTo("abc")); + } + + @Test + void allowsNewDecoderToDecodeMissingAddedVarDataAsNullValue1() + { + final AddVarDataV0Encoder encoder = new AddVarDataV0Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + + modifyHeaderToLookLikeVersion0(); + + final AddVarDataV1Decoder decoder = new AddVarDataV1Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(42)); + assertThat(decoder.b(), equalTo("")); + } + + @Test + void allowsNewDecoderToDecodeMissingAddedVarDataAsNullValue2() + { + final AddVarDataV0Encoder encoder = new AddVarDataV0Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + + modifyHeaderToLookLikeVersion0(); + + final AddVarDataV1Decoder decoder = new AddVarDataV1Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(42)); + assertThat(decoder.getB(new StringBuilder()), equalTo(0)); + } + + @Test + void allowsNewDecoderToDecodeMissingAddedVarDataAsNullValue3() + { + final AddVarDataV0Encoder encoder = new AddVarDataV0Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + + modifyHeaderToLookLikeVersion0(); + + final AddVarDataV1Decoder decoder = new AddVarDataV1Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(42)); + assertThat(decoder.getB(new byte[3], 0, 3), equalTo(0)); + } + + @Test + void allowsNewDecoderToDecodeMissingAddedVarDataAsNullValue4() + { + final AddVarDataV0Encoder encoder = new AddVarDataV0Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + + modifyHeaderToLookLikeVersion0(); + + final AddVarDataV1Decoder decoder = new AddVarDataV1Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(42)); + assertThat(decoder.getB(new ExpandableArrayBuffer(), 0, 3), equalTo(0)); + } + + @Test + void allowsNewDecoderToDecodeMissingAddedVarDataAsNullValue5() + { + final AddVarDataV0Encoder encoder = new AddVarDataV0Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + + modifyHeaderToLookLikeVersion0(); + + final AddVarDataV1Decoder decoder = new AddVarDataV1Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(42)); + assertThat(decoder.bLength(), equalTo(0)); + } + + @Test + void allowsEncodingAndDecodingAsciiInsideGroupInSchemaDefinedOrder1() + { + final AsciiInsideGroupEncoder encoder = new AsciiInsideGroupEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a("GBPUSD"); + encoder.bCount(1) + .next() + .c("EURUSD"); + + final AsciiInsideGroupDecoder decoder = new AsciiInsideGroupDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo("GBPUSD")); + final AsciiInsideGroupDecoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + b.next(); + assertThat(b.c(), equalTo("EURUSD")); + } + + @Test + void allowsEncodingAndDecodingAsciiInsideGroupInSchemaDefinedOrder2() + { + final AsciiInsideGroupEncoder encoder = new AsciiInsideGroupEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + final byte[] gbpUsdBytes = "GBPUSD".getBytes(StandardCharsets.US_ASCII); + encoder.putA(gbpUsdBytes, 0); + encoder.bCount(1) + .next() + .c(0, (byte)'E') + .c(1, (byte)'U') + .c(2, (byte)'R') + .c(3, (byte)'U') + .c(4, (byte)'S') + .c(5, (byte)'D'); + + final AsciiInsideGroupDecoder decoder = new AsciiInsideGroupDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + final byte[] aBytes = new byte[6]; + decoder.getA(aBytes, 0); + assertThat(aBytes, equalTo(gbpUsdBytes)); + final AsciiInsideGroupDecoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + b.next(); + assertThat(b.c(0), equalTo((byte)'E')); + assertThat(b.c(1), equalTo((byte)'U')); + assertThat(b.c(2), equalTo((byte)'R')); + assertThat(b.c(3), equalTo((byte)'U')); + assertThat(b.c(4), equalTo((byte)'S')); + assertThat(b.c(5), equalTo((byte)'D')); + } + + @Test + void disallowsEncodingAsciiInsideGroupBeforeCallingNext1() + { + final AsciiInsideGroupEncoder.BEncoder bEncoder = encodeUntilGroupWithAsciiInside(); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.c("EURUSD")); + } + + @Test + void disallowsEncodingAsciiInsideGroupBeforeCallingNext2() + { + final AsciiInsideGroupEncoder.BEncoder bEncoder = encodeUntilGroupWithAsciiInside(); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.c(0, (byte)'E')); + } + + @Test + void disallowsEncodingAsciiInsideGroupBeforeCallingNext3() + { + final AsciiInsideGroupEncoder.BEncoder bEncoder = encodeUntilGroupWithAsciiInside(); + final byte[] eurUsdBytes = "EURUSD".getBytes(StandardCharsets.US_ASCII); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.putC(eurUsdBytes, 0)); + } + + private AsciiInsideGroupEncoder.BEncoder encodeUntilGroupWithAsciiInside() + { + final AsciiInsideGroupEncoder encoder = new AsciiInsideGroupEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a("GBPUSD"); + return encoder.bCount(1); + } + + @Test + void disallowsDecodingAsciiInsideGroupBeforeCallingNext1() + { + final AsciiInsideGroupDecoder.BDecoder b = decodeUntilGroupWithAsciiInside(); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> b.c(0)); + } + + @Test + void disallowsDecodingAsciiInsideGroupBeforeCallingNext2() + { + final AsciiInsideGroupDecoder.BDecoder b = decodeUntilGroupWithAsciiInside(); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> b.getC(new byte[6], 0)); + } + + @Test + void disallowsDecodingAsciiInsideGroupBeforeCallingNext3() + { + final AsciiInsideGroupDecoder.BDecoder b = decodeUntilGroupWithAsciiInside(); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, b::c); + } + + @Test + void disallowsDecodingAsciiInsideGroupBeforeCallingNext4() + { + final AsciiInsideGroupDecoder.BDecoder b = decodeUntilGroupWithAsciiInside(); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> b.getC(new StringBuilder())); + } + + private AsciiInsideGroupDecoder.BDecoder decodeUntilGroupWithAsciiInside() + { + final AsciiInsideGroupEncoder encoder = new AsciiInsideGroupEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a("GBPUSD"); + encoder.bCount(1) + .next() + .c("EURUSD"); + + final AsciiInsideGroupDecoder decoder = new AsciiInsideGroupDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo("GBPUSD")); + final AsciiInsideGroupDecoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + return b; + } + + @Test + @Disabled("Our access checks are too strict to allow the behaviour in this test.") + void allowsReEncodingTopLevelAsciiViaReWrap() + { + final AsciiInsideGroupEncoder encoder = new AsciiInsideGroupEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a("GBPUSD"); + encoder.bCount(1) + .next() + .c("EURUSD"); + + encoder.a("CADUSD"); + + final AsciiInsideGroupDecoder decoder = new AsciiInsideGroupDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo("CADUSD")); + final AsciiInsideGroupDecoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + b.next(); + assertThat(b.c(), equalTo("EURUSD")); + } + + @Test + void allowsNewDecoderToDecodeAddedAsciiFieldBeforeGroup1() + { + final AddAsciiBeforeGroupV1Encoder encoder = new AddAsciiBeforeGroupV1Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(1) + .d("EURUSD") + .bCount(1) + .next().c(2); + + final AddAsciiBeforeGroupV1Decoder decoder = new AddAsciiBeforeGroupV1Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(1)); + assertThat(decoder.d(), equalTo("EURUSD")); + final AddAsciiBeforeGroupV1Decoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + assertThat(b.next().c(), equalTo(2)); + } + + @Test + void allowsNewDecoderToDecodeAddedAsciiFieldBeforeGroup2() + { + final AddAsciiBeforeGroupV1Encoder encoder = new AddAsciiBeforeGroupV1Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(1) + .d("EURUSD") + .bCount(1) + .next().c(2); + + final AddAsciiBeforeGroupV1Decoder decoder = new AddAsciiBeforeGroupV1Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(1)); + final StringBuilder aValue = new StringBuilder(); + decoder.getD(aValue); + assertThat(aValue.toString(), equalTo("EURUSD")); + final AddAsciiBeforeGroupV1Decoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + assertThat(b.next().c(), equalTo(2)); + } + + + @Test + void allowsNewDecoderToDecodeAddedAsciiFieldBeforeGroup3() + { + final AddAsciiBeforeGroupV1Encoder encoder = new AddAsciiBeforeGroupV1Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + final byte[] eurUsdBytes = "EURUSD".getBytes(StandardCharsets.US_ASCII); + encoder.a(1) + .putD(eurUsdBytes, 0) + .bCount(1) + .next().c(2); + + final AddAsciiBeforeGroupV1Decoder decoder = new AddAsciiBeforeGroupV1Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(1)); + final byte[] aBytes = new byte[6]; + decoder.getD(aBytes, 0); + assertThat(aBytes, equalTo(eurUsdBytes)); + final AddAsciiBeforeGroupV1Decoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + assertThat(b.next().c(), equalTo(2)); + } + + + @Test + void allowsNewDecoderToDecodeAddedAsciiFieldBeforeGroup4() + { + final AddAsciiBeforeGroupV1Encoder encoder = new AddAsciiBeforeGroupV1Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(1) + .d(0, (byte)'E') + .d(1, (byte)'U') + .d(2, (byte)'R') + .d(3, (byte)'U') + .d(4, (byte)'S') + .d(5, (byte)'D') + .bCount(1) + .next().c(2); + + final AddAsciiBeforeGroupV1Decoder decoder = new AddAsciiBeforeGroupV1Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(1)); + assertThat(decoder.d(0), equalTo((byte)'E')); + assertThat(decoder.d(1), equalTo((byte)'U')); + assertThat(decoder.d(2), equalTo((byte)'R')); + assertThat(decoder.d(3), equalTo((byte)'U')); + assertThat(decoder.d(4), equalTo((byte)'S')); + assertThat(decoder.d(5), equalTo((byte)'D')); + final AddAsciiBeforeGroupV1Decoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + assertThat(b.next().c(), equalTo(2)); + } + + @Test + void allowsNewDecoderToDecodeMissingAsciiFieldBeforeGroupAsNullValue1() + { + final AddAsciiBeforeGroupV0Encoder encoder = new AddAsciiBeforeGroupV0Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(1).bCount(1).next().c(2); + + modifyHeaderToLookLikeVersion0(); + + final AddAsciiBeforeGroupV1Decoder decoder = new AddAsciiBeforeGroupV1Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(1)); + assertThat(decoder.d(0), equalTo(AddAsciiBeforeGroupV1Decoder.dNullValue())); + assertThat(decoder.d(1), equalTo(AddAsciiBeforeGroupV1Decoder.dNullValue())); + assertThat(decoder.d(2), equalTo(AddAsciiBeforeGroupV1Decoder.dNullValue())); + assertThat(decoder.d(3), equalTo(AddAsciiBeforeGroupV1Decoder.dNullValue())); + assertThat(decoder.d(4), equalTo(AddAsciiBeforeGroupV1Decoder.dNullValue())); + assertThat(decoder.d(5), equalTo(AddAsciiBeforeGroupV1Decoder.dNullValue())); + final AddAsciiBeforeGroupV1Decoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + assertThat(b.next().c(), equalTo(2)); + } + + @Test + void allowsNewDecoderToDecodeMissingAsciiFieldBeforeGroupAsNullValue2() + { + final AddAsciiBeforeGroupV0Encoder encoder = new AddAsciiBeforeGroupV0Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(1).bCount(1).next().c(2); + + modifyHeaderToLookLikeVersion0(); + + final AddAsciiBeforeGroupV1Decoder decoder = new AddAsciiBeforeGroupV1Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(1)); + assertThat(decoder.d(), equalTo("")); + final AddAsciiBeforeGroupV1Decoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + assertThat(b.next().c(), equalTo(2)); + } + + @Test + void allowsNewDecoderToDecodeMissingAsciiFieldBeforeGroupAsNullValue3() + { + final AddAsciiBeforeGroupV0Encoder encoder = new AddAsciiBeforeGroupV0Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(1).bCount(1).next().c(2); + + modifyHeaderToLookLikeVersion0(); + + final AddAsciiBeforeGroupV1Decoder decoder = new AddAsciiBeforeGroupV1Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(1)); + final StringBuilder aValue = new StringBuilder(); + assertThat(decoder.getD(aValue), equalTo(0)); + assertThat(aValue.length(), equalTo(0)); + final AddAsciiBeforeGroupV1Decoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + assertThat(b.next().c(), equalTo(2)); + } + + @Test + void allowsNewDecoderToDecodeMissingAsciiFieldBeforeGroupAsNullValue4() + { + final AddAsciiBeforeGroupV0Encoder encoder = new AddAsciiBeforeGroupV0Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(1).bCount(1).next().c(2); + + modifyHeaderToLookLikeVersion0(); + + final AddAsciiBeforeGroupV1Decoder decoder = new AddAsciiBeforeGroupV1Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(1)); + assertThat(decoder.getD(new byte[6], 0), equalTo(0)); + final AddAsciiBeforeGroupV1Decoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + assertThat(b.next().c(), equalTo(2)); + } + + @Test + void allowsNewDecoderToSkipPresentButAddedAsciiFieldBeforeGroup() + { + final AddAsciiBeforeGroupV1Encoder encoder = new AddAsciiBeforeGroupV1Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(1) + .d("EURUSD") + .bCount(1) + .next().c(2); + + final AddAsciiBeforeGroupV1Decoder decoder = new AddAsciiBeforeGroupV1Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(1)); + final AddAsciiBeforeGroupV1Decoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + assertThat(b.next().c(), equalTo(2)); + } + + @Test + void allowsOldDecoderToSkipAddedAsciiFieldBeforeGroup() + { + final AddAsciiBeforeGroupV1Encoder encoder = new AddAsciiBeforeGroupV1Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(1) + .d("EURUSD") + .bCount(1) + .next().c(2); + + modifyHeaderToLookLikeVersion1(); + + final AddAsciiBeforeGroupV0Decoder decoder = new AddAsciiBeforeGroupV0Decoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(1)); + final AddAsciiBeforeGroupV0Decoder.BDecoder b = decoder.b(); + assertThat(b.count(), equalTo(1)); + assertThat(b.next().c(), equalTo(2)); + } + private void modifyHeaderToLookLikeVersion0() { messageHeaderDecoder.wrap(buffer, OFFSET); @@ -2058,5 +2563,6 @@ private void modifyHeaderToLookLikeVersion1() messageHeaderEncoder.wrap(buffer, OFFSET); messageHeaderEncoder.templateId(v0TemplateId); } - // TODO improve and test error message + + // TODO test error message } diff --git a/sbe-tool/src/test/resources/field-order-check-schema.xml b/sbe-tool/src/test/resources/field-order-check-schema.xml index 75756b4906..96abf6e7cd 100644 --- a/sbe-tool/src/test/resources/field-order-check-schema.xml +++ b/sbe-tool/src/test/resources/field-order-check-schema.xml @@ -35,6 +35,7 @@ 2 4 + @@ -236,4 +237,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 3d05f7871e46d917d6cccae11e3e1451eadb81aa Mon Sep 17 00:00:00 2001 From: Zach Bray Date: Thu, 15 Jun 2023 09:11:03 +0100 Subject: [PATCH 08/50] [Java] Test field checks do not affect toString. --- .../real_logic/sbe/FieldOrderCheckTest.java | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java b/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java index 6f2f26f2fc..bfa05cfa8d 100644 --- a/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java +++ b/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java @@ -27,9 +27,7 @@ import java.nio.charset.StandardCharsets; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.notNullValue; -import static org.hamcrest.Matchers.nullValue; +import static org.hamcrest.Matchers.*; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assumptions.assumeFalse; @@ -62,6 +60,7 @@ void allowsEncodingAndDecodingVariableLengthFieldsInSchemaDefinedOrder() assertThat(decoder.a(), equalTo(42)); assertThat(decoder.b(), equalTo("abc")); assertThat(decoder.c(), equalTo("def")); + assertThat(decoder.toString(), containsString("a=42|b='abc'|c='def'")); } @Test @@ -239,6 +238,7 @@ void allowsEncodingAndDecodingGroupAndVariableLengthFieldsInSchemaDefinedOrder() assertThat(bs.next().c(), equalTo(1)); assertThat(bs.next().c(), equalTo(2)); assertThat(decoder.d(), equalTo("abc")); + assertThat(decoder.toString(), containsString("a=42|b=[(c=1),(c=2)]|d='abc'")); } @Test @@ -256,6 +256,7 @@ void allowsEncodingAndDecodingEmptyGroupAndVariableLengthFieldsInSchemaDefinedOr final GroupAndVarLengthDecoder.BDecoder bs = decoder.b(); assertThat(bs.count(), equalTo(0)); assertThat(decoder.d(), equalTo("abc")); + assertThat(decoder.toString(), containsString("a=42|b=[]|d='abc'")); } @Test @@ -496,6 +497,7 @@ void allowsEncodingAndDecodingVariableLengthFieldInsideGroupInSchemaDefinedOrder assertThat(bs.next().c(), equalTo(2)); assertThat(bs.d(), equalTo("def")); assertThat(decoder.e(), equalTo("ghi")); + assertThat(decoder.toString(), containsString("a=42|b=[(c=1|d='abc'),(c=2|d='def')]|e='ghi'")); } @Test @@ -719,6 +721,8 @@ void allowsEncodingNestedGroupsInSchemaDefinedOrder() final NestedGroupsDecoder.HDecoder hs = decoder.h(); assertThat(hs.count(), equalTo(1)); assertThat(hs.next().i(), equalTo(8)); + assertThat(decoder.toString(), + containsString("a=42|b=[(c=1|d=[(e=2),(e=3)]|f=[(g=4)]),(c=5|d=[(e=6)]|f=[(g=7)])]|h=[(i=8)]")); } @Test @@ -737,6 +741,7 @@ void allowsEncodingEmptyNestedGroupsInSchemaDefinedOrder() assertThat(bs.count(), equalTo(0)); final NestedGroupsDecoder.HDecoder hs = decoder.h(); assertThat(hs.count(), equalTo(0)); + assertThat(decoder.toString(), containsString("a=42|b=[]|h=[]")); } @Test @@ -771,6 +776,7 @@ void allowsEncodingAndDecodingCompositeInsideGroupInSchemaDefinedOrder() final PointDecoder c = b.next().c(); assertThat(c.x(), equalTo(3)); assertThat(c.y(), equalTo(4)); + assertThat(decoder.toString(), containsString("a=(x=1|y=2)|b=[(c=(x=3|y=4))]")); } @Test @@ -1247,6 +1253,7 @@ void allowsNewDecoderToDecodeMissingGroupBeforeVarDataAsNullValue() final AddGroupBeforeVarDataV1Decoder.CDecoder c = decoder.c(); assertThat(c.count(), equalTo(0)); assertThat(decoder.b(), equalTo("abc")); + assertThat(decoder.toString(), containsString("a=1|c=[]|b='abc'")); } @Test @@ -1674,6 +1681,7 @@ void allowsEncodingAndDecodingEnumInsideGroupInSchemaDefinedOrder() final EnumInsideGroupDecoder.BDecoder b = decoder.b(); assertThat(b.count(), equalTo(1)); assertThat(b.next().c(), equalTo(Direction.SELL)); + assertThat(decoder.toString(), containsString("a=BUY|b=[(c=SELL)]")); } @Test @@ -1748,6 +1756,7 @@ void allowsEncodingAndDecodingBitSetInsideGroupInSchemaDefinedOrder() assertThat(c.guacamole(), equalTo(false)); assertThat(c.cheese(), equalTo(false)); assertThat(c.sourCream(), equalTo(true)); + assertThat(decoder.toString(), containsString("a={guacamole,cheese}|b=[(c={sourCream})]")); } @Test @@ -1831,6 +1840,7 @@ void allowsEncodingAndDecodingArrayInsideGroupInSchemaDefinedOrder() assertThat(b.c(1), equalTo((short)6)); assertThat(b.c(2), equalTo((short)7)); assertThat(b.c(3), equalTo((short)8)); + assertThat(decoder.toString(), containsString("a=[1,2,3,4]|b=[(c=[5,6,7,8])]")); } @Test @@ -1968,6 +1978,7 @@ void allowsEncodingAndDecodingGroupFieldsInSchemaDefinedOrder1() final MultipleGroupsDecoder.DDecoder d = decoder.d(); assertThat(d.count(), equalTo(1)); assertThat(d.next().e(), equalTo(43)); + assertThat(decoder.toString(), containsString("a=42|b=[]|d=[(e=43)]")); } @Test @@ -2127,6 +2138,7 @@ void allowsNewDecoderToDecodeMissingAddedVarDataAsNullValue1() .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); assertThat(decoder.a(), equalTo(42)); assertThat(decoder.b(), equalTo("")); + assertThat(decoder.toString(), containsString("a=42|b=''")); } @Test @@ -2206,6 +2218,7 @@ void allowsEncodingAndDecodingAsciiInsideGroupInSchemaDefinedOrder1() assertThat(b.count(), equalTo(1)); b.next(); assertThat(b.c(), equalTo("EURUSD")); + assertThat(decoder.toString(), containsString("a=GBPUSD|b=[(c=EURUSD)]")); } @Test From 3b8d557c1594ec3fcd98c98a9ec9fdc347841129 Mon Sep 17 00:00:00 2001 From: Zach Bray Date: Thu, 15 Jun 2023 10:06:43 +0100 Subject: [PATCH 09/50] [Java] Fix field order checks to work with `decodedLength()`. --- .../sbe/generation/java/JavaGenerator.java | 5 + .../sbe/ir/generated/FrameCodecDecoder.java | 6 + .../sbe/ir/generated/TokenCodecDecoder.java | 6 + .../real_logic/sbe/FieldOrderCheckTest.java | 139 ++++++++++++++++++ .../generation/java/SchemaExtensionTest.java | 2 - .../java/SkipAndDecodedLengthTest.java | 2 - 6 files changed, 156 insertions(+), 4 deletions(-) diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java index af0e4dff84..0a17fe82b4 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java @@ -3076,9 +3076,14 @@ private CharSequence generateDecoderFlyweightCode( " public int sbeDecodedLength()\n" + " {\n" + " final int currentLimit = limit();\n" + + " final CodecState currentCodecState = codecState();\n" + " sbeSkip();\n" + " final int decodedLength = encodedLength();\n" + " limit(currentLimit);\n\n" + + " if (DEBUG_MODE)\n" + + " {\n" + + " codecState(currentCodecState);\n" + + " }\n\n" + " return decodedLength;\n" + " }\n\n" + diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java index 957aab7896..3972093b8e 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java @@ -166,10 +166,16 @@ public FrameCodecDecoder sbeRewind() public int sbeDecodedLength() { final int currentLimit = limit(); + final CodecState currentCodecState = codecState(); sbeSkip(); final int decodedLength = encodedLength(); limit(currentLimit); + if (DEBUG_MODE) + { + codecState(currentCodecState); + } + return decodedLength; } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java index 6f0ace424b..2256caa905 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java @@ -189,10 +189,16 @@ public TokenCodecDecoder sbeRewind() public int sbeDecodedLength() { final int currentLimit = limit(); + final CodecState currentCodecState = codecState(); sbeSkip(); final int decodedLength = encodedLength(); limit(currentLimit); + if (DEBUG_MODE) + { + codecState(currentCodecState); + } + return decodedLength; } diff --git a/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java b/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java index bfa05cfa8d..3f4d481c68 100644 --- a/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java +++ b/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java @@ -63,6 +63,47 @@ void allowsEncodingAndDecodingVariableLengthFieldsInSchemaDefinedOrder() assertThat(decoder.toString(), containsString("a=42|b='abc'|c='def'")); } + @Test + void allowsDecodingVariableLengthFieldsAfterRewind() + { + final MultipleVarLengthEncoder encoder = new MultipleVarLengthEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + encoder.b("abc"); + encoder.c("def"); + + final MultipleVarLengthDecoder decoder = new MultipleVarLengthDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(42)); + assertThat(decoder.b(), equalTo("abc")); + assertThat(decoder.c(), equalTo("def")); + assertThat(decoder.toString(), containsString("a=42|b='abc'|c='def'")); + + decoder.sbeRewind(); + + assertThat(decoder.a(), equalTo(42)); + assertThat(decoder.b(), equalTo("abc")); + assertThat(decoder.c(), equalTo("def")); + assertThat(decoder.toString(), containsString("a=42|b='abc'|c='def'")); + } + + @Test + void allowsDecodingToSkipVariableLengthFields() + { + final MultipleVarLengthEncoder encoder = new MultipleVarLengthEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + encoder.b("abc"); + encoder.c("def"); + + final MultipleVarLengthDecoder decoder = new MultipleVarLengthDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(42)); + assertThat(decoder.skipB(), equalTo(3)); + assertThat(decoder.c(), equalTo("def")); + assertThat(decoder.toString(), containsString("a=42|b='abc'|c='def'")); + } + @Test @Disabled("Our access checks are too strict to allow the behaviour in this test.") void allowsReEncodingTopLevelPrimitiveFields() @@ -241,6 +282,104 @@ void allowsEncodingAndDecodingGroupAndVariableLengthFieldsInSchemaDefinedOrder() assertThat(decoder.toString(), containsString("a=42|b=[(c=1),(c=2)]|d='abc'")); } + @Test + void allowsDecodingGroupAndVariableLengthFieldsAfterRewind() + { + final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + encoder.bCount(2) + .next() + .c(1) + .next() + .c(2); + encoder.d("abc"); + + final GroupAndVarLengthDecoder decoder = new GroupAndVarLengthDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(42)); + final GroupAndVarLengthDecoder.BDecoder bs = decoder.b(); + assertThat(bs.count(), equalTo(2)); + assertThat(bs.next().c(), equalTo(1)); + assertThat(bs.next().c(), equalTo(2)); + assertThat(decoder.d(), equalTo("abc")); + assertThat(decoder.toString(), containsString("a=42|b=[(c=1),(c=2)]|d='abc'")); + + decoder.sbeRewind(); + + assertThat(decoder.a(), equalTo(42)); + final GroupAndVarLengthDecoder.BDecoder bs2 = decoder.b(); + assertThat(bs2.count(), equalTo(2)); + assertThat(bs2.next().c(), equalTo(1)); + assertThat(bs2.next().c(), equalTo(2)); + assertThat(decoder.d(), equalTo("abc")); + assertThat(decoder.toString(), containsString("a=42|b=[(c=1),(c=2)]|d='abc'")); + } + + @Test + void allowsDecodingToSkipMessage() + { + final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + encoder.bCount(2) + .next() + .c(1) + .next() + .c(2); + encoder.d("abc"); + + final int nextEncodeOffset = encoder.limit(); + encoder.wrapAndApplyHeader(buffer, nextEncodeOffset, messageHeaderEncoder); + encoder.a(43); + encoder.bCount(2) + .next() + .c(3) + .next() + .c(4); + encoder.d("def"); + + final GroupAndVarLengthDecoder decoder = new GroupAndVarLengthDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + + decoder.sbeSkip(); + final int nextDecodeOffset = decoder.limit(); + assertThat(nextDecodeOffset, equalTo(nextEncodeOffset)); + + decoder.wrapAndApplyHeader(buffer, nextDecodeOffset, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(43)); + final GroupAndVarLengthDecoder.BDecoder bs = decoder.b(); + assertThat(bs.count(), equalTo(2)); + assertThat(bs.next().c(), equalTo(3)); + assertThat(bs.next().c(), equalTo(4)); + assertThat(decoder.d(), equalTo("def")); + } + + @Test + void allowsDecodingToDetermineMessageLengthBeforeReadingFields() + { + final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(43); + encoder.bCount(2) + .next() + .c(3) + .next() + .c(4); + encoder.d("def"); + + final GroupAndVarLengthDecoder decoder = new GroupAndVarLengthDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + + assertThat(decoder.sbeDecodedLength(), equalTo(18)); + assertThat(decoder.a(), equalTo(43)); + final GroupAndVarLengthDecoder.BDecoder bs = decoder.b(); + assertThat(bs.count(), equalTo(2)); + assertThat(bs.next().c(), equalTo(3)); + assertThat(bs.next().c(), equalTo(4)); + assertThat(decoder.d(), equalTo("def")); + } + @Test void allowsEncodingAndDecodingEmptyGroupAndVariableLengthFieldsInSchemaDefinedOrder() { diff --git a/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/SchemaExtensionTest.java b/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/SchemaExtensionTest.java index 78bcc0e9c7..1774666139 100644 --- a/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/SchemaExtensionTest.java +++ b/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/SchemaExtensionTest.java @@ -21,7 +21,6 @@ import org.agrona.generation.CompilerUtil; import org.agrona.generation.StringWriterOutputManager; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import uk.co.real_logic.sbe.Tests; import uk.co.real_logic.sbe.ir.Ir; @@ -43,7 +42,6 @@ import static uk.co.real_logic.sbe.generation.java.ReflectionUtil.*; @SuppressWarnings("MethodLength") -@Disabled // TODO class SchemaExtensionTest { private static final Class BUFFER_CLASS = MutableDirectBuffer.class; diff --git a/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/SkipAndDecodedLengthTest.java b/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/SkipAndDecodedLengthTest.java index 101bf6ccc7..8d8fcf3d4d 100644 --- a/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/SkipAndDecodedLengthTest.java +++ b/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/SkipAndDecodedLengthTest.java @@ -18,7 +18,6 @@ import baseline.CarDecoder; import baseline.MessageHeaderDecoder; import org.agrona.concurrent.UnsafeBuffer; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import uk.co.real_logic.sbe.EncodedCarTestBase; @@ -31,7 +30,6 @@ class SkipAndDecodedLengthTest extends EncodedCarTestBase private static final int MSG_BUFFER_CAPACITY = 4 * 1024; @Test - @Disabled // TODO void shouldRewindAfterReadingFullMessage() { final ByteBuffer encodedMsgBuffer = ByteBuffer.allocate(MSG_BUFFER_CAPACITY); From 6e105459c5de59bbdce4892accf4c372fa86b705 Mon Sep 17 00:00:00 2001 From: Zach Bray Date: Thu, 15 Jun 2023 11:17:49 +0100 Subject: [PATCH 10/50] [Java] Test content of field order error messages. There is still work to do in this area. In particular: 1. States should be more descriptive. 1. Errors should contain full field paths. 2. Group error messages should be more clear, e.g., "cannot write group element before calling next()". --- .../real_logic/sbe/FieldOrderCheckTest.java | 242 +++++++++++++----- 1 file changed, 180 insertions(+), 62 deletions(-) diff --git a/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java b/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java index 3f4d481c68..4fc6400c13 100644 --- a/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java +++ b/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java @@ -128,7 +128,9 @@ void disallowsSkippingEncodingOfVariableLengthField1() final MultipleVarLengthEncoder encoder = new MultipleVarLengthEncoder() .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); encoder.a(42); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.c("def")); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.c("def")); + assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_BLOCK")); } @Test @@ -138,7 +140,9 @@ void disallowsSkippingEncodingOfVariableLengthField2() .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); encoder.a(42); final CharSequence def = new StringBuilder("def"); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.c(def)); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.c(def)); + assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_BLOCK")); } @Test @@ -148,7 +152,9 @@ void disallowsSkippingEncodingOfVariableLengthField3() .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); encoder.a(42); final byte[] value = "def".getBytes(StandardCharsets.US_ASCII); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.putC(value, 0, value.length)); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.putC(value, 0, value.length)); + assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_BLOCK")); } @Test @@ -159,7 +165,9 @@ void disallowsSkippingEncodingOfVariableLengthField4() encoder.a(42); final byte[] value = "def".getBytes(StandardCharsets.US_ASCII); final UnsafeBuffer buffer = new UnsafeBuffer(value); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.putC(buffer, 0, buffer.capacity())); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.putC(buffer, 0, buffer.capacity())); + assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_BLOCK")); } @Test @@ -170,7 +178,9 @@ void disallowsReEncodingEarlierVariableLengthFields() encoder.a(42); encoder.b("abc"); encoder.c("def"); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.b("ghi")); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.b("ghi")); + assertThat(exception.getMessage(), containsString("Cannot access field \"b\" in state: V0_C_DONE")); } @Test @@ -181,49 +191,64 @@ void disallowsReEncodingLatestVariableLengthField() encoder.a(42); encoder.b("abc"); encoder.c("def"); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.c("ghi")); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.c("ghi")); + assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_C_DONE")); } @Test void disallowsSkippingDecodingOfVariableLengthField1() { final MultipleVarLengthDecoder decoder = decodeUntilVarLengthFields(); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::c); + + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::c); + assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_BLOCK")); } @Test void disallowsSkippingDecodingOfVariableLengthField2() { final MultipleVarLengthDecoder decoder = decodeUntilVarLengthFields(); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> decoder.wrapC(new UnsafeBuffer())); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> decoder.wrapC(new UnsafeBuffer())); + assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_BLOCK")); } @Test void disallowsSkippingDecodingOfVariableLengthField3() { final MultipleVarLengthDecoder decoder = decodeUntilVarLengthFields(); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> decoder.getC(new StringBuilder())); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> decoder.getC(new StringBuilder())); + assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_BLOCK")); } @Test void disallowsSkippingDecodingOfVariableLengthField4() { final MultipleVarLengthDecoder decoder = decodeUntilVarLengthFields(); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> decoder.getC(new byte[3], 0, 3)); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> decoder.getC(new byte[3], 0, 3)); + assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_BLOCK")); } @Test void disallowsSkippingDecodingOfVariableLengthField5() { final MultipleVarLengthDecoder decoder = decodeUntilVarLengthFields(); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::cLength); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::cLength); + assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_BLOCK")); } @Test void disallowsSkippingDecodingOfVariableLengthField6() { final MultipleVarLengthDecoder decoder = decodeUntilVarLengthFields(); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> decoder.getC(new ExpandableArrayBuffer(), 0, 3)); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> decoder.getC(new ExpandableArrayBuffer(), 0, 3)); + assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_BLOCK")); } @Test @@ -232,7 +257,9 @@ void disallowsReDecodingEarlierVariableLengthField() final MultipleVarLengthDecoder decoder = decodeUntilVarLengthFields(); assertThat(decoder.b(), equalTo("abc")); assertThat(decoder.c(), equalTo("def")); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::b); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::b); + assertThat(exception.getMessage(), containsString("Cannot access field \"b\" in state: V0_C_DONE")); } @Test @@ -241,7 +268,9 @@ void disallowsReDecodingLatestVariableLengthField() final MultipleVarLengthDecoder decoder = decodeUntilVarLengthFields(); assertThat(decoder.b(), equalTo("abc")); assertThat(decoder.c(), equalTo("def")); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::c); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::c); + assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_C_DONE")); } private MultipleVarLengthDecoder decodeUntilVarLengthFields() @@ -456,7 +485,9 @@ void disallowsEncodingGroupElementBeforeCallingNext() .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); encoder.a(42); final GroupAndVarLengthEncoder.BEncoder bEncoder = encoder.bCount(1); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.c(1)); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.c(1)); + assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_B_N")); } @Test @@ -477,7 +508,9 @@ void disallowsDecodingGroupElementBeforeCallingNext() assertThat(decoder.a(), equalTo(42)); final GroupAndVarLengthDecoder.BDecoder bs = decoder.b(); assertThat(bs.count(), equalTo(2)); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, bs::c); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, bs::c); + assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_B_N")); } @Test @@ -486,7 +519,9 @@ void disallowsSkippingEncodingOfGroup() final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder() .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); encoder.a(42); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.d("abc")); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.d("abc")); + assertThat(exception.getMessage(), containsString("Cannot access field \"d\" in state: V0_BLOCK")); } @Test @@ -501,7 +536,9 @@ void disallowsReEncodingVariableLengthFieldAfterGroup() .next() .c(2); encoder.d("abc"); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.d("def")); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.d("def")); + assertThat(exception.getMessage(), containsString("Cannot access field \"d\" in state: V0_D_DONE")); } @Test @@ -516,7 +553,9 @@ void disallowsReEncodingGroupLength() .next() .c(2); encoder.d("abc"); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.bCount(1)); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.bCount(1)); + assertThat(exception.getMessage(), containsString("Cannot access field \"b\" in state: V0_D_DONE")); } @Test @@ -560,7 +599,9 @@ void disallowsMissedDecodingOfGroupBeforeVariableLengthField() final GroupAndVarLengthDecoder decoder = new GroupAndVarLengthDecoder() .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); assertThat(decoder.a(), equalTo(42)); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::d); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::d); + assertThat(exception.getMessage(), containsString("Cannot access field \"d\" in state: V0_BLOCK")); } @Test @@ -584,11 +625,13 @@ void disallowsReDecodingVariableLengthFieldAfterGroup() assertThat(bs.next().c(), equalTo(1)); assertThat(bs.next().c(), equalTo(2)); assertThat(decoder.d(), equalTo("abc")); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::d); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::d); + assertThat(exception.getMessage(), containsString("Cannot access field \"d\" in state: V0_D_DONE")); } @Test - void disallowsReDecodingGroupBeforeVariableLengthField() + void disallowsReDecodingGroupAfterVariableLengthField() { final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder() .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); @@ -608,7 +651,9 @@ void disallowsReDecodingGroupBeforeVariableLengthField() assertThat(bs.next().c(), equalTo(1)); assertThat(bs.next().c(), equalTo(2)); assertThat(decoder.d(), equalTo("abc")); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::b); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::b); + assertThat(exception.getMessage(), containsString("Cannot access field \"b\" in state: V0_D_DONE")); } @Test @@ -671,7 +716,9 @@ void disallowsMissedGroupElementVariableLengthFieldToEncodeAtTopLevel() .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); encoder.a(42); encoder.bCount(1).next().c(1); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.e("abc")); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.e("abc")); + assertThat(exception.getMessage(), containsString("Cannot access field \"e\" in state: V0_B_1_BLOCK")); } @Test @@ -682,7 +729,9 @@ void disallowsMissedGroupElementVariableLengthFieldToEncodeNextElement() encoder.a(42); final VarLengthInsideGroupEncoder.BEncoder b = encoder.bCount(2) .next(); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, b::next); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, b::next); + assertThat(exception.getMessage(), containsString("Cannot access field \"b\" in state: V0_B_N_BLOCK")); } @Test @@ -695,7 +744,9 @@ void disallowsMissedGroupElementEncoding() .next() .c(1) .d("abc"); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.e("abc")); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.e("abc")); + assertThat(exception.getMessage(), containsString("Cannot access field \"e\" in state: V0_B_N_D_DONE")); } @Test @@ -709,7 +760,9 @@ void disallowsReEncodingGroupElementVariableLengthField() .c(1) .d("abc"); encoder.e("def"); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> b.d("ghi")); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> b.d("ghi")); + assertThat(exception.getMessage(), containsString("Cannot access field \"d\" in state: V0_E_DONE")); } @Test @@ -734,7 +787,9 @@ void disallowsReDecodingGroupElementVariableLengthField() assertThat(bs.count(), equalTo(2)); assertThat(bs.next().c(), equalTo(1)); assertThat(bs.d(), equalTo("abc")); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, bs::d); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, bs::d); + assertThat(exception.getMessage(), containsString("Cannot access field \"d\" in state: V0_B_N_D_DONE")); } @Test @@ -758,7 +813,9 @@ void disallowsMissedDecodingOfGroupElementVariableLengthFieldToNextElement() final VarLengthInsideGroupDecoder.BDecoder bs = decoder.b(); assertThat(bs.count(), equalTo(2)); assertThat(bs.next().c(), equalTo(1)); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, bs::next); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, bs::next); + assertThat(exception.getMessage(), containsString("Cannot access field \"b\" in state: V0_B_N_BLOCK")); } @Test @@ -779,7 +836,9 @@ void disallowsMissedDecodingOfGroupElementVariableLengthFieldToTopLevel() final VarLengthInsideGroupDecoder.BDecoder bs = decoder.b(); assertThat(bs.count(), equalTo(1)); assertThat(bs.next().c(), equalTo(1)); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::e); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::e); + assertThat(exception.getMessage(), containsString("Cannot access field \"e\" in state: V0_B_1_BLOCK")); } @Test @@ -803,7 +862,9 @@ void disallowsMissedDecodingOfGroupElement() final VarLengthInsideGroupDecoder.BDecoder bs = decoder.b(); assertThat(bs.count(), equalTo(2)); assertThat(bs.next().c(), equalTo(1)); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::e); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::e); + assertThat(exception.getMessage(), containsString("Cannot access field \"e\" in state: V0_B_N_BLOCK")); } @Test @@ -892,7 +953,9 @@ void disallowsMissedEncodingOfNestedGroup() final NestedGroupsEncoder.BEncoder b = encoder.bCount(1) .next() .c(1); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> b.fCount(1)); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> b.fCount(1)); + assertThat(exception.getMessage(), containsString("Cannot access field \"f\" in state: V0_B_1_BLOCK")); } @Test @@ -925,7 +988,9 @@ void disallowsEncodingCompositeInsideGroupBeforeCallingNext() .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); encoder.a().x(1).y(2); final CompositeInsideGroupEncoder.BEncoder bEncoder = encoder.bCount(1); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, bEncoder::c); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, bEncoder::c); + assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_B_N")); } @Test @@ -945,7 +1010,9 @@ void disallowsDecodingCompositeInsideGroupBeforeCallingNext() assertThat(a.y(), equalTo(2)); final CompositeInsideGroupDecoder.BDecoder b = decoder.b(); assertThat(b.count(), equalTo(1)); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, b::c); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, b::c); + assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_B_N")); } @Test @@ -1421,7 +1488,9 @@ void disallowsNewDecoderToSkipPresentButAddedGroupBeforeVarData() final AddGroupBeforeVarDataV1Decoder decoder = new AddGroupBeforeVarDataV1Decoder() .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); assertThat(decoder.a(), equalTo(1)); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::b); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::b); + assertThat(exception.getMessage(), containsString("Cannot access field \"b\" in state: V1_BLOCK")); } @Test @@ -1830,7 +1899,9 @@ void disallowsEncodingEnumInsideGroupBeforeCallingNext() .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); encoder.a(Direction.BUY); final EnumInsideGroupEncoder.BEncoder bEncoder = encoder.bCount(1); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.c(Direction.SELL)); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.c(Direction.SELL)); + assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_B_N")); } @Test @@ -1848,7 +1919,9 @@ void disallowsDecodingEnumInsideGroupBeforeCallingNext() assertThat(decoder.a(), equalTo(Direction.BUY)); final EnumInsideGroupDecoder.BDecoder b = decoder.b(); assertThat(b.count(), equalTo(1)); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, b::c); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, b::c); + assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_B_N")); } @Test @@ -1905,7 +1978,9 @@ void disallowsEncodingBitSetInsideGroupBeforeCallingNext() .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); encoder.a().cheese(true).guacamole(true); final BitSetInsideGroupEncoder.BEncoder bEncoder = encoder.bCount(1); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, bEncoder::c); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, bEncoder::c); + assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_B_N")); } @Test @@ -1926,7 +2001,9 @@ void disallowsDecodingBitSetInsideGroupBeforeCallingNext() assertThat(a.sourCream(), equalTo(false)); final BitSetInsideGroupDecoder.BDecoder b = decoder.b(); assertThat(b.count(), equalTo(1)); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, b::c); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, b::c); + assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_B_N")); } @Test @@ -1986,22 +2063,28 @@ void allowsEncodingAndDecodingArrayInsideGroupInSchemaDefinedOrder() void disallowsEncodingArrayInsideGroupBeforeCallingNext1() { final ArrayInsideGroupEncoder.BEncoder bEncoder = encodeUntilGroupWithArrayInside(); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.putC((short)5, (short)6, (short)7, (short)8)); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.putC((short)5, (short)6, (short)7, (short)8)); + assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_B_N")); } @Test void disallowsEncodingArrayInsideGroupBeforeCallingNext2() { final ArrayInsideGroupEncoder.BEncoder bEncoder = encodeUntilGroupWithArrayInside(); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.c(0, (short)5)); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.c(0, (short)5)); + assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_B_N")); } @Test void disallowsEncodingArrayInsideGroupBeforeCallingNext3() { final ArrayInsideGroupEncoder.BEncoder bEncoder = encodeUntilGroupWithArrayInside(); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> - bEncoder.putC(new byte[] {5, 0, 6, 0, 7, 0, 8, 0}, 0, 8)); + final byte[] bytes = {5, 0, 6, 0, 7, 0, 8, 0}; + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.putC(bytes, 0, 8)); + assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_B_N")); } @Test @@ -2013,8 +2096,9 @@ void disallowsEncodingArrayInsideGroupBeforeCallingNext4() buffer.putShort(2, (short)6); buffer.putShort(4, (short)7); buffer.putShort(6, (short)8); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> - bEncoder.putC(buffer, 0, 8)); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.putC(buffer, 0, 8)); + assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_B_N")); } private ArrayInsideGroupEncoder.BEncoder encodeUntilGroupWithArrayInside() @@ -2029,28 +2113,36 @@ private ArrayInsideGroupEncoder.BEncoder encodeUntilGroupWithArrayInside() void disallowsDecodingArrayInsideGroupBeforeCallingNext1() { final ArrayInsideGroupDecoder.BDecoder b = decodeUntilGroupWithArrayInside(); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> b.c(0)); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> b.c(0)); + assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_B_N")); } @Test void disallowsDecodingArrayInsideGroupBeforeCallingNext2() { final ArrayInsideGroupDecoder.BDecoder b = decodeUntilGroupWithArrayInside(); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> b.getC(new byte[8], 0, 8)); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> b.getC(new byte[8], 0, 8)); + assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_B_N")); } @Test void disallowsDecodingArrayInsideGroupBeforeCallingNext3() { final ArrayInsideGroupDecoder.BDecoder b = decodeUntilGroupWithArrayInside(); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> b.getC(new ExpandableArrayBuffer(), 0, 8)); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> b.getC(new ExpandableArrayBuffer(), 0, 8)); + assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_B_N")); } @Test void disallowsDecodingArrayInsideGroupBeforeCallingNext4() { final ArrayInsideGroupDecoder.BDecoder b = decodeUntilGroupWithArrayInside(); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> b.wrapC(new UnsafeBuffer())); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> b.wrapC(new UnsafeBuffer())); + assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_B_N")); } private ArrayInsideGroupDecoder.BDecoder decodeUntilGroupWithArrayInside() @@ -2168,7 +2260,9 @@ void disallowsMissedEncodingOfGroupField() final MultipleGroupsEncoder encoder = new MultipleGroupsEncoder() .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); encoder.a(41); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.dCount(0)); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.dCount(0)); + assertThat(exception.getMessage(), containsString("Cannot access field \"d\" in state: V0_BLOCK")); } @Test @@ -2179,7 +2273,9 @@ void disallowsReEncodingEarlierGroupFields() encoder.a(41); encoder.bCount(1).next().c(42); encoder.dCount(1).next().e(43); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.bCount(1)); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.bCount(1)); + assertThat(exception.getMessage(), containsString("Cannot access field \"b\" in state: V0_D_1_BLOCK")); } @Test @@ -2190,7 +2286,9 @@ void disallowsReEncodingLatestGroupField() encoder.a(41); encoder.bCount(1).next().c(42); encoder.dCount(1).next().e(43); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.dCount(1)); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.dCount(1)); + assertThat(exception.getMessage(), containsString("Cannot access field \"d\" in state: V0_D_1_BLOCK")); } @Test @@ -2205,7 +2303,9 @@ void disallowsMissedDecodingOfGroupField() final MultipleGroupsDecoder decoder = new MultipleGroupsDecoder() .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); assertThat(decoder.a(), equalTo(41)); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::d); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::d); + assertThat(exception.getMessage(), containsString("Cannot access field \"d\" in state: V0_BLOCK")); } @Test @@ -2226,7 +2326,9 @@ void disallowsReDecodingEarlierGroupField() final MultipleGroupsDecoder.DDecoder d = decoder.d(); assertThat(d.count(), equalTo(1)); assertThat(d.next().e(), equalTo(43)); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::b); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::b); + assertThat(exception.getMessage(), containsString("Cannot access field \"b\" in state: V0_D_1_BLOCK")); } @Test @@ -2247,7 +2349,9 @@ void disallowsReDecodingLatestGroupField() final MultipleGroupsDecoder.DDecoder d = decoder.d(); assertThat(d.count(), equalTo(1)); assertThat(d.next().e(), equalTo(43)); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::d); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::d); + assertThat(exception.getMessage(), containsString("Cannot access field \"d\" in state: V0_D_1_BLOCK")); } @Test @@ -2396,14 +2500,18 @@ void allowsEncodingAndDecodingAsciiInsideGroupInSchemaDefinedOrder2() void disallowsEncodingAsciiInsideGroupBeforeCallingNext1() { final AsciiInsideGroupEncoder.BEncoder bEncoder = encodeUntilGroupWithAsciiInside(); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.c("EURUSD")); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.c("EURUSD")); + assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_B_N")); } @Test void disallowsEncodingAsciiInsideGroupBeforeCallingNext2() { final AsciiInsideGroupEncoder.BEncoder bEncoder = encodeUntilGroupWithAsciiInside(); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.c(0, (byte)'E')); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.c(0, (byte)'E')); + assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_B_N")); } @Test @@ -2411,7 +2519,9 @@ void disallowsEncodingAsciiInsideGroupBeforeCallingNext3() { final AsciiInsideGroupEncoder.BEncoder bEncoder = encodeUntilGroupWithAsciiInside(); final byte[] eurUsdBytes = "EURUSD".getBytes(StandardCharsets.US_ASCII); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.putC(eurUsdBytes, 0)); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.putC(eurUsdBytes, 0)); + assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_B_N")); } private AsciiInsideGroupEncoder.BEncoder encodeUntilGroupWithAsciiInside() @@ -2426,28 +2536,36 @@ private AsciiInsideGroupEncoder.BEncoder encodeUntilGroupWithAsciiInside() void disallowsDecodingAsciiInsideGroupBeforeCallingNext1() { final AsciiInsideGroupDecoder.BDecoder b = decodeUntilGroupWithAsciiInside(); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> b.c(0)); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> b.c(0)); + assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_B_N")); } @Test void disallowsDecodingAsciiInsideGroupBeforeCallingNext2() { final AsciiInsideGroupDecoder.BDecoder b = decodeUntilGroupWithAsciiInside(); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> b.getC(new byte[6], 0)); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> b.getC(new byte[6], 0)); + assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_B_N")); } @Test void disallowsDecodingAsciiInsideGroupBeforeCallingNext3() { final AsciiInsideGroupDecoder.BDecoder b = decodeUntilGroupWithAsciiInside(); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, b::c); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, b::c); + assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_B_N")); } @Test void disallowsDecodingAsciiInsideGroupBeforeCallingNext4() { final AsciiInsideGroupDecoder.BDecoder b = decodeUntilGroupWithAsciiInside(); - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> b.getC(new StringBuilder())); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> b.getC(new StringBuilder())); + assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_B_N")); } private AsciiInsideGroupDecoder.BDecoder decodeUntilGroupWithAsciiInside() From f879284737862aaa7d5d0d268a5720b8e2b66b45 Mon Sep 17 00:00:00 2001 From: Zach Bray Date: Thu, 15 Jun 2023 14:58:43 +0100 Subject: [PATCH 11/50] [Java] Fix field access order in tests and re-enable. --- .../generation/java/JavaGeneratorTest.java | 46 +++++++++++-------- .../sbe/generation/java/ReflectionUtil.java | 34 +++++++++++++- 2 files changed, 58 insertions(+), 22 deletions(-) diff --git a/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/JavaGeneratorTest.java b/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/JavaGeneratorTest.java index 93f6af13e4..e6ddf52142 100644 --- a/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/JavaGeneratorTest.java +++ b/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/JavaGeneratorTest.java @@ -21,8 +21,9 @@ import org.agrona.generation.CompilerUtil; import org.agrona.generation.StringWriterOutputManager; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import uk.co.real_logic.sbe.Tests; import uk.co.real_logic.sbe.ir.Ir; import uk.co.real_logic.sbe.xml.IrGenerator; @@ -36,10 +37,11 @@ import java.util.Map; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.startsWith; import static org.hamcrest.Matchers.*; import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import static uk.co.real_logic.sbe.generation.java.JavaGenerator.MESSAGE_HEADER_DECODER_TYPE; import static uk.co.real_logic.sbe.generation.java.ReflectionUtil.*; import static uk.co.real_logic.sbe.xml.XmlSchemaParser.parse; @@ -289,7 +291,6 @@ void shouldGenerateReadOnlyMessage() throws Exception } @Test - @Disabled("TODO: invalid field encode order") void shouldGenerateVarDataCodecs() throws Exception { final String expectedManufacturer = "Ford"; @@ -300,8 +301,12 @@ void shouldGenerateVarDataCodecs() throws Exception final Object encoder = wrap(buffer, compileCarEncoder().getConstructor().newInstance()); final Object decoder = getCarDecoder(buffer, encoder); + setEmptyFuelFiguresGroup(encoder); + setEmptyPerformanceFiguresGroup(encoder); setManufacturer(encoder, expectedManufacturer); + skipFuelFiguresGroup(decoder); + skipPerformanceFiguresGroup(decoder); final String manufacturer = getManufacturer(decoder); assertEquals(expectedManufacturer, manufacturer); @@ -404,29 +409,30 @@ void shouldGenerateGetFixedLengthStringUsingAppendable() throws Exception assertThat(result.toString(), is("R11R12")); } - @Test - @Disabled("TODO: invalid field encode order") - void shouldGenerateGetVariableStringUsingAppendable() throws Exception + @ParameterizedTest + @ValueSource(strings = {"Red", "", "Red and Blue"}) + void shouldGenerateGetVariableStringUsingAppendable(final String color) throws Exception { final UnsafeBuffer buffer = new UnsafeBuffer(new byte[4096]); final StringBuilder result = new StringBuilder(); generator().generate(); final Object encoder = wrap(buffer, compileCarEncoder().getDeclaredConstructor().newInstance()); - final Object decoder = getCarDecoder(buffer, encoder); - set(encoder, "color", String.class, "Red"); - get(decoder, "color", result); - assertThat(result.toString(), is("Red")); + setEmptyFuelFiguresGroup(encoder); + setEmptyPerformanceFiguresGroup(encoder); + set(encoder, "manufacturer", String.class, "Bristol"); + set(encoder, "model", String.class, "Britannia"); + set(encoder, "activationCode", String.class, "12345"); + set(encoder, "color", String.class, color); - result.setLength(0); - set(encoder, "color", String.class, ""); - get(decoder, "color", result); - assertThat(result.toString(), is("")); - - result.setLength(0); - set(encoder, "color", String.class, "Red and Blue"); - get(decoder, "color", result); - assertThat(result.toString(), is("Red and Blue")); + final Object decoder = getCarDecoder(buffer, encoder); + skipFuelFiguresGroup(decoder); + skipPerformanceFiguresGroup(decoder); + assertThat(get(decoder, "manufacturer"), equalTo("Bristol")); + assertThat(get(decoder, "model"), equalTo("Britannia")); + assertThat(get(decoder, "activationCode"), equalTo("12345")); + assertThat(get(decoder, "color", result), equalTo(color.length())); + assertThat(result.toString(), equalTo(color)); } @Test diff --git a/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/ReflectionUtil.java b/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/ReflectionUtil.java index a48074eb89..beead1799e 100644 --- a/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/ReflectionUtil.java +++ b/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/ReflectionUtil.java @@ -41,10 +41,30 @@ static Object get(final Object object, final String fieldName) throws Exception return object.getClass().getMethod(fieldName).invoke(object); } - static void get(final Object object, final String fieldName, final Appendable arg) throws Exception + static Object get(final Object object, final String fieldName, final Appendable arg) throws Exception { final String methodName = "get" + Generators.toUpperFirstChar(fieldName); - object.getClass().getMethod(methodName, Appendable.class).invoke(object, arg); + return object.getClass().getMethod(methodName, Appendable.class).invoke(object, arg); + } + + static void skipFuelFiguresGroup(final Object decoder) throws Exception + { + skipGroup(decoder, "fuelFigures"); + } + + static void skipPerformanceFiguresGroup(final Object decoder) throws Exception + { + skipGroup(decoder, "performanceFigures"); + } + + private static void skipGroup(final Object decoder, final String groupName) throws Exception + { + final Object group = get(decoder, groupName); + while ((boolean)get(group, "hasNext")) + { + get(group, "next"); + get(group, "sbeSkip"); + } } static String getManufacturer(final Object decoder) throws Exception @@ -52,6 +72,16 @@ static String getManufacturer(final Object decoder) throws Exception return (String)get(decoder, "manufacturer"); } + static void setEmptyFuelFiguresGroup(final Object encoder) throws Exception + { + encoder.getClass().getMethod("fuelFiguresCount", int.class).invoke(encoder, 0); + } + + static void setEmptyPerformanceFiguresGroup(final Object encoder) throws Exception + { + encoder.getClass().getMethod("performanceFiguresCount", int.class).invoke(encoder, 0); + } + static void setManufacturer(final Object encoder, final String value) throws Exception { encoder.getClass().getMethod("manufacturer", String.class).invoke(encoder, value); From b5f4ba1c709990c4155b19c476fc530495a4d04b Mon Sep 17 00:00:00 2001 From: Zach Bray Date: Thu, 15 Jun 2023 16:44:31 +0100 Subject: [PATCH 12/50] [Java] Permit block field access whenever it is safe (and, unfortunately, in some cases where it is not). This commit makes the field order checks more-lenient. The aim is to allow the application code to access fields in the root/top-level block or blocks inside groups whenever it is safe. However, the approach is broken and does not work in general. To (try to) achieve this aim, we rely on the fact that once a block is positioned correctly, it is safe to access during the remainder of encoding/decoding. For example, the following code is safe: ``` encoder.wrap(...); encoder.blockField1(42); encoder.varData("abc"); encoder.blockField2(43); ``` As soon as `wrap(...)` is called, the top-level block will be positioned over a valid region for the remainder of encoding. Groups are slightly trickier. The following code is _safe_: ``` encoder.wrap(...); Foo.BarEncoder bar = encoder.groupBarCount(2); bar.next() .x(1).y(2) .next(); .x(3).y(4); ``` But the following code is _unsafe_: ``` encoder.wrap(...); Foo.BarEncoder bar = encoder.groupBarCount(2); bar.x(1).y(2) .next(); .x(3).y(4); ``` And the following code is _unsafe_ too: ``` encoder.wrap(...) Foo.BarEncoder bar = encoder.groupBarCount(0); encoder.baz("abc"); bar.x(1).y(2); ``` The `offset` for the group encoder is only set when `next()` is called. Therefore, calling `x(1)` and `y(2)` will likely overwrite some data elsewhere in the buffer (probably near offset 0). We can still rely on the fact that once `next()` has been called - even if it is called multiple times, or too many times (causing it to throw) - the group's block fields will be safe to encode/decode. Thinking about this in terms of our state machine that models codec state and field accesses, we can put this another way. There is at least one transition that represents the `next()` method call. The states (transitively) reachable from these transitions can safely access the associated group block. As already mentioned, despite the passing tests, the approach in this commit doesn't always work. I had thought it would be possible to assign a number to each state as we find them using DFS and use this number as a shortcut to determining whether a block access was safe. For example, in each block field we would check: ``` if (currentState.number() >= firstStateAfterNextIsCalled.number()) { // ALLOW } else { // DENY } ``` However, (even ignoring the cycles due to repeating groups and the use of DFS) this solution makes the following assumptions: 1. There is a single state: `firstStateAfterNextIsCalled`, such that all subsequent transitively reachable states are safe. 2. It is possible to number the states in such a way that `numberOfState(b) >= numberOfState(a)` implies `b` is (transitively) reachable from `a`. Let's consider the following psuedo-schema w.r.t. our assumptions above: ``` group a { x: int32 } group b { y: int32 } ``` Can we set `x` if we've called `BEncoder#next`? Sometimes. The following code is invalid. ``` AEncoder a = encoder.aCount(0); BEncoder b = encoder.bCount(1).next(); b.y(2); a.x(1); // Whoops! We overwrote some data somewhere. ``` But this code is valid. ``` AEncoder a = encoder.aCount(1).next(); BEncoder b = encoder.bCount(1).next(); b.y(2); a.x(1); ``` Therefore, multiple transitions must exist for the `BEncoder#next` call with different exit states, which breaks Assumption 1. Assumption 2 is also problematic. As alluded to above, we must have a bifurcation of states depending on whether a group has elements or is empty. With arbitrary bifurcations, it is not possible to number the states in such a way that Assumption 2 holds. Consider the following state diagram: ``` state N / \ empty non-empty / \ state X state Y ``` Depending on whether a group is empty, e.g., `aCount(0)`, or not, e.g., `aCount(1)`, we transition to either `state X` or `state Y` from `state N`. For Assumption 2 to hold, we need `numberOf(state X) >= numberOf(state N)` (reachable) and `numberOf(state Y) >= numberOf(state N)` (reachable) and `numberOf(state X) < numberOf(state Y)` (unreachable) and `numberOf(state Y) < numberOf(state X)` (unreachable). Clearly, this is impossible. Prior to this commit, we avoided such problems by enforcing a more-strict ordering of field accesses. With this more-strict ordering, setting fields in a group block in later states was not allowed. Therefore, the state machine didn't have to have to split into mutually exclusive subgraphs of reachable states depending on whether or not the group is empty. For example, let's consider a psuedo-schema with two groups. ``` group a { x: int32 } group b { y: int32 } ``` Once the application code calls `encoder.bCount(2).next()`, our state machine will enter the `V0_B_N_BLOCK` state regardless of whether previously the application code called `encoder.aCount(0)` or `encoder.aCount(1).next()`. In `V_B_N_BLOCK` the application code is not allowed to call `AEncoder#x`; therefore, it is safe, i.e., it has no false positives. However, it is also too-strict, i.e., it has false negatives, e.g., the `encoder.aCount(1).next()` case. How likely are these false negatives to come up in the "wild"? If we think false negatives will often come up, is it possible to remove them whilst still using a state machine? We could consider generating more states to represent the reachable states under each fork of group emptiness. The number of states would grow exponentially w.r.t. the number of top-level groups, but let's ignore that for the moment and just consider whether or not it is possible. Continuing the example above with two groups, when the application code calls `encoder.bCount(2).next()`, the state it enters would depend on whether it had previously called `encoder.aCount(0)` or `encoder.aCount(n)...next()` for some value `n > 0`. To safely model this example, we'd need at least the following states: 1. has no `a` elements 2. has no `a` elements, and has no `b` elements 3. has `a` elements, but has not called `AEncoder#next` 4. has `a` elements, and has called `AEncoder#next` 5. has `a` elements, has called `AEncoder#next`, has `b` elements, but has not called `BEncoder#next` 6. has `a` elements, has called `AEncoder#next`, has `b` elements, and has called `BEncoder#next` 7. has no `a` elements, has `b` elements, but has not called `BEncoder#next` 8. has no `a` elements, has `b` elements, and has called `BEncoder#next` A trickier case is with nested groups: ``` group foo { x: int32 group bar { y: int32 } r: string s: string } ``` When is `Bar#y` safe to call? I believe it is safe to call in states where `FooEncoder#next` and `BarEncoder#next` have been called in-order more recently than `BarEncoder#count(0)`. Note, as `bar` is a group within `foo`, the `BarEncoder#next` and `BarEncoder#count` methods may be called repeatedly during the encoding of a single message. Implementing a more-permissive safety check, using this model, would entail checking that the current state of the encoder/decoder is in a state where this condition holds. To safely model this example, we'd need (at least) the following states: 1. has no `foo` elements 2. has `foo` elements, but has not called `FooEncoder#next` 3. has `foo` elements, and has called `FooEncoder#next` 4. has `foo` elements, has called `FooEncoder#next`, and has no `bar` elements 5. has `foo` elements, has called `FooEncoder#next`, has no `bar` elements, and has encoded `r` 6. has `foo` elements, has called `FooEncoder#next`, has no `bar` elements, and has encoded `s` 7. has `foo` elements, has called `FooEncoder#next`, has `bar` elements, but has not called `BarEncoder#next` 8. has `foo` elements, has called `FooEncoder#next`, has `bar` elements, has not called `BarEncoder#next`, and has encoded `r` 9. has `foo` elements, has called `FooEncoder#next`, has `bar` elements, has not called `BarEncoder#next`, and has encoded `s` 10. has `foo` elements, has called `FooEncoder#next`, has `bar` elements, and has called `BarEncoder#next` 11. has `foo` elements, has called `FooEncoder#next`, has `bar` elements, and has called `BarEncoder#next`, and has encoded `r` 12. has `foo` elements, has called `FooEncoder#next`, has `bar` elements, and has called `BarEncoder#next`, and has encoded `s` ``` FooEncoder foo = encoder.fooCount(3); // State 2 foo.next(); // State 3 foo.barCount(0); // State 4 foo.r("abc"); // State 5 foo.s("def"); // State 6 foo.next(); // State 4 BarEncoder bar = foo.barCount(1); // State 7 foo.r("abc"); // State 8 bar.next(); // State 11 bar.y(42); // State 11 foo.s("def"); // State 12 foo.next(); // State X? bar.y(43); // Weird but allowed. foo.barCount(0); // State Y? State X cannot be State 10, as State 10 should not allow _re-encoding_ of the bar group count ``` OK. We didn't have enough states. In the case where we "wrap back around", we need to model whether `bar` block access is allowed (State 13 vs State 3): 13. has `foo` elements, has called `FooEncoder#next`, has called `BarEncoder#next` for last iteration, but hasn't set `bar` group size in this iteration. An even trickier case is with doubly-nested groups: ``` group foo { x: int32 group bar { y: int32 group baz { z: int32 } } } ``` When is `Baz#z` safe to call? I believe it is safe to call in states where `FooEncoder#next`, `BarEncoder#next` and `BazEncoder#next` have been called in-order more recently than `BarEncoder#count(0)` or `BazEncoder#count(0). Encoding this in states is likely to result in more cases like State 13 above. Generalising: a group block field may be accessed only when all outer groups' and its own `#next` methods have been called in-order more recently than any outer groups' or its own `#count` method with a `0` argument. Can model "more-recently" exactly, i.e., with no false positives and no false negatives, using a state machine? Another way of looking at the state machines we generate, is as parsers for a language. Where our language, based on an SBE schema, comprises valid "strings" of calls to an encoder/decoder. Is this language a regular language? If not, representing it using a FSM will not work. Cue Pumping Lemma? Next steps: - Add more tests to show where this approach breaks - Revert the changes and see if it is acceptable that some valid uses of codecs will be prevented when enabling field order checking. I.e., it will give false negatives when testing the hypothesis "this encoding/decoding is valid". - Personally, I think false negatives are better than false positives. (False positives are possible as of this commit.) Having said that, it will discourage use of these checks in existing systems where valid code already exists that does not pass the checks. --- .../sbe/generation/java/FieldOrderModel.java | 49 ++++++-- .../sbe/generation/java/JavaGenerator.java | 56 +++++++-- .../sbe/ir/generated/FrameCodecDecoder.java | 37 ++++-- .../sbe/ir/generated/FrameCodecEncoder.java | 37 ++++-- .../sbe/ir/generated/TokenCodecDecoder.java | 108 +++++++++++++----- .../sbe/ir/generated/TokenCodecEncoder.java | 88 ++++++++++---- .../real_logic/sbe/FieldOrderCheckTest.java | 48 +++++--- .../resources/field-order-check-schema.xml | 10 ++ 8 files changed, 331 insertions(+), 102 deletions(-) diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java index b28c4d5958..740e643548 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java @@ -35,7 +35,7 @@ final class FieldOrderModel { private final Int2ObjectHashMap states = new Int2ObjectHashMap<>(); - private final Map transitions = new LinkedHashMap<>(); + private final Map transitions = new LinkedHashMap<>(); private final Int2ObjectHashMap versionWrappedStates = new Int2ObjectHashMap<>(); private final Set reservedNames = new HashSet<>(); private final State notWrappedState = allocateState("NOT_WRAPPED"); @@ -71,13 +71,24 @@ public void forEachState(final Consumer consumer) public List getTransitions(final TransitionContext context, final Token token) { - final TransitionGroup transitionGroup = transitions.get(token); - if (transitionGroup == null) + final FieldTransitions fieldTransitions = transitions.get(token); + if (fieldTransitions == null) { return Collections.emptyList(); } - return transitionGroup.transitions.get(context); + return fieldTransitions.transitions.get(context); + } + + public State minimumEntryState(final Token token) + { + final FieldTransitions fieldTransitions = transitions.get(token); + if (fieldTransitions == null) + { + return null; + } + + return fieldTransitions.minimumEntryState(); } public static FieldOrderModel findTransitions( @@ -128,8 +139,8 @@ public void generateGraph( final String indent) { sb.append(indent).append("digraph G {\n"); - transitions.values().forEach(transitionGroup -> - transitionGroup.transitions.forEach((context, transitions1) -> + transitions.values().forEach(fieldTransitions -> + fieldTransitions.transitions.forEach((context, transitions1) -> { transitions1.forEach(transition -> { @@ -382,9 +393,9 @@ private void allocateTransition( final List from, final State to) { - final TransitionGroup transitionGroup = transitions.computeIfAbsent(token, ignored -> new TransitionGroup()); + final FieldTransitions fieldTransitions = transitions.computeIfAbsent(token, ignored -> new FieldTransitions()); final Transition transition = new Transition(description, from, to); - transitionGroup.add(firingContext, transition); + fieldTransitions.add(firingContext, transition); } static final class State @@ -461,7 +472,7 @@ enum TransitionContext LAST_ELEMENT_IN_GROUP } - private static final class TransitionGroup + private static final class FieldTransitions { private final Map> transitions = new LinkedHashMap<>(); @@ -490,5 +501,25 @@ public void add(final Object context, final Transition transition) transitionsForContext.add(transition); } + + public State minimumEntryState() + { + final MutableReference minimumState = new MutableReference<>(); + transitions.values().forEach(transitionsForContext -> + { + transitionsForContext.forEach(transition -> + { + transition.from.forEach(state -> + { + if (null == minimumState.get() || state.number < minimumState.get().number) + { + minimumState.set(state); + } + }); + }); + }); + + return minimumState.get(); + } } } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java index 0a17fe82b4..c0b6c35bfc 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java @@ -23,6 +23,7 @@ import org.agrona.MutableDirectBuffer; import org.agrona.Strings; import org.agrona.Verify; +import org.agrona.collections.MutableBoolean; import org.agrona.generation.DynamicPackageOutputManager; import org.agrona.sbe.*; @@ -295,10 +296,32 @@ private static CharSequence generateFieldOrderStates(final FieldOrderModel field sb.append(" * }\n"); sb.append(" */\n"); sb.append(" private enum CodecState\n") - .append(" {\n"); + .append(" {"); + final MutableBoolean isFirstState = new MutableBoolean(true); fieldOrderModel.forEachState(state -> - sb.append(" ").append(unqualifiedStateCase(state)).append(",\n")); - + { + if (isFirstState.get()) + { + isFirstState.set(false); + } + else + { + sb.append(","); + } + sb.append("\n ").append(unqualifiedStateCase(state)) + .append("(").append(state.number()).append(")"); + }); + sb.append(";\n\n"); + + sb.append(" private final int stateNumber;\n\n"); + sb.append(" CodecState(final int stateNumber)\n"); + sb.append(" {\n"); + sb.append(" this.stateNumber = stateNumber;\n"); + sb.append(" }\n\n"); + sb.append(" int stateNumber()\n"); + sb.append(" {\n"); + sb.append(" return stateNumber;\n"); + sb.append(" }\n"); sb.append(" }\n\n"); sb.append(" private CodecState codecState = ") @@ -372,7 +395,7 @@ private static CharSequence generateFieldOrderStateTransitions( generateFieldOrderStateTransitions( sb, - indent + " ", + indent + " ", fieldOrderModel, FieldOrderModel.TransitionContext.SELECT_MULTI_ELEMENT_GROUP, token); @@ -404,11 +427,26 @@ private static void generateFieldOrderStateTransitions( .append(indent).append(" break;\n"); }); - sb.append(indent).append(" default:\n") - .append(indent).append(" throw new IllegalStateException(") - .append("\"Cannot access field \\\"").append(token.name()) - .append("\\\" in state: \" + codecState());\n") - .append(indent).append("}\n"); + sb.append(indent).append(" default:\n"); + final FieldOrderModel.State minimumEntryState = fieldOrderModel.minimumEntryState(token); + if (token.signal() == Signal.BEGIN_FIELD && minimumEntryState != null) + { + sb.append(indent).append(" if (codecState().stateNumber() < ") + .append(qualifiedStateCase(minimumEntryState)).append(".stateNumber())\n"); + sb.append(indent).append(" {\n"); + sb.append(indent).append(" throw new IllegalStateException(") + .append("\"Cannot access field \\\"").append(token.name()) + .append("\\\" in state: \" + codecState());\n"); + sb.append(indent).append(" }\n"); + } + else + { + sb.append(indent).append(" throw new IllegalStateException(") + .append("\"Cannot access field \\\"").append(token.name()) + .append("\\\" in state: \" + codecState());\n"); + } + + sb.append(indent).append("}\n"); } private static CharSequence generateFieldOrderStateTransitionsForNextGroupElement( diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java index 3972093b8e..10bf5cb39f 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java @@ -33,11 +33,23 @@ public final class FrameCodecDecoder */ private enum CodecState { - NOT_WRAPPED, - V0_BLOCK, - V0_PACKAGENAME_DONE, - V0_NAMESPACENAME_DONE, - V0_SEMANTICVERSION_DONE, + NOT_WRAPPED(0), + V0_BLOCK(1), + V0_PACKAGENAME_DONE(2), + V0_NAMESPACENAME_DONE(3), + V0_SEMANTICVERSION_DONE(4); + + private final int stateNumber; + + CodecState(final int stateNumber) + { + this.stateNumber = stateNumber; + } + + int stateNumber() + { + return stateNumber; + } } private CodecState codecState = CodecState.NOT_WRAPPED; @@ -254,7 +266,10 @@ public int irId() codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Cannot access field \"irId\" in state: " + codecState()); + if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) + { + throw new IllegalStateException("Cannot access field \"irId\" in state: " + codecState()); + } } } @@ -317,7 +332,10 @@ public int irVersion() codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Cannot access field \"irVersion\" in state: " + codecState()); + if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) + { + throw new IllegalStateException("Cannot access field \"irVersion\" in state: " + codecState()); + } } } @@ -380,7 +398,10 @@ public int schemaVersion() codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Cannot access field \"schemaVersion\" in state: " + codecState()); + if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) + { + throw new IllegalStateException("Cannot access field \"schemaVersion\" in state: " + codecState()); + } } } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java index 5de82c0b5c..51de5fdc66 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java @@ -33,11 +33,23 @@ public final class FrameCodecEncoder */ private enum CodecState { - NOT_WRAPPED, - V0_BLOCK, - V0_PACKAGENAME_DONE, - V0_NAMESPACENAME_DONE, - V0_SEMANTICVERSION_DONE, + NOT_WRAPPED(0), + V0_BLOCK(1), + V0_PACKAGENAME_DONE(2), + V0_NAMESPACENAME_DONE(3), + V0_SEMANTICVERSION_DONE(4); + + private final int stateNumber; + + CodecState(final int stateNumber) + { + this.stateNumber = stateNumber; + } + + int stateNumber() + { + return stateNumber; + } } private CodecState codecState = CodecState.NOT_WRAPPED; @@ -206,7 +218,10 @@ public FrameCodecEncoder irId(final int value) codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Cannot access field \"irId\" in state: " + codecState()); + if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) + { + throw new IllegalStateException("Cannot access field \"irId\" in state: " + codecState()); + } } } @@ -270,7 +285,10 @@ public FrameCodecEncoder irVersion(final int value) codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Cannot access field \"irVersion\" in state: " + codecState()); + if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) + { + throw new IllegalStateException("Cannot access field \"irVersion\" in state: " + codecState()); + } } } @@ -334,7 +352,10 @@ public FrameCodecEncoder schemaVersion(final int value) codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Cannot access field \"schemaVersion\" in state: " + codecState()); + if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) + { + throw new IllegalStateException("Cannot access field \"schemaVersion\" in state: " + codecState()); + } } } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java index 2256caa905..1159a845be 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java @@ -48,19 +48,31 @@ public final class TokenCodecDecoder */ private enum CodecState { - NOT_WRAPPED, - V0_BLOCK, - V0_NAME_DONE, - V0_CONSTVALUE_DONE, - V0_MINVALUE_DONE, - V0_MAXVALUE_DONE, - V0_NULLVALUE_DONE, - V0_CHARACTERENCODING_DONE, - V0_EPOCH_DONE, - V0_TIMEUNIT_DONE, - V0_SEMANTICTYPE_DONE, - V0_DESCRIPTION_DONE, - V0_REFERENCEDNAME_DONE, + NOT_WRAPPED(0), + V0_BLOCK(1), + V0_NAME_DONE(2), + V0_CONSTVALUE_DONE(3), + V0_MINVALUE_DONE(4), + V0_MAXVALUE_DONE(5), + V0_NULLVALUE_DONE(6), + V0_CHARACTERENCODING_DONE(7), + V0_EPOCH_DONE(8), + V0_TIMEUNIT_DONE(9), + V0_SEMANTICTYPE_DONE(10), + V0_DESCRIPTION_DONE(11), + V0_REFERENCEDNAME_DONE(12); + + private final int stateNumber; + + CodecState(final int stateNumber) + { + this.stateNumber = stateNumber; + } + + int stateNumber() + { + return stateNumber; + } } private CodecState codecState = CodecState.NOT_WRAPPED; @@ -277,7 +289,10 @@ public int tokenOffset() codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Cannot access field \"tokenOffset\" in state: " + codecState()); + if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) + { + throw new IllegalStateException("Cannot access field \"tokenOffset\" in state: " + codecState()); + } } } @@ -340,7 +355,10 @@ public int tokenSize() codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Cannot access field \"tokenSize\" in state: " + codecState()); + if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) + { + throw new IllegalStateException("Cannot access field \"tokenSize\" in state: " + codecState()); + } } } @@ -403,7 +421,10 @@ public int fieldId() codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Cannot access field \"fieldId\" in state: " + codecState()); + if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) + { + throw new IllegalStateException("Cannot access field \"fieldId\" in state: " + codecState()); + } } } @@ -466,7 +487,10 @@ public int tokenVersion() codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Cannot access field \"tokenVersion\" in state: " + codecState()); + if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) + { + throw new IllegalStateException("Cannot access field \"tokenVersion\" in state: " + codecState()); + } } } @@ -529,7 +553,10 @@ public int componentTokenCount() codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Cannot access field \"componentTokenCount\" in state: " + codecState()); + if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) + { + throw new IllegalStateException("Cannot access field \"componentTokenCount\" in state: " + codecState()); + } } } @@ -577,7 +604,10 @@ public short signalRaw() codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Cannot access field \"signal\" in state: " + codecState()); + if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) + { + throw new IllegalStateException("Cannot access field \"signal\" in state: " + codecState()); + } } } @@ -594,7 +624,10 @@ public SignalCodec signal() codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Cannot access field \"signal\" in state: " + codecState()); + if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) + { + throw new IllegalStateException("Cannot access field \"signal\" in state: " + codecState()); + } } } @@ -642,7 +675,10 @@ public short primitiveTypeRaw() codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Cannot access field \"primitiveType\" in state: " + codecState()); + if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) + { + throw new IllegalStateException("Cannot access field \"primitiveType\" in state: " + codecState()); + } } } @@ -659,7 +695,10 @@ public PrimitiveTypeCodec primitiveType() codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Cannot access field \"primitiveType\" in state: " + codecState()); + if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) + { + throw new IllegalStateException("Cannot access field \"primitiveType\" in state: " + codecState()); + } } } @@ -707,7 +746,10 @@ public short byteOrderRaw() codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Cannot access field \"byteOrder\" in state: " + codecState()); + if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) + { + throw new IllegalStateException("Cannot access field \"byteOrder\" in state: " + codecState()); + } } } @@ -724,7 +766,10 @@ public ByteOrderCodec byteOrder() codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Cannot access field \"byteOrder\" in state: " + codecState()); + if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) + { + throw new IllegalStateException("Cannot access field \"byteOrder\" in state: " + codecState()); + } } } @@ -772,7 +817,10 @@ public short presenceRaw() codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Cannot access field \"presence\" in state: " + codecState()); + if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) + { + throw new IllegalStateException("Cannot access field \"presence\" in state: " + codecState()); + } } } @@ -789,7 +837,10 @@ public PresenceCodec presence() codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Cannot access field \"presence\" in state: " + codecState()); + if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) + { + throw new IllegalStateException("Cannot access field \"presence\" in state: " + codecState()); + } } } @@ -852,7 +903,10 @@ public int deprecated() codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Cannot access field \"deprecated\" in state: " + codecState()); + if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) + { + throw new IllegalStateException("Cannot access field \"deprecated\" in state: " + codecState()); + } } } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java index 54dc2633e3..833af578f8 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java @@ -48,19 +48,31 @@ public final class TokenCodecEncoder */ private enum CodecState { - NOT_WRAPPED, - V0_BLOCK, - V0_NAME_DONE, - V0_CONSTVALUE_DONE, - V0_MINVALUE_DONE, - V0_MAXVALUE_DONE, - V0_NULLVALUE_DONE, - V0_CHARACTERENCODING_DONE, - V0_EPOCH_DONE, - V0_TIMEUNIT_DONE, - V0_SEMANTICTYPE_DONE, - V0_DESCRIPTION_DONE, - V0_REFERENCEDNAME_DONE, + NOT_WRAPPED(0), + V0_BLOCK(1), + V0_NAME_DONE(2), + V0_CONSTVALUE_DONE(3), + V0_MINVALUE_DONE(4), + V0_MAXVALUE_DONE(5), + V0_NULLVALUE_DONE(6), + V0_CHARACTERENCODING_DONE(7), + V0_EPOCH_DONE(8), + V0_TIMEUNIT_DONE(9), + V0_SEMANTICTYPE_DONE(10), + V0_DESCRIPTION_DONE(11), + V0_REFERENCEDNAME_DONE(12); + + private final int stateNumber; + + CodecState(final int stateNumber) + { + this.stateNumber = stateNumber; + } + + int stateNumber() + { + return stateNumber; + } } private CodecState codecState = CodecState.NOT_WRAPPED; @@ -229,7 +241,10 @@ public TokenCodecEncoder tokenOffset(final int value) codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Cannot access field \"tokenOffset\" in state: " + codecState()); + if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) + { + throw new IllegalStateException("Cannot access field \"tokenOffset\" in state: " + codecState()); + } } } @@ -293,7 +308,10 @@ public TokenCodecEncoder tokenSize(final int value) codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Cannot access field \"tokenSize\" in state: " + codecState()); + if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) + { + throw new IllegalStateException("Cannot access field \"tokenSize\" in state: " + codecState()); + } } } @@ -357,7 +375,10 @@ public TokenCodecEncoder fieldId(final int value) codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Cannot access field \"fieldId\" in state: " + codecState()); + if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) + { + throw new IllegalStateException("Cannot access field \"fieldId\" in state: " + codecState()); + } } } @@ -421,7 +442,10 @@ public TokenCodecEncoder tokenVersion(final int value) codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Cannot access field \"tokenVersion\" in state: " + codecState()); + if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) + { + throw new IllegalStateException("Cannot access field \"tokenVersion\" in state: " + codecState()); + } } } @@ -485,7 +509,10 @@ public TokenCodecEncoder componentTokenCount(final int value) codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Cannot access field \"componentTokenCount\" in state: " + codecState()); + if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) + { + throw new IllegalStateException("Cannot access field \"componentTokenCount\" in state: " + codecState()); + } } } @@ -534,7 +561,10 @@ public TokenCodecEncoder signal(final SignalCodec value) codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Cannot access field \"signal\" in state: " + codecState()); + if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) + { + throw new IllegalStateException("Cannot access field \"signal\" in state: " + codecState()); + } } } @@ -582,7 +612,10 @@ public TokenCodecEncoder primitiveType(final PrimitiveTypeCodec value) codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Cannot access field \"primitiveType\" in state: " + codecState()); + if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) + { + throw new IllegalStateException("Cannot access field \"primitiveType\" in state: " + codecState()); + } } } @@ -630,7 +663,10 @@ public TokenCodecEncoder byteOrder(final ByteOrderCodec value) codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Cannot access field \"byteOrder\" in state: " + codecState()); + if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) + { + throw new IllegalStateException("Cannot access field \"byteOrder\" in state: " + codecState()); + } } } @@ -678,7 +714,10 @@ public TokenCodecEncoder presence(final PresenceCodec value) codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Cannot access field \"presence\" in state: " + codecState()); + if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) + { + throw new IllegalStateException("Cannot access field \"presence\" in state: " + codecState()); + } } } @@ -741,7 +780,10 @@ public TokenCodecEncoder deprecated(final int value) codecState(CodecState.V0_BLOCK); break; default: - throw new IllegalStateException("Cannot access field \"deprecated\" in state: " + codecState()); + if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) + { + throw new IllegalStateException("Cannot access field \"deprecated\" in state: " + codecState()); + } } } diff --git a/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java b/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java index 4fc6400c13..59c5096dfb 100644 --- a/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java +++ b/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java @@ -21,7 +21,6 @@ import org.agrona.MutableDirectBuffer; import org.agrona.concurrent.UnsafeBuffer; import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import java.nio.charset.StandardCharsets; @@ -105,7 +104,6 @@ void allowsDecodingToSkipVariableLengthFields() } @Test - @Disabled("Our access checks are too strict to allow the behaviour in this test.") void allowsReEncodingTopLevelPrimitiveFields() { final MultipleVarLengthEncoder encoder = new MultipleVarLengthEncoder() @@ -428,7 +426,6 @@ void allowsEncodingAndDecodingEmptyGroupAndVariableLengthFieldsInSchemaDefinedOr } @Test - @Disabled("Our access checks are too strict to allow the behaviour in this test.") void allowsReEncodingPrimitiveFieldInGroupElementAfterTopLevelVariableLengthField() { final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder() @@ -454,8 +451,7 @@ void allowsReEncodingPrimitiveFieldInGroupElementAfterTopLevelVariableLengthFiel } @Test - @Disabled("Our access checks are too strict to allow the behaviour in this test.") - void allowsReWrappingGroupDecoderAfterAccessingLength() + void disallowsReWrappingGroupDecoder() { final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder() .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); @@ -472,10 +468,9 @@ void allowsReWrappingGroupDecoderAfterAccessingLength() .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); assertThat(decoder.a(), equalTo(42)); assertThat(decoder.b().count(), equalTo(2)); - final GroupAndVarLengthDecoder.BDecoder b = decoder.b(); - assertThat(b.next().c(), equalTo(1)); - assertThat(b.next().c(), equalTo(3)); - assertThat(decoder.d(), equalTo("abc")); + final Exception exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::b); + assertThat(exception.getMessage(), containsString("Cannot access field \"b\" in state: V0_B_N")); } @Test @@ -559,7 +554,6 @@ void disallowsReEncodingGroupLength() } @Test - @Disabled("Our access checks are too strict to allow the behaviour in this test.") void allowsReEncodingGroupElementBlockFieldAfterTopLevelVariableLengthField() { final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder() @@ -685,7 +679,6 @@ void allowsEncodingAndDecodingVariableLengthFieldInsideGroupInSchemaDefinedOrder } @Test - @Disabled("Our access checks are too strict to allow the behaviour in this test.") void allowsReEncodingGroupElementPrimitiveFieldAfterElementVariableLengthField() { final VarLengthInsideGroupEncoder encoder = new VarLengthInsideGroupEncoder() @@ -1016,7 +1009,6 @@ void disallowsDecodingCompositeInsideGroupBeforeCallingNext() } @Test - @Disabled("Our access checks are too strict to allow the behaviour in this test.") void allowsReEncodingTopLevelCompositeViaReWrap() { final CompositeInsideGroupEncoder encoder = new CompositeInsideGroupEncoder() @@ -1109,7 +1101,6 @@ void allowsReEncodingGroupElementCompositeViaEncoderReference() } @Test - @Disabled("Our access checks are too strict to allow the behaviour in this test.") void allowsReDecodingTopLevelCompositeViaReWrap() { final CompositeInsideGroupEncoder encoder = new CompositeInsideGroupEncoder() @@ -1925,7 +1916,6 @@ void disallowsDecodingEnumInsideGroupBeforeCallingNext() } @Test - @Disabled("Our access checks are too strict to allow the behaviour in this test.") void allowsReEncodingTopLevelEnum() { final EnumInsideGroupEncoder encoder = new EnumInsideGroupEncoder() @@ -2007,7 +1997,6 @@ void disallowsDecodingBitSetInsideGroupBeforeCallingNext() } @Test - @Disabled("Our access checks are too strict to allow the behaviour in this test.") void allowsReEncodingTopLevelBitSetViaReWrap() { final BitSetInsideGroupEncoder encoder = new BitSetInsideGroupEncoder() @@ -2166,7 +2155,6 @@ private ArrayInsideGroupDecoder.BDecoder decodeUntilGroupWithArrayInside() } @Test - @Disabled("Our access checks are too strict to allow the behaviour in this test.") void allowsReEncodingTopLevelArrayViaReWrap() { final ArrayInsideGroupEncoder encoder = new ArrayInsideGroupEncoder() @@ -2233,7 +2221,6 @@ void allowsEncodingAndDecodingGroupFieldsInSchemaDefinedOrder2() } @Test - @Disabled("Our access checks are too strict to allow the behaviour in this test.") void allowsReEncodingTopLevelPrimitiveFieldsAfterGroups() { final MultipleGroupsEncoder encoder = new MultipleGroupsEncoder() @@ -2586,7 +2573,6 @@ private AsciiInsideGroupDecoder.BDecoder decodeUntilGroupWithAsciiInside() } @Test - @Disabled("Our access checks are too strict to allow the behaviour in this test.") void allowsReEncodingTopLevelAsciiViaReWrap() { final AsciiInsideGroupEncoder encoder = new AsciiInsideGroupEncoder() @@ -2817,6 +2803,32 @@ void allowsOldDecoderToSkipAddedAsciiFieldBeforeGroup() assertThat(b.next().c(), equalTo(2)); } + @Test + void allowsEncodeAndDecodeOfMessagesWithNoABlock() + { + final NoBlockEncoder encoder = new NoBlockEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a("abc"); + + final NoBlockDecoder decoder = new NoBlockDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo("abc")); + } + + @Test + void allowsEncodeAndDecodeOfGroupsWithNoBlock() + { + final GroupWithNoBlockEncoder encoder = new GroupWithNoBlockEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.aCount(1).next().b("abc"); + + final GroupWithNoBlockDecoder decoder = new GroupWithNoBlockDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + final GroupWithNoBlockDecoder.ADecoder a = decoder.a(); + assertThat(a.count(), equalTo(1)); + assertThat(a.next().b(), equalTo("abc")); + } + private void modifyHeaderToLookLikeVersion0() { messageHeaderDecoder.wrap(buffer, OFFSET); diff --git a/sbe-tool/src/test/resources/field-order-check-schema.xml b/sbe-tool/src/test/resources/field-order-check-schema.xml index 96abf6e7cd..c435410f9a 100644 --- a/sbe-tool/src/test/resources/field-order-check-schema.xml +++ b/sbe-tool/src/test/resources/field-order-check-schema.xml @@ -268,4 +268,14 @@ + + + + + + + + + + From 3e77828587b4db38a111a3b4fcdc8cc5c7f52704 Mon Sep 17 00:00:00 2001 From: Zach Bray Date: Fri, 16 Jun 2023 10:52:21 +0100 Subject: [PATCH 13/50] [Java] Add (disabled) failing tests for false positives. (When testing the hypothesis "this decoding/encoding code is valid".) These tests show that there are problems with the approach in the last commit. --- .../real_logic/sbe/FieldOrderCheckTest.java | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java b/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java index 59c5096dfb..14e3cce60e 100644 --- a/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java +++ b/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java @@ -21,6 +21,7 @@ import org.agrona.MutableDirectBuffer; import org.agrona.concurrent.UnsafeBuffer; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import java.nio.charset.StandardCharsets; @@ -2829,6 +2830,100 @@ void allowsEncodeAndDecodeOfGroupsWithNoBlock() assertThat(a.next().b(), equalTo("abc")); } + @Test + @Disabled("False positive since making checks more-lenient") + void disallowsEncodingElementOfEmptyGroup1() + { + final MultipleGroupsEncoder encoder = new MultipleGroupsEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + final MultipleGroupsEncoder.BEncoder bEncoder = encoder.bCount(0); + encoder.dCount(1).next().e(43); + final IllegalStateException exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.c(44)); + assertThat(exception.getMessage(), containsString("TODO")); + } + + @Test + @Disabled("False positive since making checks more-lenient") + void disallowsEncodingElementOfEmptyGroup2() + { + final NestedGroupsEncoder encoder = new NestedGroupsEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + final NestedGroupsEncoder.BEncoder bEncoder = encoder.bCount(1); + bEncoder.next(); + bEncoder.c(43); + final NestedGroupsEncoder.BEncoder.DEncoder dEncoder = bEncoder.dCount(0); + bEncoder.fCount(0); + encoder.hCount(0); + final IllegalStateException exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> dEncoder.e(44)); + assertThat(exception.getMessage(), containsString("TODO")); + } + + @Test + @Disabled("False positive since making checks more-lenient") + void disallowsEncodingElementOfEmptyGroup3() + { + final NestedGroupsEncoder encoder = new NestedGroupsEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + final NestedGroupsEncoder.BEncoder bEncoder = encoder.bCount(1); + bEncoder.next(); + bEncoder.c(43); + final NestedGroupsEncoder.BEncoder.DEncoder dEncoder = bEncoder.dCount(0); + bEncoder.fCount(0); + encoder.hCount(0); + final IllegalStateException exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> dEncoder.e(44)); + assertThat(exception.getMessage(), containsString("TODO")); + } + + @Test + @Disabled("False positive since making checks more-lenient") + void disallowsEncodingElementOfEmptyGroup4() + { + final NestedGroupsEncoder encoder = new NestedGroupsEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + final NestedGroupsEncoder.BEncoder bEncoder = encoder.bCount(1); + bEncoder.next(); + bEncoder.c(43); + final NestedGroupsEncoder.BEncoder.DEncoder dEncoder = bEncoder.dCount(0); + bEncoder.fCount(0); + final IllegalStateException exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> dEncoder.e(44)); + assertThat(exception.getMessage(), containsString("TODO")); + } + + @Test + @Disabled("False positive since making checks more-lenient") + void disallowsEncodingElementOfEmptyGroup5() + { + final AddPrimitiveInsideGroupV1Encoder encoder = new AddPrimitiveInsideGroupV1Encoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + final AddPrimitiveInsideGroupV1Encoder.BEncoder bEncoder = encoder.bCount(0); + final IllegalStateException exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.c(43)); + assertThat(exception.getMessage(), containsString("TODO")); + } + + @Test + @Disabled("False positive since making checks more-lenient") + void disallowsEncodingElementOfEmptyGroup6() + { + final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + final GroupAndVarLengthEncoder.BEncoder bEncoder = encoder.bCount(0); + encoder.d("abc"); + final IllegalStateException exception = + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.c(43)); + assertThat(exception.getMessage(), containsString("TODO")); + } + private void modifyHeaderToLookLikeVersion0() { messageHeaderDecoder.wrap(buffer, OFFSET); From 46bd3d3dd6f1cba8c49353d90e1e64ae57c1c494 Mon Sep 17 00:00:00 2001 From: Zach Bray Date: Fri, 16 Jun 2023 15:23:19 +0100 Subject: [PATCH 14/50] Revert "[Java] Permit block field access whenever it is safe (and, unfortunately, in some cases where it is not)." This reverts commit d9eb33dc793d7f62e696e4f40a1b4cf36d50316f. --- .../sbe/generation/java/FieldOrderModel.java | 49 ++------ .../sbe/generation/java/JavaGenerator.java | 56 ++------- .../sbe/ir/generated/FrameCodecDecoder.java | 37 ++---- .../sbe/ir/generated/FrameCodecEncoder.java | 37 ++---- .../sbe/ir/generated/TokenCodecDecoder.java | 108 +++++------------- .../sbe/ir/generated/TokenCodecEncoder.java | 88 ++++---------- .../real_logic/sbe/FieldOrderCheckTest.java | 41 ++++--- 7 files changed, 107 insertions(+), 309 deletions(-) diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java index 740e643548..b28c4d5958 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java @@ -35,7 +35,7 @@ final class FieldOrderModel { private final Int2ObjectHashMap states = new Int2ObjectHashMap<>(); - private final Map transitions = new LinkedHashMap<>(); + private final Map transitions = new LinkedHashMap<>(); private final Int2ObjectHashMap versionWrappedStates = new Int2ObjectHashMap<>(); private final Set reservedNames = new HashSet<>(); private final State notWrappedState = allocateState("NOT_WRAPPED"); @@ -71,24 +71,13 @@ public void forEachState(final Consumer consumer) public List getTransitions(final TransitionContext context, final Token token) { - final FieldTransitions fieldTransitions = transitions.get(token); - if (fieldTransitions == null) + final TransitionGroup transitionGroup = transitions.get(token); + if (transitionGroup == null) { return Collections.emptyList(); } - return fieldTransitions.transitions.get(context); - } - - public State minimumEntryState(final Token token) - { - final FieldTransitions fieldTransitions = transitions.get(token); - if (fieldTransitions == null) - { - return null; - } - - return fieldTransitions.minimumEntryState(); + return transitionGroup.transitions.get(context); } public static FieldOrderModel findTransitions( @@ -139,8 +128,8 @@ public void generateGraph( final String indent) { sb.append(indent).append("digraph G {\n"); - transitions.values().forEach(fieldTransitions -> - fieldTransitions.transitions.forEach((context, transitions1) -> + transitions.values().forEach(transitionGroup -> + transitionGroup.transitions.forEach((context, transitions1) -> { transitions1.forEach(transition -> { @@ -393,9 +382,9 @@ private void allocateTransition( final List from, final State to) { - final FieldTransitions fieldTransitions = transitions.computeIfAbsent(token, ignored -> new FieldTransitions()); + final TransitionGroup transitionGroup = transitions.computeIfAbsent(token, ignored -> new TransitionGroup()); final Transition transition = new Transition(description, from, to); - fieldTransitions.add(firingContext, transition); + transitionGroup.add(firingContext, transition); } static final class State @@ -472,7 +461,7 @@ enum TransitionContext LAST_ELEMENT_IN_GROUP } - private static final class FieldTransitions + private static final class TransitionGroup { private final Map> transitions = new LinkedHashMap<>(); @@ -501,25 +490,5 @@ public void add(final Object context, final Transition transition) transitionsForContext.add(transition); } - - public State minimumEntryState() - { - final MutableReference minimumState = new MutableReference<>(); - transitions.values().forEach(transitionsForContext -> - { - transitionsForContext.forEach(transition -> - { - transition.from.forEach(state -> - { - if (null == minimumState.get() || state.number < minimumState.get().number) - { - minimumState.set(state); - } - }); - }); - }); - - return minimumState.get(); - } } } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java index c0b6c35bfc..0a17fe82b4 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java @@ -23,7 +23,6 @@ import org.agrona.MutableDirectBuffer; import org.agrona.Strings; import org.agrona.Verify; -import org.agrona.collections.MutableBoolean; import org.agrona.generation.DynamicPackageOutputManager; import org.agrona.sbe.*; @@ -296,32 +295,10 @@ private static CharSequence generateFieldOrderStates(final FieldOrderModel field sb.append(" * }\n"); sb.append(" */\n"); sb.append(" private enum CodecState\n") - .append(" {"); - final MutableBoolean isFirstState = new MutableBoolean(true); + .append(" {\n"); fieldOrderModel.forEachState(state -> - { - if (isFirstState.get()) - { - isFirstState.set(false); - } - else - { - sb.append(","); - } - sb.append("\n ").append(unqualifiedStateCase(state)) - .append("(").append(state.number()).append(")"); - }); - sb.append(";\n\n"); - - sb.append(" private final int stateNumber;\n\n"); - sb.append(" CodecState(final int stateNumber)\n"); - sb.append(" {\n"); - sb.append(" this.stateNumber = stateNumber;\n"); - sb.append(" }\n\n"); - sb.append(" int stateNumber()\n"); - sb.append(" {\n"); - sb.append(" return stateNumber;\n"); - sb.append(" }\n"); + sb.append(" ").append(unqualifiedStateCase(state)).append(",\n")); + sb.append(" }\n\n"); sb.append(" private CodecState codecState = ") @@ -395,7 +372,7 @@ private static CharSequence generateFieldOrderStateTransitions( generateFieldOrderStateTransitions( sb, - indent + " ", + indent + " ", fieldOrderModel, FieldOrderModel.TransitionContext.SELECT_MULTI_ELEMENT_GROUP, token); @@ -427,26 +404,11 @@ private static void generateFieldOrderStateTransitions( .append(indent).append(" break;\n"); }); - sb.append(indent).append(" default:\n"); - final FieldOrderModel.State minimumEntryState = fieldOrderModel.minimumEntryState(token); - if (token.signal() == Signal.BEGIN_FIELD && minimumEntryState != null) - { - sb.append(indent).append(" if (codecState().stateNumber() < ") - .append(qualifiedStateCase(minimumEntryState)).append(".stateNumber())\n"); - sb.append(indent).append(" {\n"); - sb.append(indent).append(" throw new IllegalStateException(") - .append("\"Cannot access field \\\"").append(token.name()) - .append("\\\" in state: \" + codecState());\n"); - sb.append(indent).append(" }\n"); - } - else - { - sb.append(indent).append(" throw new IllegalStateException(") - .append("\"Cannot access field \\\"").append(token.name()) - .append("\\\" in state: \" + codecState());\n"); - } - - sb.append(indent).append("}\n"); + sb.append(indent).append(" default:\n") + .append(indent).append(" throw new IllegalStateException(") + .append("\"Cannot access field \\\"").append(token.name()) + .append("\\\" in state: \" + codecState());\n") + .append(indent).append("}\n"); } private static CharSequence generateFieldOrderStateTransitionsForNextGroupElement( diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java index 10bf5cb39f..3972093b8e 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java @@ -33,23 +33,11 @@ public final class FrameCodecDecoder */ private enum CodecState { - NOT_WRAPPED(0), - V0_BLOCK(1), - V0_PACKAGENAME_DONE(2), - V0_NAMESPACENAME_DONE(3), - V0_SEMANTICVERSION_DONE(4); - - private final int stateNumber; - - CodecState(final int stateNumber) - { - this.stateNumber = stateNumber; - } - - int stateNumber() - { - return stateNumber; - } + NOT_WRAPPED, + V0_BLOCK, + V0_PACKAGENAME_DONE, + V0_NAMESPACENAME_DONE, + V0_SEMANTICVERSION_DONE, } private CodecState codecState = CodecState.NOT_WRAPPED; @@ -266,10 +254,7 @@ public int irId() codecState(CodecState.V0_BLOCK); break; default: - if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) - { - throw new IllegalStateException("Cannot access field \"irId\" in state: " + codecState()); - } + throw new IllegalStateException("Cannot access field \"irId\" in state: " + codecState()); } } @@ -332,10 +317,7 @@ public int irVersion() codecState(CodecState.V0_BLOCK); break; default: - if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) - { - throw new IllegalStateException("Cannot access field \"irVersion\" in state: " + codecState()); - } + throw new IllegalStateException("Cannot access field \"irVersion\" in state: " + codecState()); } } @@ -398,10 +380,7 @@ public int schemaVersion() codecState(CodecState.V0_BLOCK); break; default: - if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) - { - throw new IllegalStateException("Cannot access field \"schemaVersion\" in state: " + codecState()); - } + throw new IllegalStateException("Cannot access field \"schemaVersion\" in state: " + codecState()); } } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java index 51de5fdc66..5de82c0b5c 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java @@ -33,23 +33,11 @@ public final class FrameCodecEncoder */ private enum CodecState { - NOT_WRAPPED(0), - V0_BLOCK(1), - V0_PACKAGENAME_DONE(2), - V0_NAMESPACENAME_DONE(3), - V0_SEMANTICVERSION_DONE(4); - - private final int stateNumber; - - CodecState(final int stateNumber) - { - this.stateNumber = stateNumber; - } - - int stateNumber() - { - return stateNumber; - } + NOT_WRAPPED, + V0_BLOCK, + V0_PACKAGENAME_DONE, + V0_NAMESPACENAME_DONE, + V0_SEMANTICVERSION_DONE, } private CodecState codecState = CodecState.NOT_WRAPPED; @@ -218,10 +206,7 @@ public FrameCodecEncoder irId(final int value) codecState(CodecState.V0_BLOCK); break; default: - if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) - { - throw new IllegalStateException("Cannot access field \"irId\" in state: " + codecState()); - } + throw new IllegalStateException("Cannot access field \"irId\" in state: " + codecState()); } } @@ -285,10 +270,7 @@ public FrameCodecEncoder irVersion(final int value) codecState(CodecState.V0_BLOCK); break; default: - if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) - { - throw new IllegalStateException("Cannot access field \"irVersion\" in state: " + codecState()); - } + throw new IllegalStateException("Cannot access field \"irVersion\" in state: " + codecState()); } } @@ -352,10 +334,7 @@ public FrameCodecEncoder schemaVersion(final int value) codecState(CodecState.V0_BLOCK); break; default: - if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) - { - throw new IllegalStateException("Cannot access field \"schemaVersion\" in state: " + codecState()); - } + throw new IllegalStateException("Cannot access field \"schemaVersion\" in state: " + codecState()); } } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java index 1159a845be..2256caa905 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java @@ -48,31 +48,19 @@ public final class TokenCodecDecoder */ private enum CodecState { - NOT_WRAPPED(0), - V0_BLOCK(1), - V0_NAME_DONE(2), - V0_CONSTVALUE_DONE(3), - V0_MINVALUE_DONE(4), - V0_MAXVALUE_DONE(5), - V0_NULLVALUE_DONE(6), - V0_CHARACTERENCODING_DONE(7), - V0_EPOCH_DONE(8), - V0_TIMEUNIT_DONE(9), - V0_SEMANTICTYPE_DONE(10), - V0_DESCRIPTION_DONE(11), - V0_REFERENCEDNAME_DONE(12); - - private final int stateNumber; - - CodecState(final int stateNumber) - { - this.stateNumber = stateNumber; - } - - int stateNumber() - { - return stateNumber; - } + NOT_WRAPPED, + V0_BLOCK, + V0_NAME_DONE, + V0_CONSTVALUE_DONE, + V0_MINVALUE_DONE, + V0_MAXVALUE_DONE, + V0_NULLVALUE_DONE, + V0_CHARACTERENCODING_DONE, + V0_EPOCH_DONE, + V0_TIMEUNIT_DONE, + V0_SEMANTICTYPE_DONE, + V0_DESCRIPTION_DONE, + V0_REFERENCEDNAME_DONE, } private CodecState codecState = CodecState.NOT_WRAPPED; @@ -289,10 +277,7 @@ public int tokenOffset() codecState(CodecState.V0_BLOCK); break; default: - if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) - { - throw new IllegalStateException("Cannot access field \"tokenOffset\" in state: " + codecState()); - } + throw new IllegalStateException("Cannot access field \"tokenOffset\" in state: " + codecState()); } } @@ -355,10 +340,7 @@ public int tokenSize() codecState(CodecState.V0_BLOCK); break; default: - if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) - { - throw new IllegalStateException("Cannot access field \"tokenSize\" in state: " + codecState()); - } + throw new IllegalStateException("Cannot access field \"tokenSize\" in state: " + codecState()); } } @@ -421,10 +403,7 @@ public int fieldId() codecState(CodecState.V0_BLOCK); break; default: - if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) - { - throw new IllegalStateException("Cannot access field \"fieldId\" in state: " + codecState()); - } + throw new IllegalStateException("Cannot access field \"fieldId\" in state: " + codecState()); } } @@ -487,10 +466,7 @@ public int tokenVersion() codecState(CodecState.V0_BLOCK); break; default: - if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) - { - throw new IllegalStateException("Cannot access field \"tokenVersion\" in state: " + codecState()); - } + throw new IllegalStateException("Cannot access field \"tokenVersion\" in state: " + codecState()); } } @@ -553,10 +529,7 @@ public int componentTokenCount() codecState(CodecState.V0_BLOCK); break; default: - if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) - { - throw new IllegalStateException("Cannot access field \"componentTokenCount\" in state: " + codecState()); - } + throw new IllegalStateException("Cannot access field \"componentTokenCount\" in state: " + codecState()); } } @@ -604,10 +577,7 @@ public short signalRaw() codecState(CodecState.V0_BLOCK); break; default: - if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) - { - throw new IllegalStateException("Cannot access field \"signal\" in state: " + codecState()); - } + throw new IllegalStateException("Cannot access field \"signal\" in state: " + codecState()); } } @@ -624,10 +594,7 @@ public SignalCodec signal() codecState(CodecState.V0_BLOCK); break; default: - if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) - { - throw new IllegalStateException("Cannot access field \"signal\" in state: " + codecState()); - } + throw new IllegalStateException("Cannot access field \"signal\" in state: " + codecState()); } } @@ -675,10 +642,7 @@ public short primitiveTypeRaw() codecState(CodecState.V0_BLOCK); break; default: - if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) - { - throw new IllegalStateException("Cannot access field \"primitiveType\" in state: " + codecState()); - } + throw new IllegalStateException("Cannot access field \"primitiveType\" in state: " + codecState()); } } @@ -695,10 +659,7 @@ public PrimitiveTypeCodec primitiveType() codecState(CodecState.V0_BLOCK); break; default: - if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) - { - throw new IllegalStateException("Cannot access field \"primitiveType\" in state: " + codecState()); - } + throw new IllegalStateException("Cannot access field \"primitiveType\" in state: " + codecState()); } } @@ -746,10 +707,7 @@ public short byteOrderRaw() codecState(CodecState.V0_BLOCK); break; default: - if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) - { - throw new IllegalStateException("Cannot access field \"byteOrder\" in state: " + codecState()); - } + throw new IllegalStateException("Cannot access field \"byteOrder\" in state: " + codecState()); } } @@ -766,10 +724,7 @@ public ByteOrderCodec byteOrder() codecState(CodecState.V0_BLOCK); break; default: - if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) - { - throw new IllegalStateException("Cannot access field \"byteOrder\" in state: " + codecState()); - } + throw new IllegalStateException("Cannot access field \"byteOrder\" in state: " + codecState()); } } @@ -817,10 +772,7 @@ public short presenceRaw() codecState(CodecState.V0_BLOCK); break; default: - if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) - { - throw new IllegalStateException("Cannot access field \"presence\" in state: " + codecState()); - } + throw new IllegalStateException("Cannot access field \"presence\" in state: " + codecState()); } } @@ -837,10 +789,7 @@ public PresenceCodec presence() codecState(CodecState.V0_BLOCK); break; default: - if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) - { - throw new IllegalStateException("Cannot access field \"presence\" in state: " + codecState()); - } + throw new IllegalStateException("Cannot access field \"presence\" in state: " + codecState()); } } @@ -903,10 +852,7 @@ public int deprecated() codecState(CodecState.V0_BLOCK); break; default: - if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) - { - throw new IllegalStateException("Cannot access field \"deprecated\" in state: " + codecState()); - } + throw new IllegalStateException("Cannot access field \"deprecated\" in state: " + codecState()); } } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java index 833af578f8..54dc2633e3 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java @@ -48,31 +48,19 @@ public final class TokenCodecEncoder */ private enum CodecState { - NOT_WRAPPED(0), - V0_BLOCK(1), - V0_NAME_DONE(2), - V0_CONSTVALUE_DONE(3), - V0_MINVALUE_DONE(4), - V0_MAXVALUE_DONE(5), - V0_NULLVALUE_DONE(6), - V0_CHARACTERENCODING_DONE(7), - V0_EPOCH_DONE(8), - V0_TIMEUNIT_DONE(9), - V0_SEMANTICTYPE_DONE(10), - V0_DESCRIPTION_DONE(11), - V0_REFERENCEDNAME_DONE(12); - - private final int stateNumber; - - CodecState(final int stateNumber) - { - this.stateNumber = stateNumber; - } - - int stateNumber() - { - return stateNumber; - } + NOT_WRAPPED, + V0_BLOCK, + V0_NAME_DONE, + V0_CONSTVALUE_DONE, + V0_MINVALUE_DONE, + V0_MAXVALUE_DONE, + V0_NULLVALUE_DONE, + V0_CHARACTERENCODING_DONE, + V0_EPOCH_DONE, + V0_TIMEUNIT_DONE, + V0_SEMANTICTYPE_DONE, + V0_DESCRIPTION_DONE, + V0_REFERENCEDNAME_DONE, } private CodecState codecState = CodecState.NOT_WRAPPED; @@ -241,10 +229,7 @@ public TokenCodecEncoder tokenOffset(final int value) codecState(CodecState.V0_BLOCK); break; default: - if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) - { - throw new IllegalStateException("Cannot access field \"tokenOffset\" in state: " + codecState()); - } + throw new IllegalStateException("Cannot access field \"tokenOffset\" in state: " + codecState()); } } @@ -308,10 +293,7 @@ public TokenCodecEncoder tokenSize(final int value) codecState(CodecState.V0_BLOCK); break; default: - if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) - { - throw new IllegalStateException("Cannot access field \"tokenSize\" in state: " + codecState()); - } + throw new IllegalStateException("Cannot access field \"tokenSize\" in state: " + codecState()); } } @@ -375,10 +357,7 @@ public TokenCodecEncoder fieldId(final int value) codecState(CodecState.V0_BLOCK); break; default: - if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) - { - throw new IllegalStateException("Cannot access field \"fieldId\" in state: " + codecState()); - } + throw new IllegalStateException("Cannot access field \"fieldId\" in state: " + codecState()); } } @@ -442,10 +421,7 @@ public TokenCodecEncoder tokenVersion(final int value) codecState(CodecState.V0_BLOCK); break; default: - if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) - { - throw new IllegalStateException("Cannot access field \"tokenVersion\" in state: " + codecState()); - } + throw new IllegalStateException("Cannot access field \"tokenVersion\" in state: " + codecState()); } } @@ -509,10 +485,7 @@ public TokenCodecEncoder componentTokenCount(final int value) codecState(CodecState.V0_BLOCK); break; default: - if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) - { - throw new IllegalStateException("Cannot access field \"componentTokenCount\" in state: " + codecState()); - } + throw new IllegalStateException("Cannot access field \"componentTokenCount\" in state: " + codecState()); } } @@ -561,10 +534,7 @@ public TokenCodecEncoder signal(final SignalCodec value) codecState(CodecState.V0_BLOCK); break; default: - if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) - { - throw new IllegalStateException("Cannot access field \"signal\" in state: " + codecState()); - } + throw new IllegalStateException("Cannot access field \"signal\" in state: " + codecState()); } } @@ -612,10 +582,7 @@ public TokenCodecEncoder primitiveType(final PrimitiveTypeCodec value) codecState(CodecState.V0_BLOCK); break; default: - if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) - { - throw new IllegalStateException("Cannot access field \"primitiveType\" in state: " + codecState()); - } + throw new IllegalStateException("Cannot access field \"primitiveType\" in state: " + codecState()); } } @@ -663,10 +630,7 @@ public TokenCodecEncoder byteOrder(final ByteOrderCodec value) codecState(CodecState.V0_BLOCK); break; default: - if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) - { - throw new IllegalStateException("Cannot access field \"byteOrder\" in state: " + codecState()); - } + throw new IllegalStateException("Cannot access field \"byteOrder\" in state: " + codecState()); } } @@ -714,10 +678,7 @@ public TokenCodecEncoder presence(final PresenceCodec value) codecState(CodecState.V0_BLOCK); break; default: - if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) - { - throw new IllegalStateException("Cannot access field \"presence\" in state: " + codecState()); - } + throw new IllegalStateException("Cannot access field \"presence\" in state: " + codecState()); } } @@ -780,10 +741,7 @@ public TokenCodecEncoder deprecated(final int value) codecState(CodecState.V0_BLOCK); break; default: - if (codecState().stateNumber() < CodecState.V0_BLOCK.stateNumber()) - { - throw new IllegalStateException("Cannot access field \"deprecated\" in state: " + codecState()); - } + throw new IllegalStateException("Cannot access field \"deprecated\" in state: " + codecState()); } } diff --git a/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java b/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java index 14e3cce60e..ffc26d58a9 100644 --- a/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java +++ b/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java @@ -105,6 +105,7 @@ void allowsDecodingToSkipVariableLengthFields() } @Test + @Disabled("Our access checks are too strict to allow the behaviour in this test.") void allowsReEncodingTopLevelPrimitiveFields() { final MultipleVarLengthEncoder encoder = new MultipleVarLengthEncoder() @@ -427,6 +428,7 @@ void allowsEncodingAndDecodingEmptyGroupAndVariableLengthFieldsInSchemaDefinedOr } @Test + @Disabled("Our access checks are too strict to allow the behaviour in this test.") void allowsReEncodingPrimitiveFieldInGroupElementAfterTopLevelVariableLengthField() { final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder() @@ -452,7 +454,8 @@ void allowsReEncodingPrimitiveFieldInGroupElementAfterTopLevelVariableLengthFiel } @Test - void disallowsReWrappingGroupDecoder() + @Disabled("Our access checks are too strict to allow the behaviour in this test.") + void allowsReWrappingGroupDecoderAfterAccessingLength() { final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder() .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); @@ -469,9 +472,10 @@ void disallowsReWrappingGroupDecoder() .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); assertThat(decoder.a(), equalTo(42)); assertThat(decoder.b().count(), equalTo(2)); - final Exception exception = - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::b); - assertThat(exception.getMessage(), containsString("Cannot access field \"b\" in state: V0_B_N")); + final GroupAndVarLengthDecoder.BDecoder b = decoder.b(); + assertThat(b.next().c(), equalTo(1)); + assertThat(b.next().c(), equalTo(3)); + assertThat(decoder.d(), equalTo("abc")); } @Test @@ -555,6 +559,7 @@ void disallowsReEncodingGroupLength() } @Test + @Disabled("Our access checks are too strict to allow the behaviour in this test.") void allowsReEncodingGroupElementBlockFieldAfterTopLevelVariableLengthField() { final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder() @@ -680,6 +685,7 @@ void allowsEncodingAndDecodingVariableLengthFieldInsideGroupInSchemaDefinedOrder } @Test + @Disabled("Our access checks are too strict to allow the behaviour in this test.") void allowsReEncodingGroupElementPrimitiveFieldAfterElementVariableLengthField() { final VarLengthInsideGroupEncoder encoder = new VarLengthInsideGroupEncoder() @@ -1010,6 +1016,7 @@ void disallowsDecodingCompositeInsideGroupBeforeCallingNext() } @Test + @Disabled("Our access checks are too strict to allow the behaviour in this test.") void allowsReEncodingTopLevelCompositeViaReWrap() { final CompositeInsideGroupEncoder encoder = new CompositeInsideGroupEncoder() @@ -1102,6 +1109,7 @@ void allowsReEncodingGroupElementCompositeViaEncoderReference() } @Test + @Disabled("Our access checks are too strict to allow the behaviour in this test.") void allowsReDecodingTopLevelCompositeViaReWrap() { final CompositeInsideGroupEncoder encoder = new CompositeInsideGroupEncoder() @@ -1917,6 +1925,7 @@ void disallowsDecodingEnumInsideGroupBeforeCallingNext() } @Test + @Disabled("Our access checks are too strict to allow the behaviour in this test.") void allowsReEncodingTopLevelEnum() { final EnumInsideGroupEncoder encoder = new EnumInsideGroupEncoder() @@ -1998,6 +2007,7 @@ void disallowsDecodingBitSetInsideGroupBeforeCallingNext() } @Test + @Disabled("Our access checks are too strict to allow the behaviour in this test.") void allowsReEncodingTopLevelBitSetViaReWrap() { final BitSetInsideGroupEncoder encoder = new BitSetInsideGroupEncoder() @@ -2156,6 +2166,7 @@ private ArrayInsideGroupDecoder.BDecoder decodeUntilGroupWithArrayInside() } @Test + @Disabled("Our access checks are too strict to allow the behaviour in this test.") void allowsReEncodingTopLevelArrayViaReWrap() { final ArrayInsideGroupEncoder encoder = new ArrayInsideGroupEncoder() @@ -2222,6 +2233,7 @@ void allowsEncodingAndDecodingGroupFieldsInSchemaDefinedOrder2() } @Test + @Disabled("Our access checks are too strict to allow the behaviour in this test.") void allowsReEncodingTopLevelPrimitiveFieldsAfterGroups() { final MultipleGroupsEncoder encoder = new MultipleGroupsEncoder() @@ -2574,6 +2586,7 @@ private AsciiInsideGroupDecoder.BDecoder decodeUntilGroupWithAsciiInside() } @Test + @Disabled("Our access checks are too strict to allow the behaviour in this test.") void allowsReEncodingTopLevelAsciiViaReWrap() { final AsciiInsideGroupEncoder encoder = new AsciiInsideGroupEncoder() @@ -2831,7 +2844,6 @@ void allowsEncodeAndDecodeOfGroupsWithNoBlock() } @Test - @Disabled("False positive since making checks more-lenient") void disallowsEncodingElementOfEmptyGroup1() { final MultipleGroupsEncoder encoder = new MultipleGroupsEncoder() @@ -2841,11 +2853,10 @@ void disallowsEncodingElementOfEmptyGroup1() encoder.dCount(1).next().e(43); final IllegalStateException exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.c(44)); - assertThat(exception.getMessage(), containsString("TODO")); + assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_D_1_BLOCK")); } @Test - @Disabled("False positive since making checks more-lenient") void disallowsEncodingElementOfEmptyGroup2() { final NestedGroupsEncoder encoder = new NestedGroupsEncoder() @@ -2859,11 +2870,10 @@ void disallowsEncodingElementOfEmptyGroup2() encoder.hCount(0); final IllegalStateException exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> dEncoder.e(44)); - assertThat(exception.getMessage(), containsString("TODO")); + assertThat(exception.getMessage(), containsString("Cannot access field \"e\" in state: V0_H_0")); } @Test - @Disabled("False positive since making checks more-lenient") void disallowsEncodingElementOfEmptyGroup3() { final NestedGroupsEncoder encoder = new NestedGroupsEncoder() @@ -2877,11 +2887,10 @@ void disallowsEncodingElementOfEmptyGroup3() encoder.hCount(0); final IllegalStateException exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> dEncoder.e(44)); - assertThat(exception.getMessage(), containsString("TODO")); + assertThat(exception.getMessage(), containsString("Cannot access field \"e\" in state: V0_H_0")); } @Test - @Disabled("False positive since making checks more-lenient") void disallowsEncodingElementOfEmptyGroup4() { final NestedGroupsEncoder encoder = new NestedGroupsEncoder() @@ -2894,11 +2903,10 @@ void disallowsEncodingElementOfEmptyGroup4() bEncoder.fCount(0); final IllegalStateException exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> dEncoder.e(44)); - assertThat(exception.getMessage(), containsString("TODO")); + assertThat(exception.getMessage(), containsString("Cannot access field \"e\" in state: V0_B_1_F_0")); } @Test - @Disabled("False positive since making checks more-lenient") void disallowsEncodingElementOfEmptyGroup5() { final AddPrimitiveInsideGroupV1Encoder encoder = new AddPrimitiveInsideGroupV1Encoder() @@ -2907,11 +2915,10 @@ void disallowsEncodingElementOfEmptyGroup5() final AddPrimitiveInsideGroupV1Encoder.BEncoder bEncoder = encoder.bCount(0); final IllegalStateException exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.c(43)); - assertThat(exception.getMessage(), containsString("TODO")); + assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V1_B_0")); } @Test - @Disabled("False positive since making checks more-lenient") void disallowsEncodingElementOfEmptyGroup6() { final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder() @@ -2921,7 +2928,7 @@ void disallowsEncodingElementOfEmptyGroup6() encoder.d("abc"); final IllegalStateException exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.c(43)); - assertThat(exception.getMessage(), containsString("TODO")); + assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_D_DONE")); } private void modifyHeaderToLookLikeVersion0() @@ -2940,6 +2947,4 @@ private void modifyHeaderToLookLikeVersion1() messageHeaderEncoder.wrap(buffer, OFFSET); messageHeaderEncoder.templateId(v0TemplateId); } - - // TODO test error message } From 1d9df0dff767c0ed4a4360a378f42cb4faa92bf8 Mon Sep 17 00:00:00 2001 From: Zach Bray Date: Fri, 16 Jun 2023 15:40:59 +0100 Subject: [PATCH 15/50] [Java] Treat top-level block fields as a special case. To reduce the number of false negatives the field access order checker generates, in this commit, we treat the top-level block fields differently. We know that once the top-level encoder is wrapped, these fields are always safe to access. As a result, we were able to enable several tests that previously failed. --- .../sbe/generation/java/FieldOrderModel.java | 81 +++++++------ .../sbe/generation/java/JavaGenerator.java | 51 +++++--- .../sbe/ir/generated/FrameCodecDecoder.java | 24 +--- .../sbe/ir/generated/FrameCodecEncoder.java | 24 +--- .../sbe/ir/generated/TokenCodecDecoder.java | 112 +++++------------- .../sbe/ir/generated/TokenCodecEncoder.java | 80 ++++--------- .../real_logic/sbe/FieldOrderCheckTest.java | 8 -- 7 files changed, 140 insertions(+), 240 deletions(-) diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java index b28c4d5958..cff2531ae3 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java @@ -36,6 +36,7 @@ final class FieldOrderModel { private final Int2ObjectHashMap states = new Int2ObjectHashMap<>(); private final Map transitions = new LinkedHashMap<>(); + private final Set topLevelBlockFields = new HashSet<>(); private final Int2ObjectHashMap versionWrappedStates = new Int2ObjectHashMap<>(); private final Set reservedNames = new HashSet<>(); private final State notWrappedState = allocateState("NOT_WRAPPED"); @@ -69,6 +70,11 @@ public void forEachState(final Consumer consumer) .forEach(consumer); } + public boolean isTopLevelBlockField(final Token token) + { + return topLevelBlockFields.contains(token); + } + public List getTransitions(final TransitionContext context, final Token token) { final TransitionGroup transitionGroup = transitions.get(token); @@ -80,45 +86,14 @@ public List getTransitions(final TransitionContext context, final To return transitionGroup.transitions.get(context); } - public static FieldOrderModel findTransitions( + public static FieldOrderModel newInstance( final Token msgToken, final List fields, final List groups, final List varData) { final FieldOrderModel model = new FieldOrderModel(); - - final IntHashSet versions = new IntHashSet(); - versions.add(msgToken.version()); - findVersions(versions, fields, groups, varData); - - versions.stream().sorted().forEach(version -> - { - final State versionWrappedState = model.allocateState("V" + version + "_BLOCK"); - - model.versionWrappedStates.put(version, versionWrappedState); - - model.encoderWrappedState = versionWrappedState; - - model.allocateTransition( - version, - ".wrap(...)", - null, - Collections.singletonList(model.notWrappedState), - versionWrappedState - ); - - model.findTransitions( - Collections.singletonList(versionWrappedState), - versionWrappedState, - "V" + version + "_", - fields, - groups, - varData, - token -> token.version() <= version - ); - }); - + model.findTransitions(msgToken, fields, groups, varData); return model; } @@ -192,6 +167,46 @@ private static void findVersions( } } + private void findTransitions( + final Token msgToken, + final List fields, + final List groups, + final List varData) + { + final IntHashSet versions = new IntHashSet(); + versions.add(msgToken.version()); + findVersions(versions, fields, groups, varData); + + Generators.forEachField(fields, (fieldToken, ignored) -> topLevelBlockFields.add(fieldToken)); + + versions.stream().sorted().forEach(version -> + { + final State versionWrappedState = allocateState("V" + version + "_BLOCK"); + + versionWrappedStates.put(version, versionWrappedState); + + encoderWrappedState = versionWrappedState; + + allocateTransition( + version, + ".wrap(...)", + null, + Collections.singletonList(notWrappedState), + versionWrappedState + ); + + findTransitions( + Collections.singletonList(versionWrappedState), + versionWrappedState, + "V" + version + "_", + fields, + groups, + varData, + token -> token.version() <= version + ); + }); + } + @SuppressWarnings("checkstyle:MethodLength") private List findTransitions( final List entryStates, diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java index 0a17fe82b4..4e24622962 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java @@ -250,8 +250,7 @@ private void generateEncoder( generateAnnotations(BASE_INDENT, className, groups, out, this::encoderName); } out.append(generateDeclaration(className, implementsString, msgToken)); - final FieldOrderModel fieldOrderModel = FieldOrderModel.findTransitions( - msgToken, fields, groups, varData); + final FieldOrderModel fieldOrderModel = FieldOrderModel.newInstance(msgToken, fields, groups, varData); out.append(generateFieldOrderStates(fieldOrderModel)); out.append(generateEncoderFlyweightCode(className, fieldOrderModel, msgToken)); @@ -390,25 +389,39 @@ private static void generateFieldOrderStateTransitions( final FieldOrderModel.TransitionContext context, final Token token) { - sb.append(indent).append("switch (codecState())\n") - .append(indent).append("{\n"); + if (fieldOrderModel.isTopLevelBlockField(token)) + { + sb.append(indent).append("if (codecState() == ") + .append(qualifiedStateCase(fieldOrderModel.notWrappedState())) + .append(")\n") + .append(indent).append("{\n") + .append(indent).append(" throw new IllegalStateException(") + .append("\"Cannot access field \\\"").append(token.name()) + .append("\\\" in state: \" + codecState());\n") + .append(indent).append("}\n"); + } + else + { + sb.append(indent).append("switch (codecState())\n") + .append(indent).append("{\n"); - final List transitions = fieldOrderModel.getTransitions(context, token); + final List transitions = fieldOrderModel.getTransitions(context, token); - transitions.forEach(transition -> - { - transition.forEachStartState(startState -> - sb.append(indent).append(" case ").append(unqualifiedStateCase(startState)).append(":\n")); - sb.append(indent).append(" codecState(") - .append(qualifiedStateCase(transition.endState())).append(");\n") - .append(indent).append(" break;\n"); - }); + transitions.forEach(transition -> + { + transition.forEachStartState(startState -> + sb.append(indent).append(" case ").append(unqualifiedStateCase(startState)).append(":\n")); + sb.append(indent).append(" codecState(") + .append(qualifiedStateCase(transition.endState())).append(");\n") + .append(indent).append(" break;\n"); + }); - sb.append(indent).append(" default:\n") - .append(indent).append(" throw new IllegalStateException(") - .append("\"Cannot access field \\\"").append(token.name()) - .append("\\\" in state: \" + codecState());\n") - .append(indent).append("}\n"); + sb.append(indent).append(" default:\n") + .append(indent).append(" throw new IllegalStateException(") + .append("\"Cannot access field \\\"").append(token.name()) + .append("\\\" in state: \" + codecState());\n") + .append(indent).append("}\n"); + } } private static CharSequence generateFieldOrderStateTransitionsForNextGroupElement( @@ -499,7 +512,7 @@ private void generateDecoder( generateAnnotations(BASE_INDENT, className, groups, out, this::decoderName); } out.append(generateDeclaration(className, implementsString, msgToken)); - final FieldOrderModel fieldOrderModel = FieldOrderModel.findTransitions( + final FieldOrderModel fieldOrderModel = FieldOrderModel.newInstance( msgToken, fields, groups, varData); out.append(generateFieldOrderStates(fieldOrderModel)); out.append(generateDecoderFlyweightCode(fieldOrderModel, className, msgToken)); diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java index 3972093b8e..0492c8f902 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java @@ -248,13 +248,9 @@ public int irId() { if (DEBUG_MODE) { - switch (codecState()) + if (codecState() == CodecState.NOT_WRAPPED) { - case V0_BLOCK: - codecState(CodecState.V0_BLOCK); - break; - default: - throw new IllegalStateException("Cannot access field \"irId\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"irId\" in state: " + codecState()); } } @@ -311,13 +307,9 @@ public int irVersion() { if (DEBUG_MODE) { - switch (codecState()) + if (codecState() == CodecState.NOT_WRAPPED) { - case V0_BLOCK: - codecState(CodecState.V0_BLOCK); - break; - default: - throw new IllegalStateException("Cannot access field \"irVersion\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"irVersion\" in state: " + codecState()); } } @@ -374,13 +366,9 @@ public int schemaVersion() { if (DEBUG_MODE) { - switch (codecState()) + if (codecState() == CodecState.NOT_WRAPPED) { - case V0_BLOCK: - codecState(CodecState.V0_BLOCK); - break; - default: - throw new IllegalStateException("Cannot access field \"schemaVersion\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"schemaVersion\" in state: " + codecState()); } } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java index 5de82c0b5c..e5e3b4334e 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java @@ -200,13 +200,9 @@ public FrameCodecEncoder irId(final int value) { if (DEBUG_MODE) { - switch (codecState()) + if (codecState() == CodecState.NOT_WRAPPED) { - case V0_BLOCK: - codecState(CodecState.V0_BLOCK); - break; - default: - throw new IllegalStateException("Cannot access field \"irId\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"irId\" in state: " + codecState()); } } @@ -264,13 +260,9 @@ public FrameCodecEncoder irVersion(final int value) { if (DEBUG_MODE) { - switch (codecState()) + if (codecState() == CodecState.NOT_WRAPPED) { - case V0_BLOCK: - codecState(CodecState.V0_BLOCK); - break; - default: - throw new IllegalStateException("Cannot access field \"irVersion\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"irVersion\" in state: " + codecState()); } } @@ -328,13 +320,9 @@ public FrameCodecEncoder schemaVersion(final int value) { if (DEBUG_MODE) { - switch (codecState()) + if (codecState() == CodecState.NOT_WRAPPED) { - case V0_BLOCK: - codecState(CodecState.V0_BLOCK); - break; - default: - throw new IllegalStateException("Cannot access field \"schemaVersion\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"schemaVersion\" in state: " + codecState()); } } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java index 2256caa905..0b90305373 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java @@ -271,13 +271,9 @@ public int tokenOffset() { if (DEBUG_MODE) { - switch (codecState()) + if (codecState() == CodecState.NOT_WRAPPED) { - case V0_BLOCK: - codecState(CodecState.V0_BLOCK); - break; - default: - throw new IllegalStateException("Cannot access field \"tokenOffset\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"tokenOffset\" in state: " + codecState()); } } @@ -334,13 +330,9 @@ public int tokenSize() { if (DEBUG_MODE) { - switch (codecState()) + if (codecState() == CodecState.NOT_WRAPPED) { - case V0_BLOCK: - codecState(CodecState.V0_BLOCK); - break; - default: - throw new IllegalStateException("Cannot access field \"tokenSize\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"tokenSize\" in state: " + codecState()); } } @@ -397,13 +389,9 @@ public int fieldId() { if (DEBUG_MODE) { - switch (codecState()) + if (codecState() == CodecState.NOT_WRAPPED) { - case V0_BLOCK: - codecState(CodecState.V0_BLOCK); - break; - default: - throw new IllegalStateException("Cannot access field \"fieldId\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"fieldId\" in state: " + codecState()); } } @@ -460,13 +448,9 @@ public int tokenVersion() { if (DEBUG_MODE) { - switch (codecState()) + if (codecState() == CodecState.NOT_WRAPPED) { - case V0_BLOCK: - codecState(CodecState.V0_BLOCK); - break; - default: - throw new IllegalStateException("Cannot access field \"tokenVersion\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"tokenVersion\" in state: " + codecState()); } } @@ -523,13 +507,9 @@ public int componentTokenCount() { if (DEBUG_MODE) { - switch (codecState()) + if (codecState() == CodecState.NOT_WRAPPED) { - case V0_BLOCK: - codecState(CodecState.V0_BLOCK); - break; - default: - throw new IllegalStateException("Cannot access field \"componentTokenCount\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"componentTokenCount\" in state: " + codecState()); } } @@ -571,13 +551,9 @@ public short signalRaw() { if (DEBUG_MODE) { - switch (codecState()) + if (codecState() == CodecState.NOT_WRAPPED) { - case V0_BLOCK: - codecState(CodecState.V0_BLOCK); - break; - default: - throw new IllegalStateException("Cannot access field \"signal\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"signal\" in state: " + codecState()); } } @@ -588,13 +564,9 @@ public SignalCodec signal() { if (DEBUG_MODE) { - switch (codecState()) + if (codecState() == CodecState.NOT_WRAPPED) { - case V0_BLOCK: - codecState(CodecState.V0_BLOCK); - break; - default: - throw new IllegalStateException("Cannot access field \"signal\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"signal\" in state: " + codecState()); } } @@ -636,13 +608,9 @@ public short primitiveTypeRaw() { if (DEBUG_MODE) { - switch (codecState()) + if (codecState() == CodecState.NOT_WRAPPED) { - case V0_BLOCK: - codecState(CodecState.V0_BLOCK); - break; - default: - throw new IllegalStateException("Cannot access field \"primitiveType\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"primitiveType\" in state: " + codecState()); } } @@ -653,13 +621,9 @@ public PrimitiveTypeCodec primitiveType() { if (DEBUG_MODE) { - switch (codecState()) + if (codecState() == CodecState.NOT_WRAPPED) { - case V0_BLOCK: - codecState(CodecState.V0_BLOCK); - break; - default: - throw new IllegalStateException("Cannot access field \"primitiveType\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"primitiveType\" in state: " + codecState()); } } @@ -701,13 +665,9 @@ public short byteOrderRaw() { if (DEBUG_MODE) { - switch (codecState()) + if (codecState() == CodecState.NOT_WRAPPED) { - case V0_BLOCK: - codecState(CodecState.V0_BLOCK); - break; - default: - throw new IllegalStateException("Cannot access field \"byteOrder\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"byteOrder\" in state: " + codecState()); } } @@ -718,13 +678,9 @@ public ByteOrderCodec byteOrder() { if (DEBUG_MODE) { - switch (codecState()) + if (codecState() == CodecState.NOT_WRAPPED) { - case V0_BLOCK: - codecState(CodecState.V0_BLOCK); - break; - default: - throw new IllegalStateException("Cannot access field \"byteOrder\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"byteOrder\" in state: " + codecState()); } } @@ -766,13 +722,9 @@ public short presenceRaw() { if (DEBUG_MODE) { - switch (codecState()) + if (codecState() == CodecState.NOT_WRAPPED) { - case V0_BLOCK: - codecState(CodecState.V0_BLOCK); - break; - default: - throw new IllegalStateException("Cannot access field \"presence\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"presence\" in state: " + codecState()); } } @@ -783,13 +735,9 @@ public PresenceCodec presence() { if (DEBUG_MODE) { - switch (codecState()) + if (codecState() == CodecState.NOT_WRAPPED) { - case V0_BLOCK: - codecState(CodecState.V0_BLOCK); - break; - default: - throw new IllegalStateException("Cannot access field \"presence\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"presence\" in state: " + codecState()); } } @@ -846,13 +794,9 @@ public int deprecated() { if (DEBUG_MODE) { - switch (codecState()) + if (codecState() == CodecState.NOT_WRAPPED) { - case V0_BLOCK: - codecState(CodecState.V0_BLOCK); - break; - default: - throw new IllegalStateException("Cannot access field \"deprecated\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"deprecated\" in state: " + codecState()); } } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java index 54dc2633e3..f8c1a66cbb 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java @@ -223,13 +223,9 @@ public TokenCodecEncoder tokenOffset(final int value) { if (DEBUG_MODE) { - switch (codecState()) + if (codecState() == CodecState.NOT_WRAPPED) { - case V0_BLOCK: - codecState(CodecState.V0_BLOCK); - break; - default: - throw new IllegalStateException("Cannot access field \"tokenOffset\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"tokenOffset\" in state: " + codecState()); } } @@ -287,13 +283,9 @@ public TokenCodecEncoder tokenSize(final int value) { if (DEBUG_MODE) { - switch (codecState()) + if (codecState() == CodecState.NOT_WRAPPED) { - case V0_BLOCK: - codecState(CodecState.V0_BLOCK); - break; - default: - throw new IllegalStateException("Cannot access field \"tokenSize\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"tokenSize\" in state: " + codecState()); } } @@ -351,13 +343,9 @@ public TokenCodecEncoder fieldId(final int value) { if (DEBUG_MODE) { - switch (codecState()) + if (codecState() == CodecState.NOT_WRAPPED) { - case V0_BLOCK: - codecState(CodecState.V0_BLOCK); - break; - default: - throw new IllegalStateException("Cannot access field \"fieldId\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"fieldId\" in state: " + codecState()); } } @@ -415,13 +403,9 @@ public TokenCodecEncoder tokenVersion(final int value) { if (DEBUG_MODE) { - switch (codecState()) + if (codecState() == CodecState.NOT_WRAPPED) { - case V0_BLOCK: - codecState(CodecState.V0_BLOCK); - break; - default: - throw new IllegalStateException("Cannot access field \"tokenVersion\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"tokenVersion\" in state: " + codecState()); } } @@ -479,13 +463,9 @@ public TokenCodecEncoder componentTokenCount(final int value) { if (DEBUG_MODE) { - switch (codecState()) + if (codecState() == CodecState.NOT_WRAPPED) { - case V0_BLOCK: - codecState(CodecState.V0_BLOCK); - break; - default: - throw new IllegalStateException("Cannot access field \"componentTokenCount\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"componentTokenCount\" in state: " + codecState()); } } @@ -528,13 +508,9 @@ public TokenCodecEncoder signal(final SignalCodec value) { if (DEBUG_MODE) { - switch (codecState()) + if (codecState() == CodecState.NOT_WRAPPED) { - case V0_BLOCK: - codecState(CodecState.V0_BLOCK); - break; - default: - throw new IllegalStateException("Cannot access field \"signal\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"signal\" in state: " + codecState()); } } @@ -576,13 +552,9 @@ public TokenCodecEncoder primitiveType(final PrimitiveTypeCodec value) { if (DEBUG_MODE) { - switch (codecState()) + if (codecState() == CodecState.NOT_WRAPPED) { - case V0_BLOCK: - codecState(CodecState.V0_BLOCK); - break; - default: - throw new IllegalStateException("Cannot access field \"primitiveType\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"primitiveType\" in state: " + codecState()); } } @@ -624,13 +596,9 @@ public TokenCodecEncoder byteOrder(final ByteOrderCodec value) { if (DEBUG_MODE) { - switch (codecState()) + if (codecState() == CodecState.NOT_WRAPPED) { - case V0_BLOCK: - codecState(CodecState.V0_BLOCK); - break; - default: - throw new IllegalStateException("Cannot access field \"byteOrder\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"byteOrder\" in state: " + codecState()); } } @@ -672,13 +640,9 @@ public TokenCodecEncoder presence(final PresenceCodec value) { if (DEBUG_MODE) { - switch (codecState()) + if (codecState() == CodecState.NOT_WRAPPED) { - case V0_BLOCK: - codecState(CodecState.V0_BLOCK); - break; - default: - throw new IllegalStateException("Cannot access field \"presence\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"presence\" in state: " + codecState()); } } @@ -735,13 +699,9 @@ public TokenCodecEncoder deprecated(final int value) { if (DEBUG_MODE) { - switch (codecState()) + if (codecState() == CodecState.NOT_WRAPPED) { - case V0_BLOCK: - codecState(CodecState.V0_BLOCK); - break; - default: - throw new IllegalStateException("Cannot access field \"deprecated\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"deprecated\" in state: " + codecState()); } } diff --git a/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java b/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java index ffc26d58a9..bda9ac2617 100644 --- a/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java +++ b/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java @@ -105,7 +105,6 @@ void allowsDecodingToSkipVariableLengthFields() } @Test - @Disabled("Our access checks are too strict to allow the behaviour in this test.") void allowsReEncodingTopLevelPrimitiveFields() { final MultipleVarLengthEncoder encoder = new MultipleVarLengthEncoder() @@ -1016,7 +1015,6 @@ void disallowsDecodingCompositeInsideGroupBeforeCallingNext() } @Test - @Disabled("Our access checks are too strict to allow the behaviour in this test.") void allowsReEncodingTopLevelCompositeViaReWrap() { final CompositeInsideGroupEncoder encoder = new CompositeInsideGroupEncoder() @@ -1109,7 +1107,6 @@ void allowsReEncodingGroupElementCompositeViaEncoderReference() } @Test - @Disabled("Our access checks are too strict to allow the behaviour in this test.") void allowsReDecodingTopLevelCompositeViaReWrap() { final CompositeInsideGroupEncoder encoder = new CompositeInsideGroupEncoder() @@ -1925,7 +1922,6 @@ void disallowsDecodingEnumInsideGroupBeforeCallingNext() } @Test - @Disabled("Our access checks are too strict to allow the behaviour in this test.") void allowsReEncodingTopLevelEnum() { final EnumInsideGroupEncoder encoder = new EnumInsideGroupEncoder() @@ -2007,7 +2003,6 @@ void disallowsDecodingBitSetInsideGroupBeforeCallingNext() } @Test - @Disabled("Our access checks are too strict to allow the behaviour in this test.") void allowsReEncodingTopLevelBitSetViaReWrap() { final BitSetInsideGroupEncoder encoder = new BitSetInsideGroupEncoder() @@ -2166,7 +2161,6 @@ private ArrayInsideGroupDecoder.BDecoder decodeUntilGroupWithArrayInside() } @Test - @Disabled("Our access checks are too strict to allow the behaviour in this test.") void allowsReEncodingTopLevelArrayViaReWrap() { final ArrayInsideGroupEncoder encoder = new ArrayInsideGroupEncoder() @@ -2233,7 +2227,6 @@ void allowsEncodingAndDecodingGroupFieldsInSchemaDefinedOrder2() } @Test - @Disabled("Our access checks are too strict to allow the behaviour in this test.") void allowsReEncodingTopLevelPrimitiveFieldsAfterGroups() { final MultipleGroupsEncoder encoder = new MultipleGroupsEncoder() @@ -2586,7 +2579,6 @@ private AsciiInsideGroupDecoder.BDecoder decodeUntilGroupWithAsciiInside() } @Test - @Disabled("Our access checks are too strict to allow the behaviour in this test.") void allowsReEncodingTopLevelAsciiViaReWrap() { final AsciiInsideGroupEncoder encoder = new AsciiInsideGroupEncoder() From d9621e6f9ec6cddc42f09c8ff06759fbfd8bfac4 Mon Sep 17 00:00:00 2001 From: Zach Bray Date: Mon, 19 Jun 2023 14:25:56 +0100 Subject: [PATCH 16/50] [Java] Support turning on access checks independently of bounds checks. Introduces the system property: ``` sbe.enable.access.order.checks ``` --- .../sbe/generation/java/JavaGenerator.java | 18 +- .../sbe/ir/generated/FrameCodecDecoder.java | 52 +++--- .../sbe/ir/generated/FrameCodecEncoder.java | 32 ++-- .../sbe/ir/generated/TokenCodecDecoder.java | 170 +++++++++--------- .../sbe/ir/generated/TokenCodecEncoder.java | 94 +++++----- 5 files changed, 193 insertions(+), 173 deletions(-) diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java index 4e24622962..3687bb5690 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java @@ -280,8 +280,12 @@ private static CharSequence generateFieldOrderStates(final FieldOrderModel field { final StringBuilder sb = new StringBuilder(); - sb.append(" private static final boolean DEBUG_MODE = ") + sb.append(" private static final boolean ENABLE_BOUNDS_CHECKS = ") .append("!Boolean.getBoolean(\"agrona.disable.bounds.checks\");\n\n"); + sb.append(" private static final boolean ENABLE_ACCESS_ORDER_CHECKS = ") + .append("Boolean.parseBoolean(System.getProperty(\n") + .append(" \"sbe.enable.access.order.checks\",\n") + .append(" Boolean.toString(ENABLE_BOUNDS_CHECKS)));\n\n"); sb.append(" /**\n"); sb.append(" * The states in which a encoder/decoder/codec can live.\n"); @@ -329,7 +333,7 @@ private static CharSequence generateFieldOrderStateTransitions( final StringBuilder sb = new StringBuilder(); - sb.append(indent).append("if (DEBUG_MODE)\n") + sb.append(indent).append("if (ENABLE_ACCESS_ORDER_CHECKS)\n") .append(indent).append("{\n"); generateFieldOrderStateTransitions( @@ -352,7 +356,7 @@ private static CharSequence generateFieldOrderStateTransitions( { final StringBuilder sb = new StringBuilder(); - sb.append(indent).append("if (DEBUG_MODE)\n") + sb.append(indent).append("if (ENABLE_ACCESS_ORDER_CHECKS)\n") .append(indent).append("{\n") .append(indent).append(" final int remaining = ").append(remainingExpression).append(";\n") .append(indent).append(" if (remaining == 0)\n") @@ -432,7 +436,7 @@ private static CharSequence generateFieldOrderStateTransitionsForNextGroupElemen { final StringBuilder sb = new StringBuilder(); - sb.append(indent).append("if (DEBUG_MODE)\n") + sb.append(indent).append("if (ENABLE_ACCESS_ORDER_CHECKS)\n") .append(indent).append("{\n") .append(indent).append(" final int remaining = ").append(remainingExpression).append(";\n") .append(indent).append(" if (remaining > 1)\n") @@ -468,7 +472,7 @@ private static CharSequence generateFieldOrderStateTransitionForDecoderWrap( final String actingVersion) { final StringBuilder sb = new StringBuilder(); - sb.append(indent).append("if (DEBUG_MODE)\n") + sb.append(indent).append("if (ENABLE_ACCESS_ORDER_CHECKS)\n") .append(indent).append("{\n") .append(indent).append(" switch(" + actingVersion + ")") .append(indent).append(" {\n"); @@ -3093,7 +3097,7 @@ private CharSequence generateDecoderFlyweightCode( " sbeSkip();\n" + " final int decodedLength = encodedLength();\n" + " limit(currentLimit);\n\n" + - " if (DEBUG_MODE)\n" + + " if (ENABLE_ACCESS_ORDER_CHECKS)\n" + " {\n" + " codecState(currentCodecState);\n" + " }\n\n" + @@ -3226,7 +3230,7 @@ private CharSequence generateEncoderFlyweightCode( " this.initialOffset = offset;\n" + " this.offset = offset;\n" + " limit(offset + BLOCK_LENGTH);\n\n" + - " if (DEBUG_MODE)\n" + + " if (ENABLE_ACCESS_ORDER_CHECKS)\n" + " {\n" + " codecState(" + qualifiedStateCase(fieldOrderModel.latestVersionWrappedState()) + ");\n" + " }\n\n" + diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java index 0492c8f902..bb92a28ffb 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java @@ -11,7 +11,11 @@ @SuppressWarnings("all") public final class FrameCodecDecoder { - private static final boolean DEBUG_MODE = !Boolean.getBoolean("agrona.disable.bounds.checks"); + private static final boolean ENABLE_BOUNDS_CHECKS = !Boolean.getBoolean("agrona.disable.bounds.checks"); + + private static final boolean ENABLE_ACCESS_ORDER_CHECKS = Boolean.parseBoolean(System.getProperty( + "sbe.enable.access.order.checks", + Boolean.toString(ENABLE_BOUNDS_CHECKS))); /** * The states in which a encoder/decoder/codec can live. @@ -123,7 +127,7 @@ public FrameCodecDecoder wrap( this.actingVersion = actingVersion; limit(offset + actingBlockLength); - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch(actingVersion) { case 0: @@ -171,7 +175,7 @@ public int sbeDecodedLength() final int decodedLength = encodedLength(); limit(currentLimit); - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { codecState(currentCodecState); } @@ -246,7 +250,7 @@ public static int irIdMaxValue() public int irId() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { if (codecState() == CodecState.NOT_WRAPPED) { @@ -305,7 +309,7 @@ public static int irVersionMaxValue() public int irVersion() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { if (codecState() == CodecState.NOT_WRAPPED) { @@ -364,7 +368,7 @@ public static int schemaVersionMaxValue() public int schemaVersion() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { if (codecState() == CodecState.NOT_WRAPPED) { @@ -408,7 +412,7 @@ public static int packageNameHeaderLength() public int packageNameLength() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -426,7 +430,7 @@ public int packageNameLength() public int skipPackageName() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -449,7 +453,7 @@ public int skipPackageName() public int getPackageName(final MutableDirectBuffer dst, final int dstOffset, final int length) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -473,7 +477,7 @@ public int getPackageName(final MutableDirectBuffer dst, final int dstOffset, fi public int getPackageName(final byte[] dst, final int dstOffset, final int length) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -497,7 +501,7 @@ public int getPackageName(final byte[] dst, final int dstOffset, final int lengt public void wrapPackageName(final DirectBuffer wrapBuffer) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -518,7 +522,7 @@ public void wrapPackageName(final DirectBuffer wrapBuffer) public String packageName() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -578,7 +582,7 @@ public static int namespaceNameHeaderLength() public int namespaceNameLength() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -596,7 +600,7 @@ public int namespaceNameLength() public int skipNamespaceName() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -619,7 +623,7 @@ public int skipNamespaceName() public int getNamespaceName(final MutableDirectBuffer dst, final int dstOffset, final int length) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -643,7 +647,7 @@ public int getNamespaceName(final MutableDirectBuffer dst, final int dstOffset, public int getNamespaceName(final byte[] dst, final int dstOffset, final int length) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -667,7 +671,7 @@ public int getNamespaceName(final byte[] dst, final int dstOffset, final int len public void wrapNamespaceName(final DirectBuffer wrapBuffer) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -688,7 +692,7 @@ public void wrapNamespaceName(final DirectBuffer wrapBuffer) public String namespaceName() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -748,7 +752,7 @@ public static int semanticVersionHeaderLength() public int semanticVersionLength() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -766,7 +770,7 @@ public int semanticVersionLength() public int skipSemanticVersion() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -789,7 +793,7 @@ public int skipSemanticVersion() public int getSemanticVersion(final MutableDirectBuffer dst, final int dstOffset, final int length) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -813,7 +817,7 @@ public int getSemanticVersion(final MutableDirectBuffer dst, final int dstOffset public int getSemanticVersion(final byte[] dst, final int dstOffset, final int length) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -837,7 +841,7 @@ public int getSemanticVersion(final byte[] dst, final int dstOffset, final int l public void wrapSemanticVersion(final DirectBuffer wrapBuffer) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -858,7 +862,7 @@ public void wrapSemanticVersion(final DirectBuffer wrapBuffer) public String semanticVersion() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java index e5e3b4334e..b49bed53a1 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java @@ -11,7 +11,11 @@ @SuppressWarnings("all") public final class FrameCodecEncoder { - private static final boolean DEBUG_MODE = !Boolean.getBoolean("agrona.disable.bounds.checks"); + private static final boolean ENABLE_BOUNDS_CHECKS = !Boolean.getBoolean("agrona.disable.bounds.checks"); + + private static final boolean ENABLE_ACCESS_ORDER_CHECKS = Boolean.parseBoolean(System.getProperty( + "sbe.enable.access.order.checks", + Boolean.toString(ENABLE_BOUNDS_CHECKS))); /** * The states in which a encoder/decoder/codec can live. @@ -115,7 +119,7 @@ public FrameCodecEncoder wrap(final MutableDirectBuffer buffer, final int offset this.offset = offset; limit(offset + BLOCK_LENGTH); - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { codecState(CodecState.V0_BLOCK); } @@ -198,7 +202,7 @@ public static int irIdMaxValue() public FrameCodecEncoder irId(final int value) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { if (codecState() == CodecState.NOT_WRAPPED) { @@ -258,7 +262,7 @@ public static int irVersionMaxValue() public FrameCodecEncoder irVersion(final int value) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { if (codecState() == CodecState.NOT_WRAPPED) { @@ -318,7 +322,7 @@ public static int schemaVersionMaxValue() public FrameCodecEncoder schemaVersion(final int value) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { if (codecState() == CodecState.NOT_WRAPPED) { @@ -363,7 +367,7 @@ public FrameCodecEncoder putPackageName(final DirectBuffer src, final int srcOff throw new IllegalStateException("length > maxValue for type: " + length); } - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -391,7 +395,7 @@ public FrameCodecEncoder putPackageName(final byte[] src, final int srcOffset, f throw new IllegalStateException("length > maxValue for type: " + length); } - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -422,7 +426,7 @@ public FrameCodecEncoder packageName(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -475,7 +479,7 @@ public FrameCodecEncoder putNamespaceName(final DirectBuffer src, final int srcO throw new IllegalStateException("length > maxValue for type: " + length); } - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -503,7 +507,7 @@ public FrameCodecEncoder putNamespaceName(final byte[] src, final int srcOffset, throw new IllegalStateException("length > maxValue for type: " + length); } - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -534,7 +538,7 @@ public FrameCodecEncoder namespaceName(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -587,7 +591,7 @@ public FrameCodecEncoder putSemanticVersion(final DirectBuffer src, final int sr throw new IllegalStateException("length > maxValue for type: " + length); } - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -615,7 +619,7 @@ public FrameCodecEncoder putSemanticVersion(final byte[] src, final int srcOffse throw new IllegalStateException("length > maxValue for type: " + length); } - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -646,7 +650,7 @@ public FrameCodecEncoder semanticVersion(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java index 0b90305373..5be665a181 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java @@ -11,7 +11,11 @@ @SuppressWarnings("all") public final class TokenCodecDecoder { - private static final boolean DEBUG_MODE = !Boolean.getBoolean("agrona.disable.bounds.checks"); + private static final boolean ENABLE_BOUNDS_CHECKS = !Boolean.getBoolean("agrona.disable.bounds.checks"); + + private static final boolean ENABLE_ACCESS_ORDER_CHECKS = Boolean.parseBoolean(System.getProperty( + "sbe.enable.access.order.checks", + Boolean.toString(ENABLE_BOUNDS_CHECKS))); /** * The states in which a encoder/decoder/codec can live. @@ -146,7 +150,7 @@ public TokenCodecDecoder wrap( this.actingVersion = actingVersion; limit(offset + actingBlockLength); - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch(actingVersion) { case 0: @@ -194,7 +198,7 @@ public int sbeDecodedLength() final int decodedLength = encodedLength(); limit(currentLimit); - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { codecState(currentCodecState); } @@ -269,7 +273,7 @@ public static int tokenOffsetMaxValue() public int tokenOffset() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { if (codecState() == CodecState.NOT_WRAPPED) { @@ -328,7 +332,7 @@ public static int tokenSizeMaxValue() public int tokenSize() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { if (codecState() == CodecState.NOT_WRAPPED) { @@ -387,7 +391,7 @@ public static int fieldIdMaxValue() public int fieldId() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { if (codecState() == CodecState.NOT_WRAPPED) { @@ -446,7 +450,7 @@ public static int tokenVersionMaxValue() public int tokenVersion() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { if (codecState() == CodecState.NOT_WRAPPED) { @@ -505,7 +509,7 @@ public static int componentTokenCountMaxValue() public int componentTokenCount() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { if (codecState() == CodecState.NOT_WRAPPED) { @@ -549,7 +553,7 @@ public static String signalMetaAttribute(final MetaAttribute metaAttribute) public short signalRaw() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { if (codecState() == CodecState.NOT_WRAPPED) { @@ -562,7 +566,7 @@ public short signalRaw() public SignalCodec signal() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { if (codecState() == CodecState.NOT_WRAPPED) { @@ -606,7 +610,7 @@ public static String primitiveTypeMetaAttribute(final MetaAttribute metaAttribut public short primitiveTypeRaw() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { if (codecState() == CodecState.NOT_WRAPPED) { @@ -619,7 +623,7 @@ public short primitiveTypeRaw() public PrimitiveTypeCodec primitiveType() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { if (codecState() == CodecState.NOT_WRAPPED) { @@ -663,7 +667,7 @@ public static String byteOrderMetaAttribute(final MetaAttribute metaAttribute) public short byteOrderRaw() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { if (codecState() == CodecState.NOT_WRAPPED) { @@ -676,7 +680,7 @@ public short byteOrderRaw() public ByteOrderCodec byteOrder() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { if (codecState() == CodecState.NOT_WRAPPED) { @@ -720,7 +724,7 @@ public static String presenceMetaAttribute(final MetaAttribute metaAttribute) public short presenceRaw() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { if (codecState() == CodecState.NOT_WRAPPED) { @@ -733,7 +737,7 @@ public short presenceRaw() public PresenceCodec presence() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { if (codecState() == CodecState.NOT_WRAPPED) { @@ -792,7 +796,7 @@ public static int deprecatedMaxValue() public int deprecated() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { if (codecState() == CodecState.NOT_WRAPPED) { @@ -836,7 +840,7 @@ public static int nameHeaderLength() public int nameLength() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -854,7 +858,7 @@ public int nameLength() public int skipName() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -877,7 +881,7 @@ public int skipName() public int getName(final MutableDirectBuffer dst, final int dstOffset, final int length) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -901,7 +905,7 @@ public int getName(final MutableDirectBuffer dst, final int dstOffset, final int public int getName(final byte[] dst, final int dstOffset, final int length) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -925,7 +929,7 @@ public int getName(final byte[] dst, final int dstOffset, final int length) public void wrapName(final DirectBuffer wrapBuffer) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -946,7 +950,7 @@ public void wrapName(final DirectBuffer wrapBuffer) public String name() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1006,7 +1010,7 @@ public static int constValueHeaderLength() public int constValueLength() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1024,7 +1028,7 @@ public int constValueLength() public int skipConstValue() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1047,7 +1051,7 @@ public int skipConstValue() public int getConstValue(final MutableDirectBuffer dst, final int dstOffset, final int length) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1071,7 +1075,7 @@ public int getConstValue(final MutableDirectBuffer dst, final int dstOffset, fin public int getConstValue(final byte[] dst, final int dstOffset, final int length) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1095,7 +1099,7 @@ public int getConstValue(final byte[] dst, final int dstOffset, final int length public void wrapConstValue(final DirectBuffer wrapBuffer) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1116,7 +1120,7 @@ public void wrapConstValue(final DirectBuffer wrapBuffer) public String constValue() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1176,7 +1180,7 @@ public static int minValueHeaderLength() public int minValueLength() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1194,7 +1198,7 @@ public int minValueLength() public int skipMinValue() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1217,7 +1221,7 @@ public int skipMinValue() public int getMinValue(final MutableDirectBuffer dst, final int dstOffset, final int length) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1241,7 +1245,7 @@ public int getMinValue(final MutableDirectBuffer dst, final int dstOffset, final public int getMinValue(final byte[] dst, final int dstOffset, final int length) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1265,7 +1269,7 @@ public int getMinValue(final byte[] dst, final int dstOffset, final int length) public void wrapMinValue(final DirectBuffer wrapBuffer) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1286,7 +1290,7 @@ public void wrapMinValue(final DirectBuffer wrapBuffer) public String minValue() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1346,7 +1350,7 @@ public static int maxValueHeaderLength() public int maxValueLength() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1364,7 +1368,7 @@ public int maxValueLength() public int skipMaxValue() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1387,7 +1391,7 @@ public int skipMaxValue() public int getMaxValue(final MutableDirectBuffer dst, final int dstOffset, final int length) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1411,7 +1415,7 @@ public int getMaxValue(final MutableDirectBuffer dst, final int dstOffset, final public int getMaxValue(final byte[] dst, final int dstOffset, final int length) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1435,7 +1439,7 @@ public int getMaxValue(final byte[] dst, final int dstOffset, final int length) public void wrapMaxValue(final DirectBuffer wrapBuffer) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1456,7 +1460,7 @@ public void wrapMaxValue(final DirectBuffer wrapBuffer) public String maxValue() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1516,7 +1520,7 @@ public static int nullValueHeaderLength() public int nullValueLength() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1534,7 +1538,7 @@ public int nullValueLength() public int skipNullValue() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1557,7 +1561,7 @@ public int skipNullValue() public int getNullValue(final MutableDirectBuffer dst, final int dstOffset, final int length) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1581,7 +1585,7 @@ public int getNullValue(final MutableDirectBuffer dst, final int dstOffset, fina public int getNullValue(final byte[] dst, final int dstOffset, final int length) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1605,7 +1609,7 @@ public int getNullValue(final byte[] dst, final int dstOffset, final int length) public void wrapNullValue(final DirectBuffer wrapBuffer) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1626,7 +1630,7 @@ public void wrapNullValue(final DirectBuffer wrapBuffer) public String nullValue() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1686,7 +1690,7 @@ public static int characterEncodingHeaderLength() public int characterEncodingLength() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1704,7 +1708,7 @@ public int characterEncodingLength() public int skipCharacterEncoding() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1727,7 +1731,7 @@ public int skipCharacterEncoding() public int getCharacterEncoding(final MutableDirectBuffer dst, final int dstOffset, final int length) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1751,7 +1755,7 @@ public int getCharacterEncoding(final MutableDirectBuffer dst, final int dstOffs public int getCharacterEncoding(final byte[] dst, final int dstOffset, final int length) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1775,7 +1779,7 @@ public int getCharacterEncoding(final byte[] dst, final int dstOffset, final int public void wrapCharacterEncoding(final DirectBuffer wrapBuffer) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1796,7 +1800,7 @@ public void wrapCharacterEncoding(final DirectBuffer wrapBuffer) public String characterEncoding() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1856,7 +1860,7 @@ public static int epochHeaderLength() public int epochLength() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1874,7 +1878,7 @@ public int epochLength() public int skipEpoch() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1897,7 +1901,7 @@ public int skipEpoch() public int getEpoch(final MutableDirectBuffer dst, final int dstOffset, final int length) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1921,7 +1925,7 @@ public int getEpoch(final MutableDirectBuffer dst, final int dstOffset, final in public int getEpoch(final byte[] dst, final int dstOffset, final int length) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1945,7 +1949,7 @@ public int getEpoch(final byte[] dst, final int dstOffset, final int length) public void wrapEpoch(final DirectBuffer wrapBuffer) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1966,7 +1970,7 @@ public void wrapEpoch(final DirectBuffer wrapBuffer) public String epoch() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -2026,7 +2030,7 @@ public static int timeUnitHeaderLength() public int timeUnitLength() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -2044,7 +2048,7 @@ public int timeUnitLength() public int skipTimeUnit() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -2067,7 +2071,7 @@ public int skipTimeUnit() public int getTimeUnit(final MutableDirectBuffer dst, final int dstOffset, final int length) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -2091,7 +2095,7 @@ public int getTimeUnit(final MutableDirectBuffer dst, final int dstOffset, final public int getTimeUnit(final byte[] dst, final int dstOffset, final int length) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -2115,7 +2119,7 @@ public int getTimeUnit(final byte[] dst, final int dstOffset, final int length) public void wrapTimeUnit(final DirectBuffer wrapBuffer) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -2136,7 +2140,7 @@ public void wrapTimeUnit(final DirectBuffer wrapBuffer) public String timeUnit() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -2196,7 +2200,7 @@ public static int semanticTypeHeaderLength() public int semanticTypeLength() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -2214,7 +2218,7 @@ public int semanticTypeLength() public int skipSemanticType() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -2237,7 +2241,7 @@ public int skipSemanticType() public int getSemanticType(final MutableDirectBuffer dst, final int dstOffset, final int length) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -2261,7 +2265,7 @@ public int getSemanticType(final MutableDirectBuffer dst, final int dstOffset, f public int getSemanticType(final byte[] dst, final int dstOffset, final int length) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -2285,7 +2289,7 @@ public int getSemanticType(final byte[] dst, final int dstOffset, final int leng public void wrapSemanticType(final DirectBuffer wrapBuffer) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -2306,7 +2310,7 @@ public void wrapSemanticType(final DirectBuffer wrapBuffer) public String semanticType() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -2366,7 +2370,7 @@ public static int descriptionHeaderLength() public int descriptionLength() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -2384,7 +2388,7 @@ public int descriptionLength() public int skipDescription() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -2407,7 +2411,7 @@ public int skipDescription() public int getDescription(final MutableDirectBuffer dst, final int dstOffset, final int length) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -2431,7 +2435,7 @@ public int getDescription(final MutableDirectBuffer dst, final int dstOffset, fi public int getDescription(final byte[] dst, final int dstOffset, final int length) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -2455,7 +2459,7 @@ public int getDescription(final byte[] dst, final int dstOffset, final int lengt public void wrapDescription(final DirectBuffer wrapBuffer) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -2476,7 +2480,7 @@ public void wrapDescription(final DirectBuffer wrapBuffer) public String description() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -2536,7 +2540,7 @@ public static int referencedNameHeaderLength() public int referencedNameLength() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -2554,7 +2558,7 @@ public int referencedNameLength() public int skipReferencedName() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -2577,7 +2581,7 @@ public int skipReferencedName() public int getReferencedName(final MutableDirectBuffer dst, final int dstOffset, final int length) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -2601,7 +2605,7 @@ public int getReferencedName(final MutableDirectBuffer dst, final int dstOffset, public int getReferencedName(final byte[] dst, final int dstOffset, final int length) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -2625,7 +2629,7 @@ public int getReferencedName(final byte[] dst, final int dstOffset, final int le public void wrapReferencedName(final DirectBuffer wrapBuffer) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -2646,7 +2650,7 @@ public void wrapReferencedName(final DirectBuffer wrapBuffer) public String referencedName() { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java index f8c1a66cbb..2f672ffe35 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java @@ -11,7 +11,11 @@ @SuppressWarnings("all") public final class TokenCodecEncoder { - private static final boolean DEBUG_MODE = !Boolean.getBoolean("agrona.disable.bounds.checks"); + private static final boolean ENABLE_BOUNDS_CHECKS = !Boolean.getBoolean("agrona.disable.bounds.checks"); + + private static final boolean ENABLE_ACCESS_ORDER_CHECKS = Boolean.parseBoolean(System.getProperty( + "sbe.enable.access.order.checks", + Boolean.toString(ENABLE_BOUNDS_CHECKS))); /** * The states in which a encoder/decoder/codec can live. @@ -138,7 +142,7 @@ public TokenCodecEncoder wrap(final MutableDirectBuffer buffer, final int offset this.offset = offset; limit(offset + BLOCK_LENGTH); - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { codecState(CodecState.V0_BLOCK); } @@ -221,7 +225,7 @@ public static int tokenOffsetMaxValue() public TokenCodecEncoder tokenOffset(final int value) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { if (codecState() == CodecState.NOT_WRAPPED) { @@ -281,7 +285,7 @@ public static int tokenSizeMaxValue() public TokenCodecEncoder tokenSize(final int value) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { if (codecState() == CodecState.NOT_WRAPPED) { @@ -341,7 +345,7 @@ public static int fieldIdMaxValue() public TokenCodecEncoder fieldId(final int value) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { if (codecState() == CodecState.NOT_WRAPPED) { @@ -401,7 +405,7 @@ public static int tokenVersionMaxValue() public TokenCodecEncoder tokenVersion(final int value) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { if (codecState() == CodecState.NOT_WRAPPED) { @@ -461,7 +465,7 @@ public static int componentTokenCountMaxValue() public TokenCodecEncoder componentTokenCount(final int value) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { if (codecState() == CodecState.NOT_WRAPPED) { @@ -506,7 +510,7 @@ public static String signalMetaAttribute(final MetaAttribute metaAttribute) public TokenCodecEncoder signal(final SignalCodec value) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { if (codecState() == CodecState.NOT_WRAPPED) { @@ -550,7 +554,7 @@ public static String primitiveTypeMetaAttribute(final MetaAttribute metaAttribut public TokenCodecEncoder primitiveType(final PrimitiveTypeCodec value) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { if (codecState() == CodecState.NOT_WRAPPED) { @@ -594,7 +598,7 @@ public static String byteOrderMetaAttribute(final MetaAttribute metaAttribute) public TokenCodecEncoder byteOrder(final ByteOrderCodec value) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { if (codecState() == CodecState.NOT_WRAPPED) { @@ -638,7 +642,7 @@ public static String presenceMetaAttribute(final MetaAttribute metaAttribute) public TokenCodecEncoder presence(final PresenceCodec value) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { if (codecState() == CodecState.NOT_WRAPPED) { @@ -697,7 +701,7 @@ public static int deprecatedMaxValue() public TokenCodecEncoder deprecated(final int value) { - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { if (codecState() == CodecState.NOT_WRAPPED) { @@ -742,7 +746,7 @@ public TokenCodecEncoder putName(final DirectBuffer src, final int srcOffset, fi throw new IllegalStateException("length > maxValue for type: " + length); } - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -770,7 +774,7 @@ public TokenCodecEncoder putName(final byte[] src, final int srcOffset, final in throw new IllegalStateException("length > maxValue for type: " + length); } - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -801,7 +805,7 @@ public TokenCodecEncoder name(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -854,7 +858,7 @@ public TokenCodecEncoder putConstValue(final DirectBuffer src, final int srcOffs throw new IllegalStateException("length > maxValue for type: " + length); } - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -882,7 +886,7 @@ public TokenCodecEncoder putConstValue(final byte[] src, final int srcOffset, fi throw new IllegalStateException("length > maxValue for type: " + length); } - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -913,7 +917,7 @@ public TokenCodecEncoder constValue(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -966,7 +970,7 @@ public TokenCodecEncoder putMinValue(final DirectBuffer src, final int srcOffset throw new IllegalStateException("length > maxValue for type: " + length); } - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -994,7 +998,7 @@ public TokenCodecEncoder putMinValue(final byte[] src, final int srcOffset, fina throw new IllegalStateException("length > maxValue for type: " + length); } - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1025,7 +1029,7 @@ public TokenCodecEncoder minValue(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1078,7 +1082,7 @@ public TokenCodecEncoder putMaxValue(final DirectBuffer src, final int srcOffset throw new IllegalStateException("length > maxValue for type: " + length); } - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1106,7 +1110,7 @@ public TokenCodecEncoder putMaxValue(final byte[] src, final int srcOffset, fina throw new IllegalStateException("length > maxValue for type: " + length); } - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1137,7 +1141,7 @@ public TokenCodecEncoder maxValue(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1190,7 +1194,7 @@ public TokenCodecEncoder putNullValue(final DirectBuffer src, final int srcOffse throw new IllegalStateException("length > maxValue for type: " + length); } - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1218,7 +1222,7 @@ public TokenCodecEncoder putNullValue(final byte[] src, final int srcOffset, fin throw new IllegalStateException("length > maxValue for type: " + length); } - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1249,7 +1253,7 @@ public TokenCodecEncoder nullValue(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1302,7 +1306,7 @@ public TokenCodecEncoder putCharacterEncoding(final DirectBuffer src, final int throw new IllegalStateException("length > maxValue for type: " + length); } - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1330,7 +1334,7 @@ public TokenCodecEncoder putCharacterEncoding(final byte[] src, final int srcOff throw new IllegalStateException("length > maxValue for type: " + length); } - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1361,7 +1365,7 @@ public TokenCodecEncoder characterEncoding(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1414,7 +1418,7 @@ public TokenCodecEncoder putEpoch(final DirectBuffer src, final int srcOffset, f throw new IllegalStateException("length > maxValue for type: " + length); } - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1442,7 +1446,7 @@ public TokenCodecEncoder putEpoch(final byte[] src, final int srcOffset, final i throw new IllegalStateException("length > maxValue for type: " + length); } - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1473,7 +1477,7 @@ public TokenCodecEncoder epoch(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1526,7 +1530,7 @@ public TokenCodecEncoder putTimeUnit(final DirectBuffer src, final int srcOffset throw new IllegalStateException("length > maxValue for type: " + length); } - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1554,7 +1558,7 @@ public TokenCodecEncoder putTimeUnit(final byte[] src, final int srcOffset, fina throw new IllegalStateException("length > maxValue for type: " + length); } - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1585,7 +1589,7 @@ public TokenCodecEncoder timeUnit(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1638,7 +1642,7 @@ public TokenCodecEncoder putSemanticType(final DirectBuffer src, final int srcOf throw new IllegalStateException("length > maxValue for type: " + length); } - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1666,7 +1670,7 @@ public TokenCodecEncoder putSemanticType(final byte[] src, final int srcOffset, throw new IllegalStateException("length > maxValue for type: " + length); } - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1697,7 +1701,7 @@ public TokenCodecEncoder semanticType(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1750,7 +1754,7 @@ public TokenCodecEncoder putDescription(final DirectBuffer src, final int srcOff throw new IllegalStateException("length > maxValue for type: " + length); } - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1778,7 +1782,7 @@ public TokenCodecEncoder putDescription(final byte[] src, final int srcOffset, f throw new IllegalStateException("length > maxValue for type: " + length); } - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1809,7 +1813,7 @@ public TokenCodecEncoder description(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1862,7 +1866,7 @@ public TokenCodecEncoder putReferencedName(final DirectBuffer src, final int src throw new IllegalStateException("length > maxValue for type: " + length); } - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1890,7 +1894,7 @@ public TokenCodecEncoder putReferencedName(final byte[] src, final int srcOffset throw new IllegalStateException("length > maxValue for type: " + length); } - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { @@ -1921,7 +1925,7 @@ public TokenCodecEncoder referencedName(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } - if (DEBUG_MODE) + if (ENABLE_ACCESS_ORDER_CHECKS) { switch (codecState()) { From 8fbc6caa9f34767b0bca9b393267f5f8cec8697f Mon Sep 17 00:00:00 2001 From: Zach Bray Date: Mon, 19 Jun 2023 17:35:36 +0100 Subject: [PATCH 17/50] [Java] Attempt to optimise code with disabled access order checks. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some JIT compilers are able to eliminate dead code within branches of if-statements where the conditional is a static expression. Some JIT compilers perform inlining to some degree and use heuristics, e.g., method size. It isn't clear whether code size metrics will be calculated after dead code elimination is performed. In this commit, I've moved the code that performs state transitions when access order checks are enabled into separate methods that the encoders/decoders call upon field accesses, e.g., the getter for a field `xyz` might look like this: ``` int xyz() { if (ENABLED_ACCESS_ORDER_CHECKS) { onXyzAccessed(); } // ... } ``` This keeps the initial size of `xyz` smaller than when the code of `onXyzAccessed()` is "inlined" by the code generator. There is a lot of noise on my ThinkPad P14s, but benchmarks do show improvements after this change. Before: ``` $ java -jar -Dagrona.disable.bounds.checks=true -Dsbe.enable.access.order.checks=false ./sbe-benchmarks/build/libs/sbe-benchmarks.jar ".*Car.*" -bm thrpt -f 2 -i 3 -wi 2 \# JMH version: 1.36 \# VM version: JDK 1.8.0_302, OpenJDK 64-Bit Server VM, 25.302-b08 \# VM invoker: /home/zach/.asdf/installs/java/zulu-8.56.0.21/jre/bin/java \# VM options: -Dagrona.disable.bounds.checks=true -Dsbe.enable.access.order.checks=false ... Benchmark Mode Cnt Score Error Units CarBenchmark.testDecode thrpt 6 18051405.792 ± 444296.898 ops/s CarBenchmark.testEncode thrpt 6 11964827.418 ± 221996.747 ops/s ``` After: ``` $ java -jar -Dagrona.disable.bounds.checks=true -Dsbe.enable.access.order.checks=false ./sbe-benchmarks/build/libs/sbe-benchmarks.jar ".*Car.*" -bm thrpt -f 2 -i 3 -wi 2 \# JMH version: 1.36 \# VM version: JDK 1.8.0_302, OpenJDK 64-Bit Server VM, 25.302-b08 \# VM invoker: /home/zach/.asdf/installs/java/zulu-8.56.0.21/jre/bin/java \# VM options: -Dagrona.disable.bounds.checks=true -Dsbe.enable.access.order.checks=false ... Benchmark Mode Cnt Score Error Units CarBenchmark.testDecode thrpt 6 18132922.415 ± 1964596.044 ops/s CarBenchmark.testEncode thrpt 6 20018131.180 ± 671367.070 ops/s ``` Baseline (d2ec8f8a225808a1392872473f6e5e85b828056e): ``` $ java -jar -Dagrona.disable.bounds.checks=true -Dsbe.enable.access.order.checks=false ./sbe-benchmarks/build/libs/sbe-benchmarks.jar ".*Car.*" -bm thrpt -f 2 -i 3 -wi 2 \# JMH version: 1.36 \# VM version: JDK 1.8.0_302, OpenJDK 64-Bit Server VM, 25.302-b08 \# VM invoker: /home/zach/.asdf/installs/java/zulu-8.56.0.21/jre/bin/java \# VM options: -Dagrona.disable.bounds.checks=true -Dsbe.enable.access.order.checks=false ... Benchmark Mode Cnt Score Error Units CarBenchmark.testDecode thrpt 6 19270366.357 ± 2618336.027 ops/s CarBenchmark.testEncode thrpt 6 22519718.535 ± 794169.180 ops/s ``` --- .../sbe/generation/java/JavaGenerator.java | 343 ++++--- .../sbe/ir/generated/FrameCodecDecoder.java | 259 ++--- .../sbe/ir/generated/FrameCodecEncoder.java | 156 ++- .../sbe/ir/generated/TokenCodecDecoder.java | 898 ++++++------------ .../sbe/ir/generated/TokenCodecEncoder.java | 559 +++++------ .../real_logic/sbe/FieldOrderCheckTest.java | 14 +- 6 files changed, 934 insertions(+), 1295 deletions(-) diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java index 3687bb5690..64c9f207de 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java @@ -321,48 +321,94 @@ private static CharSequence generateFieldOrderStates(final FieldOrderModel field return sb; } - private static CharSequence generateFieldOrderStateTransitions( + private static String accessOrderListenerMethodName(final Token token) + { + return "on" + Generators.toUpperFirstChar(token.name()) + "Accessed"; + } + + private static void generateAccessOrderListenerMethod( + final StringBuilder sb, final FieldOrderModel fieldOrderModel, final String indent, final Token token) { if (fieldOrderModel == null) { - return ""; + return; } - final StringBuilder sb = new StringBuilder(); - - sb.append(indent).append("if (ENABLE_ACCESS_ORDER_CHECKS)\n") + sb.append("\n") + .append(indent).append("private void ").append(accessOrderListenerMethodName(token)).append("()\n") .append(indent).append("{\n"); - generateFieldOrderStateTransitions( + generateAccessOrderListener( sb, indent + " ", fieldOrderModel, FieldOrderModel.TransitionContext.NONE, token); - sb.append(indent).append("}\n\n"); - - return sb; + sb.append(indent).append("}\n"); } - private static CharSequence generateFieldOrderStateTransitions( + private static CharSequence generateAccessOrderListenerCall( final FieldOrderModel fieldOrderModel, final String indent, final Token token, - final String remainingExpression) + final String... arguments) { - final StringBuilder sb = new StringBuilder(); + return generateAccessOrderListenerCall( + fieldOrderModel, + indent, + accessOrderListenerMethodName(token), + arguments); + } + private static CharSequence generateAccessOrderListenerCall( + final FieldOrderModel fieldOrderModel, + final String indent, + final String methodName, + final String... arguments) + { + if (fieldOrderModel == null) + { + return ""; + } + + final StringBuilder sb = new StringBuilder(); sb.append(indent).append("if (ENABLE_ACCESS_ORDER_CHECKS)\n") .append(indent).append("{\n") - .append(indent).append(" final int remaining = ").append(remainingExpression).append(";\n") + .append(indent).append(" ").append(methodName).append("("); + + for (int i = 0; i < arguments.length; i++) + { + if (i > 0) + { + sb.append(", "); + } + sb.append(arguments[i]); + } + sb.append(");\n"); + + sb.append(indent).append("}\n\n"); + + return sb; + } + + private static void generateAccessOrderListenerMethodForGroupWrap( + final StringBuilder sb, + final FieldOrderModel fieldOrderModel, + final String indent, + final Token token) + { + sb.append("\n") + .append(indent).append("private void ").append(accessOrderListenerMethodName(token)) + .append("(final int remaining)\n") + .append(indent).append("{\n") .append(indent).append(" if (remaining == 0)\n") .append(indent).append(" {\n"); - generateFieldOrderStateTransitions( + generateAccessOrderListener( sb, indent + " ", fieldOrderModel, @@ -373,20 +419,18 @@ private static CharSequence generateFieldOrderStateTransitions( .append(indent).append(" else\n") .append(indent).append(" {\n"); - generateFieldOrderStateTransitions( + generateAccessOrderListener( sb, - indent + " ", + indent + " ", fieldOrderModel, FieldOrderModel.TransitionContext.SELECT_MULTI_ELEMENT_GROUP, token); sb.append(indent).append(" }\n") - .append(indent).append("}\n\n"); - - return sb; + .append(indent).append("}\n"); } - private static void generateFieldOrderStateTransitions( + private static void generateAccessOrderListener( final StringBuilder sb, final String indent, final FieldOrderModel fieldOrderModel, @@ -428,21 +472,19 @@ private static void generateFieldOrderStateTransitions( } } - private static CharSequence generateFieldOrderStateTransitionsForNextGroupElement( + private static void generateAccessOrderListenerMethodForNextGroupElement( + final StringBuilder sb, final FieldOrderModel fieldOrderModel, final String indent, - final Token token, - final String remainingExpression) + final Token token) { - final StringBuilder sb = new StringBuilder(); - - sb.append(indent).append("if (ENABLE_ACCESS_ORDER_CHECKS)\n") + sb.append(indent).append("private void onNextElementAccessed()\n") .append(indent).append("{\n") - .append(indent).append(" final int remaining = ").append(remainingExpression).append(";\n") + .append(indent).append(" final int remaining = ").append("count - index").append(";\n") .append(indent).append(" if (remaining > 1)\n") .append(indent).append(" {\n"); - generateFieldOrderStateTransitions( + generateAccessOrderListener( sb, indent + " ", fieldOrderModel, @@ -453,7 +495,7 @@ private static CharSequence generateFieldOrderStateTransitionsForNextGroupElemen .append(indent).append(" else if (remaining == 1)\n") .append(indent).append(" {\n"); - generateFieldOrderStateTransitions( + generateAccessOrderListener( sb, indent + " ", fieldOrderModel, @@ -461,20 +503,17 @@ private static CharSequence generateFieldOrderStateTransitionsForNextGroupElemen token); sb.append(indent).append(" }\n") - .append(indent).append("}\n\n"); - - return sb; + .append(indent).append("}\n"); } - private static CharSequence generateFieldOrderStateTransitionForDecoderWrap( + private static CharSequence generateDecoderWrapListener( final FieldOrderModel fieldOrderModel, - final String indent, - final String actingVersion) + final String indent) { final StringBuilder sb = new StringBuilder(); - sb.append(indent).append("if (ENABLE_ACCESS_ORDER_CHECKS)\n") + sb.append(indent).append("private void onWrap(final int actingVersion)\n") .append(indent).append("{\n") - .append(indent).append(" switch(" + actingVersion + ")") + .append(indent).append(" switch(actingVersion)\n") .append(indent).append(" {\n"); fieldOrderModel.forEachDecoderWrappedState((version, state) -> @@ -689,6 +728,9 @@ private void generateGroupDecoderClassHeader( .append(indent).append(" count = ").append(numInGroupCast).append(numInGroupGet).append(";\n") .append(indent).append(" }\n"); + + generateAccessOrderListenerMethodForNextGroupElement(sb, fieldOrderModel, indent + " ", groupToken); + sb.append("\n") .append(indent).append(" public ").append(className).append(" next()\n") .append(indent).append(" {\n") @@ -696,8 +738,7 @@ private void generateGroupDecoderClassHeader( .append(indent).append(" {\n") .append(indent).append(" throw new java.util.NoSuchElementException();\n") .append(indent).append(" }\n\n") - .append(generateFieldOrderStateTransitionsForNextGroupElement( - fieldOrderModel, indent + " ", groupToken, "count - index")) + .append(generateAccessOrderListenerCall(fieldOrderModel, indent + " ", "onNextElementAccessed")) .append(indent).append(" offset = parentMessage.limit();\n") .append(indent).append(" parentMessage.limit(offset + blockLength);\n") .append(indent).append(" ++index;\n\n") @@ -825,11 +866,12 @@ private void generateGroupEncoderClassHeader( blockLengthPut, numInGroupPut); + generateAccessOrderListenerMethodForNextGroupElement(sb, fieldOrderModel, ind + " ", groupToken); + sb.append("\n") .append(ind).append(" public ").append(encoderName(groupName)).append(" next()\n") .append(ind).append(" {\n") - .append(generateFieldOrderStateTransitionsForNextGroupElement( - fieldOrderModel, ind + " ", groupToken, "count - index")) + .append(generateAccessOrderListenerCall(fieldOrderModel, ind + " ", "onNextElementAccessed")) .append(ind).append(" if (index >= count)\n") .append(ind).append(" {\n") .append(ind).append(" throw new java.util.NoSuchElementException();\n") @@ -1037,13 +1079,15 @@ private static void generateGroupDecoderProperty( indent + " return " + propertyName + ";\n" + indent + " }\n\n"; + generateAccessOrderListenerMethodForGroupWrap(sb, fieldOrderModel, indent + " ", token); + generateFlyweightPropertyJavadoc(sb, indent + INDENT, token, className); new Formatter(sb).format("\n" + indent + " public %1$s %2$s()\n" + indent + " {\n" + "%3$s" + indent + " %2$s.wrap(buffer);\n" + - generateFieldOrderStateTransitions(fieldOrderModel, indent + " ", token, propertyName + ".count") + + generateAccessOrderListenerCall(fieldOrderModel, indent + " ", token, propertyName + ".count") + indent + " return %2$s;\n" + indent + " }\n", className, @@ -1079,11 +1123,13 @@ private void generateGroupEncoderProperty( formatPropertyName(groupName), token.id()); + generateAccessOrderListenerMethodForGroupWrap(sb, fieldOrderModel, indent + " ", token); + generateGroupEncodePropertyJavadoc(sb, indent + INDENT, token, className); new Formatter(sb).format("\n" + indent + " public %1$s %2$sCount(final int count)\n" + indent + " {\n" + - generateFieldOrderStateTransitions(fieldOrderModel, indent + " ", token, "count") + + generateAccessOrderListenerCall(fieldOrderModel, indent + " ", token, "count") + indent + " %2$s.wrap(buffer, count);\n" + indent + " return %2$s;\n" + indent + " }\n", @@ -1126,21 +1172,22 @@ private void generateDecoderVarData( .append(indent).append(" return ").append(sizeOfLengthField).append(";\n") .append(indent).append(" }\n"); - final CharSequence stateTransition = - generateFieldOrderStateTransitions(fieldOrderModel, indent + " ", token); + generateAccessOrderListenerMethod(sb, fieldOrderModel, indent + " ", token); + final CharSequence accessOrderListenerCall = + generateAccessOrderListenerCall(fieldOrderModel, indent + " ", token); sb.append("\n") .append(indent).append(" public int ").append(methodPropName).append("Length()\n") .append(indent).append(" {\n") .append(generateArrayFieldNotPresentCondition(token.version(), indent)) - .append(stateTransition) + .append(accessOrderListenerCall) .append(indent).append(" final int limit = parentMessage.limit();\n") .append(indent).append(" return ").append(PrimitiveType.UINT32 == lengthType ? "(int)" : "") .append(generateGet(lengthType, "limit", byteOrderStr)).append(";\n") .append(indent).append(" }\n"); generateDataDecodeMethods(sb, token, propertyName, sizeOfLengthField, lengthType, - byteOrderStr, characterEncoding, stateTransition, indent); + byteOrderStr, characterEncoding, accessOrderListenerCall, indent); i += token.componentTokenCount(); } @@ -1182,11 +1229,14 @@ private void generateEncoderVarData( .append(sizeOfLengthField).append(";\n") .append(indent).append(" }\n"); + generateAccessOrderListenerMethod(sb, fieldOrderModel, indent + " ", token); + final CharSequence accessOrderListenerCall = + generateAccessOrderListenerCall(fieldOrderModel, indent + " ", token); + generateDataEncodeMethods( sb, propertyName, - fieldOrderModel, - token, + accessOrderListenerCall, sizeOfLengthField, maxLengthValue, lengthEncoding.primitiveType(), @@ -1207,14 +1257,14 @@ private void generateDataDecodeMethods( final PrimitiveType lengthType, final String byteOrderStr, final String characterEncoding, - final CharSequence stateTransition, + final CharSequence accessOrderListenerCall, final String indent) { new Formatter(sb).format("\n" + indent + " public int skip%1$s()\n" + indent + " {\n" + "%2$s" + - stateTransition + + accessOrderListenerCall + indent + " final int headerLength = %3$d;\n" + indent + " final int limit = parentMessage.limit();\n" + indent + " final int dataLength = %4$s%5$s;\n" + @@ -1236,7 +1286,7 @@ private void generateDataDecodeMethods( mutableBuffer, lengthType, byteOrderStr, - stateTransition, + accessOrderListenerCall, indent); generateVarDataTypedDecoder( @@ -1247,7 +1297,7 @@ private void generateDataDecodeMethods( "byte[]", lengthType, byteOrderStr, - stateTransition, + accessOrderListenerCall, indent); generateVarDataWrapDecoder( @@ -1257,7 +1307,7 @@ private void generateDataDecodeMethods( sizeOfLengthField, lengthType, byteOrderStr, - stateTransition, + accessOrderListenerCall, indent); if (null != characterEncoding) @@ -1266,7 +1316,7 @@ private void generateDataDecodeMethods( indent + " public String %1$s()\n" + indent + " {\n" + "%2$s" + - stateTransition + + accessOrderListenerCall + indent + " final int headerLength = %3$d;\n" + indent + " final int limit = parentMessage.limit();\n" + indent + " final int dataLength = %4$s%5$s;\n" + @@ -1292,7 +1342,7 @@ private void generateDataDecodeMethods( indent + " public int get%1$s(final Appendable appendable)\n" + indent + " {\n" + "%2$s" + - stateTransition + + accessOrderListenerCall + indent + " final int headerLength = %3$d;\n" + indent + " final int limit = parentMessage.limit();\n" + indent + " final int dataLength = %4$s%5$s;\n" + @@ -1317,13 +1367,14 @@ private void generateVarDataWrapDecoder( final int sizeOfLengthField, final PrimitiveType lengthType, final String byteOrderStr, - final CharSequence stateTransition, final String indent) + final CharSequence accessOrderListenerCall, + final String indent) { new Formatter(sb).format("\n" + indent + " public void wrap%s(final %s wrapBuffer)\n" + indent + " {\n" + "%s" + - stateTransition + + accessOrderListenerCall + indent + " final int headerLength = %d;\n" + indent + " final int limit = parentMessage.limit();\n" + indent + " final int dataLength = %s%s;\n" + @@ -1341,8 +1392,7 @@ private void generateVarDataWrapDecoder( private void generateDataEncodeMethods( final StringBuilder sb, final String propertyName, - final FieldOrderModel fieldOrderModel, - final Token fieldToken, + final CharSequence accessOrderListenerCall, final int sizeOfLengthField, final int maxLengthValue, final PrimitiveType lengthType, @@ -1355,8 +1405,7 @@ private void generateDataEncodeMethods( sb, className, propertyName, - fieldOrderModel, - fieldToken, + accessOrderListenerCall, sizeOfLengthField, maxLengthValue, readOnlyBuffer, @@ -1368,8 +1417,7 @@ private void generateDataEncodeMethods( sb, className, propertyName, - fieldOrderModel, - fieldToken, + accessOrderListenerCall, sizeOfLengthField, maxLengthValue, "byte[]", @@ -1382,8 +1430,7 @@ private void generateDataEncodeMethods( generateCharArrayEncodeMethods( sb, propertyName, - fieldOrderModel, - fieldToken, + accessOrderListenerCall, sizeOfLengthField, maxLengthValue, lengthType, @@ -1397,8 +1444,7 @@ private void generateDataEncodeMethods( private void generateCharArrayEncodeMethods( final StringBuilder sb, final String propertyName, - final FieldOrderModel fieldOrderModel, - final Token fieldToken, + final CharSequence accessOrderListenerCall, final int sizeOfLengthField, final int maxLengthValue, final PrimitiveType lengthType, @@ -1419,7 +1465,7 @@ private void generateCharArrayEncodeMethods( indent + " {\n" + indent + " throw new IllegalStateException(\"length > maxValue for type: \" + length);\n" + indent + " }\n\n" + - generateFieldOrderStateTransitions(fieldOrderModel, indent + " ", fieldToken) + + accessOrderListenerCall + indent + " final int headerLength = %4$d;\n" + indent + " final int limit = parentMessage.limit();\n" + indent + " parentMessage.limit(limit + headerLength + length);\n" + @@ -1441,7 +1487,7 @@ private void generateCharArrayEncodeMethods( indent + " {\n" + indent + " throw new IllegalStateException(\"length > maxValue for type: \" + length);\n" + indent + " }\n\n" + - generateFieldOrderStateTransitions(fieldOrderModel, indent + " ", fieldToken) + + accessOrderListenerCall + indent + " final int headerLength = %4$d;\n" + indent + " final int limit = parentMessage.limit();\n" + indent + " parentMessage.limit(limit + headerLength + length);\n" + @@ -1467,7 +1513,7 @@ private void generateCharArrayEncodeMethods( indent + " {\n" + indent + " throw new IllegalStateException(\"length > maxValue for type: \" + length);\n" + indent + " }\n\n" + - generateFieldOrderStateTransitions(fieldOrderModel, indent + " ", fieldToken) + + accessOrderListenerCall + indent + " final int headerLength = %5$d;\n" + indent + " final int limit = parentMessage.limit();\n" + indent + " parentMessage.limit(limit + headerLength + length);\n" + @@ -1492,14 +1538,14 @@ private void generateVarDataTypedDecoder( final String exchangeType, final PrimitiveType lengthType, final String byteOrderStr, - final CharSequence stateTransition, + final CharSequence accessOrderListenerCall, final String indent) { new Formatter(sb).format("\n" + indent + " public int get%s(final %s dst, final int dstOffset, final int length)\n" + indent + " {\n" + "%s" + - stateTransition + + accessOrderListenerCall + indent + " final int headerLength = %d;\n" + indent + " final int limit = parentMessage.limit();\n" + indent + " final int dataLength = %s%s;\n" + @@ -1520,8 +1566,7 @@ private void generateDataTypedEncoder( final StringBuilder sb, final String className, final String propertyName, - final FieldOrderModel fieldOrderModel, - final Token fieldToken, + final CharSequence accessOrderListenerCall, final int sizeOfLengthField, final int maxLengthValue, final String exchangeType, @@ -1538,7 +1583,7 @@ private void generateDataTypedEncoder( indent + " {\n" + indent + " throw new IllegalStateException(\"length > maxValue for type: \" + length);\n" + indent + " }\n\n" + - generateFieldOrderStateTransitions(fieldOrderModel, indent + " ", fieldToken) + + accessOrderListenerCall + indent + " final int headerLength = %5$d;\n" + indent + " final int limit = parentMessage.limit();\n" + indent + " parentMessage.limit(limit + headerLength + length);\n" + @@ -1673,6 +1718,7 @@ private void generateComposite(final List tokens) throws IOException generateEncodingOffsetMethod(sb, propertyName, encodingToken.offset(), BASE_INDENT); generateEncodingLengthMethod(sb, propertyName, encodingToken.encodedLength(), BASE_INDENT); generateFieldSinceVersionMethod(sb, encodingToken, BASE_INDENT); + final String accessOrderListenerCall = ""; switch (encodingToken.signal()) { @@ -1687,12 +1733,14 @@ private void generateComposite(final List tokens) throws IOException case BEGIN_SET: generateBitSetProperty( - sb, true, DECODER, propertyName, null, encodingToken, encodingToken, BASE_INDENT, typeName); + sb, true, DECODER, propertyName, accessOrderListenerCall, + encodingToken, encodingToken, BASE_INDENT, typeName); break; case BEGIN_COMPOSITE: generateCompositeProperty( - sb, true, DECODER, propertyName, null, encodingToken, encodingToken, BASE_INDENT, typeName); + sb, true, DECODER, propertyName, accessOrderListenerCall, + encodingToken, encodingToken, BASE_INDENT, typeName); break; default: @@ -1724,27 +1772,30 @@ private void generateComposite(final List tokens) throws IOException final StringBuilder sb = new StringBuilder(); generateEncodingOffsetMethod(sb, propertyName, encodingToken.offset(), BASE_INDENT); generateEncodingLengthMethod(sb, propertyName, encodingToken.encodedLength(), BASE_INDENT); + final String accessOrderListenerCall = ""; switch (encodingToken.signal()) { case ENCODING: generatePrimitiveEncoder(sb, encoderName, encodingToken.name(), - null, null, encodingToken, BASE_INDENT); + accessOrderListenerCall, encodingToken, BASE_INDENT); break; case BEGIN_ENUM: - generateEnumEncoder(sb, encoderName, null, + generateEnumEncoder(sb, encoderName, accessOrderListenerCall, encodingToken, propertyName, encodingToken, BASE_INDENT); break; case BEGIN_SET: generateBitSetProperty( - sb, true, ENCODER, propertyName, null, encodingToken, encodingToken, BASE_INDENT, typeName); + sb, true, ENCODER, propertyName, accessOrderListenerCall, + encodingToken, encodingToken, BASE_INDENT, typeName); break; case BEGIN_COMPOSITE: generateCompositeProperty( - sb, true, ENCODER, propertyName, null, encodingToken, encodingToken, BASE_INDENT, typeName); + sb, true, ENCODER, propertyName, accessOrderListenerCall, + encodingToken, encodingToken, BASE_INDENT, typeName); break; default: @@ -2171,7 +2222,7 @@ private void generatePrimitiveDecoder( final StringBuilder sb, final boolean inComposite, final String propertyName, - final CharSequence stateTransition, + final CharSequence accessOrderListenerCall, final Token propertyToken, final Token encodingToken, final String indent) @@ -2187,7 +2238,7 @@ private void generatePrimitiveDecoder( else { sb.append(generatePrimitivePropertyDecodeMethods( - inComposite, formattedPropertyName, stateTransition, propertyToken, encodingToken, indent)); + inComposite, formattedPropertyName, accessOrderListenerCall, propertyToken, encodingToken, indent)); } } @@ -2195,8 +2246,7 @@ private void generatePrimitiveEncoder( final StringBuilder sb, final String containingClassName, final String propertyName, - final FieldOrderModel fieldOrderModel, - final Token fieldToken, + final CharSequence accessOrderListenerCall, final Token typeToken, final String indent) { @@ -2207,7 +2257,7 @@ private void generatePrimitiveEncoder( if (!typeToken.isConstantEncoding()) { sb.append(generatePrimitivePropertyEncodeMethods( - containingClassName, formattedPropertyName, fieldOrderModel, fieldToken, typeToken, indent)); + containingClassName, formattedPropertyName, accessOrderListenerCall, typeToken, indent)); } else { @@ -2218,31 +2268,30 @@ private void generatePrimitiveEncoder( private CharSequence generatePrimitivePropertyDecodeMethods( final boolean inComposite, final String propertyName, - final CharSequence stateTransition, + final CharSequence accessOrderListenerCall, final Token propertyToken, final Token encodingToken, final String indent) { return encodingToken.matchOnLength( () -> generatePrimitivePropertyDecode( - inComposite, propertyName, stateTransition, propertyToken, encodingToken, indent), + inComposite, propertyName, accessOrderListenerCall, propertyToken, encodingToken, indent), () -> generatePrimitiveArrayPropertyDecode( - inComposite, propertyName, stateTransition, propertyToken, encodingToken, indent)); + inComposite, propertyName, accessOrderListenerCall, propertyToken, encodingToken, indent)); } private CharSequence generatePrimitivePropertyEncodeMethods( final String containingClassName, final String propertyName, - final FieldOrderModel fieldOrderModel, - final Token fieldToken, + final CharSequence accessOrderListenerCall, final Token typeToken, final String indent) { return typeToken.matchOnLength( () -> generatePrimitivePropertyEncode( - containingClassName, propertyName, fieldOrderModel, fieldToken, typeToken, indent), + containingClassName, propertyName, accessOrderListenerCall, typeToken, indent), () -> generatePrimitiveArrayPropertyEncode( - containingClassName, propertyName, fieldOrderModel, fieldToken, typeToken, indent)); + containingClassName, propertyName, accessOrderListenerCall, typeToken, indent)); } private void generatePrimitiveFieldMetaMethod( @@ -2281,7 +2330,7 @@ private void generatePrimitiveFieldMetaMethod( private CharSequence generatePrimitivePropertyDecode( final boolean inComposite, final String propertyName, - final CharSequence stateTransition, + final CharSequence accessOrderListenerCall, final Token propertyToken, final Token encodingToken, final String indent) @@ -2297,7 +2346,7 @@ private CharSequence generatePrimitivePropertyDecode( indent + " public %s %s()\n" + indent + " {\n" + "%s" + - stateTransition + + accessOrderListenerCall + indent + " return %s;\n" + indent + " }\n\n", javaTypeName, @@ -2309,8 +2358,7 @@ private CharSequence generatePrimitivePropertyDecode( private CharSequence generatePrimitivePropertyEncode( final String containingClassName, final String propertyName, - final FieldOrderModel fieldOrderModel, - final Token fieldToken, + final CharSequence accessOrderListenerCall, final Token typeToken, final String indent) { @@ -2323,7 +2371,7 @@ private CharSequence generatePrimitivePropertyEncode( "\n" + indent + " public %s %s(final %s value)\n" + indent + " {\n" + - generateFieldOrderStateTransitions(fieldOrderModel, indent + " ", fieldToken) + + accessOrderListenerCall + indent + " %s;\n" + indent + " return this;\n" + indent + " }\n\n", @@ -2430,7 +2478,7 @@ private static CharSequence generatePropertyNotPresentCondition( private CharSequence generatePrimitiveArrayPropertyDecode( final boolean inComposite, final String propertyName, - final CharSequence stateTransition, + final CharSequence accessOrderListenerCall, final Token propertyToken, final Token encodingToken, final String indent) @@ -2454,7 +2502,7 @@ private CharSequence generatePrimitiveArrayPropertyDecode( indent + " throw new IndexOutOfBoundsException(\"index out of range: index=\" + index);\n" + indent + " }\n\n" + "%s" + - stateTransition + + accessOrderListenerCall + indent + " final int pos = offset + %d + (index * %d);\n\n" + indent + " return %s;\n" + indent + " }\n\n", @@ -2480,7 +2528,7 @@ private CharSequence generatePrimitiveArrayPropertyDecode( "\"Copy will go out of range: offset=\" + dstOffset);\n" + indent + " }\n\n" + "%s" + - stateTransition + + accessOrderListenerCall + indent + " buffer.getBytes(offset + %d, dst, dstOffset, length);\n\n" + indent + " return length;\n" + indent + " }\n", @@ -2493,7 +2541,7 @@ private CharSequence generatePrimitiveArrayPropertyDecode( indent + " public String %s()\n" + indent + " {\n" + "%s" + - stateTransition + + accessOrderListenerCall + indent + " final byte[] dst = new byte[%d];\n" + indent + " buffer.getBytes(offset + %d, dst, 0, %d);\n\n" + indent + " int end = 0;\n" + @@ -2514,7 +2562,7 @@ private CharSequence generatePrimitiveArrayPropertyDecode( indent + " public int get%1$s(final Appendable value)\n" + indent + " {\n" + "%2$s" + - stateTransition + + accessOrderListenerCall + indent + " for (int i = 0; i < %3$d; ++i)\n" + indent + " {\n" + indent + " final int c = buffer.getByte(offset + %4$d + i) & 0xFF;\n" + @@ -2545,7 +2593,7 @@ else if (encoding.primitiveType() == PrimitiveType.UINT8) indent + " public int get%s(final byte[] dst, final int dstOffset, final int length)\n" + indent + " {\n" + "%s" + - stateTransition + + accessOrderListenerCall + indent + " final int bytesCopied = Math.min(length, %d);\n" + indent + " buffer.getBytes(offset + %d, dst, dstOffset, bytesCopied);\n\n" + indent + " return bytesCopied;\n" + @@ -2559,7 +2607,7 @@ else if (encoding.primitiveType() == PrimitiveType.UINT8) indent + " public int get%s(final %s dst, final int dstOffset, final int length)\n" + indent + " {\n" + "%s" + - stateTransition + + accessOrderListenerCall + indent + " final int bytesCopied = Math.min(length, %d);\n" + indent + " buffer.getBytes(offset + %d, dst, dstOffset, bytesCopied);\n\n" + indent + " return bytesCopied;\n" + @@ -2574,7 +2622,7 @@ else if (encoding.primitiveType() == PrimitiveType.UINT8) indent + " public void wrap%s(final %s wrapBuffer)\n" + indent + " {\n" + "%s" + - stateTransition + + accessOrderListenerCall + indent + " wrapBuffer.wrap(buffer, offset + %d, %d);\n" + indent + " }\n", Generators.toUpperFirstChar(propertyName), @@ -2606,8 +2654,7 @@ private String byteOrderString(final Encoding encoding) private CharSequence generatePrimitiveArrayPropertyEncode( final String containingClassName, final String propertyName, - final FieldOrderModel fieldOrderModel, - final Token fieldToken, + final CharSequence accessOrderListenerCall, final Token typeToken, final String indent) { @@ -2627,11 +2674,11 @@ private CharSequence generatePrimitiveArrayPropertyEncode( new Formatter(sb).format("\n" + indent + " public %s %s(final int index, final %s value)\n" + indent + " {\n" + - generateFieldOrderStateTransitions(fieldOrderModel, indent + " ", fieldToken) + indent + " if (index < 0 || index >= %d)\n" + indent + " {\n" + indent + " throw new IndexOutOfBoundsException(\"index out of range: index=\" + index);\n" + indent + " }\n\n" + + accessOrderListenerCall + indent + " final int pos = offset + %d + (index * %d);\n" + indent + " %s;\n\n" + indent + " return this;\n" + @@ -2660,7 +2707,7 @@ private CharSequence generatePrimitiveArrayPropertyEncode( sb.append(")\n"); sb.append(indent).append(" {\n"); - sb.append(generateFieldOrderStateTransitions(fieldOrderModel, indent + " ", fieldToken)); + sb.append(accessOrderListenerCall); for (int i = 0; i < arrayLength; i++) { @@ -2682,8 +2729,7 @@ private CharSequence generatePrimitiveArrayPropertyEncode( containingClassName, propertyName, indent, - fieldOrderModel, - fieldToken, + accessOrderListenerCall, encoding, offset, arrayLength, @@ -2695,8 +2741,7 @@ else if (primitiveType == PrimitiveType.UINT8) containingClassName, propertyName, indent, - fieldOrderModel, - fieldToken, + accessOrderListenerCall, offset, arrayLength, sb); @@ -2709,8 +2754,7 @@ private void generateCharArrayEncodeMethods( final String containingClassName, final String propertyName, final String indent, - final FieldOrderModel fieldOrderModel, - final Token fieldToken, + final CharSequence accessOrderListenerCall, final Encoding encoding, final int offset, final int fieldLength, @@ -2721,13 +2765,13 @@ private void generateCharArrayEncodeMethods( new Formatter(sb).format("\n" + indent + " public %s put%s(final byte[] src, final int srcOffset)\n" + indent + " {\n" + - generateFieldOrderStateTransitions(fieldOrderModel, indent + " ", fieldToken) + indent + " final int length = %d;\n" + indent + " if (srcOffset < 0 || srcOffset > (src.length - length))\n" + indent + " {\n" + indent + " throw new IndexOutOfBoundsException(" + "\"Copy will go out of range: offset=\" + srcOffset);\n" + indent + " }\n\n" + + accessOrderListenerCall + indent + " buffer.putBytes(offset + %d, src, srcOffset, length);\n\n" + indent + " return this;\n" + indent + " }\n", @@ -2741,7 +2785,6 @@ private void generateCharArrayEncodeMethods( new Formatter(sb).format("\n" + indent + " public %1$s %2$s(final String src)\n" + indent + " {\n" + - generateFieldOrderStateTransitions(fieldOrderModel, indent + " ", fieldToken) + indent + " final int length = %3$d;\n" + indent + " final int srcLength = null == src ? 0 : src.length();\n" + indent + " if (srcLength > length)\n" + @@ -2749,6 +2792,7 @@ private void generateCharArrayEncodeMethods( indent + " throw new IndexOutOfBoundsException(" + "\"String too large for copy: byte length=\" + srcLength);\n" + indent + " }\n\n" + + accessOrderListenerCall + indent + " buffer.putStringWithoutLengthAscii(offset + %4$d, src);\n\n" + indent + " for (int start = srcLength; start < length; ++start)\n" + indent + " {\n" + @@ -2764,7 +2808,6 @@ private void generateCharArrayEncodeMethods( new Formatter(sb).format("\n" + indent + " public %1$s %2$s(final CharSequence src)\n" + indent + " {\n" + - generateFieldOrderStateTransitions(fieldOrderModel, indent + " ", fieldToken) + indent + " final int length = %3$d;\n" + indent + " final int srcLength = null == src ? 0 : src.length();\n" + indent + " if (srcLength > length)\n" + @@ -2772,6 +2815,7 @@ private void generateCharArrayEncodeMethods( indent + " throw new IndexOutOfBoundsException(" + "\"CharSequence too large for copy: byte length=\" + srcLength);\n" + indent + " }\n\n" + + accessOrderListenerCall + indent + " buffer.putStringWithoutLengthAscii(offset + %4$d, src);\n\n" + indent + " for (int start = srcLength; start < length; ++start)\n" + indent + " {\n" + @@ -2789,7 +2833,6 @@ private void generateCharArrayEncodeMethods( new Formatter(sb).format("\n" + indent + " public %s %s(final String src)\n" + indent + " {\n" + - generateFieldOrderStateTransitions(fieldOrderModel, indent + " ", fieldToken) + indent + " final int length = %d;\n" + indent + " final byte[] bytes = (null == src || src.isEmpty()) ?" + " org.agrona.collections.ArrayUtil.EMPTY_BYTE_ARRAY : src.getBytes(%s);\n" + @@ -2798,6 +2841,7 @@ private void generateCharArrayEncodeMethods( indent + " throw new IndexOutOfBoundsException(" + "\"String too large for copy: byte length=\" + bytes.length);\n" + indent + " }\n\n" + + accessOrderListenerCall + indent + " buffer.putBytes(offset + %d, bytes, 0, bytes.length);\n\n" + indent + " for (int start = bytes.length; start < length; ++start)\n" + indent + " {\n" + @@ -2818,8 +2862,7 @@ private void generateByteArrayEncodeMethods( final String containingClassName, final String propertyName, final String indent, - final FieldOrderModel fieldOrderModel, - final Token fieldToken, + final CharSequence accessOrderListenerCall, final int offset, final int fieldLength, final StringBuilder sb) @@ -2827,12 +2870,12 @@ private void generateByteArrayEncodeMethods( new Formatter(sb).format("\n" + indent + " public %s put%s(final byte[] src, final int srcOffset, final int length)\n" + indent + " {\n" + - generateFieldOrderStateTransitions(fieldOrderModel, indent + " ", fieldToken) + indent + " if (length > %d)\n" + indent + " {\n" + indent + " throw new IllegalStateException(" + "\"length > maxValue for type: \" + length);\n" + indent + " }\n\n" + + accessOrderListenerCall + indent + " buffer.putBytes(offset + %d, src, srcOffset, length);\n" + indent + " for (int i = length; i < %d; i++)\n" + indent + " {\n" + @@ -2850,12 +2893,12 @@ private void generateByteArrayEncodeMethods( new Formatter(sb).format("\n" + indent + " public %s put%s(final %s src, final int srcOffset, final int length)\n" + indent + " {\n" + - generateFieldOrderStateTransitions(fieldOrderModel, indent + " ", fieldToken) + indent + " if (length > %d)\n" + indent + " {\n" + indent + " throw new IllegalStateException(" + "\"length > maxValue for type: \" + length);\n" + indent + " }\n\n" + + accessOrderListenerCall + indent + " buffer.putBytes(offset + %d, src, srcOffset, length);\n" + indent + " for (int i = length; i < %d; i++)\n" + indent + " {\n" + @@ -3048,6 +3091,7 @@ private CharSequence generateDecoderFlyweightCode( final String headerClassName = formatClassName(ir.headerStructure().tokens().get(0).applicableTypeName()); final String methods = + generateDecoderWrapListener(fieldOrderModel, " ") + " public " + className + " wrap(\n" + " final " + readOnlyBuffer + " buffer,\n" + " final int offset,\n" + @@ -3063,7 +3107,10 @@ private CharSequence generateDecoderFlyweightCode( " this.actingBlockLength = actingBlockLength;\n" + " this.actingVersion = actingVersion;\n" + " limit(offset + actingBlockLength);\n\n" + - generateFieldOrderStateTransitionForDecoderWrap(fieldOrderModel, " ", "actingVersion") + + " if (ENABLE_ACCESS_ORDER_CHECKS)\n" + + " {\n" + + " onWrap(actingVersion);\n" + + " }\n\n" + " return this;\n" + " }\n\n" + @@ -3300,27 +3347,32 @@ private void generateEncoderFields( generateEncodingOffsetMethod(sb, propertyName, fieldToken.offset(), indent); generateEncodingLengthMethod(sb, propertyName, typeToken.encodedLength(), indent); generateFieldMetaAttributeMethod(sb, fieldToken, indent); + generateAccessOrderListenerMethod(sb, fieldOrderModel, indent + " ", fieldToken); + final CharSequence accessOrderListenerCall = generateAccessOrderListenerCall( + fieldOrderModel, indent + " ", fieldToken); switch (typeToken.signal()) { case ENCODING: generatePrimitiveEncoder(sb, containingClassName, propertyName, - fieldOrderModel, fieldToken, typeToken, indent); + accessOrderListenerCall, typeToken, indent); break; case BEGIN_ENUM: generateEnumEncoder(sb, containingClassName, - fieldOrderModel, fieldToken, propertyName, typeToken, indent); + accessOrderListenerCall, fieldToken, propertyName, typeToken, indent); break; case BEGIN_SET: generateBitSetProperty( - sb, false, ENCODER, propertyName, fieldOrderModel, fieldToken, typeToken, indent, typeName); + sb, false, ENCODER, propertyName, accessOrderListenerCall, + fieldToken, typeToken, indent, typeName); break; case BEGIN_COMPOSITE: generateCompositeProperty( - sb, false, ENCODER, propertyName, fieldOrderModel, fieldToken, typeToken, indent, typeName); + sb, false, ENCODER, propertyName, accessOrderListenerCall, + fieldToken, typeToken, indent, typeName); break; default: @@ -3348,29 +3400,32 @@ private void generateDecoderFields( generateEncodingLengthMethod(sb, propertyName, typeToken.encodedLength(), indent); generateFieldMetaAttributeMethod(sb, fieldToken, indent); - final CharSequence stateTransition = generateFieldOrderStateTransitions( + generateAccessOrderListenerMethod(sb, fieldOrderModel, indent + " ", fieldToken); + final CharSequence accessOrderListenerCall = generateAccessOrderListenerCall( fieldOrderModel, indent + " ", fieldToken); switch (typeToken.signal()) { case ENCODING: generatePrimitiveDecoder( - sb, false, propertyName, stateTransition, fieldToken, typeToken, indent); + sb, false, propertyName, accessOrderListenerCall, fieldToken, typeToken, indent); break; case BEGIN_ENUM: generateEnumDecoder( - sb, false, stateTransition, fieldToken, propertyName, typeToken, indent); + sb, false, accessOrderListenerCall, fieldToken, propertyName, typeToken, indent); break; case BEGIN_SET: generateBitSetProperty( - sb, false, DECODER, propertyName, fieldOrderModel, fieldToken, typeToken, indent, typeName); + sb, false, DECODER, propertyName, accessOrderListenerCall, + fieldToken, typeToken, indent, typeName); break; case BEGIN_COMPOSITE: generateCompositeProperty( - sb, false, DECODER, propertyName, fieldOrderModel, fieldToken, typeToken, indent, typeName); + sb, false, DECODER, propertyName, accessOrderListenerCall, + fieldToken, typeToken, indent, typeName); break; default: @@ -3471,7 +3526,7 @@ private static void generateFieldMetaAttributeMethod(final StringBuilder sb, fin private void generateEnumDecoder( final StringBuilder sb, final boolean inComposite, - final CharSequence stateTransition, + final CharSequence accessOrderListenerCall, final Token fieldToken, final String propertyName, final Token typeToken, @@ -3515,7 +3570,7 @@ private void generateEnumDecoder( indent + " public %s %sRaw()\n" + indent + " {\n" + "%s" + - stateTransition + + accessOrderListenerCall + indent + " return %s;\n" + indent + " }\n", javaTypeName, @@ -3528,7 +3583,7 @@ private void generateEnumDecoder( indent + " public %s %s()\n" + indent + " {\n" + "%s" + - stateTransition + + accessOrderListenerCall + indent + " return %s.get(%s);\n" + indent + " }\n\n", enumName, @@ -3542,7 +3597,7 @@ private void generateEnumDecoder( private void generateEnumEncoder( final StringBuilder sb, final String containingClassName, - final FieldOrderModel fieldOrderModel, + final CharSequence accessOrderListenerCall, final Token fieldToken, final String propertyName, final Token typeToken, @@ -3558,7 +3613,7 @@ private void generateEnumEncoder( new Formatter(sb).format("\n" + indent + " public %s %s(final %s value)\n" + indent + " {\n" + - generateFieldOrderStateTransitions(fieldOrderModel, indent + " ", fieldToken) + + accessOrderListenerCall + indent + " %s;\n" + indent + " return this;\n" + indent + " }\n", @@ -3574,7 +3629,7 @@ private void generateBitSetProperty( final boolean inComposite, final CodecType codecType, final String propertyName, - final FieldOrderModel fieldOrderModel, + final CharSequence accessOrderListenerCall, final Token propertyToken, final Token bitsetToken, final String indent, @@ -3591,7 +3646,7 @@ private void generateBitSetProperty( indent + " public %s %s()\n" + indent + " {\n" + "%s" + - generateFieldOrderStateTransitions(fieldOrderModel, indent + " ", propertyToken) + + accessOrderListenerCall + indent + " %s.wrap(buffer, offset + %d);\n" + indent + " return %s;\n" + indent + " }\n", @@ -3608,7 +3663,7 @@ private void generateCompositeProperty( final boolean inComposite, final CodecType codecType, final String propertyName, - final FieldOrderModel fieldOrderModel, + final CharSequence accessOrderListenerCall, final Token propertyToken, final Token compositeToken, final String indent, @@ -3625,7 +3680,7 @@ private void generateCompositeProperty( indent + " public %s %s()\n" + indent + " {\n" + "%s" + - generateFieldOrderStateTransitions(fieldOrderModel, indent + " ", propertyToken) + + accessOrderListenerCall + indent + " %s.wrap(buffer, offset + %d);\n" + indent + " return %s;\n" + indent + " }\n", diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java index bb92a28ffb..4ca802ae6a 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java @@ -111,6 +111,19 @@ public int offset() return offset; } + private void onWrap(final int actingVersion) + { + switch(actingVersion) + { + case 0: + codecState(CodecState.V0_BLOCK); + break; + default: + codecState(CodecState.V0_BLOCK); + break; + } + } + public FrameCodecDecoder wrap( final DirectBuffer buffer, final int offset, @@ -129,14 +142,7 @@ public FrameCodecDecoder wrap( if (ENABLE_ACCESS_ORDER_CHECKS) { - switch(actingVersion) { - case 0: - codecState(CodecState.V0_BLOCK); - break; - default: - codecState(CodecState.V0_BLOCK); - break; - } + onWrap(actingVersion); } return this; @@ -233,6 +239,14 @@ public static String irIdMetaAttribute(final MetaAttribute metaAttribute) return ""; } + private void onIrIdAccessed() + { + if (codecState() == CodecState.NOT_WRAPPED) + { + throw new IllegalStateException("Cannot access field \"irId\" in state: " + codecState()); + } + } + public static int irIdNullValue() { return -2147483648; @@ -252,10 +266,7 @@ public int irId() { if (ENABLE_ACCESS_ORDER_CHECKS) { - if (codecState() == CodecState.NOT_WRAPPED) - { - throw new IllegalStateException("Cannot access field \"irId\" in state: " + codecState()); - } + onIrIdAccessed(); } return buffer.getInt(offset + 0, java.nio.ByteOrder.LITTLE_ENDIAN); @@ -292,6 +303,14 @@ public static String irVersionMetaAttribute(final MetaAttribute metaAttribute) return ""; } + private void onIrVersionAccessed() + { + if (codecState() == CodecState.NOT_WRAPPED) + { + throw new IllegalStateException("Cannot access field \"irVersion\" in state: " + codecState()); + } + } + public static int irVersionNullValue() { return -2147483648; @@ -311,10 +330,7 @@ public int irVersion() { if (ENABLE_ACCESS_ORDER_CHECKS) { - if (codecState() == CodecState.NOT_WRAPPED) - { - throw new IllegalStateException("Cannot access field \"irVersion\" in state: " + codecState()); - } + onIrVersionAccessed(); } return buffer.getInt(offset + 4, java.nio.ByteOrder.LITTLE_ENDIAN); @@ -351,6 +367,14 @@ public static String schemaVersionMetaAttribute(final MetaAttribute metaAttribut return ""; } + private void onSchemaVersionAccessed() + { + if (codecState() == CodecState.NOT_WRAPPED) + { + throw new IllegalStateException("Cannot access field \"schemaVersion\" in state: " + codecState()); + } + } + public static int schemaVersionNullValue() { return -2147483648; @@ -370,10 +394,7 @@ public int schemaVersion() { if (ENABLE_ACCESS_ORDER_CHECKS) { - if (codecState() == CodecState.NOT_WRAPPED) - { - throw new IllegalStateException("Cannot access field \"schemaVersion\" in state: " + codecState()); - } + onSchemaVersionAccessed(); } return buffer.getInt(offset + 8, java.nio.ByteOrder.LITTLE_ENDIAN); @@ -410,18 +431,23 @@ public static int packageNameHeaderLength() return 2; } + private void onPackageNameAccessed() + { + switch (codecState()) + { + case V0_BLOCK: + codecState(CodecState.V0_PACKAGENAME_DONE); + break; + default: + throw new IllegalStateException("Cannot access field \"packageName\" in state: " + codecState()); + } + } + public int packageNameLength() { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_BLOCK: - codecState(CodecState.V0_PACKAGENAME_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"packageName\" in state: " + codecState()); - } + onPackageNameAccessed(); } final int limit = parentMessage.limit(); @@ -432,14 +458,7 @@ public int skipPackageName() { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_BLOCK: - codecState(CodecState.V0_PACKAGENAME_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"packageName\" in state: " + codecState()); - } + onPackageNameAccessed(); } final int headerLength = 2; @@ -455,14 +474,7 @@ public int getPackageName(final MutableDirectBuffer dst, final int dstOffset, fi { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_BLOCK: - codecState(CodecState.V0_PACKAGENAME_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"packageName\" in state: " + codecState()); - } + onPackageNameAccessed(); } final int headerLength = 2; @@ -479,14 +491,7 @@ public int getPackageName(final byte[] dst, final int dstOffset, final int lengt { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_BLOCK: - codecState(CodecState.V0_PACKAGENAME_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"packageName\" in state: " + codecState()); - } + onPackageNameAccessed(); } final int headerLength = 2; @@ -503,14 +508,7 @@ public void wrapPackageName(final DirectBuffer wrapBuffer) { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_BLOCK: - codecState(CodecState.V0_PACKAGENAME_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"packageName\" in state: " + codecState()); - } + onPackageNameAccessed(); } final int headerLength = 2; @@ -524,14 +522,7 @@ public String packageName() { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_BLOCK: - codecState(CodecState.V0_PACKAGENAME_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"packageName\" in state: " + codecState()); - } + onPackageNameAccessed(); } final int headerLength = 2; @@ -580,18 +571,23 @@ public static int namespaceNameHeaderLength() return 2; } + private void onNamespaceNameAccessed() + { + switch (codecState()) + { + case V0_PACKAGENAME_DONE: + codecState(CodecState.V0_NAMESPACENAME_DONE); + break; + default: + throw new IllegalStateException("Cannot access field \"namespaceName\" in state: " + codecState()); + } + } + public int namespaceNameLength() { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_PACKAGENAME_DONE: - codecState(CodecState.V0_NAMESPACENAME_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"namespaceName\" in state: " + codecState()); - } + onNamespaceNameAccessed(); } final int limit = parentMessage.limit(); @@ -602,14 +598,7 @@ public int skipNamespaceName() { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_PACKAGENAME_DONE: - codecState(CodecState.V0_NAMESPACENAME_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"namespaceName\" in state: " + codecState()); - } + onNamespaceNameAccessed(); } final int headerLength = 2; @@ -625,14 +614,7 @@ public int getNamespaceName(final MutableDirectBuffer dst, final int dstOffset, { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_PACKAGENAME_DONE: - codecState(CodecState.V0_NAMESPACENAME_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"namespaceName\" in state: " + codecState()); - } + onNamespaceNameAccessed(); } final int headerLength = 2; @@ -649,14 +631,7 @@ public int getNamespaceName(final byte[] dst, final int dstOffset, final int len { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_PACKAGENAME_DONE: - codecState(CodecState.V0_NAMESPACENAME_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"namespaceName\" in state: " + codecState()); - } + onNamespaceNameAccessed(); } final int headerLength = 2; @@ -673,14 +648,7 @@ public void wrapNamespaceName(final DirectBuffer wrapBuffer) { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_PACKAGENAME_DONE: - codecState(CodecState.V0_NAMESPACENAME_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"namespaceName\" in state: " + codecState()); - } + onNamespaceNameAccessed(); } final int headerLength = 2; @@ -694,14 +662,7 @@ public String namespaceName() { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_PACKAGENAME_DONE: - codecState(CodecState.V0_NAMESPACENAME_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"namespaceName\" in state: " + codecState()); - } + onNamespaceNameAccessed(); } final int headerLength = 2; @@ -750,18 +711,23 @@ public static int semanticVersionHeaderLength() return 2; } + private void onSemanticVersionAccessed() + { + switch (codecState()) + { + case V0_NAMESPACENAME_DONE: + codecState(CodecState.V0_SEMANTICVERSION_DONE); + break; + default: + throw new IllegalStateException("Cannot access field \"semanticVersion\" in state: " + codecState()); + } + } + public int semanticVersionLength() { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_NAMESPACENAME_DONE: - codecState(CodecState.V0_SEMANTICVERSION_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"semanticVersion\" in state: " + codecState()); - } + onSemanticVersionAccessed(); } final int limit = parentMessage.limit(); @@ -772,14 +738,7 @@ public int skipSemanticVersion() { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_NAMESPACENAME_DONE: - codecState(CodecState.V0_SEMANTICVERSION_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"semanticVersion\" in state: " + codecState()); - } + onSemanticVersionAccessed(); } final int headerLength = 2; @@ -795,14 +754,7 @@ public int getSemanticVersion(final MutableDirectBuffer dst, final int dstOffset { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_NAMESPACENAME_DONE: - codecState(CodecState.V0_SEMANTICVERSION_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"semanticVersion\" in state: " + codecState()); - } + onSemanticVersionAccessed(); } final int headerLength = 2; @@ -819,14 +771,7 @@ public int getSemanticVersion(final byte[] dst, final int dstOffset, final int l { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_NAMESPACENAME_DONE: - codecState(CodecState.V0_SEMANTICVERSION_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"semanticVersion\" in state: " + codecState()); - } + onSemanticVersionAccessed(); } final int headerLength = 2; @@ -843,14 +788,7 @@ public void wrapSemanticVersion(final DirectBuffer wrapBuffer) { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_NAMESPACENAME_DONE: - codecState(CodecState.V0_SEMANTICVERSION_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"semanticVersion\" in state: " + codecState()); - } + onSemanticVersionAccessed(); } final int headerLength = 2; @@ -864,14 +802,7 @@ public String semanticVersion() { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_NAMESPACENAME_DONE: - codecState(CodecState.V0_SEMANTICVERSION_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"semanticVersion\" in state: " + codecState()); - } + onSemanticVersionAccessed(); } final int headerLength = 2; diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java index b49bed53a1..0f6b8dfc22 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java @@ -185,6 +185,14 @@ public static String irIdMetaAttribute(final MetaAttribute metaAttribute) return ""; } + private void onIrIdAccessed() + { + if (codecState() == CodecState.NOT_WRAPPED) + { + throw new IllegalStateException("Cannot access field \"irId\" in state: " + codecState()); + } + } + public static int irIdNullValue() { return -2147483648; @@ -204,10 +212,7 @@ public FrameCodecEncoder irId(final int value) { if (ENABLE_ACCESS_ORDER_CHECKS) { - if (codecState() == CodecState.NOT_WRAPPED) - { - throw new IllegalStateException("Cannot access field \"irId\" in state: " + codecState()); - } + onIrIdAccessed(); } buffer.putInt(offset + 0, value, java.nio.ByteOrder.LITTLE_ENDIAN); @@ -245,6 +250,14 @@ public static String irVersionMetaAttribute(final MetaAttribute metaAttribute) return ""; } + private void onIrVersionAccessed() + { + if (codecState() == CodecState.NOT_WRAPPED) + { + throw new IllegalStateException("Cannot access field \"irVersion\" in state: " + codecState()); + } + } + public static int irVersionNullValue() { return -2147483648; @@ -264,10 +277,7 @@ public FrameCodecEncoder irVersion(final int value) { if (ENABLE_ACCESS_ORDER_CHECKS) { - if (codecState() == CodecState.NOT_WRAPPED) - { - throw new IllegalStateException("Cannot access field \"irVersion\" in state: " + codecState()); - } + onIrVersionAccessed(); } buffer.putInt(offset + 4, value, java.nio.ByteOrder.LITTLE_ENDIAN); @@ -305,6 +315,14 @@ public static String schemaVersionMetaAttribute(final MetaAttribute metaAttribut return ""; } + private void onSchemaVersionAccessed() + { + if (codecState() == CodecState.NOT_WRAPPED) + { + throw new IllegalStateException("Cannot access field \"schemaVersion\" in state: " + codecState()); + } + } + public static int schemaVersionNullValue() { return -2147483648; @@ -324,10 +342,7 @@ public FrameCodecEncoder schemaVersion(final int value) { if (ENABLE_ACCESS_ORDER_CHECKS) { - if (codecState() == CodecState.NOT_WRAPPED) - { - throw new IllegalStateException("Cannot access field \"schemaVersion\" in state: " + codecState()); - } + onSchemaVersionAccessed(); } buffer.putInt(offset + 8, value, java.nio.ByteOrder.LITTLE_ENDIAN); @@ -360,6 +375,18 @@ public static int packageNameHeaderLength() return 2; } + private void onPackageNameAccessed() + { + switch (codecState()) + { + case V0_BLOCK: + codecState(CodecState.V0_PACKAGENAME_DONE); + break; + default: + throw new IllegalStateException("Cannot access field \"packageName\" in state: " + codecState()); + } + } + public FrameCodecEncoder putPackageName(final DirectBuffer src, final int srcOffset, final int length) { if (length > 65534) @@ -369,14 +396,7 @@ public FrameCodecEncoder putPackageName(final DirectBuffer src, final int srcOff if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_BLOCK: - codecState(CodecState.V0_PACKAGENAME_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"packageName\" in state: " + codecState()); - } + onPackageNameAccessed(); } final int headerLength = 2; @@ -397,14 +417,7 @@ public FrameCodecEncoder putPackageName(final byte[] src, final int srcOffset, f if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_BLOCK: - codecState(CodecState.V0_PACKAGENAME_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"packageName\" in state: " + codecState()); - } + onPackageNameAccessed(); } final int headerLength = 2; @@ -428,14 +441,7 @@ public FrameCodecEncoder packageName(final String value) if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_BLOCK: - codecState(CodecState.V0_PACKAGENAME_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"packageName\" in state: " + codecState()); - } + onPackageNameAccessed(); } final int headerLength = 2; @@ -472,6 +478,18 @@ public static int namespaceNameHeaderLength() return 2; } + private void onNamespaceNameAccessed() + { + switch (codecState()) + { + case V0_PACKAGENAME_DONE: + codecState(CodecState.V0_NAMESPACENAME_DONE); + break; + default: + throw new IllegalStateException("Cannot access field \"namespaceName\" in state: " + codecState()); + } + } + public FrameCodecEncoder putNamespaceName(final DirectBuffer src, final int srcOffset, final int length) { if (length > 65534) @@ -481,14 +499,7 @@ public FrameCodecEncoder putNamespaceName(final DirectBuffer src, final int srcO if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_PACKAGENAME_DONE: - codecState(CodecState.V0_NAMESPACENAME_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"namespaceName\" in state: " + codecState()); - } + onNamespaceNameAccessed(); } final int headerLength = 2; @@ -509,14 +520,7 @@ public FrameCodecEncoder putNamespaceName(final byte[] src, final int srcOffset, if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_PACKAGENAME_DONE: - codecState(CodecState.V0_NAMESPACENAME_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"namespaceName\" in state: " + codecState()); - } + onNamespaceNameAccessed(); } final int headerLength = 2; @@ -540,14 +544,7 @@ public FrameCodecEncoder namespaceName(final String value) if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_PACKAGENAME_DONE: - codecState(CodecState.V0_NAMESPACENAME_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"namespaceName\" in state: " + codecState()); - } + onNamespaceNameAccessed(); } final int headerLength = 2; @@ -584,6 +581,18 @@ public static int semanticVersionHeaderLength() return 2; } + private void onSemanticVersionAccessed() + { + switch (codecState()) + { + case V0_NAMESPACENAME_DONE: + codecState(CodecState.V0_SEMANTICVERSION_DONE); + break; + default: + throw new IllegalStateException("Cannot access field \"semanticVersion\" in state: " + codecState()); + } + } + public FrameCodecEncoder putSemanticVersion(final DirectBuffer src, final int srcOffset, final int length) { if (length > 65534) @@ -593,14 +602,7 @@ public FrameCodecEncoder putSemanticVersion(final DirectBuffer src, final int sr if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_NAMESPACENAME_DONE: - codecState(CodecState.V0_SEMANTICVERSION_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"semanticVersion\" in state: " + codecState()); - } + onSemanticVersionAccessed(); } final int headerLength = 2; @@ -621,14 +623,7 @@ public FrameCodecEncoder putSemanticVersion(final byte[] src, final int srcOffse if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_NAMESPACENAME_DONE: - codecState(CodecState.V0_SEMANTICVERSION_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"semanticVersion\" in state: " + codecState()); - } + onSemanticVersionAccessed(); } final int headerLength = 2; @@ -652,14 +647,7 @@ public FrameCodecEncoder semanticVersion(final String value) if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_NAMESPACENAME_DONE: - codecState(CodecState.V0_SEMANTICVERSION_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"semanticVersion\" in state: " + codecState()); - } + onSemanticVersionAccessed(); } final int headerLength = 2; diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java index 5be665a181..1e0494c66a 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java @@ -134,6 +134,19 @@ public int offset() return offset; } + private void onWrap(final int actingVersion) + { + switch(actingVersion) + { + case 0: + codecState(CodecState.V0_BLOCK); + break; + default: + codecState(CodecState.V0_BLOCK); + break; + } + } + public TokenCodecDecoder wrap( final DirectBuffer buffer, final int offset, @@ -152,14 +165,7 @@ public TokenCodecDecoder wrap( if (ENABLE_ACCESS_ORDER_CHECKS) { - switch(actingVersion) { - case 0: - codecState(CodecState.V0_BLOCK); - break; - default: - codecState(CodecState.V0_BLOCK); - break; - } + onWrap(actingVersion); } return this; @@ -256,6 +262,14 @@ public static String tokenOffsetMetaAttribute(final MetaAttribute metaAttribute) return ""; } + private void onTokenOffsetAccessed() + { + if (codecState() == CodecState.NOT_WRAPPED) + { + throw new IllegalStateException("Cannot access field \"tokenOffset\" in state: " + codecState()); + } + } + public static int tokenOffsetNullValue() { return -2147483648; @@ -275,10 +289,7 @@ public int tokenOffset() { if (ENABLE_ACCESS_ORDER_CHECKS) { - if (codecState() == CodecState.NOT_WRAPPED) - { - throw new IllegalStateException("Cannot access field \"tokenOffset\" in state: " + codecState()); - } + onTokenOffsetAccessed(); } return buffer.getInt(offset + 0, java.nio.ByteOrder.LITTLE_ENDIAN); @@ -315,6 +326,14 @@ public static String tokenSizeMetaAttribute(final MetaAttribute metaAttribute) return ""; } + private void onTokenSizeAccessed() + { + if (codecState() == CodecState.NOT_WRAPPED) + { + throw new IllegalStateException("Cannot access field \"tokenSize\" in state: " + codecState()); + } + } + public static int tokenSizeNullValue() { return -2147483648; @@ -334,10 +353,7 @@ public int tokenSize() { if (ENABLE_ACCESS_ORDER_CHECKS) { - if (codecState() == CodecState.NOT_WRAPPED) - { - throw new IllegalStateException("Cannot access field \"tokenSize\" in state: " + codecState()); - } + onTokenSizeAccessed(); } return buffer.getInt(offset + 4, java.nio.ByteOrder.LITTLE_ENDIAN); @@ -374,6 +390,14 @@ public static String fieldIdMetaAttribute(final MetaAttribute metaAttribute) return ""; } + private void onFieldIdAccessed() + { + if (codecState() == CodecState.NOT_WRAPPED) + { + throw new IllegalStateException("Cannot access field \"fieldId\" in state: " + codecState()); + } + } + public static int fieldIdNullValue() { return -2147483648; @@ -393,10 +417,7 @@ public int fieldId() { if (ENABLE_ACCESS_ORDER_CHECKS) { - if (codecState() == CodecState.NOT_WRAPPED) - { - throw new IllegalStateException("Cannot access field \"fieldId\" in state: " + codecState()); - } + onFieldIdAccessed(); } return buffer.getInt(offset + 8, java.nio.ByteOrder.LITTLE_ENDIAN); @@ -433,6 +454,14 @@ public static String tokenVersionMetaAttribute(final MetaAttribute metaAttribute return ""; } + private void onTokenVersionAccessed() + { + if (codecState() == CodecState.NOT_WRAPPED) + { + throw new IllegalStateException("Cannot access field \"tokenVersion\" in state: " + codecState()); + } + } + public static int tokenVersionNullValue() { return -2147483648; @@ -452,10 +481,7 @@ public int tokenVersion() { if (ENABLE_ACCESS_ORDER_CHECKS) { - if (codecState() == CodecState.NOT_WRAPPED) - { - throw new IllegalStateException("Cannot access field \"tokenVersion\" in state: " + codecState()); - } + onTokenVersionAccessed(); } return buffer.getInt(offset + 12, java.nio.ByteOrder.LITTLE_ENDIAN); @@ -492,6 +518,14 @@ public static String componentTokenCountMetaAttribute(final MetaAttribute metaAt return ""; } + private void onComponentTokenCountAccessed() + { + if (codecState() == CodecState.NOT_WRAPPED) + { + throw new IllegalStateException("Cannot access field \"componentTokenCount\" in state: " + codecState()); + } + } + public static int componentTokenCountNullValue() { return -2147483648; @@ -511,10 +545,7 @@ public int componentTokenCount() { if (ENABLE_ACCESS_ORDER_CHECKS) { - if (codecState() == CodecState.NOT_WRAPPED) - { - throw new IllegalStateException("Cannot access field \"componentTokenCount\" in state: " + codecState()); - } + onComponentTokenCountAccessed(); } return buffer.getInt(offset + 16, java.nio.ByteOrder.LITTLE_ENDIAN); @@ -551,14 +582,19 @@ public static String signalMetaAttribute(final MetaAttribute metaAttribute) return ""; } + private void onSignalAccessed() + { + if (codecState() == CodecState.NOT_WRAPPED) + { + throw new IllegalStateException("Cannot access field \"signal\" in state: " + codecState()); + } + } + public short signalRaw() { if (ENABLE_ACCESS_ORDER_CHECKS) { - if (codecState() == CodecState.NOT_WRAPPED) - { - throw new IllegalStateException("Cannot access field \"signal\" in state: " + codecState()); - } + onSignalAccessed(); } return ((short)(buffer.getByte(offset + 20) & 0xFF)); @@ -568,10 +604,7 @@ public SignalCodec signal() { if (ENABLE_ACCESS_ORDER_CHECKS) { - if (codecState() == CodecState.NOT_WRAPPED) - { - throw new IllegalStateException("Cannot access field \"signal\" in state: " + codecState()); - } + onSignalAccessed(); } return SignalCodec.get(((short)(buffer.getByte(offset + 20) & 0xFF))); @@ -608,14 +641,19 @@ public static String primitiveTypeMetaAttribute(final MetaAttribute metaAttribut return ""; } + private void onPrimitiveTypeAccessed() + { + if (codecState() == CodecState.NOT_WRAPPED) + { + throw new IllegalStateException("Cannot access field \"primitiveType\" in state: " + codecState()); + } + } + public short primitiveTypeRaw() { if (ENABLE_ACCESS_ORDER_CHECKS) { - if (codecState() == CodecState.NOT_WRAPPED) - { - throw new IllegalStateException("Cannot access field \"primitiveType\" in state: " + codecState()); - } + onPrimitiveTypeAccessed(); } return ((short)(buffer.getByte(offset + 21) & 0xFF)); @@ -625,10 +663,7 @@ public PrimitiveTypeCodec primitiveType() { if (ENABLE_ACCESS_ORDER_CHECKS) { - if (codecState() == CodecState.NOT_WRAPPED) - { - throw new IllegalStateException("Cannot access field \"primitiveType\" in state: " + codecState()); - } + onPrimitiveTypeAccessed(); } return PrimitiveTypeCodec.get(((short)(buffer.getByte(offset + 21) & 0xFF))); @@ -665,14 +700,19 @@ public static String byteOrderMetaAttribute(final MetaAttribute metaAttribute) return ""; } + private void onByteOrderAccessed() + { + if (codecState() == CodecState.NOT_WRAPPED) + { + throw new IllegalStateException("Cannot access field \"byteOrder\" in state: " + codecState()); + } + } + public short byteOrderRaw() { if (ENABLE_ACCESS_ORDER_CHECKS) { - if (codecState() == CodecState.NOT_WRAPPED) - { - throw new IllegalStateException("Cannot access field \"byteOrder\" in state: " + codecState()); - } + onByteOrderAccessed(); } return ((short)(buffer.getByte(offset + 22) & 0xFF)); @@ -682,10 +722,7 @@ public ByteOrderCodec byteOrder() { if (ENABLE_ACCESS_ORDER_CHECKS) { - if (codecState() == CodecState.NOT_WRAPPED) - { - throw new IllegalStateException("Cannot access field \"byteOrder\" in state: " + codecState()); - } + onByteOrderAccessed(); } return ByteOrderCodec.get(((short)(buffer.getByte(offset + 22) & 0xFF))); @@ -722,14 +759,19 @@ public static String presenceMetaAttribute(final MetaAttribute metaAttribute) return ""; } + private void onPresenceAccessed() + { + if (codecState() == CodecState.NOT_WRAPPED) + { + throw new IllegalStateException("Cannot access field \"presence\" in state: " + codecState()); + } + } + public short presenceRaw() { if (ENABLE_ACCESS_ORDER_CHECKS) { - if (codecState() == CodecState.NOT_WRAPPED) - { - throw new IllegalStateException("Cannot access field \"presence\" in state: " + codecState()); - } + onPresenceAccessed(); } return ((short)(buffer.getByte(offset + 23) & 0xFF)); @@ -739,10 +781,7 @@ public PresenceCodec presence() { if (ENABLE_ACCESS_ORDER_CHECKS) { - if (codecState() == CodecState.NOT_WRAPPED) - { - throw new IllegalStateException("Cannot access field \"presence\" in state: " + codecState()); - } + onPresenceAccessed(); } return PresenceCodec.get(((short)(buffer.getByte(offset + 23) & 0xFF))); @@ -779,6 +818,14 @@ public static String deprecatedMetaAttribute(final MetaAttribute metaAttribute) return ""; } + private void onDeprecatedAccessed() + { + if (codecState() == CodecState.NOT_WRAPPED) + { + throw new IllegalStateException("Cannot access field \"deprecated\" in state: " + codecState()); + } + } + public static int deprecatedNullValue() { return 0; @@ -798,10 +845,7 @@ public int deprecated() { if (ENABLE_ACCESS_ORDER_CHECKS) { - if (codecState() == CodecState.NOT_WRAPPED) - { - throw new IllegalStateException("Cannot access field \"deprecated\" in state: " + codecState()); - } + onDeprecatedAccessed(); } return buffer.getInt(offset + 24, java.nio.ByteOrder.LITTLE_ENDIAN); @@ -838,18 +882,23 @@ public static int nameHeaderLength() return 2; } + private void onNameAccessed() + { + switch (codecState()) + { + case V0_BLOCK: + codecState(CodecState.V0_NAME_DONE); + break; + default: + throw new IllegalStateException("Cannot access field \"name\" in state: " + codecState()); + } + } + public int nameLength() { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_BLOCK: - codecState(CodecState.V0_NAME_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"name\" in state: " + codecState()); - } + onNameAccessed(); } final int limit = parentMessage.limit(); @@ -860,14 +909,7 @@ public int skipName() { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_BLOCK: - codecState(CodecState.V0_NAME_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"name\" in state: " + codecState()); - } + onNameAccessed(); } final int headerLength = 2; @@ -883,14 +925,7 @@ public int getName(final MutableDirectBuffer dst, final int dstOffset, final int { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_BLOCK: - codecState(CodecState.V0_NAME_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"name\" in state: " + codecState()); - } + onNameAccessed(); } final int headerLength = 2; @@ -907,14 +942,7 @@ public int getName(final byte[] dst, final int dstOffset, final int length) { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_BLOCK: - codecState(CodecState.V0_NAME_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"name\" in state: " + codecState()); - } + onNameAccessed(); } final int headerLength = 2; @@ -931,14 +959,7 @@ public void wrapName(final DirectBuffer wrapBuffer) { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_BLOCK: - codecState(CodecState.V0_NAME_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"name\" in state: " + codecState()); - } + onNameAccessed(); } final int headerLength = 2; @@ -952,14 +973,7 @@ public String name() { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_BLOCK: - codecState(CodecState.V0_NAME_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"name\" in state: " + codecState()); - } + onNameAccessed(); } final int headerLength = 2; @@ -1008,18 +1022,23 @@ public static int constValueHeaderLength() return 2; } + private void onConstValueAccessed() + { + switch (codecState()) + { + case V0_NAME_DONE: + codecState(CodecState.V0_CONSTVALUE_DONE); + break; + default: + throw new IllegalStateException("Cannot access field \"constValue\" in state: " + codecState()); + } + } + public int constValueLength() { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_NAME_DONE: - codecState(CodecState.V0_CONSTVALUE_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"constValue\" in state: " + codecState()); - } + onConstValueAccessed(); } final int limit = parentMessage.limit(); @@ -1030,14 +1049,7 @@ public int skipConstValue() { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_NAME_DONE: - codecState(CodecState.V0_CONSTVALUE_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"constValue\" in state: " + codecState()); - } + onConstValueAccessed(); } final int headerLength = 2; @@ -1053,14 +1065,7 @@ public int getConstValue(final MutableDirectBuffer dst, final int dstOffset, fin { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_NAME_DONE: - codecState(CodecState.V0_CONSTVALUE_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"constValue\" in state: " + codecState()); - } + onConstValueAccessed(); } final int headerLength = 2; @@ -1077,14 +1082,7 @@ public int getConstValue(final byte[] dst, final int dstOffset, final int length { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_NAME_DONE: - codecState(CodecState.V0_CONSTVALUE_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"constValue\" in state: " + codecState()); - } + onConstValueAccessed(); } final int headerLength = 2; @@ -1101,14 +1099,7 @@ public void wrapConstValue(final DirectBuffer wrapBuffer) { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_NAME_DONE: - codecState(CodecState.V0_CONSTVALUE_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"constValue\" in state: " + codecState()); - } + onConstValueAccessed(); } final int headerLength = 2; @@ -1122,14 +1113,7 @@ public String constValue() { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_NAME_DONE: - codecState(CodecState.V0_CONSTVALUE_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"constValue\" in state: " + codecState()); - } + onConstValueAccessed(); } final int headerLength = 2; @@ -1178,18 +1162,23 @@ public static int minValueHeaderLength() return 2; } + private void onMinValueAccessed() + { + switch (codecState()) + { + case V0_CONSTVALUE_DONE: + codecState(CodecState.V0_MINVALUE_DONE); + break; + default: + throw new IllegalStateException("Cannot access field \"minValue\" in state: " + codecState()); + } + } + public int minValueLength() { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_CONSTVALUE_DONE: - codecState(CodecState.V0_MINVALUE_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"minValue\" in state: " + codecState()); - } + onMinValueAccessed(); } final int limit = parentMessage.limit(); @@ -1200,14 +1189,7 @@ public int skipMinValue() { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_CONSTVALUE_DONE: - codecState(CodecState.V0_MINVALUE_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"minValue\" in state: " + codecState()); - } + onMinValueAccessed(); } final int headerLength = 2; @@ -1223,14 +1205,7 @@ public int getMinValue(final MutableDirectBuffer dst, final int dstOffset, final { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_CONSTVALUE_DONE: - codecState(CodecState.V0_MINVALUE_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"minValue\" in state: " + codecState()); - } + onMinValueAccessed(); } final int headerLength = 2; @@ -1247,14 +1222,7 @@ public int getMinValue(final byte[] dst, final int dstOffset, final int length) { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_CONSTVALUE_DONE: - codecState(CodecState.V0_MINVALUE_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"minValue\" in state: " + codecState()); - } + onMinValueAccessed(); } final int headerLength = 2; @@ -1271,14 +1239,7 @@ public void wrapMinValue(final DirectBuffer wrapBuffer) { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_CONSTVALUE_DONE: - codecState(CodecState.V0_MINVALUE_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"minValue\" in state: " + codecState()); - } + onMinValueAccessed(); } final int headerLength = 2; @@ -1292,14 +1253,7 @@ public String minValue() { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_CONSTVALUE_DONE: - codecState(CodecState.V0_MINVALUE_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"minValue\" in state: " + codecState()); - } + onMinValueAccessed(); } final int headerLength = 2; @@ -1348,18 +1302,23 @@ public static int maxValueHeaderLength() return 2; } + private void onMaxValueAccessed() + { + switch (codecState()) + { + case V0_MINVALUE_DONE: + codecState(CodecState.V0_MAXVALUE_DONE); + break; + default: + throw new IllegalStateException("Cannot access field \"maxValue\" in state: " + codecState()); + } + } + public int maxValueLength() { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_MINVALUE_DONE: - codecState(CodecState.V0_MAXVALUE_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"maxValue\" in state: " + codecState()); - } + onMaxValueAccessed(); } final int limit = parentMessage.limit(); @@ -1370,14 +1329,7 @@ public int skipMaxValue() { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_MINVALUE_DONE: - codecState(CodecState.V0_MAXVALUE_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"maxValue\" in state: " + codecState()); - } + onMaxValueAccessed(); } final int headerLength = 2; @@ -1393,14 +1345,7 @@ public int getMaxValue(final MutableDirectBuffer dst, final int dstOffset, final { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_MINVALUE_DONE: - codecState(CodecState.V0_MAXVALUE_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"maxValue\" in state: " + codecState()); - } + onMaxValueAccessed(); } final int headerLength = 2; @@ -1417,14 +1362,7 @@ public int getMaxValue(final byte[] dst, final int dstOffset, final int length) { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_MINVALUE_DONE: - codecState(CodecState.V0_MAXVALUE_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"maxValue\" in state: " + codecState()); - } + onMaxValueAccessed(); } final int headerLength = 2; @@ -1441,14 +1379,7 @@ public void wrapMaxValue(final DirectBuffer wrapBuffer) { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_MINVALUE_DONE: - codecState(CodecState.V0_MAXVALUE_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"maxValue\" in state: " + codecState()); - } + onMaxValueAccessed(); } final int headerLength = 2; @@ -1462,14 +1393,7 @@ public String maxValue() { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_MINVALUE_DONE: - codecState(CodecState.V0_MAXVALUE_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"maxValue\" in state: " + codecState()); - } + onMaxValueAccessed(); } final int headerLength = 2; @@ -1518,18 +1442,23 @@ public static int nullValueHeaderLength() return 2; } + private void onNullValueAccessed() + { + switch (codecState()) + { + case V0_MAXVALUE_DONE: + codecState(CodecState.V0_NULLVALUE_DONE); + break; + default: + throw new IllegalStateException("Cannot access field \"nullValue\" in state: " + codecState()); + } + } + public int nullValueLength() { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_MAXVALUE_DONE: - codecState(CodecState.V0_NULLVALUE_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"nullValue\" in state: " + codecState()); - } + onNullValueAccessed(); } final int limit = parentMessage.limit(); @@ -1540,14 +1469,7 @@ public int skipNullValue() { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_MAXVALUE_DONE: - codecState(CodecState.V0_NULLVALUE_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"nullValue\" in state: " + codecState()); - } + onNullValueAccessed(); } final int headerLength = 2; @@ -1563,14 +1485,7 @@ public int getNullValue(final MutableDirectBuffer dst, final int dstOffset, fina { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_MAXVALUE_DONE: - codecState(CodecState.V0_NULLVALUE_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"nullValue\" in state: " + codecState()); - } + onNullValueAccessed(); } final int headerLength = 2; @@ -1587,14 +1502,7 @@ public int getNullValue(final byte[] dst, final int dstOffset, final int length) { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_MAXVALUE_DONE: - codecState(CodecState.V0_NULLVALUE_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"nullValue\" in state: " + codecState()); - } + onNullValueAccessed(); } final int headerLength = 2; @@ -1611,14 +1519,7 @@ public void wrapNullValue(final DirectBuffer wrapBuffer) { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_MAXVALUE_DONE: - codecState(CodecState.V0_NULLVALUE_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"nullValue\" in state: " + codecState()); - } + onNullValueAccessed(); } final int headerLength = 2; @@ -1632,14 +1533,7 @@ public String nullValue() { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_MAXVALUE_DONE: - codecState(CodecState.V0_NULLVALUE_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"nullValue\" in state: " + codecState()); - } + onNullValueAccessed(); } final int headerLength = 2; @@ -1688,18 +1582,23 @@ public static int characterEncodingHeaderLength() return 2; } + private void onCharacterEncodingAccessed() + { + switch (codecState()) + { + case V0_NULLVALUE_DONE: + codecState(CodecState.V0_CHARACTERENCODING_DONE); + break; + default: + throw new IllegalStateException("Cannot access field \"characterEncoding\" in state: " + codecState()); + } + } + public int characterEncodingLength() { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_NULLVALUE_DONE: - codecState(CodecState.V0_CHARACTERENCODING_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"characterEncoding\" in state: " + codecState()); - } + onCharacterEncodingAccessed(); } final int limit = parentMessage.limit(); @@ -1710,14 +1609,7 @@ public int skipCharacterEncoding() { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_NULLVALUE_DONE: - codecState(CodecState.V0_CHARACTERENCODING_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"characterEncoding\" in state: " + codecState()); - } + onCharacterEncodingAccessed(); } final int headerLength = 2; @@ -1733,14 +1625,7 @@ public int getCharacterEncoding(final MutableDirectBuffer dst, final int dstOffs { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_NULLVALUE_DONE: - codecState(CodecState.V0_CHARACTERENCODING_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"characterEncoding\" in state: " + codecState()); - } + onCharacterEncodingAccessed(); } final int headerLength = 2; @@ -1757,14 +1642,7 @@ public int getCharacterEncoding(final byte[] dst, final int dstOffset, final int { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_NULLVALUE_DONE: - codecState(CodecState.V0_CHARACTERENCODING_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"characterEncoding\" in state: " + codecState()); - } + onCharacterEncodingAccessed(); } final int headerLength = 2; @@ -1781,14 +1659,7 @@ public void wrapCharacterEncoding(final DirectBuffer wrapBuffer) { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_NULLVALUE_DONE: - codecState(CodecState.V0_CHARACTERENCODING_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"characterEncoding\" in state: " + codecState()); - } + onCharacterEncodingAccessed(); } final int headerLength = 2; @@ -1802,14 +1673,7 @@ public String characterEncoding() { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_NULLVALUE_DONE: - codecState(CodecState.V0_CHARACTERENCODING_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"characterEncoding\" in state: " + codecState()); - } + onCharacterEncodingAccessed(); } final int headerLength = 2; @@ -1858,18 +1722,23 @@ public static int epochHeaderLength() return 2; } + private void onEpochAccessed() + { + switch (codecState()) + { + case V0_CHARACTERENCODING_DONE: + codecState(CodecState.V0_EPOCH_DONE); + break; + default: + throw new IllegalStateException("Cannot access field \"epoch\" in state: " + codecState()); + } + } + public int epochLength() { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_CHARACTERENCODING_DONE: - codecState(CodecState.V0_EPOCH_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"epoch\" in state: " + codecState()); - } + onEpochAccessed(); } final int limit = parentMessage.limit(); @@ -1880,14 +1749,7 @@ public int skipEpoch() { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_CHARACTERENCODING_DONE: - codecState(CodecState.V0_EPOCH_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"epoch\" in state: " + codecState()); - } + onEpochAccessed(); } final int headerLength = 2; @@ -1903,14 +1765,7 @@ public int getEpoch(final MutableDirectBuffer dst, final int dstOffset, final in { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_CHARACTERENCODING_DONE: - codecState(CodecState.V0_EPOCH_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"epoch\" in state: " + codecState()); - } + onEpochAccessed(); } final int headerLength = 2; @@ -1927,14 +1782,7 @@ public int getEpoch(final byte[] dst, final int dstOffset, final int length) { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_CHARACTERENCODING_DONE: - codecState(CodecState.V0_EPOCH_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"epoch\" in state: " + codecState()); - } + onEpochAccessed(); } final int headerLength = 2; @@ -1951,14 +1799,7 @@ public void wrapEpoch(final DirectBuffer wrapBuffer) { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_CHARACTERENCODING_DONE: - codecState(CodecState.V0_EPOCH_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"epoch\" in state: " + codecState()); - } + onEpochAccessed(); } final int headerLength = 2; @@ -1972,14 +1813,7 @@ public String epoch() { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_CHARACTERENCODING_DONE: - codecState(CodecState.V0_EPOCH_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"epoch\" in state: " + codecState()); - } + onEpochAccessed(); } final int headerLength = 2; @@ -2028,18 +1862,23 @@ public static int timeUnitHeaderLength() return 2; } + private void onTimeUnitAccessed() + { + switch (codecState()) + { + case V0_EPOCH_DONE: + codecState(CodecState.V0_TIMEUNIT_DONE); + break; + default: + throw new IllegalStateException("Cannot access field \"timeUnit\" in state: " + codecState()); + } + } + public int timeUnitLength() { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_EPOCH_DONE: - codecState(CodecState.V0_TIMEUNIT_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"timeUnit\" in state: " + codecState()); - } + onTimeUnitAccessed(); } final int limit = parentMessage.limit(); @@ -2050,14 +1889,7 @@ public int skipTimeUnit() { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_EPOCH_DONE: - codecState(CodecState.V0_TIMEUNIT_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"timeUnit\" in state: " + codecState()); - } + onTimeUnitAccessed(); } final int headerLength = 2; @@ -2073,14 +1905,7 @@ public int getTimeUnit(final MutableDirectBuffer dst, final int dstOffset, final { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_EPOCH_DONE: - codecState(CodecState.V0_TIMEUNIT_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"timeUnit\" in state: " + codecState()); - } + onTimeUnitAccessed(); } final int headerLength = 2; @@ -2097,14 +1922,7 @@ public int getTimeUnit(final byte[] dst, final int dstOffset, final int length) { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_EPOCH_DONE: - codecState(CodecState.V0_TIMEUNIT_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"timeUnit\" in state: " + codecState()); - } + onTimeUnitAccessed(); } final int headerLength = 2; @@ -2121,14 +1939,7 @@ public void wrapTimeUnit(final DirectBuffer wrapBuffer) { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_EPOCH_DONE: - codecState(CodecState.V0_TIMEUNIT_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"timeUnit\" in state: " + codecState()); - } + onTimeUnitAccessed(); } final int headerLength = 2; @@ -2142,14 +1953,7 @@ public String timeUnit() { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_EPOCH_DONE: - codecState(CodecState.V0_TIMEUNIT_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"timeUnit\" in state: " + codecState()); - } + onTimeUnitAccessed(); } final int headerLength = 2; @@ -2198,18 +2002,23 @@ public static int semanticTypeHeaderLength() return 2; } + private void onSemanticTypeAccessed() + { + switch (codecState()) + { + case V0_TIMEUNIT_DONE: + codecState(CodecState.V0_SEMANTICTYPE_DONE); + break; + default: + throw new IllegalStateException("Cannot access field \"semanticType\" in state: " + codecState()); + } + } + public int semanticTypeLength() { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_TIMEUNIT_DONE: - codecState(CodecState.V0_SEMANTICTYPE_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"semanticType\" in state: " + codecState()); - } + onSemanticTypeAccessed(); } final int limit = parentMessage.limit(); @@ -2220,14 +2029,7 @@ public int skipSemanticType() { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_TIMEUNIT_DONE: - codecState(CodecState.V0_SEMANTICTYPE_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"semanticType\" in state: " + codecState()); - } + onSemanticTypeAccessed(); } final int headerLength = 2; @@ -2243,14 +2045,7 @@ public int getSemanticType(final MutableDirectBuffer dst, final int dstOffset, f { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_TIMEUNIT_DONE: - codecState(CodecState.V0_SEMANTICTYPE_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"semanticType\" in state: " + codecState()); - } + onSemanticTypeAccessed(); } final int headerLength = 2; @@ -2267,14 +2062,7 @@ public int getSemanticType(final byte[] dst, final int dstOffset, final int leng { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_TIMEUNIT_DONE: - codecState(CodecState.V0_SEMANTICTYPE_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"semanticType\" in state: " + codecState()); - } + onSemanticTypeAccessed(); } final int headerLength = 2; @@ -2291,14 +2079,7 @@ public void wrapSemanticType(final DirectBuffer wrapBuffer) { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_TIMEUNIT_DONE: - codecState(CodecState.V0_SEMANTICTYPE_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"semanticType\" in state: " + codecState()); - } + onSemanticTypeAccessed(); } final int headerLength = 2; @@ -2312,14 +2093,7 @@ public String semanticType() { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_TIMEUNIT_DONE: - codecState(CodecState.V0_SEMANTICTYPE_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"semanticType\" in state: " + codecState()); - } + onSemanticTypeAccessed(); } final int headerLength = 2; @@ -2368,18 +2142,23 @@ public static int descriptionHeaderLength() return 2; } + private void onDescriptionAccessed() + { + switch (codecState()) + { + case V0_SEMANTICTYPE_DONE: + codecState(CodecState.V0_DESCRIPTION_DONE); + break; + default: + throw new IllegalStateException("Cannot access field \"description\" in state: " + codecState()); + } + } + public int descriptionLength() { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_SEMANTICTYPE_DONE: - codecState(CodecState.V0_DESCRIPTION_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"description\" in state: " + codecState()); - } + onDescriptionAccessed(); } final int limit = parentMessage.limit(); @@ -2390,14 +2169,7 @@ public int skipDescription() { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_SEMANTICTYPE_DONE: - codecState(CodecState.V0_DESCRIPTION_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"description\" in state: " + codecState()); - } + onDescriptionAccessed(); } final int headerLength = 2; @@ -2413,14 +2185,7 @@ public int getDescription(final MutableDirectBuffer dst, final int dstOffset, fi { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_SEMANTICTYPE_DONE: - codecState(CodecState.V0_DESCRIPTION_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"description\" in state: " + codecState()); - } + onDescriptionAccessed(); } final int headerLength = 2; @@ -2437,14 +2202,7 @@ public int getDescription(final byte[] dst, final int dstOffset, final int lengt { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_SEMANTICTYPE_DONE: - codecState(CodecState.V0_DESCRIPTION_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"description\" in state: " + codecState()); - } + onDescriptionAccessed(); } final int headerLength = 2; @@ -2461,14 +2219,7 @@ public void wrapDescription(final DirectBuffer wrapBuffer) { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_SEMANTICTYPE_DONE: - codecState(CodecState.V0_DESCRIPTION_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"description\" in state: " + codecState()); - } + onDescriptionAccessed(); } final int headerLength = 2; @@ -2482,14 +2233,7 @@ public String description() { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_SEMANTICTYPE_DONE: - codecState(CodecState.V0_DESCRIPTION_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"description\" in state: " + codecState()); - } + onDescriptionAccessed(); } final int headerLength = 2; @@ -2538,18 +2282,23 @@ public static int referencedNameHeaderLength() return 2; } + private void onReferencedNameAccessed() + { + switch (codecState()) + { + case V0_DESCRIPTION_DONE: + codecState(CodecState.V0_REFERENCEDNAME_DONE); + break; + default: + throw new IllegalStateException("Cannot access field \"referencedName\" in state: " + codecState()); + } + } + public int referencedNameLength() { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_DESCRIPTION_DONE: - codecState(CodecState.V0_REFERENCEDNAME_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"referencedName\" in state: " + codecState()); - } + onReferencedNameAccessed(); } final int limit = parentMessage.limit(); @@ -2560,14 +2309,7 @@ public int skipReferencedName() { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_DESCRIPTION_DONE: - codecState(CodecState.V0_REFERENCEDNAME_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"referencedName\" in state: " + codecState()); - } + onReferencedNameAccessed(); } final int headerLength = 2; @@ -2583,14 +2325,7 @@ public int getReferencedName(final MutableDirectBuffer dst, final int dstOffset, { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_DESCRIPTION_DONE: - codecState(CodecState.V0_REFERENCEDNAME_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"referencedName\" in state: " + codecState()); - } + onReferencedNameAccessed(); } final int headerLength = 2; @@ -2607,14 +2342,7 @@ public int getReferencedName(final byte[] dst, final int dstOffset, final int le { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_DESCRIPTION_DONE: - codecState(CodecState.V0_REFERENCEDNAME_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"referencedName\" in state: " + codecState()); - } + onReferencedNameAccessed(); } final int headerLength = 2; @@ -2631,14 +2359,7 @@ public void wrapReferencedName(final DirectBuffer wrapBuffer) { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_DESCRIPTION_DONE: - codecState(CodecState.V0_REFERENCEDNAME_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"referencedName\" in state: " + codecState()); - } + onReferencedNameAccessed(); } final int headerLength = 2; @@ -2652,14 +2373,7 @@ public String referencedName() { if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_DESCRIPTION_DONE: - codecState(CodecState.V0_REFERENCEDNAME_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"referencedName\" in state: " + codecState()); - } + onReferencedNameAccessed(); } final int headerLength = 2; diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java index 2f672ffe35..fec09c0bd5 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java @@ -208,6 +208,14 @@ public static String tokenOffsetMetaAttribute(final MetaAttribute metaAttribute) return ""; } + private void onTokenOffsetAccessed() + { + if (codecState() == CodecState.NOT_WRAPPED) + { + throw new IllegalStateException("Cannot access field \"tokenOffset\" in state: " + codecState()); + } + } + public static int tokenOffsetNullValue() { return -2147483648; @@ -227,10 +235,7 @@ public TokenCodecEncoder tokenOffset(final int value) { if (ENABLE_ACCESS_ORDER_CHECKS) { - if (codecState() == CodecState.NOT_WRAPPED) - { - throw new IllegalStateException("Cannot access field \"tokenOffset\" in state: " + codecState()); - } + onTokenOffsetAccessed(); } buffer.putInt(offset + 0, value, java.nio.ByteOrder.LITTLE_ENDIAN); @@ -268,6 +273,14 @@ public static String tokenSizeMetaAttribute(final MetaAttribute metaAttribute) return ""; } + private void onTokenSizeAccessed() + { + if (codecState() == CodecState.NOT_WRAPPED) + { + throw new IllegalStateException("Cannot access field \"tokenSize\" in state: " + codecState()); + } + } + public static int tokenSizeNullValue() { return -2147483648; @@ -287,10 +300,7 @@ public TokenCodecEncoder tokenSize(final int value) { if (ENABLE_ACCESS_ORDER_CHECKS) { - if (codecState() == CodecState.NOT_WRAPPED) - { - throw new IllegalStateException("Cannot access field \"tokenSize\" in state: " + codecState()); - } + onTokenSizeAccessed(); } buffer.putInt(offset + 4, value, java.nio.ByteOrder.LITTLE_ENDIAN); @@ -328,6 +338,14 @@ public static String fieldIdMetaAttribute(final MetaAttribute metaAttribute) return ""; } + private void onFieldIdAccessed() + { + if (codecState() == CodecState.NOT_WRAPPED) + { + throw new IllegalStateException("Cannot access field \"fieldId\" in state: " + codecState()); + } + } + public static int fieldIdNullValue() { return -2147483648; @@ -347,10 +365,7 @@ public TokenCodecEncoder fieldId(final int value) { if (ENABLE_ACCESS_ORDER_CHECKS) { - if (codecState() == CodecState.NOT_WRAPPED) - { - throw new IllegalStateException("Cannot access field \"fieldId\" in state: " + codecState()); - } + onFieldIdAccessed(); } buffer.putInt(offset + 8, value, java.nio.ByteOrder.LITTLE_ENDIAN); @@ -388,6 +403,14 @@ public static String tokenVersionMetaAttribute(final MetaAttribute metaAttribute return ""; } + private void onTokenVersionAccessed() + { + if (codecState() == CodecState.NOT_WRAPPED) + { + throw new IllegalStateException("Cannot access field \"tokenVersion\" in state: " + codecState()); + } + } + public static int tokenVersionNullValue() { return -2147483648; @@ -407,10 +430,7 @@ public TokenCodecEncoder tokenVersion(final int value) { if (ENABLE_ACCESS_ORDER_CHECKS) { - if (codecState() == CodecState.NOT_WRAPPED) - { - throw new IllegalStateException("Cannot access field \"tokenVersion\" in state: " + codecState()); - } + onTokenVersionAccessed(); } buffer.putInt(offset + 12, value, java.nio.ByteOrder.LITTLE_ENDIAN); @@ -448,6 +468,14 @@ public static String componentTokenCountMetaAttribute(final MetaAttribute metaAt return ""; } + private void onComponentTokenCountAccessed() + { + if (codecState() == CodecState.NOT_WRAPPED) + { + throw new IllegalStateException("Cannot access field \"componentTokenCount\" in state: " + codecState()); + } + } + public static int componentTokenCountNullValue() { return -2147483648; @@ -467,10 +495,7 @@ public TokenCodecEncoder componentTokenCount(final int value) { if (ENABLE_ACCESS_ORDER_CHECKS) { - if (codecState() == CodecState.NOT_WRAPPED) - { - throw new IllegalStateException("Cannot access field \"componentTokenCount\" in state: " + codecState()); - } + onComponentTokenCountAccessed(); } buffer.putInt(offset + 16, value, java.nio.ByteOrder.LITTLE_ENDIAN); @@ -508,14 +533,19 @@ public static String signalMetaAttribute(final MetaAttribute metaAttribute) return ""; } + private void onSignalAccessed() + { + if (codecState() == CodecState.NOT_WRAPPED) + { + throw new IllegalStateException("Cannot access field \"signal\" in state: " + codecState()); + } + } + public TokenCodecEncoder signal(final SignalCodec value) { if (ENABLE_ACCESS_ORDER_CHECKS) { - if (codecState() == CodecState.NOT_WRAPPED) - { - throw new IllegalStateException("Cannot access field \"signal\" in state: " + codecState()); - } + onSignalAccessed(); } buffer.putByte(offset + 20, (byte)value.value()); @@ -552,14 +582,19 @@ public static String primitiveTypeMetaAttribute(final MetaAttribute metaAttribut return ""; } + private void onPrimitiveTypeAccessed() + { + if (codecState() == CodecState.NOT_WRAPPED) + { + throw new IllegalStateException("Cannot access field \"primitiveType\" in state: " + codecState()); + } + } + public TokenCodecEncoder primitiveType(final PrimitiveTypeCodec value) { if (ENABLE_ACCESS_ORDER_CHECKS) { - if (codecState() == CodecState.NOT_WRAPPED) - { - throw new IllegalStateException("Cannot access field \"primitiveType\" in state: " + codecState()); - } + onPrimitiveTypeAccessed(); } buffer.putByte(offset + 21, (byte)value.value()); @@ -596,14 +631,19 @@ public static String byteOrderMetaAttribute(final MetaAttribute metaAttribute) return ""; } + private void onByteOrderAccessed() + { + if (codecState() == CodecState.NOT_WRAPPED) + { + throw new IllegalStateException("Cannot access field \"byteOrder\" in state: " + codecState()); + } + } + public TokenCodecEncoder byteOrder(final ByteOrderCodec value) { if (ENABLE_ACCESS_ORDER_CHECKS) { - if (codecState() == CodecState.NOT_WRAPPED) - { - throw new IllegalStateException("Cannot access field \"byteOrder\" in state: " + codecState()); - } + onByteOrderAccessed(); } buffer.putByte(offset + 22, (byte)value.value()); @@ -640,14 +680,19 @@ public static String presenceMetaAttribute(final MetaAttribute metaAttribute) return ""; } + private void onPresenceAccessed() + { + if (codecState() == CodecState.NOT_WRAPPED) + { + throw new IllegalStateException("Cannot access field \"presence\" in state: " + codecState()); + } + } + public TokenCodecEncoder presence(final PresenceCodec value) { if (ENABLE_ACCESS_ORDER_CHECKS) { - if (codecState() == CodecState.NOT_WRAPPED) - { - throw new IllegalStateException("Cannot access field \"presence\" in state: " + codecState()); - } + onPresenceAccessed(); } buffer.putByte(offset + 23, (byte)value.value()); @@ -684,6 +729,14 @@ public static String deprecatedMetaAttribute(final MetaAttribute metaAttribute) return ""; } + private void onDeprecatedAccessed() + { + if (codecState() == CodecState.NOT_WRAPPED) + { + throw new IllegalStateException("Cannot access field \"deprecated\" in state: " + codecState()); + } + } + public static int deprecatedNullValue() { return 0; @@ -703,10 +756,7 @@ public TokenCodecEncoder deprecated(final int value) { if (ENABLE_ACCESS_ORDER_CHECKS) { - if (codecState() == CodecState.NOT_WRAPPED) - { - throw new IllegalStateException("Cannot access field \"deprecated\" in state: " + codecState()); - } + onDeprecatedAccessed(); } buffer.putInt(offset + 24, value, java.nio.ByteOrder.LITTLE_ENDIAN); @@ -739,6 +789,18 @@ public static int nameHeaderLength() return 2; } + private void onNameAccessed() + { + switch (codecState()) + { + case V0_BLOCK: + codecState(CodecState.V0_NAME_DONE); + break; + default: + throw new IllegalStateException("Cannot access field \"name\" in state: " + codecState()); + } + } + public TokenCodecEncoder putName(final DirectBuffer src, final int srcOffset, final int length) { if (length > 65534) @@ -748,14 +810,7 @@ public TokenCodecEncoder putName(final DirectBuffer src, final int srcOffset, fi if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_BLOCK: - codecState(CodecState.V0_NAME_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"name\" in state: " + codecState()); - } + onNameAccessed(); } final int headerLength = 2; @@ -776,14 +831,7 @@ public TokenCodecEncoder putName(final byte[] src, final int srcOffset, final in if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_BLOCK: - codecState(CodecState.V0_NAME_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"name\" in state: " + codecState()); - } + onNameAccessed(); } final int headerLength = 2; @@ -807,14 +855,7 @@ public TokenCodecEncoder name(final String value) if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_BLOCK: - codecState(CodecState.V0_NAME_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"name\" in state: " + codecState()); - } + onNameAccessed(); } final int headerLength = 2; @@ -851,6 +892,18 @@ public static int constValueHeaderLength() return 2; } + private void onConstValueAccessed() + { + switch (codecState()) + { + case V0_NAME_DONE: + codecState(CodecState.V0_CONSTVALUE_DONE); + break; + default: + throw new IllegalStateException("Cannot access field \"constValue\" in state: " + codecState()); + } + } + public TokenCodecEncoder putConstValue(final DirectBuffer src, final int srcOffset, final int length) { if (length > 65534) @@ -860,14 +913,7 @@ public TokenCodecEncoder putConstValue(final DirectBuffer src, final int srcOffs if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_NAME_DONE: - codecState(CodecState.V0_CONSTVALUE_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"constValue\" in state: " + codecState()); - } + onConstValueAccessed(); } final int headerLength = 2; @@ -888,14 +934,7 @@ public TokenCodecEncoder putConstValue(final byte[] src, final int srcOffset, fi if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_NAME_DONE: - codecState(CodecState.V0_CONSTVALUE_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"constValue\" in state: " + codecState()); - } + onConstValueAccessed(); } final int headerLength = 2; @@ -919,14 +958,7 @@ public TokenCodecEncoder constValue(final String value) if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_NAME_DONE: - codecState(CodecState.V0_CONSTVALUE_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"constValue\" in state: " + codecState()); - } + onConstValueAccessed(); } final int headerLength = 2; @@ -963,6 +995,18 @@ public static int minValueHeaderLength() return 2; } + private void onMinValueAccessed() + { + switch (codecState()) + { + case V0_CONSTVALUE_DONE: + codecState(CodecState.V0_MINVALUE_DONE); + break; + default: + throw new IllegalStateException("Cannot access field \"minValue\" in state: " + codecState()); + } + } + public TokenCodecEncoder putMinValue(final DirectBuffer src, final int srcOffset, final int length) { if (length > 65534) @@ -972,14 +1016,7 @@ public TokenCodecEncoder putMinValue(final DirectBuffer src, final int srcOffset if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_CONSTVALUE_DONE: - codecState(CodecState.V0_MINVALUE_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"minValue\" in state: " + codecState()); - } + onMinValueAccessed(); } final int headerLength = 2; @@ -1000,14 +1037,7 @@ public TokenCodecEncoder putMinValue(final byte[] src, final int srcOffset, fina if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_CONSTVALUE_DONE: - codecState(CodecState.V0_MINVALUE_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"minValue\" in state: " + codecState()); - } + onMinValueAccessed(); } final int headerLength = 2; @@ -1031,14 +1061,7 @@ public TokenCodecEncoder minValue(final String value) if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_CONSTVALUE_DONE: - codecState(CodecState.V0_MINVALUE_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"minValue\" in state: " + codecState()); - } + onMinValueAccessed(); } final int headerLength = 2; @@ -1075,6 +1098,18 @@ public static int maxValueHeaderLength() return 2; } + private void onMaxValueAccessed() + { + switch (codecState()) + { + case V0_MINVALUE_DONE: + codecState(CodecState.V0_MAXVALUE_DONE); + break; + default: + throw new IllegalStateException("Cannot access field \"maxValue\" in state: " + codecState()); + } + } + public TokenCodecEncoder putMaxValue(final DirectBuffer src, final int srcOffset, final int length) { if (length > 65534) @@ -1084,14 +1119,7 @@ public TokenCodecEncoder putMaxValue(final DirectBuffer src, final int srcOffset if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_MINVALUE_DONE: - codecState(CodecState.V0_MAXVALUE_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"maxValue\" in state: " + codecState()); - } + onMaxValueAccessed(); } final int headerLength = 2; @@ -1112,14 +1140,7 @@ public TokenCodecEncoder putMaxValue(final byte[] src, final int srcOffset, fina if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_MINVALUE_DONE: - codecState(CodecState.V0_MAXVALUE_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"maxValue\" in state: " + codecState()); - } + onMaxValueAccessed(); } final int headerLength = 2; @@ -1143,14 +1164,7 @@ public TokenCodecEncoder maxValue(final String value) if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_MINVALUE_DONE: - codecState(CodecState.V0_MAXVALUE_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"maxValue\" in state: " + codecState()); - } + onMaxValueAccessed(); } final int headerLength = 2; @@ -1187,6 +1201,18 @@ public static int nullValueHeaderLength() return 2; } + private void onNullValueAccessed() + { + switch (codecState()) + { + case V0_MAXVALUE_DONE: + codecState(CodecState.V0_NULLVALUE_DONE); + break; + default: + throw new IllegalStateException("Cannot access field \"nullValue\" in state: " + codecState()); + } + } + public TokenCodecEncoder putNullValue(final DirectBuffer src, final int srcOffset, final int length) { if (length > 65534) @@ -1196,14 +1222,7 @@ public TokenCodecEncoder putNullValue(final DirectBuffer src, final int srcOffse if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_MAXVALUE_DONE: - codecState(CodecState.V0_NULLVALUE_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"nullValue\" in state: " + codecState()); - } + onNullValueAccessed(); } final int headerLength = 2; @@ -1224,14 +1243,7 @@ public TokenCodecEncoder putNullValue(final byte[] src, final int srcOffset, fin if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_MAXVALUE_DONE: - codecState(CodecState.V0_NULLVALUE_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"nullValue\" in state: " + codecState()); - } + onNullValueAccessed(); } final int headerLength = 2; @@ -1255,14 +1267,7 @@ public TokenCodecEncoder nullValue(final String value) if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_MAXVALUE_DONE: - codecState(CodecState.V0_NULLVALUE_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"nullValue\" in state: " + codecState()); - } + onNullValueAccessed(); } final int headerLength = 2; @@ -1299,6 +1304,18 @@ public static int characterEncodingHeaderLength() return 2; } + private void onCharacterEncodingAccessed() + { + switch (codecState()) + { + case V0_NULLVALUE_DONE: + codecState(CodecState.V0_CHARACTERENCODING_DONE); + break; + default: + throw new IllegalStateException("Cannot access field \"characterEncoding\" in state: " + codecState()); + } + } + public TokenCodecEncoder putCharacterEncoding(final DirectBuffer src, final int srcOffset, final int length) { if (length > 65534) @@ -1308,14 +1325,7 @@ public TokenCodecEncoder putCharacterEncoding(final DirectBuffer src, final int if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_NULLVALUE_DONE: - codecState(CodecState.V0_CHARACTERENCODING_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"characterEncoding\" in state: " + codecState()); - } + onCharacterEncodingAccessed(); } final int headerLength = 2; @@ -1336,14 +1346,7 @@ public TokenCodecEncoder putCharacterEncoding(final byte[] src, final int srcOff if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_NULLVALUE_DONE: - codecState(CodecState.V0_CHARACTERENCODING_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"characterEncoding\" in state: " + codecState()); - } + onCharacterEncodingAccessed(); } final int headerLength = 2; @@ -1367,14 +1370,7 @@ public TokenCodecEncoder characterEncoding(final String value) if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_NULLVALUE_DONE: - codecState(CodecState.V0_CHARACTERENCODING_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"characterEncoding\" in state: " + codecState()); - } + onCharacterEncodingAccessed(); } final int headerLength = 2; @@ -1411,6 +1407,18 @@ public static int epochHeaderLength() return 2; } + private void onEpochAccessed() + { + switch (codecState()) + { + case V0_CHARACTERENCODING_DONE: + codecState(CodecState.V0_EPOCH_DONE); + break; + default: + throw new IllegalStateException("Cannot access field \"epoch\" in state: " + codecState()); + } + } + public TokenCodecEncoder putEpoch(final DirectBuffer src, final int srcOffset, final int length) { if (length > 65534) @@ -1420,14 +1428,7 @@ public TokenCodecEncoder putEpoch(final DirectBuffer src, final int srcOffset, f if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_CHARACTERENCODING_DONE: - codecState(CodecState.V0_EPOCH_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"epoch\" in state: " + codecState()); - } + onEpochAccessed(); } final int headerLength = 2; @@ -1448,14 +1449,7 @@ public TokenCodecEncoder putEpoch(final byte[] src, final int srcOffset, final i if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_CHARACTERENCODING_DONE: - codecState(CodecState.V0_EPOCH_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"epoch\" in state: " + codecState()); - } + onEpochAccessed(); } final int headerLength = 2; @@ -1479,14 +1473,7 @@ public TokenCodecEncoder epoch(final String value) if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_CHARACTERENCODING_DONE: - codecState(CodecState.V0_EPOCH_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"epoch\" in state: " + codecState()); - } + onEpochAccessed(); } final int headerLength = 2; @@ -1523,6 +1510,18 @@ public static int timeUnitHeaderLength() return 2; } + private void onTimeUnitAccessed() + { + switch (codecState()) + { + case V0_EPOCH_DONE: + codecState(CodecState.V0_TIMEUNIT_DONE); + break; + default: + throw new IllegalStateException("Cannot access field \"timeUnit\" in state: " + codecState()); + } + } + public TokenCodecEncoder putTimeUnit(final DirectBuffer src, final int srcOffset, final int length) { if (length > 65534) @@ -1532,14 +1531,7 @@ public TokenCodecEncoder putTimeUnit(final DirectBuffer src, final int srcOffset if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_EPOCH_DONE: - codecState(CodecState.V0_TIMEUNIT_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"timeUnit\" in state: " + codecState()); - } + onTimeUnitAccessed(); } final int headerLength = 2; @@ -1560,14 +1552,7 @@ public TokenCodecEncoder putTimeUnit(final byte[] src, final int srcOffset, fina if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_EPOCH_DONE: - codecState(CodecState.V0_TIMEUNIT_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"timeUnit\" in state: " + codecState()); - } + onTimeUnitAccessed(); } final int headerLength = 2; @@ -1591,14 +1576,7 @@ public TokenCodecEncoder timeUnit(final String value) if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_EPOCH_DONE: - codecState(CodecState.V0_TIMEUNIT_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"timeUnit\" in state: " + codecState()); - } + onTimeUnitAccessed(); } final int headerLength = 2; @@ -1635,6 +1613,18 @@ public static int semanticTypeHeaderLength() return 2; } + private void onSemanticTypeAccessed() + { + switch (codecState()) + { + case V0_TIMEUNIT_DONE: + codecState(CodecState.V0_SEMANTICTYPE_DONE); + break; + default: + throw new IllegalStateException("Cannot access field \"semanticType\" in state: " + codecState()); + } + } + public TokenCodecEncoder putSemanticType(final DirectBuffer src, final int srcOffset, final int length) { if (length > 65534) @@ -1644,14 +1634,7 @@ public TokenCodecEncoder putSemanticType(final DirectBuffer src, final int srcOf if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_TIMEUNIT_DONE: - codecState(CodecState.V0_SEMANTICTYPE_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"semanticType\" in state: " + codecState()); - } + onSemanticTypeAccessed(); } final int headerLength = 2; @@ -1672,14 +1655,7 @@ public TokenCodecEncoder putSemanticType(final byte[] src, final int srcOffset, if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_TIMEUNIT_DONE: - codecState(CodecState.V0_SEMANTICTYPE_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"semanticType\" in state: " + codecState()); - } + onSemanticTypeAccessed(); } final int headerLength = 2; @@ -1703,14 +1679,7 @@ public TokenCodecEncoder semanticType(final String value) if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_TIMEUNIT_DONE: - codecState(CodecState.V0_SEMANTICTYPE_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"semanticType\" in state: " + codecState()); - } + onSemanticTypeAccessed(); } final int headerLength = 2; @@ -1747,6 +1716,18 @@ public static int descriptionHeaderLength() return 2; } + private void onDescriptionAccessed() + { + switch (codecState()) + { + case V0_SEMANTICTYPE_DONE: + codecState(CodecState.V0_DESCRIPTION_DONE); + break; + default: + throw new IllegalStateException("Cannot access field \"description\" in state: " + codecState()); + } + } + public TokenCodecEncoder putDescription(final DirectBuffer src, final int srcOffset, final int length) { if (length > 65534) @@ -1756,14 +1737,7 @@ public TokenCodecEncoder putDescription(final DirectBuffer src, final int srcOff if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_SEMANTICTYPE_DONE: - codecState(CodecState.V0_DESCRIPTION_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"description\" in state: " + codecState()); - } + onDescriptionAccessed(); } final int headerLength = 2; @@ -1784,14 +1758,7 @@ public TokenCodecEncoder putDescription(final byte[] src, final int srcOffset, f if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_SEMANTICTYPE_DONE: - codecState(CodecState.V0_DESCRIPTION_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"description\" in state: " + codecState()); - } + onDescriptionAccessed(); } final int headerLength = 2; @@ -1815,14 +1782,7 @@ public TokenCodecEncoder description(final String value) if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_SEMANTICTYPE_DONE: - codecState(CodecState.V0_DESCRIPTION_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"description\" in state: " + codecState()); - } + onDescriptionAccessed(); } final int headerLength = 2; @@ -1859,6 +1819,18 @@ public static int referencedNameHeaderLength() return 2; } + private void onReferencedNameAccessed() + { + switch (codecState()) + { + case V0_DESCRIPTION_DONE: + codecState(CodecState.V0_REFERENCEDNAME_DONE); + break; + default: + throw new IllegalStateException("Cannot access field \"referencedName\" in state: " + codecState()); + } + } + public TokenCodecEncoder putReferencedName(final DirectBuffer src, final int srcOffset, final int length) { if (length > 65534) @@ -1868,14 +1840,7 @@ public TokenCodecEncoder putReferencedName(final DirectBuffer src, final int src if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_DESCRIPTION_DONE: - codecState(CodecState.V0_REFERENCEDNAME_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"referencedName\" in state: " + codecState()); - } + onReferencedNameAccessed(); } final int headerLength = 2; @@ -1896,14 +1861,7 @@ public TokenCodecEncoder putReferencedName(final byte[] src, final int srcOffset if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_DESCRIPTION_DONE: - codecState(CodecState.V0_REFERENCEDNAME_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"referencedName\" in state: " + codecState()); - } + onReferencedNameAccessed(); } final int headerLength = 2; @@ -1927,14 +1885,7 @@ public TokenCodecEncoder referencedName(final String value) if (ENABLE_ACCESS_ORDER_CHECKS) { - switch (codecState()) - { - case V0_DESCRIPTION_DONE: - codecState(CodecState.V0_REFERENCEDNAME_DONE); - break; - default: - throw new IllegalStateException("Cannot access field \"referencedName\" in state: " + codecState()); - } + onReferencedNameAccessed(); } final int headerLength = 2; diff --git a/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java b/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java index bda9ac2617..05720f3f75 100644 --- a/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java +++ b/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java @@ -2076,9 +2076,9 @@ void disallowsEncodingArrayInsideGroupBeforeCallingNext2() void disallowsEncodingArrayInsideGroupBeforeCallingNext3() { final ArrayInsideGroupEncoder.BEncoder bEncoder = encodeUntilGroupWithArrayInside(); - final byte[] bytes = {5, 0, 6, 0, 7, 0, 8, 0}; + final byte[] bytes = {5, 6, 7, 8}; final Exception exception = - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.putC(bytes, 0, 8)); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.putC(bytes, 0, 4)); assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_B_N")); } @@ -2087,12 +2087,12 @@ void disallowsEncodingArrayInsideGroupBeforeCallingNext4() { final ArrayInsideGroupEncoder.BEncoder bEncoder = encodeUntilGroupWithArrayInside(); final UnsafeBuffer buffer = new UnsafeBuffer(new byte[8]); - buffer.putShort(0, (short)5); - buffer.putShort(2, (short)6); - buffer.putShort(4, (short)7); - buffer.putShort(6, (short)8); + buffer.putByte(0, (byte)5); + buffer.putByte(2, (byte)6); + buffer.putByte(4, (byte)7); + buffer.putByte(6, (byte)8); final Exception exception = - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.putC(buffer, 0, 8)); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.putC(buffer, 0, 4)); assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_B_N")); } From 19a64482ecc9e05dc7048ffb9949c324a097d020 Mon Sep 17 00:00:00 2001 From: Zach Bray Date: Tue, 20 Jun 2023 11:10:39 +0100 Subject: [PATCH 18/50] [Java] Add feature toggle for access order check generation. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The code generator will omit checks when the `sbe.generate.access.order.checks` system property is set to `false`. Adding the feature toggle allowed me to run the following benchmarks on my ThinkPad P14s, where (unfortunately) there is some noise. Baseline (d2ec8f8a225808a1392872473f6e5e85b828056e): ``` $ java -jar -Dagrona.disable.bounds.checks=true -Dsbe.enable.access.order.checks=false ./sbe-benchmarks/build/libs/sbe-benchmarks.jar ".*Car.*" -bm thrpt -f 2 -i 3 -wi 2 \# JMH version: 1.36 \# JMH version: 1.36 \# JMH version: 1.36 \# JMH version: 1.36 \# VM version: JDK 1.8.0_302, OpenJDK 64-Bit Server VM, 25.302-b08 \# VM invoker: /home/zach/.asdf/installs/java/zulu-8.56.0.21/jre/bin/java \# VM options: -Dagrona.disable.bounds.checks=true -Dsbe.enable.access.order.checks=false ... Benchmark Mode Cnt Score Error Units CarBenchmark.testDecode thrpt 6 19270366.357 ± 2618336.027 ops/s CarBenchmark.testEncode thrpt 6 22519718.535 ± 794169.180 ops/s ``` This commit with new code generation disabled, i.e., `-Dsbe.generate.access.order.checks=false` at build time: ``` $ java -jar -Dagrona.disable.bounds.checks=true -Dsbe.enable.access.order.checks=false ./sbe-benchmarks/build/libs/sbe-benchmarks.jar ".*Car.*" -bm thrpt -f 2 -i 3 -wi 2 \# JMH version: 1.36 \# VM version: JDK 1.8.0_302, OpenJDK 64-Bit Server VM, 25.302-b08 \# VM invoker: /home/zach/.asdf/installs/java/zulu-8.56.0.21/jre/bin/java \# VM options: -Dagrona.disable.bounds.checks=true -Dsbe.enable.access.order.checks=false ... Benchmark Mode Cnt Score Error Units CarBenchmark.testDecode thrpt 6 19290928.253 ± 2226604.698 ops/s CarBenchmark.testEncode thrpt 6 21957767.395 ± 567000.570 ops/s ``` This commit with new code generation enabled, but disabled checks, i.e., `-Dsbe.enable.access.order.checks=false` at runtime. ``` $ java -jar -Dagrona.disable.bounds.checks=true -Dsbe.enable.access.order.checks=false ./sbe-benchmarks/build/libs/sbe-benchmarks.jar ".*Car.*" -bm thrpt -f 2 -i 3 -wi 2 \# JMH version: 1.36 \# VM version: JDK 1.8.0_302, OpenJDK 64-Bit Server VM, 25.302-b08 \# VM invoker: /home/zach/.asdf/installs/java/zulu-8.56.0.21/jre/bin/java \# VM options: -Dagrona.disable.bounds.checks=true -Dsbe.enable.access.order.checks=false ... Benchmark Mode Cnt Score Error Units CarBenchmark.testDecode thrpt 6 19238066.328 ± 2557533.640 ops/s CarBenchmark.testEncode thrpt 6 22284274.779 ± 520984.898 ops/s ``` Whilst it is hard to definitively conclude anything using my noisy machine to benchmark, it seems like there is no obvious performance regression when the runtime checks are disabled in comparison to omitting the checks at build time. --- build.gradle | 3 +- .../sbe/generation/java/FieldOrderModel.java | 7 + .../sbe/generation/java/JavaGenerator.java | 240 +++++++++++------- 3 files changed, 158 insertions(+), 92 deletions(-) diff --git a/build.gradle b/build.gradle index 786ec9c07b..7f0c5264aa 100644 --- a/build.gradle +++ b/build.gradle @@ -542,7 +542,8 @@ project(':sbe-benchmarks') { 'sbe.validation.stop.on.error': 'true', 'sbe.validation.xsd': validationXsdPath, 'sbe.java.encoding.buffer.type': 'org.agrona.concurrent.UnsafeBuffer', - 'sbe.java.decoding.buffer.type': 'org.agrona.concurrent.UnsafeBuffer') + 'sbe.java.decoding.buffer.type': 'org.agrona.concurrent.UnsafeBuffer', + 'sbe.generate.access.order.checks': 'false') args = ['src/main/resources/car.xml', 'src/main/resources/fix-message-samples.xml'] } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java index cff2531ae3..e2ba3f6e72 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java @@ -34,6 +34,8 @@ @SuppressWarnings("DuplicatedCode") // there is no abstraction for visiting fields, groups, and varData final class FieldOrderModel { + private static final boolean GENERATE_ACCESS_ORDER_CHECKS = Boolean.parseBoolean( + System.getProperty("sbe.generate.access.order.checks", "true")); private final Int2ObjectHashMap states = new Int2ObjectHashMap<>(); private final Map transitions = new LinkedHashMap<>(); private final Set topLevelBlockFields = new HashSet<>(); @@ -86,6 +88,11 @@ public List getTransitions(final TransitionContext context, final To return transitionGroup.transitions.get(context); } + public static boolean generateAccessOrderChecks() + { + return GENERATE_ACCESS_ORDER_CHECKS; + } + public static FieldOrderModel newInstance( final Token msgToken, final List fields, diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java index 64c9f207de..662e5194ba 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java @@ -250,7 +250,9 @@ private void generateEncoder( generateAnnotations(BASE_INDENT, className, groups, out, this::encoderName); } out.append(generateDeclaration(className, implementsString, msgToken)); - final FieldOrderModel fieldOrderModel = FieldOrderModel.newInstance(msgToken, fields, groups, varData); + final FieldOrderModel fieldOrderModel = FieldOrderModel.generateAccessOrderChecks() ? + FieldOrderModel.newInstance(msgToken, fields, groups, varData) : + null; out.append(generateFieldOrderStates(fieldOrderModel)); out.append(generateEncoderFlyweightCode(className, fieldOrderModel, msgToken)); @@ -278,6 +280,11 @@ private static CharSequence unqualifiedStateCase(final FieldOrderModel.State sta private static CharSequence generateFieldOrderStates(final FieldOrderModel fieldOrderModel) { + if (null == fieldOrderModel) + { + return ""; + } + final StringBuilder sb = new StringBuilder(); sb.append(" private static final boolean ENABLE_BOUNDS_CHECKS = ") @@ -332,7 +339,7 @@ private static void generateAccessOrderListenerMethod( final String indent, final Token token) { - if (fieldOrderModel == null) + if (null == fieldOrderModel) { return; } @@ -370,7 +377,7 @@ private static CharSequence generateAccessOrderListenerCall( final String methodName, final String... arguments) { - if (fieldOrderModel == null) + if (null == fieldOrderModel) { return ""; } @@ -401,6 +408,11 @@ private static void generateAccessOrderListenerMethodForGroupWrap( final String indent, final Token token) { + if (null == fieldOrderModel) + { + return; + } + sb.append("\n") .append(indent).append("private void ").append(accessOrderListenerMethodName(token)) .append("(final int remaining)\n") @@ -478,6 +490,11 @@ private static void generateAccessOrderListenerMethodForNextGroupElement( final String indent, final Token token) { + if (null == fieldOrderModel) + { + return; + } + sb.append(indent).append("private void onNextElementAccessed()\n") .append(indent).append("{\n") .append(indent).append(" final int remaining = ").append("count - index").append(";\n") @@ -510,6 +527,11 @@ private static CharSequence generateDecoderWrapListener( final FieldOrderModel fieldOrderModel, final String indent) { + if (null == fieldOrderModel) + { + return ""; + } + final StringBuilder sb = new StringBuilder(); sb.append(indent).append("private void onWrap(final int actingVersion)\n") .append(indent).append("{\n") @@ -534,6 +556,24 @@ private static CharSequence generateDecoderWrapListener( return sb; } + private CharSequence generateEncoderWrapListener( + final FieldOrderModel fieldOrderModel, + final String indent) + { + if (null == fieldOrderModel) + { + return ""; + } + + final StringBuilder sb = new StringBuilder(); + sb.append(indent).append("if (ENABLE_ACCESS_ORDER_CHECKS)") + .append("\n").append(indent).append("{\n") + .append(indent).append(" codecState(") + .append(qualifiedStateCase(fieldOrderModel.latestVersionWrappedState())) + .append(");\n") + .append(indent).append("}\n\n"); + return sb; + } private void generateDecoder( final Token msgToken, @@ -555,8 +595,13 @@ private void generateDecoder( generateAnnotations(BASE_INDENT, className, groups, out, this::decoderName); } out.append(generateDeclaration(className, implementsString, msgToken)); - final FieldOrderModel fieldOrderModel = FieldOrderModel.newInstance( - msgToken, fields, groups, varData); + + FieldOrderModel fieldOrderModel = null; + if (FieldOrderModel.generateAccessOrderChecks()) + { + fieldOrderModel = FieldOrderModel.newInstance(msgToken, fields, groups, varData); + } + out.append(generateFieldOrderStates(fieldOrderModel)); out.append(generateDecoderFlyweightCode(fieldOrderModel, className, msgToken)); @@ -787,17 +832,20 @@ private void generateGroupDecoderClassHeader( .append(indent).append(" return index < count;\n") .append(indent).append(" }\n"); - sb.append("\n") - .append(indent).append(" private CodecState codecState()\n") - .append(indent).append(" {\n") - .append(indent).append(" return parentMessage.codecState();\n") - .append(indent).append(" }\n"); + if (null != fieldOrderModel) + { + sb.append("\n") + .append(indent).append(" private CodecState codecState()\n") + .append(indent).append(" {\n") + .append(indent).append(" return parentMessage.codecState();\n") + .append(indent).append(" }\n"); - sb.append("\n") - .append(indent).append(" private void codecState(final CodecState newState)\n") - .append(indent).append(" {\n") - .append(indent).append(" parentMessage.codecState(newState);\n") - .append(indent).append(" }\n"); + sb.append("\n") + .append(indent).append(" private void codecState(final CodecState newState)\n") + .append(indent).append(" {\n") + .append(indent).append(" parentMessage.codecState(newState);\n") + .append(indent).append(" }\n"); + } } private void generateGroupEncoderClassHeader( @@ -914,17 +962,20 @@ private void generateGroupEncoderClassHeader( .append(ind).append(" return ").append(blockLength).append(";\n") .append(ind).append(" }\n"); - sb.append("\n") - .append(ind).append(" private CodecState codecState()\n") - .append(ind).append(" {\n") - .append(ind).append(" return parentMessage.codecState();\n") - .append(ind).append(" }\n"); + if (null != fieldOrderModel) + { + sb.append("\n") + .append(ind).append(" private CodecState codecState()\n") + .append(ind).append(" {\n") + .append(ind).append(" return parentMessage.codecState();\n") + .append(ind).append(" }\n"); - sb.append("\n") - .append(ind).append(" private void codecState(final CodecState newState)\n") - .append(ind).append(" {\n") - .append(ind).append(" parentMessage.codecState(newState);\n") - .append(ind).append(" }\n"); + sb.append("\n") + .append(ind).append(" private void codecState(final CodecState newState)\n") + .append(ind).append(" {\n") + .append(ind).append(" parentMessage.codecState(newState);\n") + .append(ind).append(" }\n"); + } } private static String primitiveTypeName(final Token token) @@ -3090,73 +3141,83 @@ private CharSequence generateDecoderFlyweightCode( { final String headerClassName = formatClassName(ir.headerStructure().tokens().get(0).applicableTypeName()); - final String methods = - generateDecoderWrapListener(fieldOrderModel, " ") + - " public " + className + " wrap(\n" + - " final " + readOnlyBuffer + " buffer,\n" + - " final int offset,\n" + - " final int actingBlockLength,\n" + - " final int actingVersion)\n" + - " {\n" + - " if (buffer != this.buffer)\n" + - " {\n" + - " this.buffer = buffer;\n" + - " }\n" + - " this.initialOffset = offset;\n" + - " this.offset = offset;\n" + - " this.actingBlockLength = actingBlockLength;\n" + - " this.actingVersion = actingVersion;\n" + - " limit(offset + actingBlockLength);\n\n" + - " if (ENABLE_ACCESS_ORDER_CHECKS)\n" + - " {\n" + - " onWrap(actingVersion);\n" + - " }\n\n" + - " return this;\n" + - " }\n\n" + + final StringBuilder methods = new StringBuilder(); - " public " + className + " wrapAndApplyHeader(\n" + - " final " + readOnlyBuffer + " buffer,\n" + - " final int offset,\n" + - " final " + headerClassName + "Decoder headerDecoder)\n" + - " {\n" + - " headerDecoder.wrap(buffer, offset);\n\n" + - " final int templateId = headerDecoder.templateId();\n" + - " if (TEMPLATE_ID != templateId)\n" + - " {\n" + - " throw new IllegalStateException(\"Invalid TEMPLATE_ID: \" + templateId);\n" + - " }\n\n" + - " return wrap(\n" + - " buffer,\n" + - " offset + " + headerClassName + "Decoder.ENCODED_LENGTH,\n" + - " headerDecoder.blockLength(),\n" + - " headerDecoder.version());\n" + - " }\n\n" + + methods.append(generateDecoderWrapListener(fieldOrderModel, " ")); - " public " + className + " sbeRewind()\n" + - " {\n" + - " return wrap(buffer, initialOffset, actingBlockLength, actingVersion);\n" + - " }\n\n" + + methods.append(" public ").append(className).append(" wrap(\n") + .append(" final ").append(readOnlyBuffer).append(" buffer,\n") + .append(" final int offset,\n") + .append(" final int actingBlockLength,\n") + .append(" final int actingVersion)\n") + .append(" {\n") + .append(" if (buffer != this.buffer)\n") + .append(" {\n") + .append(" this.buffer = buffer;\n") + .append(" }\n") + .append(" this.initialOffset = offset;\n") + .append(" this.offset = offset;\n") + .append(" this.actingBlockLength = actingBlockLength;\n") + .append(" this.actingVersion = actingVersion;\n") + .append(" limit(offset + actingBlockLength);\n\n") + .append(generateAccessOrderListenerCall(fieldOrderModel, " ", "onWrap", "actingVersion")) + .append(" return this;\n") + .append(" }\n\n"); - " public int sbeDecodedLength()\n" + - " {\n" + - " final int currentLimit = limit();\n" + - " final CodecState currentCodecState = codecState();\n" + - " sbeSkip();\n" + - " final int decodedLength = encodedLength();\n" + - " limit(currentLimit);\n\n" + - " if (ENABLE_ACCESS_ORDER_CHECKS)\n" + - " {\n" + - " codecState(currentCodecState);\n" + - " }\n\n" + - " return decodedLength;\n" + - " }\n\n" + + methods.append(" public ").append(className).append(" wrapAndApplyHeader(\n") + .append(" final ").append(readOnlyBuffer).append(" buffer,\n") + .append(" final int offset,\n") + .append(" final ").append(headerClassName).append("Decoder headerDecoder)\n") + .append(" {\n") + .append(" headerDecoder.wrap(buffer, offset);\n\n") + .append(" final int templateId = headerDecoder.templateId();\n") + .append(" if (TEMPLATE_ID != templateId)\n") + .append(" {\n") + .append(" throw new IllegalStateException(\"Invalid TEMPLATE_ID: \" + templateId);\n") + .append(" }\n\n") + .append(" return wrap(\n") + .append(" buffer,\n") + .append(" offset + ").append(headerClassName).append("Decoder.ENCODED_LENGTH,\n") + .append(" headerDecoder.blockLength(),\n") + .append(" headerDecoder.version());\n") + .append(" }\n\n"); - " public int actingVersion()\n" + - " {\n" + - " return actingVersion;\n" + - " }\n\n"; + methods.append(" public ").append(className).append(" sbeRewind()\n") + .append(" {\n") + .append(" return wrap(buffer, initialOffset, actingBlockLength, actingVersion);\n") + .append(" }\n\n"); + + methods.append(" public int sbeDecodedLength()\n") + .append(" {\n") + .append(" final int currentLimit = limit();\n"); + + if (null != fieldOrderModel) + { + methods.append(" final CodecState currentCodecState = codecState();\n"); + } + + methods + .append(" sbeSkip();\n") + .append(" final int decodedLength = encodedLength();\n") + .append(" limit(currentLimit);\n\n"); + + if (null != fieldOrderModel) + { + methods.append(" if (ENABLE_ACCESS_ORDER_CHECKS)\n") + .append(" {\n") + .append(" codecState(currentCodecState);\n") + .append(" }\n\n"); + } - return generateFlyweightCode(DECODER, className, token, methods, readOnlyBuffer); + methods.append(" return decodedLength;\n") + .append(" }\n\n"); + + methods.append(" public int actingVersion()\n") + .append(" {\n") + .append(" return actingVersion;\n") + .append(" }\n\n"); + + return generateFlyweightCode(DECODER, className, token, methods.toString(), readOnlyBuffer); } private CharSequence generateFlyweightCode( @@ -3277,10 +3338,7 @@ private CharSequence generateEncoderFlyweightCode( " this.initialOffset = offset;\n" + " this.offset = offset;\n" + " limit(offset + BLOCK_LENGTH);\n\n" + - " if (ENABLE_ACCESS_ORDER_CHECKS)\n" + - " {\n" + - " codecState(" + qualifiedStateCase(fieldOrderModel.latestVersionWrappedState()) + ");\n" + - " }\n\n" + + generateEncoderWrapListener(fieldOrderModel, " ") + " return this;\n" + " }\n\n"; From e7a9cf226d10fe4d20678bd3613b8bded18bf254 Mon Sep 17 00:00:00 2001 From: Zach Bray Date: Tue, 20 Jun 2023 12:16:48 +0100 Subject: [PATCH 19/50] [Java] Improve performance of field access order checking. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit switches the generated code to using an int to represent the state rather than a reference to an enum case. The change improves performance significantly, which I suspect is due to avoiding a write barrier that updates the card table. Relevant benchmark runs are below. Perhaps different JVM versions and GC options will yield different relative improvements. All benchmark runs use: - `-Dsbe.generate.access.order.checks=true` at build time - `-Dsbe.enable.access.order.checks=true` at runtime Using an enum: ``` $ java -jar -Dagrona.disable.bounds.checks=true -Dsbe.enable.access.order.checks=true ./sbe-benchmarks/build/libs/sbe-benchmarks.jar ".*Car.*" -bm thrpt -f 2 -i 3 -wi 2 \# JMH version: 1.36 \# VM version: JDK 1.8.0_302, OpenJDK 64-Bit Server VM, 25.302-b08 \# VM invoker: /home/zach/.asdf/installs/java/zulu-8.56.0.21/jre/bin/java \# VM options: -Dagrona.disable.bounds.checks=true -Dsbe.enable.access.order.checks=true ... Benchmark Mode Cnt Score Error Units CarBenchmark.testDecode thrpt 6 8075519.641 ± 363346.224 ops/s CarBenchmark.testEncode thrpt 6 7211523.145 ± 504174.913 ops/s ``` Using an int: ``` $ java -jar -Dagrona.disable.bounds.checks=true -Dsbe.enable.access.order.checks=true ./sbe-benchmarks/build/libs/sbe-benchmarks.jar ".*Car.*" -bm thrpt -f 2 -i 3 -wi 2 \# JMH version: 1.36 \# VM version: JDK 1.8.0_302, OpenJDK 64-Bit Server VM, 25.302-b08 \# VM invoker: /home/zach/.asdf/installs/java/zulu-8.56.0.21/jre/bin/java \# VM options: -Dagrona.disable.bounds.checks=true -Dsbe.enable.access.order.checks=true ... Benchmark Mode Cnt Score Error Units CarBenchmark.testDecode thrpt 6 13030684.127 ± 155782.171 ops/s CarBenchmark.testEncode thrpt 6 9781612.505 ± 1342559.378 ops/s ``` --- .../sbe/generation/java/JavaGenerator.java | 50 ++++-- .../sbe/ir/generated/FrameCodecDecoder.java | 68 ++++--- .../sbe/ir/generated/FrameCodecEncoder.java | 64 ++++--- .../sbe/ir/generated/TokenCodecDecoder.java | 168 ++++++++++-------- .../sbe/ir/generated/TokenCodecEncoder.java | 164 +++++++++-------- 5 files changed, 303 insertions(+), 211 deletions(-) diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java index 662e5194ba..5a4a3e490a 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java @@ -270,7 +270,12 @@ private void generateEncoder( private static CharSequence qualifiedStateCase(final FieldOrderModel.State state) { - return "CodecState." + state.name(); + return "CodecStates." + state.name(); + } + + private static CharSequence stateCaseForSwitchCase(final FieldOrderModel.State state) + { + return qualifiedStateCase(state); } private static CharSequence unqualifiedStateCase(final FieldOrderModel.State state) @@ -304,23 +309,38 @@ private static CharSequence generateFieldOrderStates(final FieldOrderModel field fieldOrderModel.generateGraph(sb, " * "); sb.append(" * }\n"); sb.append(" */\n"); - sb.append(" private enum CodecState\n") + sb.append(" private static class CodecStates\n") .append(" {\n"); fieldOrderModel.forEachState(state -> - sb.append(" ").append(unqualifiedStateCase(state)).append(",\n")); - + { + sb.append(" private static final int ") + .append(unqualifiedStateCase(state)) + .append(" = ").append(state.number()) + .append(";\n"); + }); + sb.append("\n").append(" private static final String[] STATE_NAME_LOOKUP =\n") + .append(" {\n"); + fieldOrderModel.forEachState(state -> + { + sb.append(" \"").append(state.name()).append("\",\n"); + }); + sb.append(" };\n\n"); + sb.append(" private static String name(final int state)\n") + .append(" {\n") + .append(" return STATE_NAME_LOOKUP[state];\n") + .append(" }\n"); sb.append(" }\n\n"); - sb.append(" private CodecState codecState = ") + sb.append(" private int codecState = ") .append(qualifiedStateCase(fieldOrderModel.notWrappedState())) .append(";\n\n"); - sb.append(" private CodecState codecState()\n") + sb.append(" private int codecState()\n") .append(" {\n") .append(" return codecState;\n") .append(" }\n\n"); - sb.append(" private void codecState(CodecState newState)\n") + sb.append(" private void codecState(int newState)\n") .append(" {\n") .append(" codecState = newState;\n") .append(" }\n\n"); @@ -457,7 +477,7 @@ private static void generateAccessOrderListener( .append(indent).append("{\n") .append(indent).append(" throw new IllegalStateException(") .append("\"Cannot access field \\\"").append(token.name()) - .append("\\\" in state: \" + codecState());\n") + .append("\\\" in state: \" + CodecStates.name(codecState()));\n") .append(indent).append("}\n"); } else @@ -470,7 +490,7 @@ private static void generateAccessOrderListener( transitions.forEach(transition -> { transition.forEachStartState(startState -> - sb.append(indent).append(" case ").append(unqualifiedStateCase(startState)).append(":\n")); + sb.append(indent).append(" case ").append(stateCaseForSwitchCase(startState)).append(":\n")); sb.append(indent).append(" codecState(") .append(qualifiedStateCase(transition.endState())).append(");\n") .append(indent).append(" break;\n"); @@ -479,7 +499,7 @@ private static void generateAccessOrderListener( sb.append(indent).append(" default:\n") .append(indent).append(" throw new IllegalStateException(") .append("\"Cannot access field \\\"").append(token.name()) - .append("\\\" in state: \" + codecState());\n") + .append("\\\" in state: \" + CodecStates.name(codecState()));\n") .append(indent).append("}\n"); } } @@ -835,13 +855,13 @@ private void generateGroupDecoderClassHeader( if (null != fieldOrderModel) { sb.append("\n") - .append(indent).append(" private CodecState codecState()\n") + .append(indent).append(" private int codecState()\n") .append(indent).append(" {\n") .append(indent).append(" return parentMessage.codecState();\n") .append(indent).append(" }\n"); sb.append("\n") - .append(indent).append(" private void codecState(final CodecState newState)\n") + .append(indent).append(" private void codecState(final int newState)\n") .append(indent).append(" {\n") .append(indent).append(" parentMessage.codecState(newState);\n") .append(indent).append(" }\n"); @@ -965,13 +985,13 @@ private void generateGroupEncoderClassHeader( if (null != fieldOrderModel) { sb.append("\n") - .append(ind).append(" private CodecState codecState()\n") + .append(ind).append(" private int codecState()\n") .append(ind).append(" {\n") .append(ind).append(" return parentMessage.codecState();\n") .append(ind).append(" }\n"); sb.append("\n") - .append(ind).append(" private void codecState(final CodecState newState)\n") + .append(ind).append(" private void codecState(final int newState)\n") .append(ind).append(" {\n") .append(ind).append(" parentMessage.codecState(newState);\n") .append(ind).append(" }\n"); @@ -3193,7 +3213,7 @@ private CharSequence generateDecoderFlyweightCode( if (null != fieldOrderModel) { - methods.append(" final CodecState currentCodecState = codecState();\n"); + methods.append(" final int currentCodecState = codecState();\n"); } methods diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java index 4ca802ae6a..e4fb9233c9 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java @@ -35,23 +35,37 @@ public final class FrameCodecDecoder * } * } */ - private enum CodecState + private static class CodecStates { - NOT_WRAPPED, - V0_BLOCK, - V0_PACKAGENAME_DONE, - V0_NAMESPACENAME_DONE, - V0_SEMANTICVERSION_DONE, + private static final int NOT_WRAPPED = 0; + private static final int V0_BLOCK = 1; + private static final int V0_PACKAGENAME_DONE = 2; + private static final int V0_NAMESPACENAME_DONE = 3; + private static final int V0_SEMANTICVERSION_DONE = 4; + + private static final String[] STATE_NAME_LOOKUP = + { + "NOT_WRAPPED", + "V0_BLOCK", + "V0_PACKAGENAME_DONE", + "V0_NAMESPACENAME_DONE", + "V0_SEMANTICVERSION_DONE", + }; + + private static String name(final int state) + { + return STATE_NAME_LOOKUP[state]; + } } - private CodecState codecState = CodecState.NOT_WRAPPED; + private int codecState = CodecStates.NOT_WRAPPED; - private CodecState codecState() + private int codecState() { return codecState; } - private void codecState(CodecState newState) + private void codecState(int newState) { codecState = newState; } @@ -116,10 +130,10 @@ private void onWrap(final int actingVersion) switch(actingVersion) { case 0: - codecState(CodecState.V0_BLOCK); + codecState(CodecStates.V0_BLOCK); break; default: - codecState(CodecState.V0_BLOCK); + codecState(CodecStates.V0_BLOCK); break; } } @@ -176,7 +190,7 @@ public FrameCodecDecoder sbeRewind() public int sbeDecodedLength() { final int currentLimit = limit(); - final CodecState currentCodecState = codecState(); + final int currentCodecState = codecState(); sbeSkip(); final int decodedLength = encodedLength(); limit(currentLimit); @@ -241,9 +255,9 @@ public static String irIdMetaAttribute(final MetaAttribute metaAttribute) private void onIrIdAccessed() { - if (codecState() == CodecState.NOT_WRAPPED) + if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"irId\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"irId\" in state: " + CodecStates.name(codecState())); } } @@ -305,9 +319,9 @@ public static String irVersionMetaAttribute(final MetaAttribute metaAttribute) private void onIrVersionAccessed() { - if (codecState() == CodecState.NOT_WRAPPED) + if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"irVersion\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"irVersion\" in state: " + CodecStates.name(codecState())); } } @@ -369,9 +383,9 @@ public static String schemaVersionMetaAttribute(final MetaAttribute metaAttribut private void onSchemaVersionAccessed() { - if (codecState() == CodecState.NOT_WRAPPED) + if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"schemaVersion\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"schemaVersion\" in state: " + CodecStates.name(codecState())); } } @@ -435,11 +449,11 @@ private void onPackageNameAccessed() { switch (codecState()) { - case V0_BLOCK: - codecState(CodecState.V0_PACKAGENAME_DONE); + case CodecStates.V0_BLOCK: + codecState(CodecStates.V0_PACKAGENAME_DONE); break; default: - throw new IllegalStateException("Cannot access field \"packageName\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"packageName\" in state: " + CodecStates.name(codecState())); } } @@ -575,11 +589,11 @@ private void onNamespaceNameAccessed() { switch (codecState()) { - case V0_PACKAGENAME_DONE: - codecState(CodecState.V0_NAMESPACENAME_DONE); + case CodecStates.V0_PACKAGENAME_DONE: + codecState(CodecStates.V0_NAMESPACENAME_DONE); break; default: - throw new IllegalStateException("Cannot access field \"namespaceName\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"namespaceName\" in state: " + CodecStates.name(codecState())); } } @@ -715,11 +729,11 @@ private void onSemanticVersionAccessed() { switch (codecState()) { - case V0_NAMESPACENAME_DONE: - codecState(CodecState.V0_SEMANTICVERSION_DONE); + case CodecStates.V0_NAMESPACENAME_DONE: + codecState(CodecStates.V0_SEMANTICVERSION_DONE); break; default: - throw new IllegalStateException("Cannot access field \"semanticVersion\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"semanticVersion\" in state: " + CodecStates.name(codecState())); } } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java index 0f6b8dfc22..a3feef8bb5 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java @@ -35,23 +35,37 @@ public final class FrameCodecEncoder * } * } */ - private enum CodecState + private static class CodecStates { - NOT_WRAPPED, - V0_BLOCK, - V0_PACKAGENAME_DONE, - V0_NAMESPACENAME_DONE, - V0_SEMANTICVERSION_DONE, + private static final int NOT_WRAPPED = 0; + private static final int V0_BLOCK = 1; + private static final int V0_PACKAGENAME_DONE = 2; + private static final int V0_NAMESPACENAME_DONE = 3; + private static final int V0_SEMANTICVERSION_DONE = 4; + + private static final String[] STATE_NAME_LOOKUP = + { + "NOT_WRAPPED", + "V0_BLOCK", + "V0_PACKAGENAME_DONE", + "V0_NAMESPACENAME_DONE", + "V0_SEMANTICVERSION_DONE", + }; + + private static String name(final int state) + { + return STATE_NAME_LOOKUP[state]; + } } - private CodecState codecState = CodecState.NOT_WRAPPED; + private int codecState = CodecStates.NOT_WRAPPED; - private CodecState codecState() + private int codecState() { return codecState; } - private void codecState(CodecState newState) + private void codecState(int newState) { codecState = newState; } @@ -121,7 +135,7 @@ public FrameCodecEncoder wrap(final MutableDirectBuffer buffer, final int offset if (ENABLE_ACCESS_ORDER_CHECKS) { - codecState(CodecState.V0_BLOCK); + codecState(CodecStates.V0_BLOCK); } return this; @@ -187,9 +201,9 @@ public static String irIdMetaAttribute(final MetaAttribute metaAttribute) private void onIrIdAccessed() { - if (codecState() == CodecState.NOT_WRAPPED) + if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"irId\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"irId\" in state: " + CodecStates.name(codecState())); } } @@ -252,9 +266,9 @@ public static String irVersionMetaAttribute(final MetaAttribute metaAttribute) private void onIrVersionAccessed() { - if (codecState() == CodecState.NOT_WRAPPED) + if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"irVersion\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"irVersion\" in state: " + CodecStates.name(codecState())); } } @@ -317,9 +331,9 @@ public static String schemaVersionMetaAttribute(final MetaAttribute metaAttribut private void onSchemaVersionAccessed() { - if (codecState() == CodecState.NOT_WRAPPED) + if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"schemaVersion\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"schemaVersion\" in state: " + CodecStates.name(codecState())); } } @@ -379,11 +393,11 @@ private void onPackageNameAccessed() { switch (codecState()) { - case V0_BLOCK: - codecState(CodecState.V0_PACKAGENAME_DONE); + case CodecStates.V0_BLOCK: + codecState(CodecStates.V0_PACKAGENAME_DONE); break; default: - throw new IllegalStateException("Cannot access field \"packageName\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"packageName\" in state: " + CodecStates.name(codecState())); } } @@ -482,11 +496,11 @@ private void onNamespaceNameAccessed() { switch (codecState()) { - case V0_PACKAGENAME_DONE: - codecState(CodecState.V0_NAMESPACENAME_DONE); + case CodecStates.V0_PACKAGENAME_DONE: + codecState(CodecStates.V0_NAMESPACENAME_DONE); break; default: - throw new IllegalStateException("Cannot access field \"namespaceName\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"namespaceName\" in state: " + CodecStates.name(codecState())); } } @@ -585,11 +599,11 @@ private void onSemanticVersionAccessed() { switch (codecState()) { - case V0_NAMESPACENAME_DONE: - codecState(CodecState.V0_SEMANTICVERSION_DONE); + case CodecStates.V0_NAMESPACENAME_DONE: + codecState(CodecStates.V0_SEMANTICVERSION_DONE); break; default: - throw new IllegalStateException("Cannot access field \"semanticVersion\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"semanticVersion\" in state: " + CodecStates.name(codecState())); } } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java index 1e0494c66a..fc853da877 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java @@ -50,31 +50,53 @@ public final class TokenCodecDecoder * } * } */ - private enum CodecState + private static class CodecStates { - NOT_WRAPPED, - V0_BLOCK, - V0_NAME_DONE, - V0_CONSTVALUE_DONE, - V0_MINVALUE_DONE, - V0_MAXVALUE_DONE, - V0_NULLVALUE_DONE, - V0_CHARACTERENCODING_DONE, - V0_EPOCH_DONE, - V0_TIMEUNIT_DONE, - V0_SEMANTICTYPE_DONE, - V0_DESCRIPTION_DONE, - V0_REFERENCEDNAME_DONE, + private static final int NOT_WRAPPED = 0; + private static final int V0_BLOCK = 1; + private static final int V0_NAME_DONE = 2; + private static final int V0_CONSTVALUE_DONE = 3; + private static final int V0_MINVALUE_DONE = 4; + private static final int V0_MAXVALUE_DONE = 5; + private static final int V0_NULLVALUE_DONE = 6; + private static final int V0_CHARACTERENCODING_DONE = 7; + private static final int V0_EPOCH_DONE = 8; + private static final int V0_TIMEUNIT_DONE = 9; + private static final int V0_SEMANTICTYPE_DONE = 10; + private static final int V0_DESCRIPTION_DONE = 11; + private static final int V0_REFERENCEDNAME_DONE = 12; + + private static final String[] STATE_NAME_LOOKUP = + { + "NOT_WRAPPED", + "V0_BLOCK", + "V0_NAME_DONE", + "V0_CONSTVALUE_DONE", + "V0_MINVALUE_DONE", + "V0_MAXVALUE_DONE", + "V0_NULLVALUE_DONE", + "V0_CHARACTERENCODING_DONE", + "V0_EPOCH_DONE", + "V0_TIMEUNIT_DONE", + "V0_SEMANTICTYPE_DONE", + "V0_DESCRIPTION_DONE", + "V0_REFERENCEDNAME_DONE", + }; + + private static String name(final int state) + { + return STATE_NAME_LOOKUP[state]; + } } - private CodecState codecState = CodecState.NOT_WRAPPED; + private int codecState = CodecStates.NOT_WRAPPED; - private CodecState codecState() + private int codecState() { return codecState; } - private void codecState(CodecState newState) + private void codecState(int newState) { codecState = newState; } @@ -139,10 +161,10 @@ private void onWrap(final int actingVersion) switch(actingVersion) { case 0: - codecState(CodecState.V0_BLOCK); + codecState(CodecStates.V0_BLOCK); break; default: - codecState(CodecState.V0_BLOCK); + codecState(CodecStates.V0_BLOCK); break; } } @@ -199,7 +221,7 @@ public TokenCodecDecoder sbeRewind() public int sbeDecodedLength() { final int currentLimit = limit(); - final CodecState currentCodecState = codecState(); + final int currentCodecState = codecState(); sbeSkip(); final int decodedLength = encodedLength(); limit(currentLimit); @@ -264,9 +286,9 @@ public static String tokenOffsetMetaAttribute(final MetaAttribute metaAttribute) private void onTokenOffsetAccessed() { - if (codecState() == CodecState.NOT_WRAPPED) + if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"tokenOffset\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"tokenOffset\" in state: " + CodecStates.name(codecState())); } } @@ -328,9 +350,9 @@ public static String tokenSizeMetaAttribute(final MetaAttribute metaAttribute) private void onTokenSizeAccessed() { - if (codecState() == CodecState.NOT_WRAPPED) + if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"tokenSize\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"tokenSize\" in state: " + CodecStates.name(codecState())); } } @@ -392,9 +414,9 @@ public static String fieldIdMetaAttribute(final MetaAttribute metaAttribute) private void onFieldIdAccessed() { - if (codecState() == CodecState.NOT_WRAPPED) + if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"fieldId\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"fieldId\" in state: " + CodecStates.name(codecState())); } } @@ -456,9 +478,9 @@ public static String tokenVersionMetaAttribute(final MetaAttribute metaAttribute private void onTokenVersionAccessed() { - if (codecState() == CodecState.NOT_WRAPPED) + if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"tokenVersion\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"tokenVersion\" in state: " + CodecStates.name(codecState())); } } @@ -520,9 +542,9 @@ public static String componentTokenCountMetaAttribute(final MetaAttribute metaAt private void onComponentTokenCountAccessed() { - if (codecState() == CodecState.NOT_WRAPPED) + if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"componentTokenCount\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"componentTokenCount\" in state: " + CodecStates.name(codecState())); } } @@ -584,9 +606,9 @@ public static String signalMetaAttribute(final MetaAttribute metaAttribute) private void onSignalAccessed() { - if (codecState() == CodecState.NOT_WRAPPED) + if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"signal\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"signal\" in state: " + CodecStates.name(codecState())); } } @@ -643,9 +665,9 @@ public static String primitiveTypeMetaAttribute(final MetaAttribute metaAttribut private void onPrimitiveTypeAccessed() { - if (codecState() == CodecState.NOT_WRAPPED) + if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"primitiveType\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"primitiveType\" in state: " + CodecStates.name(codecState())); } } @@ -702,9 +724,9 @@ public static String byteOrderMetaAttribute(final MetaAttribute metaAttribute) private void onByteOrderAccessed() { - if (codecState() == CodecState.NOT_WRAPPED) + if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"byteOrder\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"byteOrder\" in state: " + CodecStates.name(codecState())); } } @@ -761,9 +783,9 @@ public static String presenceMetaAttribute(final MetaAttribute metaAttribute) private void onPresenceAccessed() { - if (codecState() == CodecState.NOT_WRAPPED) + if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"presence\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"presence\" in state: " + CodecStates.name(codecState())); } } @@ -820,9 +842,9 @@ public static String deprecatedMetaAttribute(final MetaAttribute metaAttribute) private void onDeprecatedAccessed() { - if (codecState() == CodecState.NOT_WRAPPED) + if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"deprecated\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"deprecated\" in state: " + CodecStates.name(codecState())); } } @@ -886,11 +908,11 @@ private void onNameAccessed() { switch (codecState()) { - case V0_BLOCK: - codecState(CodecState.V0_NAME_DONE); + case CodecStates.V0_BLOCK: + codecState(CodecStates.V0_NAME_DONE); break; default: - throw new IllegalStateException("Cannot access field \"name\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"name\" in state: " + CodecStates.name(codecState())); } } @@ -1026,11 +1048,11 @@ private void onConstValueAccessed() { switch (codecState()) { - case V0_NAME_DONE: - codecState(CodecState.V0_CONSTVALUE_DONE); + case CodecStates.V0_NAME_DONE: + codecState(CodecStates.V0_CONSTVALUE_DONE); break; default: - throw new IllegalStateException("Cannot access field \"constValue\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"constValue\" in state: " + CodecStates.name(codecState())); } } @@ -1166,11 +1188,11 @@ private void onMinValueAccessed() { switch (codecState()) { - case V0_CONSTVALUE_DONE: - codecState(CodecState.V0_MINVALUE_DONE); + case CodecStates.V0_CONSTVALUE_DONE: + codecState(CodecStates.V0_MINVALUE_DONE); break; default: - throw new IllegalStateException("Cannot access field \"minValue\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"minValue\" in state: " + CodecStates.name(codecState())); } } @@ -1306,11 +1328,11 @@ private void onMaxValueAccessed() { switch (codecState()) { - case V0_MINVALUE_DONE: - codecState(CodecState.V0_MAXVALUE_DONE); + case CodecStates.V0_MINVALUE_DONE: + codecState(CodecStates.V0_MAXVALUE_DONE); break; default: - throw new IllegalStateException("Cannot access field \"maxValue\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"maxValue\" in state: " + CodecStates.name(codecState())); } } @@ -1446,11 +1468,11 @@ private void onNullValueAccessed() { switch (codecState()) { - case V0_MAXVALUE_DONE: - codecState(CodecState.V0_NULLVALUE_DONE); + case CodecStates.V0_MAXVALUE_DONE: + codecState(CodecStates.V0_NULLVALUE_DONE); break; default: - throw new IllegalStateException("Cannot access field \"nullValue\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"nullValue\" in state: " + CodecStates.name(codecState())); } } @@ -1586,11 +1608,11 @@ private void onCharacterEncodingAccessed() { switch (codecState()) { - case V0_NULLVALUE_DONE: - codecState(CodecState.V0_CHARACTERENCODING_DONE); + case CodecStates.V0_NULLVALUE_DONE: + codecState(CodecStates.V0_CHARACTERENCODING_DONE); break; default: - throw new IllegalStateException("Cannot access field \"characterEncoding\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"characterEncoding\" in state: " + CodecStates.name(codecState())); } } @@ -1726,11 +1748,11 @@ private void onEpochAccessed() { switch (codecState()) { - case V0_CHARACTERENCODING_DONE: - codecState(CodecState.V0_EPOCH_DONE); + case CodecStates.V0_CHARACTERENCODING_DONE: + codecState(CodecStates.V0_EPOCH_DONE); break; default: - throw new IllegalStateException("Cannot access field \"epoch\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"epoch\" in state: " + CodecStates.name(codecState())); } } @@ -1866,11 +1888,11 @@ private void onTimeUnitAccessed() { switch (codecState()) { - case V0_EPOCH_DONE: - codecState(CodecState.V0_TIMEUNIT_DONE); + case CodecStates.V0_EPOCH_DONE: + codecState(CodecStates.V0_TIMEUNIT_DONE); break; default: - throw new IllegalStateException("Cannot access field \"timeUnit\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"timeUnit\" in state: " + CodecStates.name(codecState())); } } @@ -2006,11 +2028,11 @@ private void onSemanticTypeAccessed() { switch (codecState()) { - case V0_TIMEUNIT_DONE: - codecState(CodecState.V0_SEMANTICTYPE_DONE); + case CodecStates.V0_TIMEUNIT_DONE: + codecState(CodecStates.V0_SEMANTICTYPE_DONE); break; default: - throw new IllegalStateException("Cannot access field \"semanticType\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"semanticType\" in state: " + CodecStates.name(codecState())); } } @@ -2146,11 +2168,11 @@ private void onDescriptionAccessed() { switch (codecState()) { - case V0_SEMANTICTYPE_DONE: - codecState(CodecState.V0_DESCRIPTION_DONE); + case CodecStates.V0_SEMANTICTYPE_DONE: + codecState(CodecStates.V0_DESCRIPTION_DONE); break; default: - throw new IllegalStateException("Cannot access field \"description\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"description\" in state: " + CodecStates.name(codecState())); } } @@ -2286,11 +2308,11 @@ private void onReferencedNameAccessed() { switch (codecState()) { - case V0_DESCRIPTION_DONE: - codecState(CodecState.V0_REFERENCEDNAME_DONE); + case CodecStates.V0_DESCRIPTION_DONE: + codecState(CodecStates.V0_REFERENCEDNAME_DONE); break; default: - throw new IllegalStateException("Cannot access field \"referencedName\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"referencedName\" in state: " + CodecStates.name(codecState())); } } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java index fec09c0bd5..41fd799864 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java @@ -50,31 +50,53 @@ public final class TokenCodecEncoder * } * } */ - private enum CodecState + private static class CodecStates { - NOT_WRAPPED, - V0_BLOCK, - V0_NAME_DONE, - V0_CONSTVALUE_DONE, - V0_MINVALUE_DONE, - V0_MAXVALUE_DONE, - V0_NULLVALUE_DONE, - V0_CHARACTERENCODING_DONE, - V0_EPOCH_DONE, - V0_TIMEUNIT_DONE, - V0_SEMANTICTYPE_DONE, - V0_DESCRIPTION_DONE, - V0_REFERENCEDNAME_DONE, + private static final int NOT_WRAPPED = 0; + private static final int V0_BLOCK = 1; + private static final int V0_NAME_DONE = 2; + private static final int V0_CONSTVALUE_DONE = 3; + private static final int V0_MINVALUE_DONE = 4; + private static final int V0_MAXVALUE_DONE = 5; + private static final int V0_NULLVALUE_DONE = 6; + private static final int V0_CHARACTERENCODING_DONE = 7; + private static final int V0_EPOCH_DONE = 8; + private static final int V0_TIMEUNIT_DONE = 9; + private static final int V0_SEMANTICTYPE_DONE = 10; + private static final int V0_DESCRIPTION_DONE = 11; + private static final int V0_REFERENCEDNAME_DONE = 12; + + private static final String[] STATE_NAME_LOOKUP = + { + "NOT_WRAPPED", + "V0_BLOCK", + "V0_NAME_DONE", + "V0_CONSTVALUE_DONE", + "V0_MINVALUE_DONE", + "V0_MAXVALUE_DONE", + "V0_NULLVALUE_DONE", + "V0_CHARACTERENCODING_DONE", + "V0_EPOCH_DONE", + "V0_TIMEUNIT_DONE", + "V0_SEMANTICTYPE_DONE", + "V0_DESCRIPTION_DONE", + "V0_REFERENCEDNAME_DONE", + }; + + private static String name(final int state) + { + return STATE_NAME_LOOKUP[state]; + } } - private CodecState codecState = CodecState.NOT_WRAPPED; + private int codecState = CodecStates.NOT_WRAPPED; - private CodecState codecState() + private int codecState() { return codecState; } - private void codecState(CodecState newState) + private void codecState(int newState) { codecState = newState; } @@ -144,7 +166,7 @@ public TokenCodecEncoder wrap(final MutableDirectBuffer buffer, final int offset if (ENABLE_ACCESS_ORDER_CHECKS) { - codecState(CodecState.V0_BLOCK); + codecState(CodecStates.V0_BLOCK); } return this; @@ -210,9 +232,9 @@ public static String tokenOffsetMetaAttribute(final MetaAttribute metaAttribute) private void onTokenOffsetAccessed() { - if (codecState() == CodecState.NOT_WRAPPED) + if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"tokenOffset\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"tokenOffset\" in state: " + CodecStates.name(codecState())); } } @@ -275,9 +297,9 @@ public static String tokenSizeMetaAttribute(final MetaAttribute metaAttribute) private void onTokenSizeAccessed() { - if (codecState() == CodecState.NOT_WRAPPED) + if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"tokenSize\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"tokenSize\" in state: " + CodecStates.name(codecState())); } } @@ -340,9 +362,9 @@ public static String fieldIdMetaAttribute(final MetaAttribute metaAttribute) private void onFieldIdAccessed() { - if (codecState() == CodecState.NOT_WRAPPED) + if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"fieldId\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"fieldId\" in state: " + CodecStates.name(codecState())); } } @@ -405,9 +427,9 @@ public static String tokenVersionMetaAttribute(final MetaAttribute metaAttribute private void onTokenVersionAccessed() { - if (codecState() == CodecState.NOT_WRAPPED) + if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"tokenVersion\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"tokenVersion\" in state: " + CodecStates.name(codecState())); } } @@ -470,9 +492,9 @@ public static String componentTokenCountMetaAttribute(final MetaAttribute metaAt private void onComponentTokenCountAccessed() { - if (codecState() == CodecState.NOT_WRAPPED) + if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"componentTokenCount\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"componentTokenCount\" in state: " + CodecStates.name(codecState())); } } @@ -535,9 +557,9 @@ public static String signalMetaAttribute(final MetaAttribute metaAttribute) private void onSignalAccessed() { - if (codecState() == CodecState.NOT_WRAPPED) + if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"signal\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"signal\" in state: " + CodecStates.name(codecState())); } } @@ -584,9 +606,9 @@ public static String primitiveTypeMetaAttribute(final MetaAttribute metaAttribut private void onPrimitiveTypeAccessed() { - if (codecState() == CodecState.NOT_WRAPPED) + if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"primitiveType\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"primitiveType\" in state: " + CodecStates.name(codecState())); } } @@ -633,9 +655,9 @@ public static String byteOrderMetaAttribute(final MetaAttribute metaAttribute) private void onByteOrderAccessed() { - if (codecState() == CodecState.NOT_WRAPPED) + if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"byteOrder\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"byteOrder\" in state: " + CodecStates.name(codecState())); } } @@ -682,9 +704,9 @@ public static String presenceMetaAttribute(final MetaAttribute metaAttribute) private void onPresenceAccessed() { - if (codecState() == CodecState.NOT_WRAPPED) + if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"presence\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"presence\" in state: " + CodecStates.name(codecState())); } } @@ -731,9 +753,9 @@ public static String deprecatedMetaAttribute(final MetaAttribute metaAttribute) private void onDeprecatedAccessed() { - if (codecState() == CodecState.NOT_WRAPPED) + if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"deprecated\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"deprecated\" in state: " + CodecStates.name(codecState())); } } @@ -793,11 +815,11 @@ private void onNameAccessed() { switch (codecState()) { - case V0_BLOCK: - codecState(CodecState.V0_NAME_DONE); + case CodecStates.V0_BLOCK: + codecState(CodecStates.V0_NAME_DONE); break; default: - throw new IllegalStateException("Cannot access field \"name\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"name\" in state: " + CodecStates.name(codecState())); } } @@ -896,11 +918,11 @@ private void onConstValueAccessed() { switch (codecState()) { - case V0_NAME_DONE: - codecState(CodecState.V0_CONSTVALUE_DONE); + case CodecStates.V0_NAME_DONE: + codecState(CodecStates.V0_CONSTVALUE_DONE); break; default: - throw new IllegalStateException("Cannot access field \"constValue\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"constValue\" in state: " + CodecStates.name(codecState())); } } @@ -999,11 +1021,11 @@ private void onMinValueAccessed() { switch (codecState()) { - case V0_CONSTVALUE_DONE: - codecState(CodecState.V0_MINVALUE_DONE); + case CodecStates.V0_CONSTVALUE_DONE: + codecState(CodecStates.V0_MINVALUE_DONE); break; default: - throw new IllegalStateException("Cannot access field \"minValue\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"minValue\" in state: " + CodecStates.name(codecState())); } } @@ -1102,11 +1124,11 @@ private void onMaxValueAccessed() { switch (codecState()) { - case V0_MINVALUE_DONE: - codecState(CodecState.V0_MAXVALUE_DONE); + case CodecStates.V0_MINVALUE_DONE: + codecState(CodecStates.V0_MAXVALUE_DONE); break; default: - throw new IllegalStateException("Cannot access field \"maxValue\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"maxValue\" in state: " + CodecStates.name(codecState())); } } @@ -1205,11 +1227,11 @@ private void onNullValueAccessed() { switch (codecState()) { - case V0_MAXVALUE_DONE: - codecState(CodecState.V0_NULLVALUE_DONE); + case CodecStates.V0_MAXVALUE_DONE: + codecState(CodecStates.V0_NULLVALUE_DONE); break; default: - throw new IllegalStateException("Cannot access field \"nullValue\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"nullValue\" in state: " + CodecStates.name(codecState())); } } @@ -1308,11 +1330,11 @@ private void onCharacterEncodingAccessed() { switch (codecState()) { - case V0_NULLVALUE_DONE: - codecState(CodecState.V0_CHARACTERENCODING_DONE); + case CodecStates.V0_NULLVALUE_DONE: + codecState(CodecStates.V0_CHARACTERENCODING_DONE); break; default: - throw new IllegalStateException("Cannot access field \"characterEncoding\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"characterEncoding\" in state: " + CodecStates.name(codecState())); } } @@ -1411,11 +1433,11 @@ private void onEpochAccessed() { switch (codecState()) { - case V0_CHARACTERENCODING_DONE: - codecState(CodecState.V0_EPOCH_DONE); + case CodecStates.V0_CHARACTERENCODING_DONE: + codecState(CodecStates.V0_EPOCH_DONE); break; default: - throw new IllegalStateException("Cannot access field \"epoch\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"epoch\" in state: " + CodecStates.name(codecState())); } } @@ -1514,11 +1536,11 @@ private void onTimeUnitAccessed() { switch (codecState()) { - case V0_EPOCH_DONE: - codecState(CodecState.V0_TIMEUNIT_DONE); + case CodecStates.V0_EPOCH_DONE: + codecState(CodecStates.V0_TIMEUNIT_DONE); break; default: - throw new IllegalStateException("Cannot access field \"timeUnit\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"timeUnit\" in state: " + CodecStates.name(codecState())); } } @@ -1617,11 +1639,11 @@ private void onSemanticTypeAccessed() { switch (codecState()) { - case V0_TIMEUNIT_DONE: - codecState(CodecState.V0_SEMANTICTYPE_DONE); + case CodecStates.V0_TIMEUNIT_DONE: + codecState(CodecStates.V0_SEMANTICTYPE_DONE); break; default: - throw new IllegalStateException("Cannot access field \"semanticType\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"semanticType\" in state: " + CodecStates.name(codecState())); } } @@ -1720,11 +1742,11 @@ private void onDescriptionAccessed() { switch (codecState()) { - case V0_SEMANTICTYPE_DONE: - codecState(CodecState.V0_DESCRIPTION_DONE); + case CodecStates.V0_SEMANTICTYPE_DONE: + codecState(CodecStates.V0_DESCRIPTION_DONE); break; default: - throw new IllegalStateException("Cannot access field \"description\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"description\" in state: " + CodecStates.name(codecState())); } } @@ -1823,11 +1845,11 @@ private void onReferencedNameAccessed() { switch (codecState()) { - case V0_DESCRIPTION_DONE: - codecState(CodecState.V0_REFERENCEDNAME_DONE); + case CodecStates.V0_DESCRIPTION_DONE: + codecState(CodecStates.V0_REFERENCEDNAME_DONE); break; default: - throw new IllegalStateException("Cannot access field \"referencedName\" in state: " + codecState()); + throw new IllegalStateException("Cannot access field \"referencedName\" in state: " + CodecStates.name(codecState())); } } From 3d4756cc7b43f661e0a85b9d259e8a9d7e0dd19e Mon Sep 17 00:00:00 2001 From: Zach Bray Date: Tue, 20 Jun 2023 12:43:13 +0100 Subject: [PATCH 20/50] [Java] Provide more context in error messages. In this commit, we associate field tokens with the "path" through the message to it. We can then use this to generate code with better error messages that include the path to the field rather than just the field name, which could have been ambiguous when there are groups. --- .../sbe/generation/java/FieldOrderModel.java | 75 ++++++++++++++----- .../sbe/generation/java/JavaGenerator.java | 4 +- .../real_logic/sbe/FieldOrderCheckTest.java | 64 ++++++++-------- 3 files changed, 90 insertions(+), 53 deletions(-) diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java index e2ba3f6e72..0321d5e558 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java @@ -31,7 +31,9 @@ import static uk.co.real_logic.sbe.ir.GenerationUtil.collectGroups; import static uk.co.real_logic.sbe.ir.GenerationUtil.collectVarData; -@SuppressWarnings("DuplicatedCode") // there is no abstraction for visiting fields, groups, and varData +// There is no abstraction for visiting fields, groups, and varData. Therefore, there is some "duplication". +// Lambdas without braces tend to conflict with checkstyle. Therefore, we allow braces when an expression is possible. +@SuppressWarnings({"DuplicatedCode", "CodeBlock2Expr"}) final class FieldOrderModel { private static final boolean GENERATE_ACCESS_ORDER_CHECKS = Boolean.parseBoolean( @@ -44,6 +46,22 @@ final class FieldOrderModel private final State notWrappedState = allocateState("NOT_WRAPPED"); private State encoderWrappedState; + public static boolean generateAccessOrderChecks() + { + return GENERATE_ACCESS_ORDER_CHECKS; + } + + public static FieldOrderModel newInstance( + final Token msgToken, + final List fields, + final List groups, + final List varData) + { + final FieldOrderModel model = new FieldOrderModel(); + model.findTransitions(msgToken, fields, groups, varData); + return model; + } + public State notWrappedState() { return notWrappedState; @@ -77,6 +95,21 @@ public boolean isTopLevelBlockField(final Token token) return topLevelBlockFields.contains(token); } + public String fieldPath(final Token token) + { + final StringBuilder sb = new StringBuilder(); + final TransitionGroup transitionGroup = transitions.get(token); + if (null != transitionGroup) + { + transitionGroup.groupPath.forEach(groupToken -> + { + sb.append(groupToken.name()).append('.'); + }); + } + sb.append(token.name()); + return sb.toString(); + } + public List getTransitions(final TransitionContext context, final Token token) { final TransitionGroup transitionGroup = transitions.get(token); @@ -88,23 +121,6 @@ public List getTransitions(final TransitionContext context, final To return transitionGroup.transitions.get(context); } - public static boolean generateAccessOrderChecks() - { - return GENERATE_ACCESS_ORDER_CHECKS; - } - - public static FieldOrderModel newInstance( - final Token msgToken, - final List fields, - final List groups, - final List varData) - { - final FieldOrderModel model = new FieldOrderModel(); - model.findTransitions(msgToken, fields, groups, varData); - return model; - } - - @SuppressWarnings("CodeBlock2Expr") // lambdas without braces tend to conflict with checkstyle public void generateGraph( final StringBuilder sb, final String indent) @@ -197,6 +213,7 @@ private void findTransitions( allocateTransition( version, ".wrap(...)", + Collections.emptyList(), null, Collections.singletonList(notWrappedState), versionWrappedState @@ -206,6 +223,7 @@ private void findTransitions( Collections.singletonList(versionWrappedState), versionWrappedState, "V" + version + "_", + new ArrayList<>(), fields, groups, varData, @@ -219,6 +237,7 @@ private List findTransitions( final List entryStates, final State blockStateOrNull, final String prefix, + final List groupPath, final List fields, final List groups, final List varData, @@ -244,6 +263,7 @@ private List findTransitions( allocateTransition( TransitionContext.NONE, "." + token.name() + "(value)", + groupPath, token, fromStates, blockState.get()); @@ -283,6 +303,8 @@ private List findTransitions( final String groupPrefix = prefix + groupName + "_"; final List beginGroupStates = new ArrayList<>(fromStates); + final List newGroupPath = new ArrayList<>(groupPath); + newGroupPath.add(token); final State nRemainingGroup = allocateState(groupPrefix + "N"); final State nRemainingGroupElement = allocateState(groupPrefix + "N_BLOCK"); @@ -293,6 +315,7 @@ private List findTransitions( allocateTransition( TransitionContext.SELECT_EMPTY_GROUP, "." + token.name() + "Length(0)", + groupPath, token, beginGroupStates, emptyGroup); @@ -301,6 +324,7 @@ private List findTransitions( allocateTransition( TransitionContext.SELECT_MULTI_ELEMENT_GROUP, "." + token.name() + "Length(N) where N > 0", + groupPath, token, beginGroupStates, nRemainingGroup); @@ -311,6 +335,7 @@ private List findTransitions( fromStates, nRemainingGroupElement, groupPrefix + "N_", + newGroupPath, groupFields, groupGroups, groupVarData, @@ -324,6 +349,7 @@ private List findTransitions( allocateTransition( TransitionContext.NEXT_ELEMENT_IN_GROUP, token.name() + ".next()\\n&& count - index > 1", + groupPath, token, fromStates, nRemainingGroupElement); @@ -336,6 +362,7 @@ private List findTransitions( allocateTransition( TransitionContext.LAST_ELEMENT_IN_GROUP, token.name() + ".next()\\n&& count - index == 1", + groupPath, token, fromStates, oneRemainingGroupElement); @@ -347,6 +374,7 @@ private List findTransitions( fromStates, oneRemainingGroupElement, groupPrefix + "1_", + newGroupPath, groupFields, groupGroups, groupVarData, @@ -375,6 +403,7 @@ private List findTransitions( allocateTransition( TransitionContext.NONE, "." + token.name() + "(value)", + groupPath, token, fromStates, state); @@ -400,11 +429,13 @@ private State allocateState(final String name) private void allocateTransition( final Object firingContext, final String description, + final List groupPath, final Token token, final List from, final State to) { - final TransitionGroup transitionGroup = transitions.computeIfAbsent(token, ignored -> new TransitionGroup()); + final TransitionGroup transitionGroup = transitions.computeIfAbsent(token, + ignored -> new TransitionGroup(groupPath)); final Transition transition = new Transition(description, from, to); transitionGroup.add(firingContext, transition); } @@ -486,6 +517,12 @@ enum TransitionContext private static final class TransitionGroup { private final Map> transitions = new LinkedHashMap<>(); + private final List groupPath; + + private TransitionGroup(final List groupPath) + { + this.groupPath = groupPath; + } public void add(final Object context, final Transition transition) { diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java index 5a4a3e490a..1207ca2217 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java @@ -476,7 +476,7 @@ private static void generateAccessOrderListener( .append(")\n") .append(indent).append("{\n") .append(indent).append(" throw new IllegalStateException(") - .append("\"Cannot access field \\\"").append(token.name()) + .append("\"Cannot access field \\\"").append(fieldOrderModel.fieldPath(token)) .append("\\\" in state: \" + CodecStates.name(codecState()));\n") .append(indent).append("}\n"); } @@ -498,7 +498,7 @@ private static void generateAccessOrderListener( sb.append(indent).append(" default:\n") .append(indent).append(" throw new IllegalStateException(") - .append("\"Cannot access field \\\"").append(token.name()) + .append("\"Cannot access field \\\"").append(fieldOrderModel.fieldPath(token)) .append("\\\" in state: \" + CodecStates.name(codecState()));\n") .append(indent).append("}\n"); } diff --git a/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java b/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java index 05720f3f75..7e13da0ea4 100644 --- a/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java +++ b/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java @@ -486,7 +486,7 @@ void disallowsEncodingGroupElementBeforeCallingNext() final GroupAndVarLengthEncoder.BEncoder bEncoder = encoder.bCount(1); final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.c(1)); - assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_B_N")); + assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_B_N")); } @Test @@ -509,7 +509,7 @@ void disallowsDecodingGroupElementBeforeCallingNext() assertThat(bs.count(), equalTo(2)); final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, bs::c); - assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_B_N")); + assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_B_N")); } @Test @@ -761,7 +761,7 @@ void disallowsReEncodingGroupElementVariableLengthField() encoder.e("def"); final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> b.d("ghi")); - assertThat(exception.getMessage(), containsString("Cannot access field \"d\" in state: V0_E_DONE")); + assertThat(exception.getMessage(), containsString("Cannot access field \"b.d\" in state: V0_E_DONE")); } @Test @@ -788,7 +788,7 @@ void disallowsReDecodingGroupElementVariableLengthField() assertThat(bs.d(), equalTo("abc")); final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, bs::d); - assertThat(exception.getMessage(), containsString("Cannot access field \"d\" in state: V0_B_N_D_DONE")); + assertThat(exception.getMessage(), containsString("Cannot access field \"b.d\" in state: V0_B_N_D_DONE")); } @Test @@ -954,7 +954,7 @@ void disallowsMissedEncodingOfNestedGroup() .c(1); final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> b.fCount(1)); - assertThat(exception.getMessage(), containsString("Cannot access field \"f\" in state: V0_B_1_BLOCK")); + assertThat(exception.getMessage(), containsString("Cannot access field \"b.f\" in state: V0_B_1_BLOCK")); } @Test @@ -989,7 +989,7 @@ void disallowsEncodingCompositeInsideGroupBeforeCallingNext() final CompositeInsideGroupEncoder.BEncoder bEncoder = encoder.bCount(1); final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, bEncoder::c); - assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_B_N")); + assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_B_N")); } @Test @@ -1011,7 +1011,7 @@ void disallowsDecodingCompositeInsideGroupBeforeCallingNext() assertThat(b.count(), equalTo(1)); final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, b::c); - assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_B_N")); + assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_B_N")); } @Test @@ -1898,7 +1898,7 @@ void disallowsEncodingEnumInsideGroupBeforeCallingNext() final EnumInsideGroupEncoder.BEncoder bEncoder = encoder.bCount(1); final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.c(Direction.SELL)); - assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_B_N")); + assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_B_N")); } @Test @@ -1918,7 +1918,7 @@ void disallowsDecodingEnumInsideGroupBeforeCallingNext() assertThat(b.count(), equalTo(1)); final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, b::c); - assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_B_N")); + assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_B_N")); } @Test @@ -1976,7 +1976,7 @@ void disallowsEncodingBitSetInsideGroupBeforeCallingNext() final BitSetInsideGroupEncoder.BEncoder bEncoder = encoder.bCount(1); final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, bEncoder::c); - assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_B_N")); + assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_B_N")); } @Test @@ -1999,7 +1999,7 @@ void disallowsDecodingBitSetInsideGroupBeforeCallingNext() assertThat(b.count(), equalTo(1)); final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, b::c); - assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_B_N")); + assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_B_N")); } @Test @@ -2060,7 +2060,7 @@ void disallowsEncodingArrayInsideGroupBeforeCallingNext1() final ArrayInsideGroupEncoder.BEncoder bEncoder = encodeUntilGroupWithArrayInside(); final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.putC((short)5, (short)6, (short)7, (short)8)); - assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_B_N")); + assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_B_N")); } @Test @@ -2069,7 +2069,7 @@ void disallowsEncodingArrayInsideGroupBeforeCallingNext2() final ArrayInsideGroupEncoder.BEncoder bEncoder = encodeUntilGroupWithArrayInside(); final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.c(0, (short)5)); - assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_B_N")); + assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_B_N")); } @Test @@ -2079,7 +2079,7 @@ void disallowsEncodingArrayInsideGroupBeforeCallingNext3() final byte[] bytes = {5, 6, 7, 8}; final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.putC(bytes, 0, 4)); - assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_B_N")); + assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_B_N")); } @Test @@ -2093,7 +2093,7 @@ void disallowsEncodingArrayInsideGroupBeforeCallingNext4() buffer.putByte(6, (byte)8); final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.putC(buffer, 0, 4)); - assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_B_N")); + assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_B_N")); } private ArrayInsideGroupEncoder.BEncoder encodeUntilGroupWithArrayInside() @@ -2110,7 +2110,7 @@ void disallowsDecodingArrayInsideGroupBeforeCallingNext1() final ArrayInsideGroupDecoder.BDecoder b = decodeUntilGroupWithArrayInside(); final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> b.c(0)); - assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_B_N")); + assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_B_N")); } @Test @@ -2119,7 +2119,7 @@ void disallowsDecodingArrayInsideGroupBeforeCallingNext2() final ArrayInsideGroupDecoder.BDecoder b = decodeUntilGroupWithArrayInside(); final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> b.getC(new byte[8], 0, 8)); - assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_B_N")); + assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_B_N")); } @Test @@ -2128,7 +2128,7 @@ void disallowsDecodingArrayInsideGroupBeforeCallingNext3() final ArrayInsideGroupDecoder.BDecoder b = decodeUntilGroupWithArrayInside(); final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> b.getC(new ExpandableArrayBuffer(), 0, 8)); - assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_B_N")); + assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_B_N")); } @Test @@ -2137,7 +2137,7 @@ void disallowsDecodingArrayInsideGroupBeforeCallingNext4() final ArrayInsideGroupDecoder.BDecoder b = decodeUntilGroupWithArrayInside(); final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> b.wrapC(new UnsafeBuffer())); - assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_B_N")); + assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_B_N")); } private ArrayInsideGroupDecoder.BDecoder decodeUntilGroupWithArrayInside() @@ -2495,7 +2495,7 @@ void disallowsEncodingAsciiInsideGroupBeforeCallingNext1() final AsciiInsideGroupEncoder.BEncoder bEncoder = encodeUntilGroupWithAsciiInside(); final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.c("EURUSD")); - assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_B_N")); + assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_B_N")); } @Test @@ -2504,7 +2504,7 @@ void disallowsEncodingAsciiInsideGroupBeforeCallingNext2() final AsciiInsideGroupEncoder.BEncoder bEncoder = encodeUntilGroupWithAsciiInside(); final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.c(0, (byte)'E')); - assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_B_N")); + assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_B_N")); } @Test @@ -2514,7 +2514,7 @@ void disallowsEncodingAsciiInsideGroupBeforeCallingNext3() final byte[] eurUsdBytes = "EURUSD".getBytes(StandardCharsets.US_ASCII); final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.putC(eurUsdBytes, 0)); - assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_B_N")); + assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_B_N")); } private AsciiInsideGroupEncoder.BEncoder encodeUntilGroupWithAsciiInside() @@ -2531,7 +2531,7 @@ void disallowsDecodingAsciiInsideGroupBeforeCallingNext1() final AsciiInsideGroupDecoder.BDecoder b = decodeUntilGroupWithAsciiInside(); final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> b.c(0)); - assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_B_N")); + assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_B_N")); } @Test @@ -2540,7 +2540,7 @@ void disallowsDecodingAsciiInsideGroupBeforeCallingNext2() final AsciiInsideGroupDecoder.BDecoder b = decodeUntilGroupWithAsciiInside(); final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> b.getC(new byte[6], 0)); - assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_B_N")); + assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_B_N")); } @Test @@ -2549,7 +2549,7 @@ void disallowsDecodingAsciiInsideGroupBeforeCallingNext3() final AsciiInsideGroupDecoder.BDecoder b = decodeUntilGroupWithAsciiInside(); final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, b::c); - assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_B_N")); + assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_B_N")); } @Test @@ -2558,7 +2558,7 @@ void disallowsDecodingAsciiInsideGroupBeforeCallingNext4() final AsciiInsideGroupDecoder.BDecoder b = decodeUntilGroupWithAsciiInside(); final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> b.getC(new StringBuilder())); - assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_B_N")); + assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_B_N")); } private AsciiInsideGroupDecoder.BDecoder decodeUntilGroupWithAsciiInside() @@ -2845,7 +2845,7 @@ void disallowsEncodingElementOfEmptyGroup1() encoder.dCount(1).next().e(43); final IllegalStateException exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.c(44)); - assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_D_1_BLOCK")); + assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_D_1_BLOCK")); } @Test @@ -2862,7 +2862,7 @@ void disallowsEncodingElementOfEmptyGroup2() encoder.hCount(0); final IllegalStateException exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> dEncoder.e(44)); - assertThat(exception.getMessage(), containsString("Cannot access field \"e\" in state: V0_H_0")); + assertThat(exception.getMessage(), containsString("Cannot access field \"b.d.e\" in state: V0_H_0")); } @Test @@ -2879,7 +2879,7 @@ void disallowsEncodingElementOfEmptyGroup3() encoder.hCount(0); final IllegalStateException exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> dEncoder.e(44)); - assertThat(exception.getMessage(), containsString("Cannot access field \"e\" in state: V0_H_0")); + assertThat(exception.getMessage(), containsString("Cannot access field \"b.d.e\" in state: V0_H_0")); } @Test @@ -2895,7 +2895,7 @@ void disallowsEncodingElementOfEmptyGroup4() bEncoder.fCount(0); final IllegalStateException exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> dEncoder.e(44)); - assertThat(exception.getMessage(), containsString("Cannot access field \"e\" in state: V0_B_1_F_0")); + assertThat(exception.getMessage(), containsString("Cannot access field \"b.d.e\" in state: V0_B_1_F_0")); } @Test @@ -2907,7 +2907,7 @@ void disallowsEncodingElementOfEmptyGroup5() final AddPrimitiveInsideGroupV1Encoder.BEncoder bEncoder = encoder.bCount(0); final IllegalStateException exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.c(43)); - assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V1_B_0")); + assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V1_B_0")); } @Test @@ -2920,7 +2920,7 @@ void disallowsEncodingElementOfEmptyGroup6() encoder.d("abc"); final IllegalStateException exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.c(43)); - assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_D_DONE")); + assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_D_DONE")); } private void modifyHeaderToLookLikeVersion0() From 623ad8ef77e107f09159352c25aef0d4426197c2 Mon Sep 17 00:00:00 2001 From: Zach Bray Date: Tue, 20 Jun 2023 13:23:00 +0100 Subject: [PATCH 21/50] [Java] Provide more context in error messages. In this commit, I've customised the error messages around group access to give a better indication of what was attempted. --- .../sbe/generation/java/FieldOrderModel.java | 29 ++++++++- .../sbe/generation/java/JavaGenerator.java | 27 +++++--- .../sbe/ir/generated/FrameCodecDecoder.java | 18 ++++-- .../sbe/ir/generated/FrameCodecEncoder.java | 18 ++++-- .../sbe/ir/generated/TokenCodecDecoder.java | 63 ++++++++++++------- .../sbe/ir/generated/TokenCodecEncoder.java | 63 ++++++++++++------- .../real_logic/sbe/FieldOrderCheckTest.java | 33 ++++++---- 7 files changed, 177 insertions(+), 74 deletions(-) diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java index 0321d5e558..a7dcf23d6c 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java @@ -82,7 +82,7 @@ public void forEachDecoderWrappedState(final IntObjConsumer consumer) } } - public void forEachState(final Consumer consumer) + public void forEachStateOrderedByNumber(final Consumer consumer) { states.values() .stream() @@ -507,13 +507,40 @@ public String toString() enum TransitionContext { + /** + * For tokens with a set of transitions that does not depend on any context, e.g., + * when a block field is accessed. As opposed to a repeating group, where the + * transitions depend both on the number of remaining elements in the group and + * whether {@code next()} is called or {@code myGroupCount(int count)}. + */ NONE, + + /** + * When a repeating group count is supplied as zero. + */ SELECT_EMPTY_GROUP, + + /** + * When a repeating group count is supplied as greater than zero. + */ SELECT_MULTI_ELEMENT_GROUP, + + /** + * When the next element in a repeating group is accessed, + * and it is not the last element. + */ NEXT_ELEMENT_IN_GROUP, + + /** + * When the next element in a repeating group is accessed, + * and it is the last element. + */ LAST_ELEMENT_IN_GROUP } + /** + * The codec state transitions possible for a given block/group/data field. + */ private static final class TransitionGroup { private final Map> transitions = new LinkedHashMap<>(); diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java index 1207ca2217..f6b55a6d68 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java @@ -311,7 +311,7 @@ private static CharSequence generateFieldOrderStates(final FieldOrderModel field sb.append(" */\n"); sb.append(" private static class CodecStates\n") .append(" {\n"); - fieldOrderModel.forEachState(state -> + fieldOrderModel.forEachStateOrderedByNumber(state -> { sb.append(" private static final int ") .append(unqualifiedStateCase(state)) @@ -320,7 +320,7 @@ private static CharSequence generateFieldOrderStates(final FieldOrderModel field }); sb.append("\n").append(" private static final String[] STATE_NAME_LOOKUP =\n") .append(" {\n"); - fieldOrderModel.forEachState(state -> + fieldOrderModel.forEachStateOrderedByNumber(state -> { sb.append(" \"").append(state.name()).append("\",\n"); }); @@ -371,6 +371,7 @@ private static void generateAccessOrderListenerMethod( generateAccessOrderListener( sb, indent + " ", + "access field", fieldOrderModel, FieldOrderModel.TransitionContext.NONE, token); @@ -424,6 +425,7 @@ private static CharSequence generateAccessOrderListenerCall( private static void generateAccessOrderListenerMethodForGroupWrap( final StringBuilder sb, + final String action, final FieldOrderModel fieldOrderModel, final String indent, final Token token) @@ -443,6 +445,7 @@ private static void generateAccessOrderListenerMethodForGroupWrap( generateAccessOrderListener( sb, indent + " ", + action + " count of repeating group", fieldOrderModel, FieldOrderModel.TransitionContext.SELECT_EMPTY_GROUP, token); @@ -454,6 +457,7 @@ private static void generateAccessOrderListenerMethodForGroupWrap( generateAccessOrderListener( sb, indent + " ", + action + " count of repeating group", fieldOrderModel, FieldOrderModel.TransitionContext.SELECT_MULTI_ELEMENT_GROUP, token); @@ -465,6 +469,7 @@ private static void generateAccessOrderListenerMethodForGroupWrap( private static void generateAccessOrderListener( final StringBuilder sb, final String indent, + final String action, final FieldOrderModel fieldOrderModel, final FieldOrderModel.TransitionContext context, final Token token) @@ -476,8 +481,10 @@ private static void generateAccessOrderListener( .append(")\n") .append(indent).append("{\n") .append(indent).append(" throw new IllegalStateException(") - .append("\"Cannot access field \\\"").append(fieldOrderModel.fieldPath(token)) - .append("\\\" in state: \" + CodecStates.name(codecState()));\n") + .append("\"Cannot ").append(action).append(" \\\"").append(fieldOrderModel.fieldPath(token)) + .append("\\\" in state: \" + CodecStates.name(codecState()) +\n") + .append(indent).append(" \". ") + .append("Please see the diagram in the Javadoc of the inner class #CodecStates.\");\n") .append(indent).append("}\n"); } else @@ -498,8 +505,10 @@ private static void generateAccessOrderListener( sb.append(indent).append(" default:\n") .append(indent).append(" throw new IllegalStateException(") - .append("\"Cannot access field \\\"").append(fieldOrderModel.fieldPath(token)) - .append("\\\" in state: \" + CodecStates.name(codecState()));\n") + .append("\"Cannot ").append(action).append(" \\\"").append(fieldOrderModel.fieldPath(token)) + .append("\\\" in state: \" + CodecStates.name(codecState()) +\n") + .append(indent).append(" \". ") + .append("Please see the diagram in the Javadoc of the inner class #CodecStates.\");\n") .append(indent).append("}\n"); } } @@ -524,6 +533,7 @@ private static void generateAccessOrderListenerMethodForNextGroupElement( generateAccessOrderListener( sb, indent + " ", + "access next element in repeating group", fieldOrderModel, FieldOrderModel.TransitionContext.NEXT_ELEMENT_IN_GROUP, token); @@ -535,6 +545,7 @@ private static void generateAccessOrderListenerMethodForNextGroupElement( generateAccessOrderListener( sb, indent + " ", + "access next element in repeating group", fieldOrderModel, FieldOrderModel.TransitionContext.LAST_ELEMENT_IN_GROUP, token); @@ -1150,7 +1161,7 @@ private static void generateGroupDecoderProperty( indent + " return " + propertyName + ";\n" + indent + " }\n\n"; - generateAccessOrderListenerMethodForGroupWrap(sb, fieldOrderModel, indent + " ", token); + generateAccessOrderListenerMethodForGroupWrap(sb, "decode", fieldOrderModel, indent + " ", token); generateFlyweightPropertyJavadoc(sb, indent + INDENT, token, className); new Formatter(sb).format("\n" + @@ -1194,7 +1205,7 @@ private void generateGroupEncoderProperty( formatPropertyName(groupName), token.id()); - generateAccessOrderListenerMethodForGroupWrap(sb, fieldOrderModel, indent + " ", token); + generateAccessOrderListenerMethodForGroupWrap(sb, "encode", fieldOrderModel, indent + " ", token); generateGroupEncodePropertyJavadoc(sb, indent + INDENT, token, className); new Formatter(sb).format("\n" + diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java index e4fb9233c9..c208070318 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java @@ -257,7 +257,8 @@ private void onIrIdAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"irId\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"irId\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -321,7 +322,8 @@ private void onIrVersionAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"irVersion\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"irVersion\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -385,7 +387,8 @@ private void onSchemaVersionAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"schemaVersion\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"schemaVersion\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -453,7 +456,8 @@ private void onPackageNameAccessed() codecState(CodecStates.V0_PACKAGENAME_DONE); break; default: - throw new IllegalStateException("Cannot access field \"packageName\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"packageName\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -593,7 +597,8 @@ private void onNamespaceNameAccessed() codecState(CodecStates.V0_NAMESPACENAME_DONE); break; default: - throw new IllegalStateException("Cannot access field \"namespaceName\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"namespaceName\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -733,7 +738,8 @@ private void onSemanticVersionAccessed() codecState(CodecStates.V0_SEMANTICVERSION_DONE); break; default: - throw new IllegalStateException("Cannot access field \"semanticVersion\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"semanticVersion\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java index a3feef8bb5..7f2b8339cb 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java @@ -203,7 +203,8 @@ private void onIrIdAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"irId\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"irId\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -268,7 +269,8 @@ private void onIrVersionAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"irVersion\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"irVersion\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -333,7 +335,8 @@ private void onSchemaVersionAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"schemaVersion\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"schemaVersion\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -397,7 +400,8 @@ private void onPackageNameAccessed() codecState(CodecStates.V0_PACKAGENAME_DONE); break; default: - throw new IllegalStateException("Cannot access field \"packageName\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"packageName\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -500,7 +504,8 @@ private void onNamespaceNameAccessed() codecState(CodecStates.V0_NAMESPACENAME_DONE); break; default: - throw new IllegalStateException("Cannot access field \"namespaceName\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"namespaceName\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -603,7 +608,8 @@ private void onSemanticVersionAccessed() codecState(CodecStates.V0_SEMANTICVERSION_DONE); break; default: - throw new IllegalStateException("Cannot access field \"semanticVersion\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"semanticVersion\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java index fc853da877..d1f5461076 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java @@ -288,7 +288,8 @@ private void onTokenOffsetAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"tokenOffset\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"tokenOffset\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -352,7 +353,8 @@ private void onTokenSizeAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"tokenSize\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"tokenSize\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -416,7 +418,8 @@ private void onFieldIdAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"fieldId\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"fieldId\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -480,7 +483,8 @@ private void onTokenVersionAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"tokenVersion\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"tokenVersion\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -544,7 +548,8 @@ private void onComponentTokenCountAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"componentTokenCount\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"componentTokenCount\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -608,7 +613,8 @@ private void onSignalAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"signal\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"signal\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -667,7 +673,8 @@ private void onPrimitiveTypeAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"primitiveType\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"primitiveType\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -726,7 +733,8 @@ private void onByteOrderAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"byteOrder\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"byteOrder\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -785,7 +793,8 @@ private void onPresenceAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"presence\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"presence\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -844,7 +853,8 @@ private void onDeprecatedAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"deprecated\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"deprecated\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -912,7 +922,8 @@ private void onNameAccessed() codecState(CodecStates.V0_NAME_DONE); break; default: - throw new IllegalStateException("Cannot access field \"name\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"name\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -1052,7 +1063,8 @@ private void onConstValueAccessed() codecState(CodecStates.V0_CONSTVALUE_DONE); break; default: - throw new IllegalStateException("Cannot access field \"constValue\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"constValue\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -1192,7 +1204,8 @@ private void onMinValueAccessed() codecState(CodecStates.V0_MINVALUE_DONE); break; default: - throw new IllegalStateException("Cannot access field \"minValue\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"minValue\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -1332,7 +1345,8 @@ private void onMaxValueAccessed() codecState(CodecStates.V0_MAXVALUE_DONE); break; default: - throw new IllegalStateException("Cannot access field \"maxValue\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"maxValue\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -1472,7 +1486,8 @@ private void onNullValueAccessed() codecState(CodecStates.V0_NULLVALUE_DONE); break; default: - throw new IllegalStateException("Cannot access field \"nullValue\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"nullValue\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -1612,7 +1627,8 @@ private void onCharacterEncodingAccessed() codecState(CodecStates.V0_CHARACTERENCODING_DONE); break; default: - throw new IllegalStateException("Cannot access field \"characterEncoding\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"characterEncoding\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -1752,7 +1768,8 @@ private void onEpochAccessed() codecState(CodecStates.V0_EPOCH_DONE); break; default: - throw new IllegalStateException("Cannot access field \"epoch\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"epoch\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -1892,7 +1909,8 @@ private void onTimeUnitAccessed() codecState(CodecStates.V0_TIMEUNIT_DONE); break; default: - throw new IllegalStateException("Cannot access field \"timeUnit\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"timeUnit\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -2032,7 +2050,8 @@ private void onSemanticTypeAccessed() codecState(CodecStates.V0_SEMANTICTYPE_DONE); break; default: - throw new IllegalStateException("Cannot access field \"semanticType\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"semanticType\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -2172,7 +2191,8 @@ private void onDescriptionAccessed() codecState(CodecStates.V0_DESCRIPTION_DONE); break; default: - throw new IllegalStateException("Cannot access field \"description\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"description\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -2312,7 +2332,8 @@ private void onReferencedNameAccessed() codecState(CodecStates.V0_REFERENCEDNAME_DONE); break; default: - throw new IllegalStateException("Cannot access field \"referencedName\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"referencedName\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java index 41fd799864..28b4d49c37 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java @@ -234,7 +234,8 @@ private void onTokenOffsetAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"tokenOffset\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"tokenOffset\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -299,7 +300,8 @@ private void onTokenSizeAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"tokenSize\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"tokenSize\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -364,7 +366,8 @@ private void onFieldIdAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"fieldId\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"fieldId\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -429,7 +432,8 @@ private void onTokenVersionAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"tokenVersion\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"tokenVersion\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -494,7 +498,8 @@ private void onComponentTokenCountAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"componentTokenCount\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"componentTokenCount\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -559,7 +564,8 @@ private void onSignalAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"signal\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"signal\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -608,7 +614,8 @@ private void onPrimitiveTypeAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"primitiveType\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"primitiveType\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -657,7 +664,8 @@ private void onByteOrderAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"byteOrder\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"byteOrder\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -706,7 +714,8 @@ private void onPresenceAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"presence\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"presence\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -755,7 +764,8 @@ private void onDeprecatedAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"deprecated\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"deprecated\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -819,7 +829,8 @@ private void onNameAccessed() codecState(CodecStates.V0_NAME_DONE); break; default: - throw new IllegalStateException("Cannot access field \"name\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"name\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -922,7 +933,8 @@ private void onConstValueAccessed() codecState(CodecStates.V0_CONSTVALUE_DONE); break; default: - throw new IllegalStateException("Cannot access field \"constValue\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"constValue\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -1025,7 +1037,8 @@ private void onMinValueAccessed() codecState(CodecStates.V0_MINVALUE_DONE); break; default: - throw new IllegalStateException("Cannot access field \"minValue\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"minValue\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -1128,7 +1141,8 @@ private void onMaxValueAccessed() codecState(CodecStates.V0_MAXVALUE_DONE); break; default: - throw new IllegalStateException("Cannot access field \"maxValue\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"maxValue\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -1231,7 +1245,8 @@ private void onNullValueAccessed() codecState(CodecStates.V0_NULLVALUE_DONE); break; default: - throw new IllegalStateException("Cannot access field \"nullValue\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"nullValue\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -1334,7 +1349,8 @@ private void onCharacterEncodingAccessed() codecState(CodecStates.V0_CHARACTERENCODING_DONE); break; default: - throw new IllegalStateException("Cannot access field \"characterEncoding\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"characterEncoding\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -1437,7 +1453,8 @@ private void onEpochAccessed() codecState(CodecStates.V0_EPOCH_DONE); break; default: - throw new IllegalStateException("Cannot access field \"epoch\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"epoch\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -1540,7 +1557,8 @@ private void onTimeUnitAccessed() codecState(CodecStates.V0_TIMEUNIT_DONE); break; default: - throw new IllegalStateException("Cannot access field \"timeUnit\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"timeUnit\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -1643,7 +1661,8 @@ private void onSemanticTypeAccessed() codecState(CodecStates.V0_SEMANTICTYPE_DONE); break; default: - throw new IllegalStateException("Cannot access field \"semanticType\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"semanticType\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -1746,7 +1765,8 @@ private void onDescriptionAccessed() codecState(CodecStates.V0_DESCRIPTION_DONE); break; default: - throw new IllegalStateException("Cannot access field \"description\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"description\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -1849,7 +1869,8 @@ private void onReferencedNameAccessed() codecState(CodecStates.V0_REFERENCEDNAME_DONE); break; default: - throw new IllegalStateException("Cannot access field \"referencedName\" in state: " + CodecStates.name(codecState())); + throw new IllegalStateException("Cannot access field \"referencedName\" in state: " + CodecStates.name(codecState()) + + ". Please see the diagram in the Javadoc of the inner class #CodecStates."); } } diff --git a/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java b/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java index 7e13da0ea4..e146c366c6 100644 --- a/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java +++ b/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java @@ -554,7 +554,8 @@ void disallowsReEncodingGroupLength() encoder.d("abc"); final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.bCount(1)); - assertThat(exception.getMessage(), containsString("Cannot access field \"b\" in state: V0_D_DONE")); + assertThat(exception.getMessage(), + containsString("Cannot encode count of repeating group \"b\" in state: V0_D_DONE")); } @Test @@ -652,7 +653,8 @@ void disallowsReDecodingGroupAfterVariableLengthField() assertThat(decoder.d(), equalTo("abc")); final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::b); - assertThat(exception.getMessage(), containsString("Cannot access field \"b\" in state: V0_D_DONE")); + assertThat(exception.getMessage(), + containsString("Cannot decode count of repeating group \"b\" in state: V0_D_DONE")); } @Test @@ -730,7 +732,8 @@ void disallowsMissedGroupElementVariableLengthFieldToEncodeNextElement() .next(); final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, b::next); - assertThat(exception.getMessage(), containsString("Cannot access field \"b\" in state: V0_B_N_BLOCK")); + assertThat(exception.getMessage(), + containsString("Cannot access next element in repeating group \"b\" in state: V0_B_N_BLOCK")); } @Test @@ -814,7 +817,8 @@ void disallowsMissedDecodingOfGroupElementVariableLengthFieldToNextElement() assertThat(bs.next().c(), equalTo(1)); final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, bs::next); - assertThat(exception.getMessage(), containsString("Cannot access field \"b\" in state: V0_B_N_BLOCK")); + assertThat(exception.getMessage(), + containsString("Cannot access next element in repeating group \"b\" in state: V0_B_N_BLOCK")); } @Test @@ -954,7 +958,8 @@ void disallowsMissedEncodingOfNestedGroup() .c(1); final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> b.fCount(1)); - assertThat(exception.getMessage(), containsString("Cannot access field \"b.f\" in state: V0_B_1_BLOCK")); + assertThat(exception.getMessage(), + containsString("Cannot encode count of repeating group \"b.f\" in state: V0_B_1_BLOCK")); } @Test @@ -2255,7 +2260,8 @@ void disallowsMissedEncodingOfGroupField() encoder.a(41); final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.dCount(0)); - assertThat(exception.getMessage(), containsString("Cannot access field \"d\" in state: V0_BLOCK")); + assertThat(exception.getMessage(), + containsString("Cannot encode count of repeating group \"d\" in state: V0_BLOCK")); } @Test @@ -2268,7 +2274,8 @@ void disallowsReEncodingEarlierGroupFields() encoder.dCount(1).next().e(43); final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.bCount(1)); - assertThat(exception.getMessage(), containsString("Cannot access field \"b\" in state: V0_D_1_BLOCK")); + assertThat(exception.getMessage(), + containsString("Cannot encode count of repeating group \"b\" in state: V0_D_1_BLOCK")); } @Test @@ -2281,7 +2288,8 @@ void disallowsReEncodingLatestGroupField() encoder.dCount(1).next().e(43); final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.dCount(1)); - assertThat(exception.getMessage(), containsString("Cannot access field \"d\" in state: V0_D_1_BLOCK")); + assertThat(exception.getMessage(), + containsString("Cannot encode count of repeating group \"d\" in state: V0_D_1_BLOCK")); } @Test @@ -2298,7 +2306,8 @@ void disallowsMissedDecodingOfGroupField() assertThat(decoder.a(), equalTo(41)); final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::d); - assertThat(exception.getMessage(), containsString("Cannot access field \"d\" in state: V0_BLOCK")); + assertThat(exception.getMessage(), + containsString("Cannot decode count of repeating group \"d\" in state: V0_BLOCK")); } @Test @@ -2321,7 +2330,8 @@ void disallowsReDecodingEarlierGroupField() assertThat(d.next().e(), equalTo(43)); final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::b); - assertThat(exception.getMessage(), containsString("Cannot access field \"b\" in state: V0_D_1_BLOCK")); + assertThat(exception.getMessage(), + containsString("Cannot decode count of repeating group \"b\" in state: V0_D_1_BLOCK")); } @Test @@ -2344,7 +2354,8 @@ void disallowsReDecodingLatestGroupField() assertThat(d.next().e(), equalTo(43)); final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::d); - assertThat(exception.getMessage(), containsString("Cannot access field \"d\" in state: V0_D_1_BLOCK")); + assertThat(exception.getMessage(), + containsString("Cannot decode count of repeating group \"d\" in state: V0_D_1_BLOCK")); } @Test From bf8cde93a7667b901a9db2d661efc84213268bd8 Mon Sep 17 00:00:00 2001 From: Zach Bray Date: Tue, 20 Jun 2023 18:26:27 +0100 Subject: [PATCH 22/50] [Java] Specify expected transitions in error messages. In this commit, I've improved the error messages around field access order checks so that it reports the legal transitions in the current state. Hopefully, this will make it easier for users to fix problems. --- .../sbe/generation/java/FieldOrderModel.java | 132 ++++++++---- .../sbe/generation/java/JavaGenerator.java | 82 ++++++-- .../sbe/ir/generated/FrameCodecDecoder.java | 69 ++++-- .../sbe/ir/generated/FrameCodecEncoder.java | 69 ++++-- .../sbe/ir/generated/TokenCodecDecoder.java | 197 ++++++++++++------ .../sbe/ir/generated/TokenCodecEncoder.java | 197 ++++++++++++------ 6 files changed, 513 insertions(+), 233 deletions(-) diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java index a7dcf23d6c..a221bb4b55 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java @@ -39,19 +39,20 @@ final class FieldOrderModel private static final boolean GENERATE_ACCESS_ORDER_CHECKS = Boolean.parseBoolean( System.getProperty("sbe.generate.access.order.checks", "true")); private final Int2ObjectHashMap states = new Int2ObjectHashMap<>(); - private final Map transitions = new LinkedHashMap<>(); + private final Map transitionsByField = new LinkedHashMap<>(); + private final Map> transitionsByState = new LinkedHashMap<>(); private final Set topLevelBlockFields = new HashSet<>(); private final Int2ObjectHashMap versionWrappedStates = new Int2ObjectHashMap<>(); private final Set reservedNames = new HashSet<>(); private final State notWrappedState = allocateState("NOT_WRAPPED"); private State encoderWrappedState; - public static boolean generateAccessOrderChecks() + static boolean generateAccessOrderChecks() { return GENERATE_ACCESS_ORDER_CHECKS; } - public static FieldOrderModel newInstance( + static FieldOrderModel newInstance( final Token msgToken, final List fields, final List groups, @@ -62,17 +63,17 @@ public static FieldOrderModel newInstance( return model; } - public State notWrappedState() + State notWrappedState() { return notWrappedState; } - public State latestVersionWrappedState() + State latestVersionWrappedState() { return encoderWrappedState; } - public void forEachDecoderWrappedState(final IntObjConsumer consumer) + void forEachDecoderWrappedState(final IntObjConsumer consumer) { final Int2ObjectHashMap.EntryIterator iterator = versionWrappedStates.entrySet().iterator(); while (iterator.hasNext()) @@ -82,7 +83,7 @@ public void forEachDecoderWrappedState(final IntObjConsumer consumer) } } - public void forEachStateOrderedByNumber(final Consumer consumer) + void forEachStateOrderedByNumber(final Consumer consumer) { states.values() .stream() @@ -90,46 +91,56 @@ public void forEachStateOrderedByNumber(final Consumer consumer) .forEach(consumer); } - public boolean isTopLevelBlockField(final Token token) + boolean isTopLevelBlockField(final Token token) { return topLevelBlockFields.contains(token); } - public String fieldPath(final Token token) + String fieldPath(final Token token) { final StringBuilder sb = new StringBuilder(); - final TransitionGroup transitionGroup = transitions.get(token); + final TransitionGroup transitionGroup = transitionsByField.get(token); if (null != transitionGroup) { - transitionGroup.groupPath.forEach(groupToken -> - { - sb.append(groupToken.name()).append('.'); - }); + sb.append(transitionGroup.groupPath); } sb.append(token.name()); return sb.toString(); } - public List getTransitions(final TransitionContext context, final Token token) + void getTransitions( + final List transitionsOut, + final TransitionContext context, + final Token token) { - final TransitionGroup transitionGroup = transitions.get(token); - if (transitionGroup == null) + final TransitionGroup transitionGroup = transitionsByField.get(token); + if (null != transitionGroup) { - return Collections.emptyList(); + final List transitionsForContext = transitionGroup.transitions.get(context); + if (null != transitionsForContext) + { + transitionsOut.addAll(transitionsForContext); + } } + } - return transitionGroup.transitions.get(context); + void forEachTransitionFrom(final State state, final Consumer consumer) + { + final List transitions = transitionsByState.get(state); + if (null != transitions) + { + transitions.forEach(consumer); + } } - public void generateGraph( - final StringBuilder sb, - final String indent) + @SuppressWarnings("SameParameterValue") + void generateGraph(final StringBuilder sb, final String indent) { sb.append(indent).append("digraph G {\n"); - transitions.values().forEach(transitionGroup -> - transitionGroup.transitions.forEach((context, transitions1) -> + transitionsByField.values().forEach(transitionGroup -> + transitionGroup.transitions.forEach((context, transitionsForContext) -> { - transitions1.forEach(transition -> + transitionsForContext.forEach(transition -> { transition.forEachStartState(startState -> { @@ -137,7 +148,14 @@ public void generateGraph( .append(startState.name) .append(" -> ") .append(transition.endState().name) - .append(" [label=\" ").append(transition.description).append(" \"];\n"); + .append(" [label=\" ").append(transition.description); + + if (!transition.conditions.isEmpty()) + { + sb.append("\\n").append(" where ").append(transition.conditions); + } + + sb.append(" \"];\n"); }); }); }) @@ -212,7 +230,8 @@ private void findTransitions( allocateTransition( version, - ".wrap(...)", + "wrap(?)", + "", Collections.emptyList(), null, Collections.singletonList(notWrappedState), @@ -262,14 +281,15 @@ private List findTransitions( allocateTransition( TransitionContext.NONE, - "." + token.name() + "(value)", + token.name() + "(?)", + "", groupPath, token, fromStates, blockState.get()); }); - if (blockState.get() != null) + if (null != blockState.get()) { fromStates.clear(); fromStates.add(blockState.get()); @@ -314,7 +334,8 @@ private List findTransitions( // fooCount(0) allocateTransition( TransitionContext.SELECT_EMPTY_GROUP, - "." + token.name() + "Length(0)", + token.name() + "Length(0)", + "", groupPath, token, beginGroupStates, @@ -323,7 +344,8 @@ private List findTransitions( // fooCount(N) where N > 0 allocateTransition( TransitionContext.SELECT_MULTI_ELEMENT_GROUP, - "." + token.name() + "Length(N) where N > 0", + token.name() + "Length(N)", + "where N > 0", groupPath, token, beginGroupStates, @@ -348,7 +370,8 @@ private List findTransitions( // where more than one element remains in the group allocateTransition( TransitionContext.NEXT_ELEMENT_IN_GROUP, - token.name() + ".next()\\n&& count - index > 1", + token.name() + ".next()", + "count - index > 1", groupPath, token, fromStates, @@ -361,7 +384,8 @@ private List findTransitions( // where only one element remains in the group allocateTransition( TransitionContext.LAST_ELEMENT_IN_GROUP, - token.name() + ".next()\\n&& count - index == 1", + token.name() + ".next()", + "count - index == 1", groupPath, token, fromStates, @@ -402,7 +426,8 @@ private List findTransitions( final State state = allocateState(prefix + token.name().toUpperCase() + "_DONE"); allocateTransition( TransitionContext.NONE, - "." + token.name() + "(value)", + token.name() + "(?)", + "", groupPath, token, fromStates, @@ -429,15 +454,21 @@ private State allocateState(final String name) private void allocateTransition( final Object firingContext, final String description, + final String conditions, final List groupPath, final Token token, final List from, final State to) { - final TransitionGroup transitionGroup = transitions.computeIfAbsent(token, + final TransitionGroup transitionGroup = transitionsByField.computeIfAbsent(token, ignored -> new TransitionGroup(groupPath)); - final Transition transition = new Transition(description, from, to); + final Transition transition = new Transition(transitionGroup.groupPath + description, conditions, from, to); transitionGroup.add(firingContext, transition); + from.forEach(fromState -> + { + transitionsByState.computeIfAbsent(fromState, ignored -> new ArrayList<>()) + .add(transition); + }); } static final class State @@ -451,12 +482,12 @@ private State(final int number, final String name) this.name = name; } - public int number() + int number() { return number; } - public String name() + String name() { return name; } @@ -474,12 +505,18 @@ public String toString() static final class Transition { private final String description; + private final String conditions; private final Set from; private final State to; - private Transition(final String description, final List from, final State to) + private Transition( + final String description, + final String conditions, + final List from, + final State to) { this.description = description; + this.conditions = conditions; this.from = new HashSet<>(from); this.to = to; } @@ -494,11 +531,17 @@ State endState() return to; } + String description() + { + return description; + } + @Override public String toString() { return "Transition{" + "description='" + description + '\'' + + ", conditions='" + conditions + '\'' + ", from=" + from + ", to=" + to + '}'; @@ -544,14 +587,19 @@ enum TransitionContext private static final class TransitionGroup { private final Map> transitions = new LinkedHashMap<>(); - private final List groupPath; + private final String groupPath; private TransitionGroup(final List groupPath) { - this.groupPath = groupPath; + final StringBuilder sb = new StringBuilder(); + groupPath.forEach(token -> + { + sb.append(token.name()).append('.'); + }); + this.groupPath = sb.toString(); } - public void add(final Object context, final Transition transition) + void add(final Object context, final Transition transition) { final List transitionsForContext = transitions.computeIfAbsent(context, ignored -> new ArrayList<>()); diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java index f6b55a6d68..074fd5c4f0 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java @@ -23,6 +23,7 @@ import org.agrona.MutableDirectBuffer; import org.agrona.Strings; import org.agrona.Verify; +import org.agrona.collections.MutableBoolean; import org.agrona.generation.DynamicPackageOutputManager; import org.agrona.sbe.*; @@ -302,8 +303,9 @@ private static CharSequence generateFieldOrderStates(final FieldOrderModel field sb.append(" /**\n"); sb.append(" * The states in which a encoder/decoder/codec can live.\n"); sb.append(" *\n"); - sb.append(" *

The state machine diagram below describes the valid state transitions\n"); - sb.append(" * according to the order in which fields may be accessed safely.\n"); + sb.append(" *

The state machine diagram below, encoded in the dot language, describes\n"); + sb.append(" * the valid state transitions according to the order in which fields may be\n"); + sb.append(" * accessed safely. Tools such as PlantUML and Graphviz can render it.\n"); sb.append(" *\n"); sb.append(" *

{@code\n");
         fieldOrderModel.generateGraph(sb, "     *   ");
@@ -318,6 +320,7 @@ private static CharSequence generateFieldOrderStates(final FieldOrderModel field
                 .append(" = ").append(state.number())
                 .append(";\n");
         });
+
         sb.append("\n").append("        private static final String[] STATE_NAME_LOOKUP =\n")
                 .append("        {\n");
         fieldOrderModel.forEachStateOrderedByNumber(state ->
@@ -325,10 +328,44 @@ private static CharSequence generateFieldOrderStates(final FieldOrderModel field
             sb.append("            \"").append(state.name()).append("\",\n");
         });
         sb.append("        };\n\n");
+
+        sb.append("        private static final String[] STATE_TRANSITIONS_LOOKUP =\n")
+                .append("        {\n");
+        fieldOrderModel.forEachStateOrderedByNumber(state ->
+        {
+            sb.append("            \"");
+            final MutableBoolean isFirst = new MutableBoolean(true);
+            final Set transitionDescriptions = new HashSet<>();
+            fieldOrderModel.forEachTransitionFrom(state, transition ->
+            {
+                if (transitionDescriptions.add(transition.description()))
+                {
+                    if (isFirst.get())
+                    {
+                        isFirst.set(false);
+                    }
+                    else
+                    {
+                        sb.append(", ");
+                    }
+
+                    sb.append("\\\"").append(transition.description()).append("\\\"");
+                }
+            });
+            sb.append("\",\n");
+        });
+        sb.append("        };\n\n");
+
         sb.append("        private static String name(final int state)\n")
             .append("        {\n")
             .append("            return STATE_NAME_LOOKUP[state];\n")
+            .append("        }\n\n");
+
+        sb.append("        private static String transitions(final int state)\n")
+            .append("        {\n")
+            .append("            return STATE_TRANSITIONS_LOOKUP[state];\n")
             .append("        }\n");
+
         sb.append("    }\n\n");
 
         sb.append("    private int codecState = ")
@@ -479,20 +516,17 @@ private static void generateAccessOrderListener(
             sb.append(indent).append("if (codecState() == ")
                 .append(qualifiedStateCase(fieldOrderModel.notWrappedState()))
                 .append(")\n")
-                .append(indent).append("{\n")
-                .append(indent).append("    throw new IllegalStateException(")
-                .append("\"Cannot ").append(action).append(" \\\"").append(fieldOrderModel.fieldPath(token))
-                .append("\\\" in state: \" + CodecStates.name(codecState()) +\n")
-                .append(indent).append("            \". ")
-                .append("Please see the diagram in the Javadoc of the inner class #CodecStates.\");\n")
-                .append(indent).append("}\n");
+                .append(indent).append("{\n");
+            generateAccessOrderException(sb, indent + "    ", action, fieldOrderModel, token);
+            sb.append(indent).append("}\n");
         }
         else
         {
             sb.append(indent).append("switch (codecState())\n")
                 .append(indent).append("{\n");
 
-            final List transitions = fieldOrderModel.getTransitions(context, token);
+            final List transitions = new ArrayList<>();
+            fieldOrderModel.getTransitions(transitions, context, token);
 
             transitions.forEach(transition ->
             {
@@ -503,16 +537,30 @@ private static void generateAccessOrderListener(
                     .append(indent).append("        break;\n");
             });
 
-            sb.append(indent).append("    default:\n")
-                .append(indent).append("        throw new IllegalStateException(")
-                .append("\"Cannot ").append(action).append(" \\\"").append(fieldOrderModel.fieldPath(token))
-                .append("\\\" in state: \" + CodecStates.name(codecState()) +\n")
-                .append(indent).append("            \". ")
-                .append("Please see the diagram in the Javadoc of the inner class #CodecStates.\");\n")
-                .append(indent).append("}\n");
+            sb.append(indent).append("    default:\n");
+            generateAccessOrderException(sb, indent + "        ", action, fieldOrderModel, token);
+            sb.append(indent).append("}\n");
         }
     }
 
+    private static void generateAccessOrderException(
+        final StringBuilder sb,
+        final String indent,
+        final String action,
+        final FieldOrderModel fieldOrderModel,
+        final Token token)
+    {
+        sb.append(indent).append("throw new IllegalStateException(")
+            .append("\"Illegal field access order. \" +\n")
+            .append(indent)
+            .append("    \"Cannot ").append(action).append(" \\\"").append(fieldOrderModel.fieldPath(token))
+            .append("\\\" in state: \" + CodecStates.name(codecState()) +\n")
+            .append(indent)
+            .append("    \". Expected one of these transitions: [\" + CodecStates.transitions(codecState()) +\n")
+            .append(indent)
+            .append("    \"]. Please see the diagram in the Javadoc of the inner class #CodecStates.\");\n");
+    }
+
     private static void generateAccessOrderListenerMethodForNextGroupElement(
         final StringBuilder sb,
         final FieldOrderModel fieldOrderModel,
diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java
index c208070318..18d5627d02 100644
--- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java
+++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java
@@ -20,18 +20,19 @@ public final class FrameCodecDecoder
     /**
      * The states in which a encoder/decoder/codec can live.
      *
-     * 

The state machine diagram below describes the valid state transitions - * according to the order in which fields may be accessed safely. + *

The state machine diagram below, encoded in the dot language, describes + * the valid state transitions according to the order in which fields may be + * accessed safely. Tools such as PlantUML and Graphviz can render it. * *

{@code
      *   digraph G {
-     *       NOT_WRAPPED -> V0_BLOCK [label="  .wrap(...)  "];
-     *       V0_BLOCK -> V0_BLOCK [label="  .irId(value)  "];
-     *       V0_BLOCK -> V0_BLOCK [label="  .irVersion(value)  "];
-     *       V0_BLOCK -> V0_BLOCK [label="  .schemaVersion(value)  "];
-     *       V0_BLOCK -> V0_PACKAGENAME_DONE [label="  .packageName(value)  "];
-     *       V0_PACKAGENAME_DONE -> V0_NAMESPACENAME_DONE [label="  .namespaceName(value)  "];
-     *       V0_NAMESPACENAME_DONE -> V0_SEMANTICVERSION_DONE [label="  .semanticVersion(value)  "];
+     *       NOT_WRAPPED -> V0_BLOCK [label="  wrap(?)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  irId(?)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  irVersion(?)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  schemaVersion(?)  "];
+     *       V0_BLOCK -> V0_PACKAGENAME_DONE [label="  packageName(?)  "];
+     *       V0_PACKAGENAME_DONE -> V0_NAMESPACENAME_DONE [label="  namespaceName(?)  "];
+     *       V0_NAMESPACENAME_DONE -> V0_SEMANTICVERSION_DONE [label="  semanticVersion(?)  "];
      *   }
      * }
*/ @@ -52,10 +53,24 @@ private static class CodecStates "V0_SEMANTICVERSION_DONE", }; + private static final String[] STATE_TRANSITIONS_LOOKUP = + { + "\"wrap(?)\"", + "\"irId(?)\", \"irVersion(?)\", \"schemaVersion(?)\", \"packageName(?)\"", + "\"namespaceName(?)\"", + "\"semanticVersion(?)\"", + "", + }; + private static String name(final int state) { return STATE_NAME_LOOKUP[state]; } + + private static String transitions(final int state) + { + return STATE_TRANSITIONS_LOOKUP[state]; + } } private int codecState = CodecStates.NOT_WRAPPED; @@ -257,8 +272,10 @@ private void onIrIdAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"irId\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"irId\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -322,8 +339,10 @@ private void onIrVersionAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"irVersion\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"irVersion\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -387,8 +406,10 @@ private void onSchemaVersionAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"schemaVersion\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"schemaVersion\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -456,8 +477,10 @@ private void onPackageNameAccessed() codecState(CodecStates.V0_PACKAGENAME_DONE); break; default: - throw new IllegalStateException("Cannot access field \"packageName\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"packageName\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -597,8 +620,10 @@ private void onNamespaceNameAccessed() codecState(CodecStates.V0_NAMESPACENAME_DONE); break; default: - throw new IllegalStateException("Cannot access field \"namespaceName\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"namespaceName\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -738,8 +763,10 @@ private void onSemanticVersionAccessed() codecState(CodecStates.V0_SEMANTICVERSION_DONE); break; default: - throw new IllegalStateException("Cannot access field \"semanticVersion\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"semanticVersion\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java index 7f2b8339cb..2482dc28df 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java @@ -20,18 +20,19 @@ public final class FrameCodecEncoder /** * The states in which a encoder/decoder/codec can live. * - *

The state machine diagram below describes the valid state transitions - * according to the order in which fields may be accessed safely. + *

The state machine diagram below, encoded in the dot language, describes + * the valid state transitions according to the order in which fields may be + * accessed safely. Tools such as PlantUML and Graphviz can render it. * *

{@code
      *   digraph G {
-     *       NOT_WRAPPED -> V0_BLOCK [label="  .wrap(...)  "];
-     *       V0_BLOCK -> V0_BLOCK [label="  .irId(value)  "];
-     *       V0_BLOCK -> V0_BLOCK [label="  .irVersion(value)  "];
-     *       V0_BLOCK -> V0_BLOCK [label="  .schemaVersion(value)  "];
-     *       V0_BLOCK -> V0_PACKAGENAME_DONE [label="  .packageName(value)  "];
-     *       V0_PACKAGENAME_DONE -> V0_NAMESPACENAME_DONE [label="  .namespaceName(value)  "];
-     *       V0_NAMESPACENAME_DONE -> V0_SEMANTICVERSION_DONE [label="  .semanticVersion(value)  "];
+     *       NOT_WRAPPED -> V0_BLOCK [label="  wrap(?)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  irId(?)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  irVersion(?)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  schemaVersion(?)  "];
+     *       V0_BLOCK -> V0_PACKAGENAME_DONE [label="  packageName(?)  "];
+     *       V0_PACKAGENAME_DONE -> V0_NAMESPACENAME_DONE [label="  namespaceName(?)  "];
+     *       V0_NAMESPACENAME_DONE -> V0_SEMANTICVERSION_DONE [label="  semanticVersion(?)  "];
      *   }
      * }
*/ @@ -52,10 +53,24 @@ private static class CodecStates "V0_SEMANTICVERSION_DONE", }; + private static final String[] STATE_TRANSITIONS_LOOKUP = + { + "\"wrap(?)\"", + "\"irId(?)\", \"irVersion(?)\", \"schemaVersion(?)\", \"packageName(?)\"", + "\"namespaceName(?)\"", + "\"semanticVersion(?)\"", + "", + }; + private static String name(final int state) { return STATE_NAME_LOOKUP[state]; } + + private static String transitions(final int state) + { + return STATE_TRANSITIONS_LOOKUP[state]; + } } private int codecState = CodecStates.NOT_WRAPPED; @@ -203,8 +218,10 @@ private void onIrIdAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"irId\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"irId\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -269,8 +286,10 @@ private void onIrVersionAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"irVersion\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"irVersion\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -335,8 +354,10 @@ private void onSchemaVersionAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"schemaVersion\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"schemaVersion\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -400,8 +421,10 @@ private void onPackageNameAccessed() codecState(CodecStates.V0_PACKAGENAME_DONE); break; default: - throw new IllegalStateException("Cannot access field \"packageName\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"packageName\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -504,8 +527,10 @@ private void onNamespaceNameAccessed() codecState(CodecStates.V0_NAMESPACENAME_DONE); break; default: - throw new IllegalStateException("Cannot access field \"namespaceName\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"namespaceName\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -608,8 +633,10 @@ private void onSemanticVersionAccessed() codecState(CodecStates.V0_SEMANTICVERSION_DONE); break; default: - throw new IllegalStateException("Cannot access field \"semanticVersion\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"semanticVersion\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java index d1f5461076..40d663eb9e 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java @@ -20,33 +20,34 @@ public final class TokenCodecDecoder /** * The states in which a encoder/decoder/codec can live. * - *

The state machine diagram below describes the valid state transitions - * according to the order in which fields may be accessed safely. + *

The state machine diagram below, encoded in the dot language, describes + * the valid state transitions according to the order in which fields may be + * accessed safely. Tools such as PlantUML and Graphviz can render it. * *

{@code
      *   digraph G {
-     *       NOT_WRAPPED -> V0_BLOCK [label="  .wrap(...)  "];
-     *       V0_BLOCK -> V0_BLOCK [label="  .tokenOffset(value)  "];
-     *       V0_BLOCK -> V0_BLOCK [label="  .tokenSize(value)  "];
-     *       V0_BLOCK -> V0_BLOCK [label="  .fieldId(value)  "];
-     *       V0_BLOCK -> V0_BLOCK [label="  .tokenVersion(value)  "];
-     *       V0_BLOCK -> V0_BLOCK [label="  .componentTokenCount(value)  "];
-     *       V0_BLOCK -> V0_BLOCK [label="  .signal(value)  "];
-     *       V0_BLOCK -> V0_BLOCK [label="  .primitiveType(value)  "];
-     *       V0_BLOCK -> V0_BLOCK [label="  .byteOrder(value)  "];
-     *       V0_BLOCK -> V0_BLOCK [label="  .presence(value)  "];
-     *       V0_BLOCK -> V0_BLOCK [label="  .deprecated(value)  "];
-     *       V0_BLOCK -> V0_NAME_DONE [label="  .name(value)  "];
-     *       V0_NAME_DONE -> V0_CONSTVALUE_DONE [label="  .constValue(value)  "];
-     *       V0_CONSTVALUE_DONE -> V0_MINVALUE_DONE [label="  .minValue(value)  "];
-     *       V0_MINVALUE_DONE -> V0_MAXVALUE_DONE [label="  .maxValue(value)  "];
-     *       V0_MAXVALUE_DONE -> V0_NULLVALUE_DONE [label="  .nullValue(value)  "];
-     *       V0_NULLVALUE_DONE -> V0_CHARACTERENCODING_DONE [label="  .characterEncoding(value)  "];
-     *       V0_CHARACTERENCODING_DONE -> V0_EPOCH_DONE [label="  .epoch(value)  "];
-     *       V0_EPOCH_DONE -> V0_TIMEUNIT_DONE [label="  .timeUnit(value)  "];
-     *       V0_TIMEUNIT_DONE -> V0_SEMANTICTYPE_DONE [label="  .semanticType(value)  "];
-     *       V0_SEMANTICTYPE_DONE -> V0_DESCRIPTION_DONE [label="  .description(value)  "];
-     *       V0_DESCRIPTION_DONE -> V0_REFERENCEDNAME_DONE [label="  .referencedName(value)  "];
+     *       NOT_WRAPPED -> V0_BLOCK [label="  wrap(?)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  tokenOffset(?)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  tokenSize(?)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  fieldId(?)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  tokenVersion(?)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  componentTokenCount(?)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  signal(?)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  primitiveType(?)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  byteOrder(?)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  presence(?)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  deprecated(?)  "];
+     *       V0_BLOCK -> V0_NAME_DONE [label="  name(?)  "];
+     *       V0_NAME_DONE -> V0_CONSTVALUE_DONE [label="  constValue(?)  "];
+     *       V0_CONSTVALUE_DONE -> V0_MINVALUE_DONE [label="  minValue(?)  "];
+     *       V0_MINVALUE_DONE -> V0_MAXVALUE_DONE [label="  maxValue(?)  "];
+     *       V0_MAXVALUE_DONE -> V0_NULLVALUE_DONE [label="  nullValue(?)  "];
+     *       V0_NULLVALUE_DONE -> V0_CHARACTERENCODING_DONE [label="  characterEncoding(?)  "];
+     *       V0_CHARACTERENCODING_DONE -> V0_EPOCH_DONE [label="  epoch(?)  "];
+     *       V0_EPOCH_DONE -> V0_TIMEUNIT_DONE [label="  timeUnit(?)  "];
+     *       V0_TIMEUNIT_DONE -> V0_SEMANTICTYPE_DONE [label="  semanticType(?)  "];
+     *       V0_SEMANTICTYPE_DONE -> V0_DESCRIPTION_DONE [label="  description(?)  "];
+     *       V0_DESCRIPTION_DONE -> V0_REFERENCEDNAME_DONE [label="  referencedName(?)  "];
      *   }
      * }
*/ @@ -83,10 +84,32 @@ private static class CodecStates "V0_REFERENCEDNAME_DONE", }; + private static final String[] STATE_TRANSITIONS_LOOKUP = + { + "\"wrap(?)\"", + "\"tokenOffset(?)\", \"tokenSize(?)\", \"fieldId(?)\", \"tokenVersion(?)\", \"componentTokenCount(?)\", \"signal(?)\", \"primitiveType(?)\", \"byteOrder(?)\", \"presence(?)\", \"deprecated(?)\", \"name(?)\"", + "\"constValue(?)\"", + "\"minValue(?)\"", + "\"maxValue(?)\"", + "\"nullValue(?)\"", + "\"characterEncoding(?)\"", + "\"epoch(?)\"", + "\"timeUnit(?)\"", + "\"semanticType(?)\"", + "\"description(?)\"", + "\"referencedName(?)\"", + "", + }; + private static String name(final int state) { return STATE_NAME_LOOKUP[state]; } + + private static String transitions(final int state) + { + return STATE_TRANSITIONS_LOOKUP[state]; + } } private int codecState = CodecStates.NOT_WRAPPED; @@ -288,8 +311,10 @@ private void onTokenOffsetAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"tokenOffset\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"tokenOffset\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -353,8 +378,10 @@ private void onTokenSizeAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"tokenSize\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"tokenSize\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -418,8 +445,10 @@ private void onFieldIdAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"fieldId\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"fieldId\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -483,8 +512,10 @@ private void onTokenVersionAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"tokenVersion\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"tokenVersion\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -548,8 +579,10 @@ private void onComponentTokenCountAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"componentTokenCount\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"componentTokenCount\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -613,8 +646,10 @@ private void onSignalAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"signal\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"signal\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -673,8 +708,10 @@ private void onPrimitiveTypeAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"primitiveType\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"primitiveType\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -733,8 +770,10 @@ private void onByteOrderAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"byteOrder\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"byteOrder\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -793,8 +832,10 @@ private void onPresenceAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"presence\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"presence\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -853,8 +894,10 @@ private void onDeprecatedAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"deprecated\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"deprecated\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -922,8 +965,10 @@ private void onNameAccessed() codecState(CodecStates.V0_NAME_DONE); break; default: - throw new IllegalStateException("Cannot access field \"name\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"name\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -1063,8 +1108,10 @@ private void onConstValueAccessed() codecState(CodecStates.V0_CONSTVALUE_DONE); break; default: - throw new IllegalStateException("Cannot access field \"constValue\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"constValue\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -1204,8 +1251,10 @@ private void onMinValueAccessed() codecState(CodecStates.V0_MINVALUE_DONE); break; default: - throw new IllegalStateException("Cannot access field \"minValue\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"minValue\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -1345,8 +1394,10 @@ private void onMaxValueAccessed() codecState(CodecStates.V0_MAXVALUE_DONE); break; default: - throw new IllegalStateException("Cannot access field \"maxValue\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"maxValue\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -1486,8 +1537,10 @@ private void onNullValueAccessed() codecState(CodecStates.V0_NULLVALUE_DONE); break; default: - throw new IllegalStateException("Cannot access field \"nullValue\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"nullValue\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -1627,8 +1680,10 @@ private void onCharacterEncodingAccessed() codecState(CodecStates.V0_CHARACTERENCODING_DONE); break; default: - throw new IllegalStateException("Cannot access field \"characterEncoding\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"characterEncoding\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -1768,8 +1823,10 @@ private void onEpochAccessed() codecState(CodecStates.V0_EPOCH_DONE); break; default: - throw new IllegalStateException("Cannot access field \"epoch\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"epoch\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -1909,8 +1966,10 @@ private void onTimeUnitAccessed() codecState(CodecStates.V0_TIMEUNIT_DONE); break; default: - throw new IllegalStateException("Cannot access field \"timeUnit\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"timeUnit\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -2050,8 +2109,10 @@ private void onSemanticTypeAccessed() codecState(CodecStates.V0_SEMANTICTYPE_DONE); break; default: - throw new IllegalStateException("Cannot access field \"semanticType\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"semanticType\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -2191,8 +2252,10 @@ private void onDescriptionAccessed() codecState(CodecStates.V0_DESCRIPTION_DONE); break; default: - throw new IllegalStateException("Cannot access field \"description\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"description\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -2332,8 +2395,10 @@ private void onReferencedNameAccessed() codecState(CodecStates.V0_REFERENCEDNAME_DONE); break; default: - throw new IllegalStateException("Cannot access field \"referencedName\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"referencedName\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java index 28b4d49c37..e1a04de370 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java @@ -20,33 +20,34 @@ public final class TokenCodecEncoder /** * The states in which a encoder/decoder/codec can live. * - *

The state machine diagram below describes the valid state transitions - * according to the order in which fields may be accessed safely. + *

The state machine diagram below, encoded in the dot language, describes + * the valid state transitions according to the order in which fields may be + * accessed safely. Tools such as PlantUML and Graphviz can render it. * *

{@code
      *   digraph G {
-     *       NOT_WRAPPED -> V0_BLOCK [label="  .wrap(...)  "];
-     *       V0_BLOCK -> V0_BLOCK [label="  .tokenOffset(value)  "];
-     *       V0_BLOCK -> V0_BLOCK [label="  .tokenSize(value)  "];
-     *       V0_BLOCK -> V0_BLOCK [label="  .fieldId(value)  "];
-     *       V0_BLOCK -> V0_BLOCK [label="  .tokenVersion(value)  "];
-     *       V0_BLOCK -> V0_BLOCK [label="  .componentTokenCount(value)  "];
-     *       V0_BLOCK -> V0_BLOCK [label="  .signal(value)  "];
-     *       V0_BLOCK -> V0_BLOCK [label="  .primitiveType(value)  "];
-     *       V0_BLOCK -> V0_BLOCK [label="  .byteOrder(value)  "];
-     *       V0_BLOCK -> V0_BLOCK [label="  .presence(value)  "];
-     *       V0_BLOCK -> V0_BLOCK [label="  .deprecated(value)  "];
-     *       V0_BLOCK -> V0_NAME_DONE [label="  .name(value)  "];
-     *       V0_NAME_DONE -> V0_CONSTVALUE_DONE [label="  .constValue(value)  "];
-     *       V0_CONSTVALUE_DONE -> V0_MINVALUE_DONE [label="  .minValue(value)  "];
-     *       V0_MINVALUE_DONE -> V0_MAXVALUE_DONE [label="  .maxValue(value)  "];
-     *       V0_MAXVALUE_DONE -> V0_NULLVALUE_DONE [label="  .nullValue(value)  "];
-     *       V0_NULLVALUE_DONE -> V0_CHARACTERENCODING_DONE [label="  .characterEncoding(value)  "];
-     *       V0_CHARACTERENCODING_DONE -> V0_EPOCH_DONE [label="  .epoch(value)  "];
-     *       V0_EPOCH_DONE -> V0_TIMEUNIT_DONE [label="  .timeUnit(value)  "];
-     *       V0_TIMEUNIT_DONE -> V0_SEMANTICTYPE_DONE [label="  .semanticType(value)  "];
-     *       V0_SEMANTICTYPE_DONE -> V0_DESCRIPTION_DONE [label="  .description(value)  "];
-     *       V0_DESCRIPTION_DONE -> V0_REFERENCEDNAME_DONE [label="  .referencedName(value)  "];
+     *       NOT_WRAPPED -> V0_BLOCK [label="  wrap(?)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  tokenOffset(?)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  tokenSize(?)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  fieldId(?)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  tokenVersion(?)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  componentTokenCount(?)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  signal(?)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  primitiveType(?)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  byteOrder(?)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  presence(?)  "];
+     *       V0_BLOCK -> V0_BLOCK [label="  deprecated(?)  "];
+     *       V0_BLOCK -> V0_NAME_DONE [label="  name(?)  "];
+     *       V0_NAME_DONE -> V0_CONSTVALUE_DONE [label="  constValue(?)  "];
+     *       V0_CONSTVALUE_DONE -> V0_MINVALUE_DONE [label="  minValue(?)  "];
+     *       V0_MINVALUE_DONE -> V0_MAXVALUE_DONE [label="  maxValue(?)  "];
+     *       V0_MAXVALUE_DONE -> V0_NULLVALUE_DONE [label="  nullValue(?)  "];
+     *       V0_NULLVALUE_DONE -> V0_CHARACTERENCODING_DONE [label="  characterEncoding(?)  "];
+     *       V0_CHARACTERENCODING_DONE -> V0_EPOCH_DONE [label="  epoch(?)  "];
+     *       V0_EPOCH_DONE -> V0_TIMEUNIT_DONE [label="  timeUnit(?)  "];
+     *       V0_TIMEUNIT_DONE -> V0_SEMANTICTYPE_DONE [label="  semanticType(?)  "];
+     *       V0_SEMANTICTYPE_DONE -> V0_DESCRIPTION_DONE [label="  description(?)  "];
+     *       V0_DESCRIPTION_DONE -> V0_REFERENCEDNAME_DONE [label="  referencedName(?)  "];
      *   }
      * }
*/ @@ -83,10 +84,32 @@ private static class CodecStates "V0_REFERENCEDNAME_DONE", }; + private static final String[] STATE_TRANSITIONS_LOOKUP = + { + "\"wrap(?)\"", + "\"tokenOffset(?)\", \"tokenSize(?)\", \"fieldId(?)\", \"tokenVersion(?)\", \"componentTokenCount(?)\", \"signal(?)\", \"primitiveType(?)\", \"byteOrder(?)\", \"presence(?)\", \"deprecated(?)\", \"name(?)\"", + "\"constValue(?)\"", + "\"minValue(?)\"", + "\"maxValue(?)\"", + "\"nullValue(?)\"", + "\"characterEncoding(?)\"", + "\"epoch(?)\"", + "\"timeUnit(?)\"", + "\"semanticType(?)\"", + "\"description(?)\"", + "\"referencedName(?)\"", + "", + }; + private static String name(final int state) { return STATE_NAME_LOOKUP[state]; } + + private static String transitions(final int state) + { + return STATE_TRANSITIONS_LOOKUP[state]; + } } private int codecState = CodecStates.NOT_WRAPPED; @@ -234,8 +257,10 @@ private void onTokenOffsetAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"tokenOffset\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"tokenOffset\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -300,8 +325,10 @@ private void onTokenSizeAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"tokenSize\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"tokenSize\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -366,8 +393,10 @@ private void onFieldIdAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"fieldId\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"fieldId\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -432,8 +461,10 @@ private void onTokenVersionAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"tokenVersion\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"tokenVersion\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -498,8 +529,10 @@ private void onComponentTokenCountAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"componentTokenCount\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"componentTokenCount\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -564,8 +597,10 @@ private void onSignalAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"signal\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"signal\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -614,8 +649,10 @@ private void onPrimitiveTypeAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"primitiveType\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"primitiveType\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -664,8 +701,10 @@ private void onByteOrderAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"byteOrder\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"byteOrder\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -714,8 +753,10 @@ private void onPresenceAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"presence\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"presence\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -764,8 +805,10 @@ private void onDeprecatedAccessed() { if (codecState() == CodecStates.NOT_WRAPPED) { - throw new IllegalStateException("Cannot access field \"deprecated\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"deprecated\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -829,8 +872,10 @@ private void onNameAccessed() codecState(CodecStates.V0_NAME_DONE); break; default: - throw new IllegalStateException("Cannot access field \"name\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"name\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -933,8 +978,10 @@ private void onConstValueAccessed() codecState(CodecStates.V0_CONSTVALUE_DONE); break; default: - throw new IllegalStateException("Cannot access field \"constValue\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"constValue\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -1037,8 +1084,10 @@ private void onMinValueAccessed() codecState(CodecStates.V0_MINVALUE_DONE); break; default: - throw new IllegalStateException("Cannot access field \"minValue\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"minValue\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -1141,8 +1190,10 @@ private void onMaxValueAccessed() codecState(CodecStates.V0_MAXVALUE_DONE); break; default: - throw new IllegalStateException("Cannot access field \"maxValue\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"maxValue\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -1245,8 +1296,10 @@ private void onNullValueAccessed() codecState(CodecStates.V0_NULLVALUE_DONE); break; default: - throw new IllegalStateException("Cannot access field \"nullValue\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"nullValue\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -1349,8 +1402,10 @@ private void onCharacterEncodingAccessed() codecState(CodecStates.V0_CHARACTERENCODING_DONE); break; default: - throw new IllegalStateException("Cannot access field \"characterEncoding\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"characterEncoding\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -1453,8 +1508,10 @@ private void onEpochAccessed() codecState(CodecStates.V0_EPOCH_DONE); break; default: - throw new IllegalStateException("Cannot access field \"epoch\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"epoch\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -1557,8 +1614,10 @@ private void onTimeUnitAccessed() codecState(CodecStates.V0_TIMEUNIT_DONE); break; default: - throw new IllegalStateException("Cannot access field \"timeUnit\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"timeUnit\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -1661,8 +1720,10 @@ private void onSemanticTypeAccessed() codecState(CodecStates.V0_SEMANTICTYPE_DONE); break; default: - throw new IllegalStateException("Cannot access field \"semanticType\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"semanticType\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -1765,8 +1826,10 @@ private void onDescriptionAccessed() codecState(CodecStates.V0_DESCRIPTION_DONE); break; default: - throw new IllegalStateException("Cannot access field \"description\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"description\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } @@ -1869,8 +1932,10 @@ private void onReferencedNameAccessed() codecState(CodecStates.V0_REFERENCEDNAME_DONE); break; default: - throw new IllegalStateException("Cannot access field \"referencedName\" in state: " + CodecStates.name(codecState()) + - ". Please see the diagram in the Javadoc of the inner class #CodecStates."); + throw new IllegalStateException("Illegal field access order. " + + "Cannot access field \"referencedName\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); } } From 9287ab2341e8fea4c6ced8ea4cfb7c9002bcbd1f Mon Sep 17 00:00:00 2001 From: Zach Bray Date: Tue, 20 Jun 2023 18:55:56 +0100 Subject: [PATCH 23/50] [Java] Use fewer states to model encoding. Encoders can only encode the latest version, unlike decoders. Therefore, we do not need to model field access patterns other than that of the latest version. --- .../sbe/generation/java/FieldOrderModel.java | 13 +++++++---- .../sbe/generation/java/JavaGenerator.java | 23 +++++++++++++------ 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java index a221bb4b55..3b3dd590cc 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java @@ -25,7 +25,9 @@ import java.util.*; import java.util.function.Consumer; +import java.util.function.Function; import java.util.function.Predicate; +import java.util.stream.IntStream; import static uk.co.real_logic.sbe.ir.GenerationUtil.collectFields; import static uk.co.real_logic.sbe.ir.GenerationUtil.collectGroups; @@ -56,10 +58,11 @@ static FieldOrderModel newInstance( final Token msgToken, final List fields, final List groups, - final List varData) + final List varData, + final Function versionsSelector) { final FieldOrderModel model = new FieldOrderModel(); - model.findTransitions(msgToken, fields, groups, varData); + model.findTransitions(msgToken, fields, groups, varData, versionsSelector); return model; } @@ -212,7 +215,8 @@ private void findTransitions( final Token msgToken, final List fields, final List groups, - final List varData) + final List varData, + final Function versionsSelector) { final IntHashSet versions = new IntHashSet(); versions.add(msgToken.version()); @@ -220,7 +224,8 @@ private void findTransitions( Generators.forEachField(fields, (fieldToken, ignored) -> topLevelBlockFields.add(fieldToken)); - versions.stream().sorted().forEach(version -> + final IntStream selectedVersions = versionsSelector.apply(versions.stream().mapToInt(i -> i)); + selectedVersions.sorted().forEach(version -> { final State versionWrappedState = allocateState("V" + version + "_BLOCK"); diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java index 074fd5c4f0..e74c857680 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java @@ -31,6 +31,7 @@ import java.io.Writer; import java.util.*; import java.util.function.Function; +import java.util.stream.IntStream; import static uk.co.real_logic.sbe.SbeTool.JAVA_INTERFACE_PACKAGE; import static uk.co.real_logic.sbe.generation.java.JavaGenerator.CodecType.DECODER; @@ -251,9 +252,18 @@ private void generateEncoder( generateAnnotations(BASE_INDENT, className, groups, out, this::encoderName); } out.append(generateDeclaration(className, implementsString, msgToken)); - final FieldOrderModel fieldOrderModel = FieldOrderModel.generateAccessOrderChecks() ? - FieldOrderModel.newInstance(msgToken, fields, groups, varData) : - null; + + FieldOrderModel fieldOrderModel = null; + if (FieldOrderModel.generateAccessOrderChecks()) + { + final Function selectLatestVersion = versions -> + { + final OptionalInt max = versions.max(); + return max.isPresent() ? IntStream.of(max.getAsInt()) : IntStream.empty(); + }; + fieldOrderModel = FieldOrderModel.newInstance(msgToken, fields, groups, varData, selectLatestVersion); + } + out.append(generateFieldOrderStates(fieldOrderModel)); out.append(generateEncoderFlyweightCode(className, fieldOrderModel, msgToken)); @@ -678,9 +688,8 @@ private void generateDecoder( FieldOrderModel fieldOrderModel = null; if (FieldOrderModel.generateAccessOrderChecks()) { - fieldOrderModel = FieldOrderModel.newInstance(msgToken, fields, groups, varData); + fieldOrderModel = FieldOrderModel.newInstance(msgToken, fields, groups, varData, Function.identity()); } - out.append(generateFieldOrderStates(fieldOrderModel)); out.append(generateDecoderFlyweightCode(fieldOrderModel, className, msgToken)); @@ -850,7 +859,7 @@ private void generateGroupDecoderClassHeader( .append(indent).append(" parentMessage.limit(limit + HEADER_SIZE);\n") .append(indent).append(" blockLength = ").append(blockLenCast).append(blockLengthGet).append(";\n") .append(indent).append(" count = ").append(numInGroupCast).append(numInGroupGet).append(";\n") - .append(indent).append(" }\n"); + .append(indent).append(" }\n\n"); generateAccessOrderListenerMethodForNextGroupElement(sb, fieldOrderModel, indent + " ", groupToken); @@ -985,7 +994,7 @@ private void generateGroupEncoderClassHeader( ind + " parentMessage.limit(limit + HEADER_SIZE);\n" + ind + " %5$s;\n" + ind + " %6$s;\n" + - ind + " }\n", + ind + " }\n\n", parentMessageClassName, mutableBuffer, numInGroupToken.encoding().applicableMinValue().longValue(), From 21718089399fd204447641c7ff01e5a4002071a7 Mon Sep 17 00:00:00 2001 From: Zach Bray Date: Wed, 21 Jun 2023 13:44:43 +0100 Subject: [PATCH 24/50] [Java] Refactor field access order checking. Overview: - `AccessOrderModel.CodecInteraction` models the possible interactions with an encoder or decoder: - Wrapping it around a buffer - Accessing its fields (block or var data) - Encoding/decoding the count of a repeating group - Moving to the next element in a repeating group - Moving to the last element in a repeating group - `AccessOrderModel` models the encoder/decoder state transitions w.r.t. these interactions --- .../sbe/generation/java/AccessOrderModel.java | 859 ++++++++++++++++++ .../sbe/generation/java/FieldOrderModel.java | 633 ------------- .../sbe/generation/java/JavaGenerator.java | 250 ++--- .../sbe/ir/generated/FrameCodecDecoder.java | 4 +- .../sbe/ir/generated/FrameCodecEncoder.java | 4 +- .../sbe/ir/generated/TokenCodecDecoder.java | 4 +- .../sbe/ir/generated/TokenCodecEncoder.java | 4 +- ...st.java => FieldAccessOrderCheckTest.java} | 2 +- 8 files changed, 997 insertions(+), 763 deletions(-) create mode 100644 sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/AccessOrderModel.java delete mode 100644 sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java rename sbe-tool/src/test/java/uk/co/real_logic/sbe/{FieldOrderCheckTest.java => FieldAccessOrderCheckTest.java} (99%) diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/AccessOrderModel.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/AccessOrderModel.java new file mode 100644 index 0000000000..0eecfa0efd --- /dev/null +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/AccessOrderModel.java @@ -0,0 +1,859 @@ +/* + * 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.java; + +import uk.co.real_logic.sbe.generation.Generators; +import uk.co.real_logic.sbe.ir.Signal; +import uk.co.real_logic.sbe.ir.Token; +import org.agrona.collections.Int2ObjectHashMap; +import org.agrona.collections.IntHashSet; +import org.agrona.collections.IntObjConsumer; + +import java.util.*; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.IntStream; + +import static uk.co.real_logic.sbe.ir.GenerationUtil.collectFields; +import static uk.co.real_logic.sbe.ir.GenerationUtil.collectGroups; +import static uk.co.real_logic.sbe.ir.GenerationUtil.collectVarData; + +// Lambdas without braces tend to conflict with the indentation Checkstyle expects. +// Therefore, we allow lambdas with code blocks even when a lambda expression is possible. +@SuppressWarnings("CodeBlock2Expr") +final class AccessOrderModel +{ + private static final boolean GENERATE_ACCESS_ORDER_CHECKS = Boolean.parseBoolean( + System.getProperty("sbe.generate.access.order.checks", "true")); + private final Map groupPathsByField = new HashMap<>(); + private final Set topLevelBlockFields = new HashSet<>(); + private final CodecInteraction.HashConsingFactory interactionFactory = + new CodecInteraction.HashConsingFactory(groupPathsByField, topLevelBlockFields); + private final Map> transitionsByInteraction = new LinkedHashMap<>(); + private final Map> transitionsByState = new HashMap<>(); + private final Int2ObjectHashMap versionWrappedStates = new Int2ObjectHashMap<>(); + private final State notWrappedState = allocateState("NOT_WRAPPED"); + private State encoderWrappedState; + + static boolean generateAccessOrderChecks() + { + return GENERATE_ACCESS_ORDER_CHECKS; + } + + static AccessOrderModel newInstance( + final Token msgToken, + final List fields, + final List groups, + final List varData, + final Function versionsSelector) + { + final AccessOrderModel model = new AccessOrderModel(); + model.findTransitions(msgToken, fields, groups, varData, versionsSelector); + return model; + } + + State notWrappedState() + { + return notWrappedState; + } + + State latestVersionWrappedState() + { + return encoderWrappedState; + } + + void forEachDecoderWrappedState(final IntObjConsumer consumer) + { + final Int2ObjectHashMap.EntryIterator iterator = versionWrappedStates.entrySet().iterator(); + while (iterator.hasNext()) + { + iterator.next(); + consumer.accept(iterator.getIntKey(), iterator.getValue()); + } + } + + void forEachStateOrderedByStateNumber(final Consumer consumer) + { + transitionsByState.keySet().stream() + .sorted(Comparator.comparingInt(s -> s.number)) + .forEach(consumer); + } + + CodecInteraction.HashConsingFactory interactionFactory() + { + return interactionFactory; + } + + void getTransitions( + final List transitionsOut, + final CodecInteraction interaction) + { + final List transitionsForContext = transitionsByInteraction.get(interaction); + if (null != transitionsForContext) + { + transitionsOut.addAll(transitionsForContext); + } + } + + void forEachTransitionFrom(final State state, final Consumer consumer) + { + final List transitionGroups = transitionsByState.get(state); + if (null != transitionGroups) + { + transitionGroups.forEach(consumer); + } + } + + @SuppressWarnings("SameParameterValue") + void generateGraph(final StringBuilder sb, final String indent) + { + sb.append(indent).append("digraph G {\n"); + transitionsByInteraction.values().forEach(transitionsForContext -> + { + transitionsForContext.forEach(transition -> + { + transition.forEachStartState(startState -> + { + sb.append(indent).append(" ") + .append(startState.name) + .append(" -> ") + .append(transition.endState().name) + .append(" [label=\" ").append(transition.interaction.exampleCode()); + + if (!transition.interaction.exampleConditions().isEmpty()) + { + sb.append("\\n").append(" where ").append(transition.interaction.exampleConditions()); + } + + sb.append(" \"];\n"); + }); + }); + }); + sb.append(indent).append("}\n"); + } + + private void findTransitions( + final Token msgToken, + final List fields, + final List groups, + final List varData, + final Function versionsSelector) + { + final IntHashSet versions = new IntHashSet(); + versions.add(msgToken.version()); + walkSchemaLevel(new VersionCollector(versions), fields, groups, varData); + walkSchemaLevel(new PathCollector(topLevelBlockFields, groupPathsByField), fields, groups, varData); + + final IntStream selectedVersions = versionsSelector.apply(versions.stream().mapToInt(i -> i)); + selectedVersions.sorted().forEach(version -> + { + final State versionWrappedState = allocateState("V" + version + "_BLOCK"); + + versionWrappedStates.put(version, versionWrappedState); + + encoderWrappedState = versionWrappedState; + + final CodecInteraction wrapInteraction = interactionFactory.wrap(version); + + allocateTransitions( + wrapInteraction, + Collections.singletonList(notWrappedState), + versionWrappedState + ); + + final TransitionCollector transitionCollector = new TransitionCollector( + "V" + version + "_", + Collections.singleton(versionWrappedState), + versionWrappedState, + token -> token.version() <= version + ); + + walkSchemaLevel(transitionCollector, fields, groups, varData); + }); + } + + private State allocateState(final String name) + { + final State state = new State(transitionsByState.size(), name); + transitionsByState.put(state, new ArrayList<>()); + return state; + } + + private void allocateTransitions( + final CodecInteraction interaction, + final Collection from, + final State to) + { + final TransitionGroup transitionGroup = new TransitionGroup(interaction, from, to); + final List transitionsForInteraction = + transitionsByInteraction.computeIfAbsent(interaction, ignored -> new ArrayList<>()); + + final boolean duplicateEndState = + transitionsForInteraction.stream().anyMatch(t -> t.to.number == transitionGroup.to.number); + + if (duplicateEndState) + { + throw new IllegalStateException("Duplicate end state: " + transitionGroup.to.name); + } + + final Optional conflictingTransition = transitionsForInteraction.stream() + .filter(t -> t.from.stream().anyMatch(transitionGroup.from::contains)) + .findAny(); + + if (conflictingTransition.isPresent()) + { + throw new IllegalStateException( + "Conflicting transition: " + transitionGroup + " conflicts with " + conflictingTransition.get()); + } + + transitionsForInteraction.add(transitionGroup); + + from.forEach(fromState -> transitionsByState.get(fromState).add(transitionGroup)); + } + + private static void walkSchemaLevel( + final SchemaConsumer consumer, + final List fields, + final List groups, + final List varData) + { + Generators.forEachField(fields, (token, ignored) -> consumer.onBlockField(token)); + + for (int i = 0; i < groups.size(); i++) + { + final Token token = groups.get(i); + if (token.signal() != Signal.BEGIN_GROUP) + { + throw new IllegalStateException("tokens must begin with BEGIN_GROUP: token=" + token); + } + + ++i; + final int groupHeaderTokenCount = groups.get(i).componentTokenCount(); + i += groupHeaderTokenCount; + + final ArrayList groupFields = new ArrayList<>(); + i = collectFields(groups, i, groupFields); + final ArrayList groupGroups = new ArrayList<>(); + i = collectGroups(groups, i, groupGroups); + final ArrayList groupVarData = new ArrayList<>(); + i = collectVarData(groups, i, groupVarData); + + consumer.onEnterRepeatingGroup(token, groupFields, groupGroups, groupVarData); + } + + for (int i = 0; i < varData.size(); ) + { + final Token token = varData.get(i); + if (token.signal() != Signal.BEGIN_VAR_DATA) + { + throw new IllegalStateException("tokens must begin with BEGIN_VAR_DATA: token=" + token); + } + i += token.componentTokenCount(); + + consumer.onVarData(token); + } + } + + private interface SchemaConsumer + { + void onBlockField(Token token); + + void onEnterRepeatingGroup( + Token token, + List groupFields, + List groupGroups, + List groupVarData); + + void onVarData(Token token); + } + + private static final class PathCollector implements SchemaConsumer + { + private final ArrayDeque groupPath = new ArrayDeque<>(); + private final Set topLevelBlockFields; + private final Map groupPathsByField; + + private PathCollector( + final Set topLevelBlockFields, + final Map groupPathsByField) + { + this.topLevelBlockFields = topLevelBlockFields; + this.groupPathsByField = groupPathsByField; + } + + @Override + public void onBlockField(final Token token) + { + if (groupPath.isEmpty()) + { + topLevelBlockFields.add(token); + } + + groupPathsByField.put(token, currentGroupPath()); + } + + @Override + public void onEnterRepeatingGroup( + final Token token, + final List groupFields, + final List groupGroups, + final List groupVarData) + { + groupPathsByField.put(token, currentGroupPath()); + groupPath.addLast(token); + walkSchemaLevel(this, groupFields, groupGroups, groupVarData); + groupPath.removeLast(); + } + + @Override + public void onVarData(final Token token) + { + groupPathsByField.put(token, currentGroupPath()); + } + + private String currentGroupPath() + { + final StringBuilder sb = new StringBuilder(); + groupPath.forEach(token -> + { + sb.append(token.name()).append('.'); + }); + return sb.toString(); + } + } + + private static final class VersionCollector implements SchemaConsumer + { + private final IntHashSet versions; + + VersionCollector(final IntHashSet versions) + { + this.versions = versions; + } + + @Override + public void onBlockField(final Token token) + { + versions.add(token.version()); + } + + @Override + public void onEnterRepeatingGroup( + final Token token, + final List groupFields, + final List groupGroups, + final List groupVarData) + { + versions.add(token.version()); + walkSchemaLevel(this, groupFields, groupGroups, groupVarData); + } + + @Override + public void onVarData(final Token token) + { + versions.add(token.version()); + } + } + + private final class TransitionCollector implements SchemaConsumer + { + private final String statePrefix; + private final HashSet currentStates; + private final State blockState; + private final Predicate filter; + + private TransitionCollector( + final String statePrefix, + final Set fromStates, + final State blockState, + final Predicate filter) + { + this.statePrefix = statePrefix; + this.currentStates = new HashSet<>(fromStates); + this.blockState = blockState; + this.filter = filter; + + currentStates.add(blockState); + } + + @Override + public void onBlockField(final Token token) + { + if (filter.test(token)) + { + final CodecInteraction codecInteraction = interactionFactory.accessField(token); + allocateTransitions(codecInteraction, currentStates, blockState); + } + } + + @Override + public void onEnterRepeatingGroup( + final Token token, + final List groupFields, + final List groupGroups, + final List groupVarData) + { + if (filter.test(token)) + { + final String groupName = token.name().toUpperCase(); + final String groupPrefix = statePrefix + groupName + "_"; + final State nRemainingGroup = allocateState(groupPrefix + "N"); + final State nRemainingGroupElement = allocateState(groupPrefix + "N_BLOCK"); + final State oneRemainingGroupElement = allocateState(groupPrefix + "1_BLOCK"); + final State emptyGroup = allocateState(groupPrefix + "0"); + + final Set beginGroupStates = new HashSet<>(currentStates); + + // fooCount(0) + final CodecInteraction emptyGroupInteraction = interactionFactory.determineGroupIsEmpty(token); + allocateTransitions(emptyGroupInteraction, beginGroupStates, emptyGroup); + + // fooCount(N) where N > 0 + final CodecInteraction nonEmptyGroupInteraction = interactionFactory.determineGroupHasElements(token); + allocateTransitions(nonEmptyGroupInteraction, beginGroupStates, nRemainingGroup); + + currentStates.clear(); + currentStates.add(nRemainingGroupElement); + final TransitionCollector nRemainingCollector = new TransitionCollector( + groupPrefix + "N_", + currentStates, + nRemainingGroupElement, + filter + ); + walkSchemaLevel(nRemainingCollector, groupFields, groupGroups, groupVarData); + + currentStates.clear(); + currentStates.add(nRemainingGroup); + currentStates.addAll(nRemainingCollector.exitStates()); + + // where more than one element remains in the group + final CodecInteraction nextGroupElementInteraction = interactionFactory.moveToNextElement(token); + allocateTransitions(nextGroupElementInteraction, currentStates, nRemainingGroupElement); + + currentStates.clear(); + currentStates.add(nRemainingGroup); + currentStates.addAll(nRemainingCollector.exitStates()); + + // where only one element remains in the group + final CodecInteraction lastGroupElementInteraction = interactionFactory.moveToLastElement(token); + allocateTransitions(lastGroupElementInteraction, currentStates, oneRemainingGroupElement); + + currentStates.clear(); + currentStates.add(oneRemainingGroupElement); + + final TransitionCollector oneRemainingCollector = new TransitionCollector( + groupPrefix + "1_", + currentStates, + oneRemainingGroupElement, + filter + ); + walkSchemaLevel(oneRemainingCollector, groupFields, groupGroups, groupVarData); + + currentStates.clear(); + currentStates.add(emptyGroup); + currentStates.addAll(oneRemainingCollector.exitStates()); + } + } + + @Override + public void onVarData(final Token token) + { + if (filter.test(token)) + { + final CodecInteraction codecInteraction = interactionFactory.accessField(token); + final State accessedState = allocateState(statePrefix + token.name().toUpperCase() + "_DONE"); + allocateTransitions(codecInteraction, currentStates, accessedState); + currentStates.clear(); + currentStates.add(accessedState); + } + } + + private Set exitStates() + { + return currentStates; + } + } + + static final class State + { + private final int number; + private final String name; + + private State(final int number, final String name) + { + this.number = number; + this.name = name; + } + + /** + * In the scope of an {@code AccessOrderModel} instance, state numbers are contiguous + * and start at 0. This numbering scheme allows easy generation of lookup tables. + * @return the state number + */ + int number() + { + return number; + } + + String name() + { + return name; + } + + @Override + public String toString() + { + return "State{" + + "number=" + number + + ", name='" + name + '\'' + + '}'; + } + } + + static final class TransitionGroup + { + private final CodecInteraction interaction; + private final Set from; + private final State to; + + private TransitionGroup( + final CodecInteraction interaction, + final Collection from, + final State to) + { + this.interaction = interaction; + this.from = new HashSet<>(from); + this.to = to; + } + + void forEachStartState(final Consumer consumer) + { + from.forEach(consumer); + } + + State endState() + { + return to; + } + + String exampleCode() + { + return interaction.exampleCode(); + } + + @Override + public String toString() + { + return "Transition{" + + "interaction='" + exampleCode() + '\'' + + ", from=" + from + + ", to=" + to + + '}'; + } + } + + abstract static class CodecInteraction + { + abstract String groupQualifiedName(); + + abstract String exampleCode(); + + abstract String exampleConditions(); + + final boolean isTopLevelBlockFieldAccess() + { + if (this instanceof AccessField) + { + final AccessField accessField = (AccessField)this; + return accessField.isTopLevelBlockField(); + } + + return false; + } + + /** + * When an encoder or decoder is wrapped around a buffer. + */ + private static final class Wrap extends CodecInteraction + { + private final int version; + + Wrap(final int version) + { + this.version = version; + } + + @Override + String groupQualifiedName() + { + return "wrap"; + } + + @Override + String exampleCode() + { + return "wrap(version=" + version + ")"; + } + + @Override + String exampleConditions() + { + return ""; + } + } + + /** + * When a block or variable-length field is accessed. + */ + private static final class AccessField extends CodecInteraction + { + private final String groupPath; + private final Token token; + private final boolean isTopLevelBlockField; + + private AccessField(final String groupPath, final Token token, final boolean isTopLevelBlockField) + { + this.isTopLevelBlockField = isTopLevelBlockField; + assert groupPath != null; + assert token.signal() == Signal.BEGIN_FIELD || token.signal() == Signal.BEGIN_VAR_DATA; + this.groupPath = groupPath; + this.token = token; + } + + boolean isTopLevelBlockField() + { + return isTopLevelBlockField; + } + + @Override + String groupQualifiedName() + { + return groupPath + token.name(); + } + + @Override + String exampleCode() + { + return groupPath + token.name() + "(?)"; + } + + @Override + String exampleConditions() + { + return ""; + } + } + + /** + * When a repeating group count is supplied as zero. + */ + private static final class DetermineGroupIsEmpty extends CodecInteraction + { + private final String groupPath; + private final Token token; + + private DetermineGroupIsEmpty(final String groupPath, final Token token) + { + assert groupPath != null; + assert token.signal() == Signal.BEGIN_GROUP; + this.groupPath = groupPath; + this.token = token; + } + + @Override + String groupQualifiedName() + { + return groupPath + token.name(); + } + + @Override + String exampleCode() + { + return groupPath + token.name() + "Count(0)"; + } + + @Override + String exampleConditions() + { + return ""; + } + } + + /** + * When a repeating group count is supplied as greater than zero. + */ + private static final class DetermineGroupHasElements extends CodecInteraction + { + private final String groupPath; + private final Token token; + + private DetermineGroupHasElements(final String groupPath, final Token token) + { + assert groupPath != null; + assert token.signal() == Signal.BEGIN_GROUP; + this.groupPath = groupPath; + this.token = token; + } + + @Override + String groupQualifiedName() + { + return groupPath + token.name(); + } + + @Override + String exampleCode() + { + return groupPath + token.name() + "Count(>0)"; + } + + @Override + String exampleConditions() + { + return ""; + } + } + + /** + * When the next element in a repeating group is accessed, + * and it is not the last element. + */ + private static final class MoveToNextElement extends CodecInteraction + { + private final String groupPath; + private final Token token; + + private MoveToNextElement(final String groupPath, final Token token) + { + assert groupPath != null; + assert token.signal() == Signal.BEGIN_GROUP; + this.groupPath = groupPath; + this.token = token; + } + + @Override + String groupQualifiedName() + { + return groupPath + token.name(); + } + + @Override + String exampleCode() + { + return groupPath + token.name() + ".next()"; + } + + @Override + String exampleConditions() + { + return "count - newIndex > 1"; + } + } + + /** + * When the next element in a repeating group is accessed, + * and it is the last element. + */ + private static final class MoveToLastElement extends CodecInteraction + { + private final String groupPath; + private final Token token; + + private MoveToLastElement(final String groupPath, final Token token) + { + assert groupPath != null; + assert token.signal() == Signal.BEGIN_GROUP; + this.groupPath = groupPath; + this.token = token; + } + + @Override + String groupQualifiedName() + { + return groupPath + token.name(); + } + + @Override + String exampleCode() + { + return groupPath + token.name() + ".next()"; + } + + @Override + String exampleConditions() + { + return "count - newIndex == 1"; + } + } + + static final class HashConsingFactory + { + private final Int2ObjectHashMap wrapInteractions = new Int2ObjectHashMap<>(); + private final Map accessFieldInteractions = new HashMap<>(); + private final Map determineGroupIsEmptyInteractions = new HashMap<>(); + private final Map determineGroupHasElementsInteractions = new HashMap<>(); + private final Map moveToNextElementInteractions = new HashMap<>(); + private final Map moveToLastElementInteractions = new HashMap<>(); + private final Map groupPathsByField; + private final Set topLevelBlockFields; + + HashConsingFactory( + final Map groupPathsByField, + final Set topLevelBlockFields) + { + + this.groupPathsByField = groupPathsByField; + this.topLevelBlockFields = topLevelBlockFields; + } + + CodecInteraction wrap(final int version) + { + return wrapInteractions.computeIfAbsent(version, Wrap::new); + } + + CodecInteraction accessField(final Token token) + { + return accessFieldInteractions.computeIfAbsent(token, + t -> new AccessField(groupPathsByField.get(t), t, topLevelBlockFields.contains(t))); + } + + CodecInteraction determineGroupIsEmpty(final Token token) + { + return determineGroupIsEmptyInteractions.computeIfAbsent(token, + t -> new DetermineGroupIsEmpty(groupPathsByField.get(t), t)); + } + + CodecInteraction determineGroupHasElements(final Token token) + { + return determineGroupHasElementsInteractions.computeIfAbsent(token, + t -> new DetermineGroupHasElements(groupPathsByField.get(t), t)); + } + + CodecInteraction moveToNextElement(final Token token) + { + return moveToNextElementInteractions.computeIfAbsent(token, + t -> new MoveToNextElement(groupPathsByField.get(t), t)); + } + + CodecInteraction moveToLastElement(final Token token) + { + return moveToLastElementInteractions.computeIfAbsent(token, + t -> new MoveToLastElement(groupPathsByField.get(t), t)); + } + } + } +} diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java deleted file mode 100644 index 3b3dd590cc..0000000000 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/FieldOrderModel.java +++ /dev/null @@ -1,633 +0,0 @@ -/* - * 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.java; - -import uk.co.real_logic.sbe.generation.Generators; -import uk.co.real_logic.sbe.ir.Signal; -import uk.co.real_logic.sbe.ir.Token; -import org.agrona.collections.Int2ObjectHashMap; -import org.agrona.collections.IntHashSet; -import org.agrona.collections.IntObjConsumer; -import org.agrona.collections.MutableReference; - -import java.util.*; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.stream.IntStream; - -import static uk.co.real_logic.sbe.ir.GenerationUtil.collectFields; -import static uk.co.real_logic.sbe.ir.GenerationUtil.collectGroups; -import static uk.co.real_logic.sbe.ir.GenerationUtil.collectVarData; - -// There is no abstraction for visiting fields, groups, and varData. Therefore, there is some "duplication". -// Lambdas without braces tend to conflict with checkstyle. Therefore, we allow braces when an expression is possible. -@SuppressWarnings({"DuplicatedCode", "CodeBlock2Expr"}) -final class FieldOrderModel -{ - private static final boolean GENERATE_ACCESS_ORDER_CHECKS = Boolean.parseBoolean( - System.getProperty("sbe.generate.access.order.checks", "true")); - private final Int2ObjectHashMap states = new Int2ObjectHashMap<>(); - private final Map transitionsByField = new LinkedHashMap<>(); - private final Map> transitionsByState = new LinkedHashMap<>(); - private final Set topLevelBlockFields = new HashSet<>(); - private final Int2ObjectHashMap versionWrappedStates = new Int2ObjectHashMap<>(); - private final Set reservedNames = new HashSet<>(); - private final State notWrappedState = allocateState("NOT_WRAPPED"); - private State encoderWrappedState; - - static boolean generateAccessOrderChecks() - { - return GENERATE_ACCESS_ORDER_CHECKS; - } - - static FieldOrderModel newInstance( - final Token msgToken, - final List fields, - final List groups, - final List varData, - final Function versionsSelector) - { - final FieldOrderModel model = new FieldOrderModel(); - model.findTransitions(msgToken, fields, groups, varData, versionsSelector); - return model; - } - - State notWrappedState() - { - return notWrappedState; - } - - State latestVersionWrappedState() - { - return encoderWrappedState; - } - - void forEachDecoderWrappedState(final IntObjConsumer consumer) - { - final Int2ObjectHashMap.EntryIterator iterator = versionWrappedStates.entrySet().iterator(); - while (iterator.hasNext()) - { - iterator.next(); - consumer.accept(iterator.getIntKey(), iterator.getValue()); - } - } - - void forEachStateOrderedByNumber(final Consumer consumer) - { - states.values() - .stream() - .sorted(Comparator.comparingInt(s -> s.number)) - .forEach(consumer); - } - - boolean isTopLevelBlockField(final Token token) - { - return topLevelBlockFields.contains(token); - } - - String fieldPath(final Token token) - { - final StringBuilder sb = new StringBuilder(); - final TransitionGroup transitionGroup = transitionsByField.get(token); - if (null != transitionGroup) - { - sb.append(transitionGroup.groupPath); - } - sb.append(token.name()); - return sb.toString(); - } - - void getTransitions( - final List transitionsOut, - final TransitionContext context, - final Token token) - { - final TransitionGroup transitionGroup = transitionsByField.get(token); - if (null != transitionGroup) - { - final List transitionsForContext = transitionGroup.transitions.get(context); - if (null != transitionsForContext) - { - transitionsOut.addAll(transitionsForContext); - } - } - } - - void forEachTransitionFrom(final State state, final Consumer consumer) - { - final List transitions = transitionsByState.get(state); - if (null != transitions) - { - transitions.forEach(consumer); - } - } - - @SuppressWarnings("SameParameterValue") - void generateGraph(final StringBuilder sb, final String indent) - { - sb.append(indent).append("digraph G {\n"); - transitionsByField.values().forEach(transitionGroup -> - transitionGroup.transitions.forEach((context, transitionsForContext) -> - { - transitionsForContext.forEach(transition -> - { - transition.forEachStartState(startState -> - { - sb.append(indent).append(" ") - .append(startState.name) - .append(" -> ") - .append(transition.endState().name) - .append(" [label=\" ").append(transition.description); - - if (!transition.conditions.isEmpty()) - { - sb.append("\\n").append(" where ").append(transition.conditions); - } - - sb.append(" \"];\n"); - }); - }); - }) - ); - sb.append(indent).append("}\n"); - } - - private static void findVersions( - final IntHashSet versions, - final List fields, - final List groups, - final List varData) - { - Generators.forEachField(fields, (token, ignored) -> versions.add(token.version())); - - for (int i = 0; i < groups.size(); i++) - { - final Token token = groups.get(i); - if (token.signal() != Signal.BEGIN_GROUP) - { - throw new IllegalStateException("tokens must begin with BEGIN_GROUP: token=" + token); - } - - versions.add(token.version()); - - ++i; - final int groupHeaderTokenCount = groups.get(i).componentTokenCount(); - i += groupHeaderTokenCount; - - final ArrayList groupFields = new ArrayList<>(); - i = collectFields(groups, i, groupFields); - final ArrayList groupGroups = new ArrayList<>(); - i = collectGroups(groups, i, groupGroups); - final ArrayList groupVarData = new ArrayList<>(); - i = collectVarData(groups, i, groupVarData); - - findVersions(versions, groupFields, groupGroups, groupVarData); - } - - for (int i = 0; i < varData.size(); ) - { - final Token token = varData.get(i); - if (token.signal() != Signal.BEGIN_VAR_DATA) - { - throw new IllegalStateException("tokens must begin with BEGIN_VAR_DATA: token=" + token); - } - i += token.componentTokenCount(); - - versions.add(token.version()); - } - } - - private void findTransitions( - final Token msgToken, - final List fields, - final List groups, - final List varData, - final Function versionsSelector) - { - final IntHashSet versions = new IntHashSet(); - versions.add(msgToken.version()); - findVersions(versions, fields, groups, varData); - - Generators.forEachField(fields, (fieldToken, ignored) -> topLevelBlockFields.add(fieldToken)); - - final IntStream selectedVersions = versionsSelector.apply(versions.stream().mapToInt(i -> i)); - selectedVersions.sorted().forEach(version -> - { - final State versionWrappedState = allocateState("V" + version + "_BLOCK"); - - versionWrappedStates.put(version, versionWrappedState); - - encoderWrappedState = versionWrappedState; - - allocateTransition( - version, - "wrap(?)", - "", - Collections.emptyList(), - null, - Collections.singletonList(notWrappedState), - versionWrappedState - ); - - findTransitions( - Collections.singletonList(versionWrappedState), - versionWrappedState, - "V" + version + "_", - new ArrayList<>(), - fields, - groups, - varData, - token -> token.version() <= version - ); - }); - } - - @SuppressWarnings("checkstyle:MethodLength") - private List findTransitions( - final List entryStates, - final State blockStateOrNull, - final String prefix, - final List groupPath, - final List fields, - final List groups, - final List varData, - final Predicate filter) - { - final MutableReference blockState = new MutableReference<>(blockStateOrNull); - - final List fromStates = new ArrayList<>(entryStates); - - Generators.forEachField(fields, (token, ignored) -> - { - if (!filter.test(token)) - { - return; - } - - if (null == blockState.get()) - { - blockState.set(allocateState(prefix + "BLOCK")); - fromStates.add(blockState.get()); - } - - allocateTransition( - TransitionContext.NONE, - token.name() + "(?)", - "", - groupPath, - token, - fromStates, - blockState.get()); - }); - - if (null != blockState.get()) - { - fromStates.clear(); - fromStates.add(blockState.get()); - } - - for (int i = 0; i < groups.size(); i++) - { - final Token token = groups.get(i); - if (token.signal() != Signal.BEGIN_GROUP) - { - throw new IllegalStateException("tokens must begin with BEGIN_GROUP: token=" + token); - } - - ++i; - final int groupHeaderTokenCount = groups.get(i).componentTokenCount(); - i += groupHeaderTokenCount; - - final ArrayList groupFields = new ArrayList<>(); - i = collectFields(groups, i, groupFields); - final ArrayList groupGroups = new ArrayList<>(); - i = collectGroups(groups, i, groupGroups); - final ArrayList groupVarData = new ArrayList<>(); - i = collectVarData(groups, i, groupVarData); - - if (!filter.test(token)) - { - continue; - } - - final String groupName = token.name().toUpperCase(); - final String groupPrefix = prefix + groupName + "_"; - - final List beginGroupStates = new ArrayList<>(fromStates); - final List newGroupPath = new ArrayList<>(groupPath); - newGroupPath.add(token); - - final State nRemainingGroup = allocateState(groupPrefix + "N"); - final State nRemainingGroupElement = allocateState(groupPrefix + "N_BLOCK"); - final State oneRemainingGroupElement = allocateState(groupPrefix + "1_BLOCK"); - final State emptyGroup = allocateState(groupPrefix + "0"); - - // fooCount(0) - allocateTransition( - TransitionContext.SELECT_EMPTY_GROUP, - token.name() + "Length(0)", - "", - groupPath, - token, - beginGroupStates, - emptyGroup); - - // fooCount(N) where N > 0 - allocateTransition( - TransitionContext.SELECT_MULTI_ELEMENT_GROUP, - token.name() + "Length(N)", - "where N > 0", - groupPath, - token, - beginGroupStates, - nRemainingGroup); - - fromStates.clear(); - fromStates.add(nRemainingGroupElement); - final List nRemainingExitStates = findTransitions( - fromStates, - nRemainingGroupElement, - groupPrefix + "N_", - newGroupPath, - groupFields, - groupGroups, - groupVarData, - filter); - - fromStates.clear(); - fromStates.add(nRemainingGroup); - fromStates.addAll(nRemainingExitStates); - - // where more than one element remains in the group - allocateTransition( - TransitionContext.NEXT_ELEMENT_IN_GROUP, - token.name() + ".next()", - "count - index > 1", - groupPath, - token, - fromStates, - nRemainingGroupElement); - - fromStates.clear(); - fromStates.add(nRemainingGroup); - fromStates.addAll(nRemainingExitStates); - - // where only one element remains in the group - allocateTransition( - TransitionContext.LAST_ELEMENT_IN_GROUP, - token.name() + ".next()", - "count - index == 1", - groupPath, - token, - fromStates, - oneRemainingGroupElement); - - fromStates.clear(); - fromStates.add(oneRemainingGroupElement); - - final List oneRemainingExitStates = findTransitions( - fromStates, - oneRemainingGroupElement, - groupPrefix + "1_", - newGroupPath, - groupFields, - groupGroups, - groupVarData, - filter); - - fromStates.clear(); - fromStates.add(emptyGroup); - fromStates.addAll(oneRemainingExitStates); - } - - for (int i = 0; i < varData.size(); ) - { - final Token token = varData.get(i); - if (token.signal() != Signal.BEGIN_VAR_DATA) - { - throw new IllegalStateException("tokens must begin with BEGIN_VAR_DATA: token=" + token); - } - i += token.componentTokenCount(); - - if (!filter.test(token)) - { - continue; - } - - final State state = allocateState(prefix + token.name().toUpperCase() + "_DONE"); - allocateTransition( - TransitionContext.NONE, - token.name() + "(?)", - "", - groupPath, - token, - fromStates, - state); - fromStates.clear(); - fromStates.add(state); - } - - return fromStates; - } - - private State allocateState(final String name) - { - if (!reservedNames.add(name)) - { - throw new IllegalStateException("Name is already reserved: " + name); - } - - final State state = new State(states.size(), name); - states.put(state.number, state); - return state; - } - - private void allocateTransition( - final Object firingContext, - final String description, - final String conditions, - final List groupPath, - final Token token, - final List from, - final State to) - { - final TransitionGroup transitionGroup = transitionsByField.computeIfAbsent(token, - ignored -> new TransitionGroup(groupPath)); - final Transition transition = new Transition(transitionGroup.groupPath + description, conditions, from, to); - transitionGroup.add(firingContext, transition); - from.forEach(fromState -> - { - transitionsByState.computeIfAbsent(fromState, ignored -> new ArrayList<>()) - .add(transition); - }); - } - - static final class State - { - private final int number; - private final String name; - - private State(final int number, final String name) - { - this.number = number; - this.name = name; - } - - int number() - { - return number; - } - - String name() - { - return name; - } - - @Override - public String toString() - { - return "State{" + - "number=" + number + - ", name='" + name + '\'' + - '}'; - } - } - - static final class Transition - { - private final String description; - private final String conditions; - private final Set from; - private final State to; - - private Transition( - final String description, - final String conditions, - final List from, - final State to) - { - this.description = description; - this.conditions = conditions; - this.from = new HashSet<>(from); - this.to = to; - } - - void forEachStartState(final Consumer consumer) - { - from.forEach(consumer); - } - - State endState() - { - return to; - } - - String description() - { - return description; - } - - @Override - public String toString() - { - return "Transition{" + - "description='" + description + '\'' + - ", conditions='" + conditions + '\'' + - ", from=" + from + - ", to=" + to + - '}'; - } - } - - enum TransitionContext - { - /** - * For tokens with a set of transitions that does not depend on any context, e.g., - * when a block field is accessed. As opposed to a repeating group, where the - * transitions depend both on the number of remaining elements in the group and - * whether {@code next()} is called or {@code myGroupCount(int count)}. - */ - NONE, - - /** - * When a repeating group count is supplied as zero. - */ - SELECT_EMPTY_GROUP, - - /** - * When a repeating group count is supplied as greater than zero. - */ - SELECT_MULTI_ELEMENT_GROUP, - - /** - * When the next element in a repeating group is accessed, - * and it is not the last element. - */ - NEXT_ELEMENT_IN_GROUP, - - /** - * When the next element in a repeating group is accessed, - * and it is the last element. - */ - LAST_ELEMENT_IN_GROUP - } - - /** - * The codec state transitions possible for a given block/group/data field. - */ - private static final class TransitionGroup - { - private final Map> transitions = new LinkedHashMap<>(); - private final String groupPath; - - private TransitionGroup(final List groupPath) - { - final StringBuilder sb = new StringBuilder(); - groupPath.forEach(token -> - { - sb.append(token.name()).append('.'); - }); - this.groupPath = sb.toString(); - } - - void add(final Object context, final Transition transition) - { - final List transitionsForContext = - transitions.computeIfAbsent(context, ignored -> new ArrayList<>()); - - final boolean duplicateEndState = - transitionsForContext.stream().anyMatch(t -> t.to.number == transition.to.number); - - if (duplicateEndState) - { - throw new IllegalStateException("Duplicate end state: " + transition.to.name); - } - - final Optional conflictingTransition = transitionsForContext.stream() - .filter(t -> t.from.stream().anyMatch(transition.from::contains)) - .findAny(); - - if (conflictingTransition.isPresent()) - { - throw new IllegalStateException( - "Conflicting transition: " + transition + " conflicts with " + conflictingTransition.get()); - } - - transitionsForContext.add(transition); - } - } -} diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java index e74c857680..292217d82e 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java @@ -253,24 +253,24 @@ private void generateEncoder( } out.append(generateDeclaration(className, implementsString, msgToken)); - FieldOrderModel fieldOrderModel = null; - if (FieldOrderModel.generateAccessOrderChecks()) + AccessOrderModel accessOrderModel = null; + if (AccessOrderModel.generateAccessOrderChecks()) { final Function selectLatestVersion = versions -> { final OptionalInt max = versions.max(); return max.isPresent() ? IntStream.of(max.getAsInt()) : IntStream.empty(); }; - fieldOrderModel = FieldOrderModel.newInstance(msgToken, fields, groups, varData, selectLatestVersion); + accessOrderModel = AccessOrderModel.newInstance(msgToken, fields, groups, varData, selectLatestVersion); } - out.append(generateFieldOrderStates(fieldOrderModel)); - out.append(generateEncoderFlyweightCode(className, fieldOrderModel, msgToken)); + out.append(generateFieldOrderStates(accessOrderModel)); + out.append(generateEncoderFlyweightCode(className, accessOrderModel, msgToken)); final StringBuilder sb = new StringBuilder(); - generateEncoderFields(sb, className, fieldOrderModel, fields, BASE_INDENT); - generateEncoderGroups(sb, className, fieldOrderModel, groups, BASE_INDENT, false); - generateEncoderVarData(sb, className, fieldOrderModel, varData, BASE_INDENT); + generateEncoderFields(sb, className, accessOrderModel, fields, BASE_INDENT); + generateEncoderGroups(sb, className, accessOrderModel, groups, BASE_INDENT, false); + generateEncoderVarData(sb, className, accessOrderModel, varData, BASE_INDENT); generateEncoderDisplay(sb, decoderName(msgToken.name())); @@ -279,24 +279,24 @@ private void generateEncoder( } } - private static CharSequence qualifiedStateCase(final FieldOrderModel.State state) + private static CharSequence qualifiedStateCase(final AccessOrderModel.State state) { return "CodecStates." + state.name(); } - private static CharSequence stateCaseForSwitchCase(final FieldOrderModel.State state) + private static CharSequence stateCaseForSwitchCase(final AccessOrderModel.State state) { return qualifiedStateCase(state); } - private static CharSequence unqualifiedStateCase(final FieldOrderModel.State state) + private static CharSequence unqualifiedStateCase(final AccessOrderModel.State state) { return state.name(); } - private static CharSequence generateFieldOrderStates(final FieldOrderModel fieldOrderModel) + private static CharSequence generateFieldOrderStates(final AccessOrderModel accessOrderModel) { - if (null == fieldOrderModel) + if (null == accessOrderModel) { return ""; } @@ -318,12 +318,12 @@ private static CharSequence generateFieldOrderStates(final FieldOrderModel field sb.append(" * accessed safely. Tools such as PlantUML and Graphviz can render it.\n"); sb.append(" *\n"); sb.append(" *
{@code\n");
-        fieldOrderModel.generateGraph(sb, "     *   ");
+        accessOrderModel.generateGraph(sb, "     *   ");
         sb.append("     * }
\n"); sb.append(" */\n"); sb.append(" private static class CodecStates\n") .append(" {\n"); - fieldOrderModel.forEachStateOrderedByNumber(state -> + accessOrderModel.forEachStateOrderedByStateNumber(state -> { sb.append(" private static final int ") .append(unqualifiedStateCase(state)) @@ -333,7 +333,7 @@ private static CharSequence generateFieldOrderStates(final FieldOrderModel field sb.append("\n").append(" private static final String[] STATE_NAME_LOOKUP =\n") .append(" {\n"); - fieldOrderModel.forEachStateOrderedByNumber(state -> + accessOrderModel.forEachStateOrderedByStateNumber(state -> { sb.append(" \"").append(state.name()).append("\",\n"); }); @@ -341,14 +341,14 @@ private static CharSequence generateFieldOrderStates(final FieldOrderModel field sb.append(" private static final String[] STATE_TRANSITIONS_LOOKUP =\n") .append(" {\n"); - fieldOrderModel.forEachStateOrderedByNumber(state -> + accessOrderModel.forEachStateOrderedByStateNumber(state -> { sb.append(" \""); final MutableBoolean isFirst = new MutableBoolean(true); final Set transitionDescriptions = new HashSet<>(); - fieldOrderModel.forEachTransitionFrom(state, transition -> + accessOrderModel.forEachTransitionFrom(state, transitionGroup -> { - if (transitionDescriptions.add(transition.description())) + if (transitionDescriptions.add(transitionGroup.exampleCode())) { if (isFirst.get()) { @@ -359,7 +359,7 @@ private static CharSequence generateFieldOrderStates(final FieldOrderModel field sb.append(", "); } - sb.append("\\\"").append(transition.description()).append("\\\""); + sb.append("\\\"").append(transitionGroup.exampleCode()).append("\\\""); } }); sb.append("\",\n"); @@ -379,7 +379,7 @@ private static CharSequence generateFieldOrderStates(final FieldOrderModel field sb.append(" }\n\n"); sb.append(" private int codecState = ") - .append(qualifiedStateCase(fieldOrderModel.notWrappedState())) + .append(qualifiedStateCase(accessOrderModel.notWrappedState())) .append(";\n\n"); sb.append(" private int codecState()\n") @@ -402,11 +402,11 @@ private static String accessOrderListenerMethodName(final Token token) private static void generateAccessOrderListenerMethod( final StringBuilder sb, - final FieldOrderModel fieldOrderModel, + final AccessOrderModel accessOrderModel, final String indent, final Token token) { - if (null == fieldOrderModel) + if (null == accessOrderModel) { return; } @@ -415,37 +415,39 @@ private static void generateAccessOrderListenerMethod( .append(indent).append("private void ").append(accessOrderListenerMethodName(token)).append("()\n") .append(indent).append("{\n"); + final AccessOrderModel.CodecInteraction fieldAccess = + accessOrderModel.interactionFactory().accessField(token); + generateAccessOrderListener( sb, indent + " ", "access field", - fieldOrderModel, - FieldOrderModel.TransitionContext.NONE, - token); + accessOrderModel, + fieldAccess); sb.append(indent).append("}\n"); } private static CharSequence generateAccessOrderListenerCall( - final FieldOrderModel fieldOrderModel, + final AccessOrderModel accessOrderModel, final String indent, final Token token, final String... arguments) { return generateAccessOrderListenerCall( - fieldOrderModel, + accessOrderModel, indent, accessOrderListenerMethodName(token), arguments); } private static CharSequence generateAccessOrderListenerCall( - final FieldOrderModel fieldOrderModel, + final AccessOrderModel accessOrderModel, final String indent, final String methodName, final String... arguments) { - if (null == fieldOrderModel) + if (null == accessOrderModel) { return ""; } @@ -473,11 +475,11 @@ private static CharSequence generateAccessOrderListenerCall( private static void generateAccessOrderListenerMethodForGroupWrap( final StringBuilder sb, final String action, - final FieldOrderModel fieldOrderModel, + final AccessOrderModel accessOrderModel, final String indent, final Token token) { - if (null == fieldOrderModel) + if (null == accessOrderModel) { return; } @@ -489,25 +491,29 @@ private static void generateAccessOrderListenerMethodForGroupWrap( .append(indent).append(" if (remaining == 0)\n") .append(indent).append(" {\n"); + final AccessOrderModel.CodecInteraction selectEmptyGroup = + accessOrderModel.interactionFactory().determineGroupIsEmpty(token); + generateAccessOrderListener( sb, indent + " ", action + " count of repeating group", - fieldOrderModel, - FieldOrderModel.TransitionContext.SELECT_EMPTY_GROUP, - token); + accessOrderModel, + selectEmptyGroup); sb.append(indent).append(" }\n") .append(indent).append(" else\n") .append(indent).append(" {\n"); + final AccessOrderModel.CodecInteraction selectNonEmptyGroup = + accessOrderModel.interactionFactory().determineGroupHasElements(token); + generateAccessOrderListener( sb, indent + " ", action + " count of repeating group", - fieldOrderModel, - FieldOrderModel.TransitionContext.SELECT_MULTI_ELEMENT_GROUP, - token); + accessOrderModel, + selectNonEmptyGroup); sb.append(indent).append(" }\n") .append(indent).append("}\n"); @@ -517,17 +523,16 @@ private static void generateAccessOrderListener( final StringBuilder sb, final String indent, final String action, - final FieldOrderModel fieldOrderModel, - final FieldOrderModel.TransitionContext context, - final Token token) + final AccessOrderModel accessOrderModel, + final AccessOrderModel.CodecInteraction interaction) { - if (fieldOrderModel.isTopLevelBlockField(token)) + if (interaction.isTopLevelBlockFieldAccess()) { sb.append(indent).append("if (codecState() == ") - .append(qualifiedStateCase(fieldOrderModel.notWrappedState())) + .append(qualifiedStateCase(accessOrderModel.notWrappedState())) .append(")\n") .append(indent).append("{\n"); - generateAccessOrderException(sb, indent + " ", action, fieldOrderModel, token); + generateAccessOrderException(sb, indent + " ", action, interaction); sb.append(indent).append("}\n"); } else @@ -535,20 +540,20 @@ private static void generateAccessOrderListener( sb.append(indent).append("switch (codecState())\n") .append(indent).append("{\n"); - final List transitions = new ArrayList<>(); - fieldOrderModel.getTransitions(transitions, context, token); + final List transitionGroups = new ArrayList<>(); + accessOrderModel.getTransitions(transitionGroups, interaction); - transitions.forEach(transition -> + transitionGroups.forEach(transitionGroup -> { - transition.forEachStartState(startState -> + transitionGroup.forEachStartState(startState -> sb.append(indent).append(" case ").append(stateCaseForSwitchCase(startState)).append(":\n")); sb.append(indent).append(" codecState(") - .append(qualifiedStateCase(transition.endState())).append(");\n") + .append(qualifiedStateCase(transitionGroup.endState())).append(");\n") .append(indent).append(" break;\n"); }); sb.append(indent).append(" default:\n"); - generateAccessOrderException(sb, indent + " ", action, fieldOrderModel, token); + generateAccessOrderException(sb, indent + " ", action, interaction); sb.append(indent).append("}\n"); } } @@ -557,13 +562,12 @@ private static void generateAccessOrderException( final StringBuilder sb, final String indent, final String action, - final FieldOrderModel fieldOrderModel, - final Token token) + final AccessOrderModel.CodecInteraction interaction) { sb.append(indent).append("throw new IllegalStateException(") .append("\"Illegal field access order. \" +\n") .append(indent) - .append(" \"Cannot ").append(action).append(" \\\"").append(fieldOrderModel.fieldPath(token)) + .append(" \"Cannot ").append(action).append(" \\\"").append(interaction.groupQualifiedName()) .append("\\\" in state: \" + CodecStates.name(codecState()) +\n") .append(indent) .append(" \". Expected one of these transitions: [\" + CodecStates.transitions(codecState()) +\n") @@ -573,11 +577,11 @@ private static void generateAccessOrderException( private static void generateAccessOrderListenerMethodForNextGroupElement( final StringBuilder sb, - final FieldOrderModel fieldOrderModel, + final AccessOrderModel accessOrderModel, final String indent, final Token token) { - if (null == fieldOrderModel) + if (null == accessOrderModel) { return; } @@ -588,35 +592,39 @@ private static void generateAccessOrderListenerMethodForNextGroupElement( .append(indent).append(" if (remaining > 1)\n") .append(indent).append(" {\n"); + final AccessOrderModel.CodecInteraction selectNextElementInGroup = + accessOrderModel.interactionFactory().moveToNextElement(token); + generateAccessOrderListener( sb, indent + " ", "access next element in repeating group", - fieldOrderModel, - FieldOrderModel.TransitionContext.NEXT_ELEMENT_IN_GROUP, - token); + accessOrderModel, + selectNextElementInGroup); sb.append(indent).append(" }\n") .append(indent).append(" else if (remaining == 1)\n") .append(indent).append(" {\n"); + final AccessOrderModel.CodecInteraction selectLastElementInGroup = + accessOrderModel.interactionFactory().moveToLastElement(token); + generateAccessOrderListener( sb, indent + " ", "access next element in repeating group", - fieldOrderModel, - FieldOrderModel.TransitionContext.LAST_ELEMENT_IN_GROUP, - token); + accessOrderModel, + selectLastElementInGroup); sb.append(indent).append(" }\n") .append(indent).append("}\n"); } private static CharSequence generateDecoderWrapListener( - final FieldOrderModel fieldOrderModel, + final AccessOrderModel accessOrderModel, final String indent) { - if (null == fieldOrderModel) + if (null == accessOrderModel) { return ""; } @@ -627,7 +635,7 @@ private static CharSequence generateDecoderWrapListener( .append(indent).append(" switch(actingVersion)\n") .append(indent).append(" {\n"); - fieldOrderModel.forEachDecoderWrappedState((version, state) -> + accessOrderModel.forEachDecoderWrappedState((version, state) -> { sb.append(indent).append(" case ").append(version).append(":\n") .append(indent).append(" codecState(") @@ -637,7 +645,7 @@ private static CharSequence generateDecoderWrapListener( sb.append(indent).append(" default:\n") .append(indent).append(" codecState(") - .append(qualifiedStateCase(fieldOrderModel.latestVersionWrappedState())).append(");\n") + .append(qualifiedStateCase(accessOrderModel.latestVersionWrappedState())).append(");\n") .append(indent).append(" break;\n") .append(indent).append(" }\n") .append(indent).append("}\n\n"); @@ -646,10 +654,10 @@ private static CharSequence generateDecoderWrapListener( } private CharSequence generateEncoderWrapListener( - final FieldOrderModel fieldOrderModel, + final AccessOrderModel accessOrderModel, final String indent) { - if (null == fieldOrderModel) + if (null == accessOrderModel) { return ""; } @@ -658,7 +666,7 @@ private CharSequence generateEncoderWrapListener( sb.append(indent).append("if (ENABLE_ACCESS_ORDER_CHECKS)") .append("\n").append(indent).append("{\n") .append(indent).append(" codecState(") - .append(qualifiedStateCase(fieldOrderModel.latestVersionWrappedState())) + .append(qualifiedStateCase(accessOrderModel.latestVersionWrappedState())) .append(");\n") .append(indent).append("}\n\n"); return sb; @@ -685,18 +693,18 @@ private void generateDecoder( } out.append(generateDeclaration(className, implementsString, msgToken)); - FieldOrderModel fieldOrderModel = null; - if (FieldOrderModel.generateAccessOrderChecks()) + AccessOrderModel accessOrderModel = null; + if (AccessOrderModel.generateAccessOrderChecks()) { - fieldOrderModel = FieldOrderModel.newInstance(msgToken, fields, groups, varData, Function.identity()); + accessOrderModel = AccessOrderModel.newInstance(msgToken, fields, groups, varData, Function.identity()); } - out.append(generateFieldOrderStates(fieldOrderModel)); - out.append(generateDecoderFlyweightCode(fieldOrderModel, className, msgToken)); + out.append(generateFieldOrderStates(accessOrderModel)); + out.append(generateDecoderFlyweightCode(accessOrderModel, className, msgToken)); final StringBuilder sb = new StringBuilder(); - generateDecoderFields(sb, fieldOrderModel, fields, BASE_INDENT); - generateDecoderGroups(sb, fieldOrderModel, className, groups, BASE_INDENT, false); - generateDecoderVarData(sb, fieldOrderModel, varData, BASE_INDENT); + generateDecoderFields(sb, accessOrderModel, fields, BASE_INDENT); + generateDecoderGroups(sb, accessOrderModel, className, groups, BASE_INDENT, false); + generateDecoderVarData(sb, accessOrderModel, varData, BASE_INDENT); generateDecoderDisplay(sb, msgToken.name(), fields, groups, varData); generateMessageLength(sb, className, true, groups, varData, BASE_INDENT); @@ -708,7 +716,7 @@ private void generateDecoder( private void generateDecoderGroups( final StringBuilder sb, - final FieldOrderModel fieldOrderModel, final String outerClassName, + final AccessOrderModel accessOrderModel, final String outerClassName, final List tokens, final String indent, final boolean isSubGroup) throws IOException @@ -737,19 +745,19 @@ private void generateDecoderGroups( final List varData = new ArrayList<>(); i = collectVarData(tokens, i, varData); - generateGroupDecoderProperty(sb, groupName, fieldOrderModel, groupToken, indent, isSubGroup); + generateGroupDecoderProperty(sb, groupName, accessOrderModel, groupToken, indent, isSubGroup); generateTypeJavadoc(sb, indent + INDENT, groupToken); if (shouldGenerateGroupOrderAnnotation) { generateAnnotations(indent + INDENT, groupName, groups, sb, this::decoderName); } - generateGroupDecoderClassHeader(sb, groupName, outerClassName, fieldOrderModel, groupToken, + generateGroupDecoderClassHeader(sb, groupName, outerClassName, accessOrderModel, groupToken, tokens, groups, index, indent + INDENT); - generateDecoderFields(sb, fieldOrderModel, fields, indent + INDENT); - generateDecoderGroups(sb, fieldOrderModel, outerClassName, groups, indent + INDENT, true); - generateDecoderVarData(sb, fieldOrderModel, varData, indent + INDENT); + generateDecoderFields(sb, accessOrderModel, fields, indent + INDENT); + generateDecoderGroups(sb, accessOrderModel, outerClassName, groups, indent + INDENT, true); + generateDecoderVarData(sb, accessOrderModel, varData, indent + INDENT); appendGroupInstanceDecoderDisplay(sb, fields, groups, varData, indent + INDENT); generateMessageLength(sb, groupName, false, groups, varData, indent + INDENT); @@ -761,7 +769,7 @@ private void generateDecoderGroups( private void generateEncoderGroups( final StringBuilder sb, final String outerClassName, - final FieldOrderModel fieldOrderModel, + final AccessOrderModel accessOrderModel, final List tokens, final String indent, final boolean isSubGroup) throws IOException @@ -791,7 +799,7 @@ private void generateEncoderGroups( final List varData = new ArrayList<>(); i = collectVarData(tokens, i, varData); - generateGroupEncoderProperty(sb, groupName, fieldOrderModel, groupToken, indent, isSubGroup); + generateGroupEncoderProperty(sb, groupName, accessOrderModel, groupToken, indent, isSubGroup); generateTypeJavadoc(sb, indent + INDENT, groupToken); if (shouldGenerateGroupOrderAnnotation) @@ -799,11 +807,11 @@ private void generateEncoderGroups( generateAnnotations(indent + INDENT, groupClassName, groups, sb, this::encoderName); } generateGroupEncoderClassHeader( - sb, groupName, outerClassName, fieldOrderModel, groupToken, tokens, groups, index, indent + INDENT); + sb, groupName, outerClassName, accessOrderModel, groupToken, tokens, groups, index, indent + INDENT); - generateEncoderFields(sb, groupClassName, fieldOrderModel, fields, indent + INDENT); - generateEncoderGroups(sb, outerClassName, fieldOrderModel, groups, indent + INDENT, true); - generateEncoderVarData(sb, groupClassName, fieldOrderModel, varData, indent + INDENT); + generateEncoderFields(sb, groupClassName, accessOrderModel, fields, indent + INDENT); + generateEncoderGroups(sb, outerClassName, accessOrderModel, groups, indent + INDENT, true); + generateEncoderVarData(sb, groupClassName, accessOrderModel, varData, indent + INDENT); sb.append(indent).append(" }\n"); } @@ -813,7 +821,7 @@ private void generateGroupDecoderClassHeader( final StringBuilder sb, final String groupName, final String parentMessageClassName, - final FieldOrderModel fieldOrderModel, + final AccessOrderModel accessOrderModel, final Token groupToken, final List tokens, final List subGroupTokens, @@ -862,7 +870,7 @@ private void generateGroupDecoderClassHeader( .append(indent).append(" }\n\n"); - generateAccessOrderListenerMethodForNextGroupElement(sb, fieldOrderModel, indent + " ", groupToken); + generateAccessOrderListenerMethodForNextGroupElement(sb, accessOrderModel, indent + " ", groupToken); sb.append("\n") .append(indent).append(" public ").append(className).append(" next()\n") @@ -871,7 +879,7 @@ private void generateGroupDecoderClassHeader( .append(indent).append(" {\n") .append(indent).append(" throw new java.util.NoSuchElementException();\n") .append(indent).append(" }\n\n") - .append(generateAccessOrderListenerCall(fieldOrderModel, indent + " ", "onNextElementAccessed")) + .append(generateAccessOrderListenerCall(accessOrderModel, indent + " ", "onNextElementAccessed")) .append(indent).append(" offset = parentMessage.limit();\n") .append(indent).append(" parentMessage.limit(offset + blockLength);\n") .append(indent).append(" ++index;\n\n") @@ -920,7 +928,7 @@ private void generateGroupDecoderClassHeader( .append(indent).append(" return index < count;\n") .append(indent).append(" }\n"); - if (null != fieldOrderModel) + if (null != accessOrderModel) { sb.append("\n") .append(indent).append(" private int codecState()\n") @@ -940,7 +948,7 @@ private void generateGroupEncoderClassHeader( final StringBuilder sb, final String groupName, final String parentMessageClassName, - final FieldOrderModel fieldOrderModel, + final AccessOrderModel accessOrderModel, final Token groupToken, final List tokens, final List subGroupTokens, @@ -1002,12 +1010,12 @@ private void generateGroupEncoderClassHeader( blockLengthPut, numInGroupPut); - generateAccessOrderListenerMethodForNextGroupElement(sb, fieldOrderModel, ind + " ", groupToken); + generateAccessOrderListenerMethodForNextGroupElement(sb, accessOrderModel, ind + " ", groupToken); sb.append("\n") .append(ind).append(" public ").append(encoderName(groupName)).append(" next()\n") .append(ind).append(" {\n") - .append(generateAccessOrderListenerCall(fieldOrderModel, ind + " ", "onNextElementAccessed")) + .append(generateAccessOrderListenerCall(accessOrderModel, ind + " ", "onNextElementAccessed")) .append(ind).append(" if (index >= count)\n") .append(ind).append(" {\n") .append(ind).append(" throw new java.util.NoSuchElementException();\n") @@ -1050,7 +1058,7 @@ private void generateGroupEncoderClassHeader( .append(ind).append(" return ").append(blockLength).append(";\n") .append(ind).append(" }\n"); - if (null != fieldOrderModel) + if (null != accessOrderModel) { sb.append("\n") .append(ind).append(" private int codecState()\n") @@ -1177,7 +1185,7 @@ private void generateGroupEncoderClassDeclaration( private static void generateGroupDecoderProperty( final StringBuilder sb, final String groupName, - final FieldOrderModel fieldOrderModel, final Token token, + final AccessOrderModel accessOrderModel, final Token token, final String indent, final boolean isSubGroup) { @@ -1218,7 +1226,7 @@ private static void generateGroupDecoderProperty( indent + " return " + propertyName + ";\n" + indent + " }\n\n"; - generateAccessOrderListenerMethodForGroupWrap(sb, "decode", fieldOrderModel, indent + " ", token); + generateAccessOrderListenerMethodForGroupWrap(sb, "decode", accessOrderModel, indent + " ", token); generateFlyweightPropertyJavadoc(sb, indent + INDENT, token, className); new Formatter(sb).format("\n" + @@ -1226,7 +1234,7 @@ private static void generateGroupDecoderProperty( indent + " {\n" + "%3$s" + indent + " %2$s.wrap(buffer);\n" + - generateAccessOrderListenerCall(fieldOrderModel, indent + " ", token, propertyName + ".count") + + generateAccessOrderListenerCall(accessOrderModel, indent + " ", token, propertyName + ".count") + indent + " return %2$s;\n" + indent + " }\n", className, @@ -1237,7 +1245,7 @@ private static void generateGroupDecoderProperty( private void generateGroupEncoderProperty( final StringBuilder sb, final String groupName, - final FieldOrderModel fieldOrderModel, + final AccessOrderModel accessOrderModel, final Token token, final String indent, final boolean isSubGroup) @@ -1262,13 +1270,13 @@ private void generateGroupEncoderProperty( formatPropertyName(groupName), token.id()); - generateAccessOrderListenerMethodForGroupWrap(sb, "encode", fieldOrderModel, indent + " ", token); + generateAccessOrderListenerMethodForGroupWrap(sb, "encode", accessOrderModel, indent + " ", token); generateGroupEncodePropertyJavadoc(sb, indent + INDENT, token, className); new Formatter(sb).format("\n" + indent + " public %1$s %2$sCount(final int count)\n" + indent + " {\n" + - generateAccessOrderListenerCall(fieldOrderModel, indent + " ", token, "count") + + generateAccessOrderListenerCall(accessOrderModel, indent + " ", token, "count") + indent + " %2$s.wrap(buffer, count);\n" + indent + " return %2$s;\n" + indent + " }\n", @@ -1278,7 +1286,7 @@ private void generateGroupEncoderProperty( private void generateDecoderVarData( final StringBuilder sb, - final FieldOrderModel fieldOrderModel, + final AccessOrderModel accessOrderModel, final List tokens, final String indent) { @@ -1311,9 +1319,9 @@ private void generateDecoderVarData( .append(indent).append(" return ").append(sizeOfLengthField).append(";\n") .append(indent).append(" }\n"); - generateAccessOrderListenerMethod(sb, fieldOrderModel, indent + " ", token); + generateAccessOrderListenerMethod(sb, accessOrderModel, indent + " ", token); final CharSequence accessOrderListenerCall = - generateAccessOrderListenerCall(fieldOrderModel, indent + " ", token); + generateAccessOrderListenerCall(accessOrderModel, indent + " ", token); sb.append("\n") .append(indent).append(" public int ").append(methodPropName).append("Length()\n") @@ -1335,7 +1343,7 @@ private void generateDecoderVarData( private void generateEncoderVarData( final StringBuilder sb, final String className, - final FieldOrderModel fieldOrderModel, + final AccessOrderModel accessOrderModel, final List tokens, final String indent) { @@ -1368,9 +1376,9 @@ private void generateEncoderVarData( .append(sizeOfLengthField).append(";\n") .append(indent).append(" }\n"); - generateAccessOrderListenerMethod(sb, fieldOrderModel, indent + " ", token); + generateAccessOrderListenerMethod(sb, accessOrderModel, indent + " ", token); final CharSequence accessOrderListenerCall = - generateAccessOrderListenerCall(fieldOrderModel, indent + " ", token); + generateAccessOrderListenerCall(accessOrderModel, indent + " ", token); generateDataEncodeMethods( sb, @@ -3223,7 +3231,7 @@ private CharSequence generateFixedFlyweightCode( } private CharSequence generateDecoderFlyweightCode( - final FieldOrderModel fieldOrderModel, + final AccessOrderModel accessOrderModel, final String className, final Token token) { @@ -3231,7 +3239,7 @@ private CharSequence generateDecoderFlyweightCode( final StringBuilder methods = new StringBuilder(); - methods.append(generateDecoderWrapListener(fieldOrderModel, " ")); + methods.append(generateDecoderWrapListener(accessOrderModel, " ")); methods.append(" public ").append(className).append(" wrap(\n") .append(" final ").append(readOnlyBuffer).append(" buffer,\n") @@ -3248,7 +3256,7 @@ private CharSequence generateDecoderFlyweightCode( .append(" this.actingBlockLength = actingBlockLength;\n") .append(" this.actingVersion = actingVersion;\n") .append(" limit(offset + actingBlockLength);\n\n") - .append(generateAccessOrderListenerCall(fieldOrderModel, " ", "onWrap", "actingVersion")) + .append(generateAccessOrderListenerCall(accessOrderModel, " ", "onWrap", "actingVersion")) .append(" return this;\n") .append(" }\n\n"); @@ -3279,7 +3287,7 @@ private CharSequence generateDecoderFlyweightCode( .append(" {\n") .append(" final int currentLimit = limit();\n"); - if (null != fieldOrderModel) + if (null != accessOrderModel) { methods.append(" final int currentCodecState = codecState();\n"); } @@ -3289,7 +3297,7 @@ private CharSequence generateDecoderFlyweightCode( .append(" final int decodedLength = encodedLength();\n") .append(" limit(currentLimit);\n\n"); - if (null != fieldOrderModel) + if (null != accessOrderModel) { methods.append(" if (ENABLE_ACCESS_ORDER_CHECKS)\n") .append(" {\n") @@ -3413,7 +3421,7 @@ private CharSequence generateFlyweightCode( private CharSequence generateEncoderFlyweightCode( final String className, - final FieldOrderModel fieldOrderModel, + final AccessOrderModel accessOrderModel, final Token token) { final String wrapMethod = @@ -3426,7 +3434,7 @@ private CharSequence generateEncoderFlyweightCode( " this.initialOffset = offset;\n" + " this.offset = offset;\n" + " limit(offset + BLOCK_LENGTH);\n\n" + - generateEncoderWrapListener(fieldOrderModel, " ") + + generateEncoderWrapListener(accessOrderModel, " ") + " return this;\n" + " }\n\n"; @@ -3477,7 +3485,7 @@ private CharSequence generateEncoderFlyweightCode( private void generateEncoderFields( final StringBuilder sb, final String containingClassName, - final FieldOrderModel fieldOrderModel, + final AccessOrderModel accessOrderModel, final List tokens, final String indent) { @@ -3493,9 +3501,9 @@ private void generateEncoderFields( generateEncodingOffsetMethod(sb, propertyName, fieldToken.offset(), indent); generateEncodingLengthMethod(sb, propertyName, typeToken.encodedLength(), indent); generateFieldMetaAttributeMethod(sb, fieldToken, indent); - generateAccessOrderListenerMethod(sb, fieldOrderModel, indent + " ", fieldToken); + generateAccessOrderListenerMethod(sb, accessOrderModel, indent + " ", fieldToken); final CharSequence accessOrderListenerCall = generateAccessOrderListenerCall( - fieldOrderModel, indent + " ", fieldToken); + accessOrderModel, indent + " ", fieldToken); switch (typeToken.signal()) { @@ -3529,7 +3537,7 @@ private void generateEncoderFields( private void generateDecoderFields( final StringBuilder sb, - final FieldOrderModel fieldOrderModel, + final AccessOrderModel accessOrderModel, final List tokens, final String indent) { @@ -3546,9 +3554,9 @@ private void generateDecoderFields( generateEncodingLengthMethod(sb, propertyName, typeToken.encodedLength(), indent); generateFieldMetaAttributeMethod(sb, fieldToken, indent); - generateAccessOrderListenerMethod(sb, fieldOrderModel, indent + " ", fieldToken); + generateAccessOrderListenerMethod(sb, accessOrderModel, indent + " ", fieldToken); final CharSequence accessOrderListenerCall = generateAccessOrderListenerCall( - fieldOrderModel, indent + " ", fieldToken); + accessOrderModel, indent + " ", fieldToken); switch (typeToken.signal()) { diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java index 18d5627d02..8b702c0236 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java @@ -26,7 +26,7 @@ public final class FrameCodecDecoder * *
{@code
      *   digraph G {
-     *       NOT_WRAPPED -> V0_BLOCK [label="  wrap(?)  "];
+     *       NOT_WRAPPED -> V0_BLOCK [label="  wrap(version=0)  "];
      *       V0_BLOCK -> V0_BLOCK [label="  irId(?)  "];
      *       V0_BLOCK -> V0_BLOCK [label="  irVersion(?)  "];
      *       V0_BLOCK -> V0_BLOCK [label="  schemaVersion(?)  "];
@@ -55,7 +55,7 @@ private static class CodecStates
 
         private static final String[] STATE_TRANSITIONS_LOOKUP =
         {
-            "\"wrap(?)\"",
+            "\"wrap(version=0)\"",
             "\"irId(?)\", \"irVersion(?)\", \"schemaVersion(?)\", \"packageName(?)\"",
             "\"namespaceName(?)\"",
             "\"semanticVersion(?)\"",
diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java
index 2482dc28df..09da58e83a 100644
--- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java
+++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java
@@ -26,7 +26,7 @@ public final class FrameCodecEncoder
      *
      * 
{@code
      *   digraph G {
-     *       NOT_WRAPPED -> V0_BLOCK [label="  wrap(?)  "];
+     *       NOT_WRAPPED -> V0_BLOCK [label="  wrap(version=0)  "];
      *       V0_BLOCK -> V0_BLOCK [label="  irId(?)  "];
      *       V0_BLOCK -> V0_BLOCK [label="  irVersion(?)  "];
      *       V0_BLOCK -> V0_BLOCK [label="  schemaVersion(?)  "];
@@ -55,7 +55,7 @@ private static class CodecStates
 
         private static final String[] STATE_TRANSITIONS_LOOKUP =
         {
-            "\"wrap(?)\"",
+            "\"wrap(version=0)\"",
             "\"irId(?)\", \"irVersion(?)\", \"schemaVersion(?)\", \"packageName(?)\"",
             "\"namespaceName(?)\"",
             "\"semanticVersion(?)\"",
diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java
index 40d663eb9e..ab75328b6e 100644
--- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java
+++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java
@@ -26,7 +26,7 @@ public final class TokenCodecDecoder
      *
      * 
{@code
      *   digraph G {
-     *       NOT_WRAPPED -> V0_BLOCK [label="  wrap(?)  "];
+     *       NOT_WRAPPED -> V0_BLOCK [label="  wrap(version=0)  "];
      *       V0_BLOCK -> V0_BLOCK [label="  tokenOffset(?)  "];
      *       V0_BLOCK -> V0_BLOCK [label="  tokenSize(?)  "];
      *       V0_BLOCK -> V0_BLOCK [label="  fieldId(?)  "];
@@ -86,7 +86,7 @@ private static class CodecStates
 
         private static final String[] STATE_TRANSITIONS_LOOKUP =
         {
-            "\"wrap(?)\"",
+            "\"wrap(version=0)\"",
             "\"tokenOffset(?)\", \"tokenSize(?)\", \"fieldId(?)\", \"tokenVersion(?)\", \"componentTokenCount(?)\", \"signal(?)\", \"primitiveType(?)\", \"byteOrder(?)\", \"presence(?)\", \"deprecated(?)\", \"name(?)\"",
             "\"constValue(?)\"",
             "\"minValue(?)\"",
diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java
index e1a04de370..b4f99f6678 100644
--- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java
+++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java
@@ -26,7 +26,7 @@ public final class TokenCodecEncoder
      *
      * 
{@code
      *   digraph G {
-     *       NOT_WRAPPED -> V0_BLOCK [label="  wrap(?)  "];
+     *       NOT_WRAPPED -> V0_BLOCK [label="  wrap(version=0)  "];
      *       V0_BLOCK -> V0_BLOCK [label="  tokenOffset(?)  "];
      *       V0_BLOCK -> V0_BLOCK [label="  tokenSize(?)  "];
      *       V0_BLOCK -> V0_BLOCK [label="  fieldId(?)  "];
@@ -86,7 +86,7 @@ private static class CodecStates
 
         private static final String[] STATE_TRANSITIONS_LOOKUP =
         {
-            "\"wrap(?)\"",
+            "\"wrap(version=0)\"",
             "\"tokenOffset(?)\", \"tokenSize(?)\", \"fieldId(?)\", \"tokenVersion(?)\", \"componentTokenCount(?)\", \"signal(?)\", \"primitiveType(?)\", \"byteOrder(?)\", \"presence(?)\", \"deprecated(?)\", \"name(?)\"",
             "\"constValue(?)\"",
             "\"minValue(?)\"",
diff --git a/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java b/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldAccessOrderCheckTest.java
similarity index 99%
rename from sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java
rename to sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldAccessOrderCheckTest.java
index e146c366c6..74a3d9b68d 100644
--- a/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldOrderCheckTest.java
+++ b/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldAccessOrderCheckTest.java
@@ -31,7 +31,7 @@
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assumptions.assumeFalse;
 
-public class FieldOrderCheckTest
+public class FieldAccessOrderCheckTest
 {
     private static final Class INCORRECT_ORDER_EXCEPTION_CLASS = IllegalStateException.class;
     private static final int OFFSET = 0;

From b9794119548ba7ff7bd19077f96ba985230cb598 Mon Sep 17 00:00:00 2001
From: Zach Bray 
Date: Thu, 22 Jun 2023 08:14:27 +0100
Subject: [PATCH 25/50] [Java] Address review comment.

A disabled test had an incorrect assertion.

Also, I've replaced the term "group length" with "group count" in the
test names.
---
 .../uk/co/real_logic/sbe/FieldAccessOrderCheckTest.java     | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldAccessOrderCheckTest.java b/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldAccessOrderCheckTest.java
index 74a3d9b68d..2e4d48d4f9 100644
--- a/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldAccessOrderCheckTest.java
+++ b/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldAccessOrderCheckTest.java
@@ -454,7 +454,7 @@ void allowsReEncodingPrimitiveFieldInGroupElementAfterTopLevelVariableLengthFiel
 
     @Test
     @Disabled("Our access checks are too strict to allow the behaviour in this test.")
-    void allowsReWrappingGroupDecoderAfterAccessingLength()
+    void allowsReWrappingGroupDecoderAfterAccessingGroupCount()
     {
         final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
@@ -473,7 +473,7 @@ void allowsReWrappingGroupDecoderAfterAccessingLength()
         assertThat(decoder.b().count(), equalTo(2));
         final GroupAndVarLengthDecoder.BDecoder b = decoder.b();
         assertThat(b.next().c(), equalTo(1));
-        assertThat(b.next().c(), equalTo(3));
+        assertThat(b.next().c(), equalTo(2));
         assertThat(decoder.d(), equalTo("abc"));
     }
 
@@ -541,7 +541,7 @@ void disallowsReEncodingVariableLengthFieldAfterGroup()
     }
 
     @Test
-    void disallowsReEncodingGroupLength()
+    void disallowsReEncodingGroupCount()
     {
         final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);

From 482d60dc6015a9f8723e5dd8df6266a82f7672f9 Mon Sep 17 00:00:00 2001
From: Zach Bray 
Date: Thu, 22 Jun 2023 09:05:52 +0100
Subject: [PATCH 26/50] [Java] Add tests for variable-length data in nested
 group.

These tests are based on Mike's review comments.
---
 .../sbe/FieldAccessOrderCheckTest.java        | 182 ++++++++++++++++++
 .../resources/field-order-check-schema.xml    |  11 ++
 2 files changed, 193 insertions(+)

diff --git a/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldAccessOrderCheckTest.java b/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldAccessOrderCheckTest.java
index 2e4d48d4f9..8ab9f35ffb 100644
--- a/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldAccessOrderCheckTest.java
+++ b/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldAccessOrderCheckTest.java
@@ -23,6 +23,8 @@
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
 
 import java.nio.charset.StandardCharsets;
 
@@ -2934,6 +2936,186 @@ void disallowsEncodingElementOfEmptyGroup6()
         assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_D_DONE"));
     }
 
+    @Test
+    void allowsEncodingAndDecodingNestedGroupWithVarDataInSchemaDefinedOrder()
+    {
+        final NestedGroupWithVarLengthEncoder encoder = new NestedGroupWithVarLengthEncoder()
+            .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+        encoder.a(1);
+        final NestedGroupWithVarLengthEncoder.BEncoder bEncoder = encoder.bCount(3);
+        bEncoder.next().c(2).dCount(0);
+        bEncoder.next().c(3).dCount(1).next().e(4).f("abc");
+        bEncoder.next().c(5).dCount(2).next().e(6).f("def").next().e(7).f("ghi");
+
+        final NestedGroupWithVarLengthDecoder decoder = new NestedGroupWithVarLengthDecoder()
+            .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+        assertThat(decoder.a(), equalTo(1));
+        final NestedGroupWithVarLengthDecoder.BDecoder bDecoder = decoder.b();
+        assertThat(bDecoder.count(), equalTo(3));
+        assertThat(bDecoder.next().c(), equalTo(2));
+        assertThat(bDecoder.d().count(), equalTo(0));
+        assertThat(bDecoder.next().c(), equalTo(3));
+        final NestedGroupWithVarLengthDecoder.BDecoder.DDecoder dDecoder = bDecoder.d();
+        assertThat(dDecoder.count(), equalTo(1));
+        assertThat(dDecoder.next().e(), equalTo(4));
+        assertThat(dDecoder.f(), equalTo("abc"));
+        assertThat(bDecoder.next().c(), equalTo(5));
+        assertThat(bDecoder.d(), sameInstance(dDecoder));
+        assertThat(dDecoder.count(), equalTo(2));
+        assertThat(dDecoder.next().e(), equalTo(6));
+        assertThat(dDecoder.f(), equalTo("def"));
+        assertThat(dDecoder.next().e(), equalTo(7));
+        assertThat(dDecoder.f(), equalTo("ghi"));
+    }
+
+    @CsvSource(value = {
+        "1,V0_B_1_D_N_BLOCK",
+        "2,V0_B_N_D_N_BLOCK"
+    })
+    @ParameterizedTest
+    void disallowsMissedEncodingOfVarLengthFieldInNestedGroupToNextInnerElement(
+        final int bCount,
+        final String expectedState)
+    {
+        final NestedGroupWithVarLengthEncoder encoder = new NestedGroupWithVarLengthEncoder()
+            .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+        encoder.a(1);
+        final NestedGroupWithVarLengthEncoder.BEncoder bEncoder = encoder.bCount(bCount);
+        final NestedGroupWithVarLengthEncoder.BEncoder.DEncoder dEncoder = bEncoder.next().c(5).dCount(2);
+        dEncoder.next().e(7);
+        final IllegalStateException exception =
+            assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, dEncoder::next);
+        assertThat(exception.getMessage(),
+            containsString("Cannot access next element in repeating group \"b.d\" in state: " + expectedState));
+        assertThat(exception.getMessage(),
+            containsString("Expected one of these transitions: [\"b.d.e(?)\", \"b.d.f(?)\"]."));
+    }
+
+    @CsvSource(value = {
+        "1,V0_B_N_D_1_BLOCK",
+        "2,V0_B_N_D_N_BLOCK",
+    })
+    @ParameterizedTest
+    void disallowsMissedEncodingOfVarLengthFieldInNestedGroupToNextOuterElement(
+        final int dCount,
+        final String expectedState)
+    {
+        final NestedGroupWithVarLengthEncoder encoder = new NestedGroupWithVarLengthEncoder()
+            .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+        encoder.a(1);
+        final NestedGroupWithVarLengthEncoder.BEncoder bEncoder = encoder.bCount(2);
+        bEncoder.next().c(3).dCount(dCount).next().e(4);
+        final IllegalStateException exception =
+            assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, bEncoder::next);
+        assertThat(exception.getMessage(),
+            containsString("Cannot access next element in repeating group \"b\" in state: " + expectedState));
+        assertThat(exception.getMessage(),
+            containsString("Expected one of these transitions: [\"b.d.e(?)\", \"b.d.f(?)\"]."));
+    }
+
+    @Test
+    void disallowsMissedDecodingOfVarLengthFieldInNestedGroupToNextInnerElement1()
+    {
+        final NestedGroupWithVarLengthEncoder encoder = new NestedGroupWithVarLengthEncoder()
+            .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+        encoder.a(1);
+        final NestedGroupWithVarLengthEncoder.BEncoder bEncoder = encoder.bCount(1);
+        bEncoder.next().c(2).dCount(2).next().e(3).f("abc").next().e(4).f("def");
+
+        final NestedGroupWithVarLengthDecoder decoder = new NestedGroupWithVarLengthDecoder()
+            .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+        assertThat(decoder.a(), equalTo(1));
+        final NestedGroupWithVarLengthDecoder.BDecoder bDecoder = decoder.b();
+        assertThat(bDecoder.count(), equalTo(1));
+        assertThat(bDecoder.next().c(), equalTo(2));
+        final NestedGroupWithVarLengthDecoder.BDecoder.DDecoder dDecoder = bDecoder.d();
+        assertThat(dDecoder.count(), equalTo(2));
+        assertThat(dDecoder.next().e(), equalTo(3));
+        final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, dDecoder::next);
+        assertThat(exception.getMessage(),
+            containsString("Cannot access next element in repeating group \"b.d\" in state: V0_B_1_D_N_BLOCK."));
+        assertThat(exception.getMessage(),
+            containsString("Expected one of these transitions: [\"b.d.e(?)\", \"b.d.f(?)\"]."));
+    }
+
+    @Test
+    void disallowsMissedDecodingOfVarLengthFieldInNestedGroupToNextInnerElement2()
+    {
+        final NestedGroupWithVarLengthEncoder encoder = new NestedGroupWithVarLengthEncoder()
+            .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+        encoder.a(1);
+        final NestedGroupWithVarLengthEncoder.BEncoder bEncoder = encoder.bCount(2);
+        bEncoder.next().c(2).dCount(2).next().e(3).f("abc").next().e(4).f("def");
+        bEncoder.next().c(5).dCount(0);
+
+        final NestedGroupWithVarLengthDecoder decoder = new NestedGroupWithVarLengthDecoder()
+            .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+        assertThat(decoder.a(), equalTo(1));
+        final NestedGroupWithVarLengthDecoder.BDecoder bDecoder = decoder.b();
+        assertThat(bDecoder.count(), equalTo(2));
+        assertThat(bDecoder.next().c(), equalTo(2));
+        final NestedGroupWithVarLengthDecoder.BDecoder.DDecoder dDecoder = bDecoder.d();
+        assertThat(dDecoder.count(), equalTo(2));
+        assertThat(dDecoder.next().e(), equalTo(3));
+        final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, dDecoder::next);
+        assertThat(exception.getMessage(),
+            containsString("Cannot access next element in repeating group \"b.d\" in state: V0_B_N_D_N_BLOCK."));
+        assertThat(exception.getMessage(),
+            containsString("Expected one of these transitions: [\"b.d.e(?)\", \"b.d.f(?)\"]."));
+    }
+
+    @Test
+    void disallowsMissedDecodingOfVarLengthFieldInNestedGroupToNextOuterElement1()
+    {
+        final NestedGroupWithVarLengthEncoder encoder = new NestedGroupWithVarLengthEncoder()
+            .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+        encoder.a(1);
+        final NestedGroupWithVarLengthEncoder.BEncoder bEncoder = encoder.bCount(2);
+        bEncoder.next().c(2).dCount(2).next().e(3).f("abc").next().e(4).f("def");
+        bEncoder.next().c(5).dCount(0);
+
+        final NestedGroupWithVarLengthDecoder decoder = new NestedGroupWithVarLengthDecoder()
+            .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+        assertThat(decoder.a(), equalTo(1));
+        final NestedGroupWithVarLengthDecoder.BDecoder bDecoder = decoder.b();
+        assertThat(bDecoder.count(), equalTo(2));
+        assertThat(bDecoder.next().c(), equalTo(2));
+        final NestedGroupWithVarLengthDecoder.BDecoder.DDecoder dDecoder = bDecoder.d();
+        assertThat(dDecoder.count(), equalTo(2));
+        assertThat(dDecoder.next().e(), equalTo(3));
+        final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, bDecoder::next);
+        assertThat(exception.getMessage(),
+            containsString("Cannot access next element in repeating group \"b\" in state: V0_B_N_D_N_BLOCK."));
+        assertThat(exception.getMessage(),
+            containsString("Expected one of these transitions: [\"b.d.e(?)\", \"b.d.f(?)\"]."));
+    }
+
+    @Test
+    void disallowsMissedDecodingOfVarLengthFieldInNestedGroupToNextOuterElement2()
+    {
+        final NestedGroupWithVarLengthEncoder encoder = new NestedGroupWithVarLengthEncoder()
+            .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+        encoder.a(1);
+        final NestedGroupWithVarLengthEncoder.BEncoder bEncoder = encoder.bCount(2);
+        bEncoder.next().c(2).dCount(1).next().e(3).f("abc");
+        bEncoder.next().c(5).dCount(0);
+
+        final NestedGroupWithVarLengthDecoder decoder = new NestedGroupWithVarLengthDecoder()
+            .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+        assertThat(decoder.a(), equalTo(1));
+        final NestedGroupWithVarLengthDecoder.BDecoder bDecoder = decoder.b();
+        assertThat(bDecoder.count(), equalTo(2));
+        assertThat(bDecoder.next().c(), equalTo(2));
+        final NestedGroupWithVarLengthDecoder.BDecoder.DDecoder dDecoder = bDecoder.d();
+        assertThat(dDecoder.count(), equalTo(1));
+        assertThat(dDecoder.next().e(), equalTo(3));
+        final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, bDecoder::next);
+        assertThat(exception.getMessage(),
+            containsString("Cannot access next element in repeating group \"b\" in state: V0_B_N_D_1_BLOCK."));
+        assertThat(exception.getMessage(),
+            containsString("Expected one of these transitions: [\"b.d.e(?)\", \"b.d.f(?)\"]."));
+    }
+
     private void modifyHeaderToLookLikeVersion0()
     {
         messageHeaderDecoder.wrap(buffer, OFFSET);
diff --git a/sbe-tool/src/test/resources/field-order-check-schema.xml b/sbe-tool/src/test/resources/field-order-check-schema.xml
index c435410f9a..e4d62ebfd6 100644
--- a/sbe-tool/src/test/resources/field-order-check-schema.xml
+++ b/sbe-tool/src/test/resources/field-order-check-schema.xml
@@ -278,4 +278,15 @@
             
         
     
+
+    
+        
+        
+            
+            
+                
+                
+            
+        
+    
 

From 4031a11429bf37e42fa2cc988623d50ea1890ce3 Mon Sep 17 00:00:00 2001
From: Zach Bray 
Date: Thu, 22 Jun 2023 09:48:43 +0100
Subject: [PATCH 27/50] [Java] Generate check that encoding is complete.

Previously, we checked the order of encoding was correct but not that
the encoding reached a terminal state.

In this commit, I've added code to generate a new method
`checkEncodingIsComplete` on Java encoders. It verifies that a terminal
state has been reached; otherwise, it throws an exception with
information about the current state and transitions.
---
 .../sbe/generation/java/AccessOrderModel.java |  12 +-
 .../sbe/generation/java/JavaGenerator.java    |  31 +++
 .../sbe/ir/generated/FrameCodecEncoder.java   |  14 ++
 .../sbe/ir/generated/TokenCodecEncoder.java   |  14 ++
 .../sbe/FieldAccessOrderCheckTest.java        | 224 ++++++++++++++++++
 5 files changed, 293 insertions(+), 2 deletions(-)

diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/AccessOrderModel.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/AccessOrderModel.java
index 0eecfa0efd..846ed49fdd 100644
--- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/AccessOrderModel.java
+++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/AccessOrderModel.java
@@ -48,6 +48,7 @@ final class AccessOrderModel
     private final Int2ObjectHashMap versionWrappedStates = new Int2ObjectHashMap<>();
     private final State notWrappedState = allocateState("NOT_WRAPPED");
     private State encoderWrappedState;
+    private Set terminalEncoderStates;
 
     static boolean generateAccessOrderChecks()
     {
@@ -86,6 +87,11 @@ void forEachDecoderWrappedState(final IntObjConsumer consumer)
         }
     }
 
+    void forEachTerminalEncoderState(final Consumer consumer)
+    {
+        terminalEncoderStates.forEach(consumer);
+    }
+
     void forEachStateOrderedByStateNumber(final Consumer consumer)
     {
         transitionsByState.keySet().stream()
@@ -165,8 +171,6 @@ private void findTransitions(
 
             versionWrappedStates.put(version, versionWrappedState);
 
-            encoderWrappedState = versionWrappedState;
-
             final CodecInteraction wrapInteraction = interactionFactory.wrap(version);
 
             allocateTransitions(
@@ -183,6 +187,10 @@ private void findTransitions(
             );
 
             walkSchemaLevel(transitionCollector, fields, groups, varData);
+
+            // Last writer (highest version) wins when there are multiple versions
+            encoderWrappedState = versionWrappedState;
+            terminalEncoderStates = transitionCollector.exitStates();
         });
     }
 
diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java
index 292217d82e..f001e84480 100644
--- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java
+++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java
@@ -273,6 +273,7 @@ private void generateEncoder(
             generateEncoderVarData(sb, className, accessOrderModel, varData, BASE_INDENT);
 
             generateEncoderDisplay(sb, decoderName(msgToken.name()));
+            generateFullyEncodedCheck(sb, accessOrderModel);
 
             out.append(sb);
             out.append("}\n");
@@ -395,6 +396,36 @@ private static CharSequence generateFieldOrderStates(final AccessOrderModel acce
         return sb;
     }
 
+    private static void generateFullyEncodedCheck(
+        final StringBuilder sb,
+        final AccessOrderModel accessOrderModel)
+    {
+        if (null == accessOrderModel)
+        {
+            return;
+        }
+
+        sb.append("\n");
+
+        sb.append("    public void checkEncodingIsComplete()\n")
+            .append("    {\n")
+            .append("        switch (codecState)\n")
+            .append("        {\n");
+
+        accessOrderModel.forEachTerminalEncoderState(state ->
+        {
+            sb.append("            case ").append(stateCaseForSwitchCase(state)).append(":\n")
+                .append("                return;\n");
+        });
+
+        sb.append("            default:\n")
+            .append("                throw new IllegalStateException(\"Not fully encoded, current state: \" +\n")
+            .append("                    CodecStates.name(codecState) + \", allowed transitions: \" +\n")
+            .append("                    CodecStates.transitions(codecState));\n")
+            .append("        }\n")
+            .append("    }\n\n");
+    }
+
     private static String accessOrderListenerMethodName(final Token token)
     {
         return "on" + Generators.toUpperFirstChar(token.name()) + "Accessed";
diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java
index 09da58e83a..5c1b582445 100644
--- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java
+++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java
@@ -728,4 +728,18 @@ public StringBuilder appendTo(final StringBuilder builder)
 
         return decoder.appendTo(builder);
     }
+
+    public void checkEncodingIsComplete()
+    {
+        switch (codecState)
+        {
+            case CodecStates.V0_SEMANTICVERSION_DONE:
+                return;
+            default:
+                throw new IllegalStateException("Not fully encoded, current state: " +
+                    CodecStates.name(codecState) + ", allowed transitions: " +
+                    CodecStates.transitions(codecState));
+        }
+    }
+
 }
diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java
index b4f99f6678..0ba727fc80 100644
--- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java
+++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java
@@ -2027,4 +2027,18 @@ public StringBuilder appendTo(final StringBuilder builder)
 
         return decoder.appendTo(builder);
     }
+
+    public void checkEncodingIsComplete()
+    {
+        switch (codecState)
+        {
+            case CodecStates.V0_REFERENCEDNAME_DONE:
+                return;
+            default:
+                throw new IllegalStateException("Not fully encoded, current state: " +
+                    CodecStates.name(codecState) + ", allowed transitions: " +
+                    CodecStates.transitions(codecState));
+        }
+    }
+
 }
diff --git a/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldAccessOrderCheckTest.java b/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldAccessOrderCheckTest.java
index 8ab9f35ffb..cbbe250187 100644
--- a/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldAccessOrderCheckTest.java
+++ b/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldAccessOrderCheckTest.java
@@ -56,6 +56,7 @@ void allowsEncodingAndDecodingVariableLengthFieldsInSchemaDefinedOrder()
         encoder.a(42);
         encoder.b("abc");
         encoder.c("def");
+        encoder.checkEncodingIsComplete();
 
         final MultipleVarLengthDecoder decoder = new MultipleVarLengthDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -73,6 +74,7 @@ void allowsDecodingVariableLengthFieldsAfterRewind()
         encoder.a(42);
         encoder.b("abc");
         encoder.c("def");
+        encoder.checkEncodingIsComplete();
 
         final MultipleVarLengthDecoder decoder = new MultipleVarLengthDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -97,6 +99,7 @@ void allowsDecodingToSkipVariableLengthFields()
         encoder.a(42);
         encoder.b("abc");
         encoder.c("def");
+        encoder.checkEncodingIsComplete();
 
         final MultipleVarLengthDecoder decoder = new MultipleVarLengthDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -115,6 +118,7 @@ void allowsReEncodingTopLevelPrimitiveFields()
         encoder.b("abc");
         encoder.c("def");
         encoder.a(43);
+        encoder.checkEncodingIsComplete();
 
         final MultipleVarLengthDecoder decoder = new MultipleVarLengthDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -281,6 +285,7 @@ private MultipleVarLengthDecoder decodeUntilVarLengthFields()
         encoder.a(42);
         encoder.b("abc");
         encoder.c("def");
+        encoder.checkEncodingIsComplete();
 
         final MultipleVarLengthDecoder decoder = new MultipleVarLengthDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -300,6 +305,7 @@ void allowsEncodingAndDecodingGroupAndVariableLengthFieldsInSchemaDefinedOrder()
             .next()
             .c(2);
         encoder.d("abc");
+        encoder.checkEncodingIsComplete();
 
         final GroupAndVarLengthDecoder decoder = new GroupAndVarLengthDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -324,6 +330,7 @@ void allowsDecodingGroupAndVariableLengthFieldsAfterRewind()
             .next()
             .c(2);
         encoder.d("abc");
+        encoder.checkEncodingIsComplete();
 
         final GroupAndVarLengthDecoder decoder = new GroupAndVarLengthDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -358,6 +365,7 @@ void allowsDecodingToSkipMessage()
             .next()
             .c(2);
         encoder.d("abc");
+        encoder.checkEncodingIsComplete();
 
         final int nextEncodeOffset = encoder.limit();
         encoder.wrapAndApplyHeader(buffer, nextEncodeOffset, messageHeaderEncoder);
@@ -368,6 +376,7 @@ void allowsDecodingToSkipMessage()
             .next()
             .c(4);
         encoder.d("def");
+        encoder.checkEncodingIsComplete();
 
         final GroupAndVarLengthDecoder decoder = new GroupAndVarLengthDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -397,6 +406,7 @@ void allowsDecodingToDetermineMessageLengthBeforeReadingFields()
             .next()
             .c(4);
         encoder.d("def");
+        encoder.checkEncodingIsComplete();
 
         final GroupAndVarLengthDecoder decoder = new GroupAndVarLengthDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -418,6 +428,7 @@ void allowsEncodingAndDecodingEmptyGroupAndVariableLengthFieldsInSchemaDefinedOr
         encoder.a(42);
         encoder.bCount(0);
         encoder.d("abc");
+        encoder.checkEncodingIsComplete();
 
         final GroupAndVarLengthDecoder decoder = new GroupAndVarLengthDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -443,6 +454,7 @@ void allowsReEncodingPrimitiveFieldInGroupElementAfterTopLevelVariableLengthFiel
             .c(2);
         encoder.d("abc");
         bEncoder.c(3);
+        encoder.checkEncodingIsComplete();
 
         final GroupAndVarLengthDecoder decoder = new GroupAndVarLengthDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -468,6 +480,7 @@ void allowsReWrappingGroupDecoderAfterAccessingGroupCount()
             .next()
             .c(2);
         encoder.d("abc");
+        encoder.checkEncodingIsComplete();
 
         final GroupAndVarLengthDecoder decoder = new GroupAndVarLengthDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -503,6 +516,7 @@ void disallowsDecodingGroupElementBeforeCallingNext()
             .next()
             .c(2);
         encoder.d("abc");
+        encoder.checkEncodingIsComplete();
 
         final GroupAndVarLengthDecoder decoder = new GroupAndVarLengthDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -574,6 +588,7 @@ void allowsReEncodingGroupElementBlockFieldAfterTopLevelVariableLengthField()
             .c(2);
         encoder.d("abc");
         b.c(3);
+        encoder.checkEncodingIsComplete();
 
         final GroupAndVarLengthDecoder decoder = new GroupAndVarLengthDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -597,6 +612,7 @@ void disallowsMissedDecodingOfGroupBeforeVariableLengthField()
             .next()
             .c(2);
         encoder.d("abc");
+        encoder.checkEncodingIsComplete();
 
         final GroupAndVarLengthDecoder decoder = new GroupAndVarLengthDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -618,6 +634,7 @@ void disallowsReDecodingVariableLengthFieldAfterGroup()
             .next()
             .c(2);
         encoder.d("abc");
+        encoder.checkEncodingIsComplete();
 
         final GroupAndVarLengthDecoder decoder = new GroupAndVarLengthDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -644,6 +661,7 @@ void disallowsReDecodingGroupAfterVariableLengthField()
             .next()
             .c(2);
         encoder.d("abc");
+        encoder.checkEncodingIsComplete();
 
         final GroupAndVarLengthDecoder decoder = new GroupAndVarLengthDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -673,6 +691,7 @@ void allowsEncodingAndDecodingVariableLengthFieldInsideGroupInSchemaDefinedOrder
             .c(2)
             .d("def");
         encoder.e("ghi");
+        encoder.checkEncodingIsComplete();
 
         final VarLengthInsideGroupDecoder decoder = new VarLengthInsideGroupDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -701,6 +720,7 @@ void allowsReEncodingGroupElementPrimitiveFieldAfterElementVariableLengthField()
             .d("abc");
         bEncoder.c(2);
         encoder.e("ghi");
+        encoder.checkEncodingIsComplete();
 
         final VarLengthInsideGroupDecoder decoder = new VarLengthInsideGroupDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -783,6 +803,7 @@ void disallowsReDecodingGroupElementVariableLengthField()
             .c(2)
             .d("def");
         encoder.e("ghi");
+        encoder.checkEncodingIsComplete();
 
         final VarLengthInsideGroupDecoder decoder = new VarLengthInsideGroupDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -810,6 +831,7 @@ void disallowsMissedDecodingOfGroupElementVariableLengthFieldToNextElement()
             .c(2)
             .d("def");
         encoder.e("ghi");
+        encoder.checkEncodingIsComplete();
 
         final VarLengthInsideGroupDecoder decoder = new VarLengthInsideGroupDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -834,6 +856,7 @@ void disallowsMissedDecodingOfGroupElementVariableLengthFieldToTopLevel()
             .c(1)
             .d("abc");
         encoder.e("ghi");
+        encoder.checkEncodingIsComplete();
 
         final VarLengthInsideGroupDecoder decoder = new VarLengthInsideGroupDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -860,6 +883,7 @@ void disallowsMissedDecodingOfGroupElement()
             .c(2)
             .d("def");
         encoder.e("ghi");
+        encoder.checkEncodingIsComplete();
 
         final VarLengthInsideGroupDecoder decoder = new VarLengthInsideGroupDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -900,6 +924,7 @@ void allowsEncodingNestedGroupsInSchemaDefinedOrder()
         encoder.hCount(1)
             .next()
             .i(8);
+        encoder.checkEncodingIsComplete();
 
         final NestedGroupsDecoder decoder = new NestedGroupsDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -938,6 +963,7 @@ void allowsEncodingEmptyNestedGroupsInSchemaDefinedOrder()
         encoder.a(42);
         encoder.bCount(0);
         encoder.hCount(0);
+        encoder.checkEncodingIsComplete();
 
         final NestedGroupsDecoder decoder = new NestedGroupsDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -973,6 +999,7 @@ void allowsEncodingAndDecodingCompositeInsideGroupInSchemaDefinedOrder()
         encoder.bCount(1)
             .next()
             .c().x(3).y(4);
+        encoder.checkEncodingIsComplete();
 
         final CompositeInsideGroupDecoder decoder = new CompositeInsideGroupDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -1031,6 +1058,7 @@ void allowsReEncodingTopLevelCompositeViaReWrap()
             .next()
             .c().x(3).y(4);
         encoder.a().x(5).y(6);
+        encoder.checkEncodingIsComplete();
 
         final CompositeInsideGroupDecoder decoder = new CompositeInsideGroupDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -1055,6 +1083,7 @@ void allowsReEncodingTopLevelCompositeViaEncoderReference()
             .next()
             .c().x(3).y(4);
         aEncoder.x(5).y(6);
+        encoder.checkEncodingIsComplete();
 
         final CompositeInsideGroupDecoder decoder = new CompositeInsideGroupDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -1077,6 +1106,7 @@ void allowsReEncodingGroupElementCompositeViaReWrap()
         final CompositeInsideGroupEncoder.BEncoder bEncoder = encoder.bCount(1).next();
         bEncoder.c().x(3).y(4);
         bEncoder.c().x(5).y(6);
+        encoder.checkEncodingIsComplete();
 
         final CompositeInsideGroupDecoder decoder = new CompositeInsideGroupDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -1100,6 +1130,7 @@ void allowsReEncodingGroupElementCompositeViaEncoderReference()
         final PointEncoder cEncoder = bEncoder.c();
         cEncoder.x(3).y(4);
         cEncoder.x(5).y(6);
+        encoder.checkEncodingIsComplete();
 
         final CompositeInsideGroupDecoder decoder = new CompositeInsideGroupDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -1122,6 +1153,7 @@ void allowsReDecodingTopLevelCompositeViaReWrap()
         encoder.bCount(1)
             .next()
             .c().x(3).y(4);
+        encoder.checkEncodingIsComplete();
 
         final CompositeInsideGroupDecoder decoder = new CompositeInsideGroupDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -1147,6 +1179,7 @@ void allowsReDecodingTopLevelCompositeViaEncoderReference()
         encoder.bCount(1)
             .next()
             .c().x(3).y(4);
+        encoder.checkEncodingIsComplete();
 
         final CompositeInsideGroupDecoder decoder = new CompositeInsideGroupDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -1171,6 +1204,7 @@ void allowsReDecodingGroupElementCompositeViaReWrap()
         encoder.bCount(1)
             .next()
             .c().x(3).y(4);
+        encoder.checkEncodingIsComplete();
 
         final CompositeInsideGroupDecoder decoder = new CompositeInsideGroupDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -1196,6 +1230,7 @@ void allowsReDecodingGroupElementCompositeViaEncoderReference()
         encoder.bCount(1)
             .next()
             .c().x(3).y(4);
+        encoder.checkEncodingIsComplete();
 
         final CompositeInsideGroupDecoder decoder = new CompositeInsideGroupDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -1217,6 +1252,7 @@ void allowsNewDecoderToDecodeAddedPrimitiveField()
         final AddPrimitiveV1Encoder encoder = new AddPrimitiveV1Encoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
         encoder.a(1).b(2);
+        encoder.checkEncodingIsComplete();
 
         final AddPrimitiveV1Decoder decoder = new AddPrimitiveV1Decoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -1230,6 +1266,7 @@ void allowsNewDecoderToDecodeMissingPrimitiveFieldAsNullValue()
         final AddPrimitiveV0Encoder encoder = new AddPrimitiveV0Encoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
         encoder.a(1);
+        encoder.checkEncodingIsComplete();
 
         modifyHeaderToLookLikeVersion0();
 
@@ -1245,6 +1282,7 @@ void allowsNewDecoderToDecodeAddedPrimitiveFieldBeforeGroup()
         final AddPrimitiveBeforeGroupV1Encoder encoder = new AddPrimitiveBeforeGroupV1Encoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
         encoder.a(1).d(3).bCount(1).next().c(2);
+        encoder.checkEncodingIsComplete();
 
         final AddPrimitiveBeforeGroupV1Decoder decoder = new AddPrimitiveBeforeGroupV1Decoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -1261,6 +1299,7 @@ void allowsNewDecoderToDecodeMissingPrimitiveFieldBeforeGroupAsNullValue()
         final AddPrimitiveBeforeGroupV0Encoder encoder = new AddPrimitiveBeforeGroupV0Encoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
         encoder.a(1).bCount(1).next().c(2);
+        encoder.checkEncodingIsComplete();
 
         modifyHeaderToLookLikeVersion0();
 
@@ -1279,6 +1318,7 @@ void allowsNewDecoderToSkipPresentButAddedPrimitiveFieldBeforeGroup()
         final AddPrimitiveBeforeGroupV1Encoder encoder = new AddPrimitiveBeforeGroupV1Encoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
         encoder.a(1).d(3).bCount(1).next().c(2);
+        encoder.checkEncodingIsComplete();
 
         final AddPrimitiveBeforeGroupV1Decoder decoder = new AddPrimitiveBeforeGroupV1Decoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -1294,6 +1334,7 @@ void allowsOldDecoderToSkipAddedPrimitiveFieldBeforeGroup()
         final AddPrimitiveBeforeGroupV1Encoder encoder = new AddPrimitiveBeforeGroupV1Encoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
         encoder.a(1).d(3).bCount(1).next().c(2);
+        encoder.checkEncodingIsComplete();
 
         modifyHeaderToLookLikeVersion1();
 
@@ -1311,6 +1352,7 @@ void allowsNewDecoderToDecodeAddedPrimitiveFieldBeforeVarData()
         final AddPrimitiveBeforeVarDataV1Encoder encoder = new AddPrimitiveBeforeVarDataV1Encoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
         encoder.a(1).c(3).b("abc");
+        encoder.checkEncodingIsComplete();
 
         final AddPrimitiveBeforeVarDataV1Decoder decoder = new AddPrimitiveBeforeVarDataV1Decoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -1325,6 +1367,7 @@ void allowsNewDecoderToDecodeMissingPrimitiveFieldBeforeVarDataAsNullValue()
         final AddPrimitiveBeforeVarDataV0Encoder encoder = new AddPrimitiveBeforeVarDataV0Encoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
         encoder.a(1).b("abc");
+        encoder.checkEncodingIsComplete();
 
         modifyHeaderToLookLikeVersion0();
 
@@ -1341,6 +1384,7 @@ void allowsNewDecoderToSkipPresentButAddedPrimitiveFieldBeforeVarData()
         final AddPrimitiveBeforeVarDataV1Encoder encoder = new AddPrimitiveBeforeVarDataV1Encoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
         encoder.a(1).c(3).b("abc");
+        encoder.checkEncodingIsComplete();
 
         final AddPrimitiveBeforeVarDataV1Decoder decoder = new AddPrimitiveBeforeVarDataV1Decoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -1354,6 +1398,7 @@ void allowsOldDecoderToSkipAddedPrimitiveFieldBeforeVarData()
         final AddPrimitiveBeforeVarDataV1Encoder encoder = new AddPrimitiveBeforeVarDataV1Encoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
         encoder.a(1).c(3).b("abc");
+        encoder.checkEncodingIsComplete();
 
         modifyHeaderToLookLikeVersion1();
 
@@ -1369,6 +1414,7 @@ void allowsNewDecoderToDecodeAddedPrimitiveFieldInsideGroup()
         final AddPrimitiveInsideGroupV1Encoder encoder = new AddPrimitiveInsideGroupV1Encoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
         encoder.a(1).bCount(1).next().c(2).d(3);
+        encoder.checkEncodingIsComplete();
 
         final AddPrimitiveInsideGroupV1Decoder decoder = new AddPrimitiveInsideGroupV1Decoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -1385,6 +1431,7 @@ void allowsNewDecoderToDecodeMissingPrimitiveFieldInsideGroupAsNullValue()
         final AddPrimitiveInsideGroupV0Encoder encoder = new AddPrimitiveInsideGroupV0Encoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
         encoder.a(1).bCount(1).next().c(2);
+        encoder.checkEncodingIsComplete();
 
         modifyHeaderToLookLikeVersion0();
 
@@ -1403,6 +1450,7 @@ void allowsNewDecoderToSkipPresentButAddedPrimitiveFieldInsideGroup()
         final AddPrimitiveInsideGroupV1Encoder encoder = new AddPrimitiveInsideGroupV1Encoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
         encoder.a(1).bCount(2).next().c(2).d(3).next().c(4).d(5);
+        encoder.checkEncodingIsComplete();
 
         final AddPrimitiveInsideGroupV1Decoder decoder = new AddPrimitiveInsideGroupV1Decoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -1419,6 +1467,7 @@ void allowsOldDecoderToSkipAddedPrimitiveFieldInsideGroup()
         final AddPrimitiveInsideGroupV1Encoder encoder = new AddPrimitiveInsideGroupV1Encoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
         encoder.a(1).bCount(2).next().c(2).d(3).next().c(4).d(5);
+        encoder.checkEncodingIsComplete();
 
         modifyHeaderToLookLikeVersion1();
 
@@ -1438,6 +1487,7 @@ void allowsNewDecoderToDecodeAddedGroupBeforeVarData()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
         encoder.a(1).cCount(1).next().d(2);
         encoder.b("abc");
+        encoder.checkEncodingIsComplete();
 
         final AddGroupBeforeVarDataV1Decoder decoder = new AddGroupBeforeVarDataV1Decoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -1454,6 +1504,7 @@ void allowsNewDecoderToDecodeMissingGroupBeforeVarDataAsNullValue()
         final AddGroupBeforeVarDataV0Encoder encoder = new AddGroupBeforeVarDataV0Encoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
         encoder.a(1).b("abc");
+        encoder.checkEncodingIsComplete();
 
         modifyHeaderToLookLikeVersion0();
 
@@ -1472,6 +1523,7 @@ void allowsNewDecoderToSkipMissingGroupBeforeVarData()
         final AddGroupBeforeVarDataV0Encoder encoder = new AddGroupBeforeVarDataV0Encoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
         encoder.a(1).b("abc");
+        encoder.checkEncodingIsComplete();
 
         modifyHeaderToLookLikeVersion0();
 
@@ -1488,6 +1540,7 @@ void disallowsNewDecoderToSkipPresentButAddedGroupBeforeVarData()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
         encoder.a(1).cCount(1).next().d(2);
         encoder.b("abc");
+        encoder.checkEncodingIsComplete();
 
         final AddGroupBeforeVarDataV1Decoder decoder = new AddGroupBeforeVarDataV1Decoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -1505,6 +1558,7 @@ void allowsOldDecoderToSkipAddedGroupBeforeVarData()
         messageHeaderEncoder.numGroups(1);
         encoder.a(1).cCount(1).next().d(2);
         encoder.b("abc");
+        encoder.checkEncodingIsComplete();
 
         modifyHeaderToLookLikeVersion1();
 
@@ -1535,6 +1589,7 @@ void allowsNewDecoderToDecodeAddedEnumFieldBeforeGroup()
         final AddEnumBeforeGroupV1Encoder encoder = new AddEnumBeforeGroupV1Encoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
         encoder.a(1).d(Direction.BUY).bCount(1).next().c(2);
+        encoder.checkEncodingIsComplete();
 
         final AddEnumBeforeGroupV1Decoder decoder = new AddEnumBeforeGroupV1Decoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -1551,6 +1606,7 @@ void allowsNewDecoderToDecodeMissingEnumFieldBeforeGroupAsNullValue()
         final AddEnumBeforeGroupV0Encoder encoder = new AddEnumBeforeGroupV0Encoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
         encoder.a(1).bCount(1).next().c(2);
+        encoder.checkEncodingIsComplete();
 
         modifyHeaderToLookLikeVersion0();
 
@@ -1569,6 +1625,7 @@ void allowsNewDecoderToSkipPresentButAddedEnumFieldBeforeGroup()
         final AddEnumBeforeGroupV1Encoder encoder = new AddEnumBeforeGroupV1Encoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
         encoder.a(1).d(Direction.SELL).bCount(1).next().c(2);
+        encoder.checkEncodingIsComplete();
 
         final AddEnumBeforeGroupV1Decoder decoder = new AddEnumBeforeGroupV1Decoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -1584,6 +1641,7 @@ void allowsOldDecoderToSkipAddedEnumFieldBeforeGroup()
         final AddEnumBeforeGroupV1Encoder encoder = new AddEnumBeforeGroupV1Encoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
         encoder.a(1).d(Direction.BUY).bCount(1).next().c(2);
+        encoder.checkEncodingIsComplete();
 
         modifyHeaderToLookLikeVersion1();
 
@@ -1602,6 +1660,7 @@ void allowsNewDecoderToDecodeAddedCompositeFieldBeforeGroup()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
         encoder.a(1).d().x(-1).y(-2);
         encoder.bCount(1).next().c(2);
+        encoder.checkEncodingIsComplete();
 
         final AddCompositeBeforeGroupV1Decoder decoder = new AddCompositeBeforeGroupV1Decoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -1621,6 +1680,7 @@ void allowsNewDecoderToDecodeMissingCompositeFieldBeforeGroupAsNullValue()
         final AddCompositeBeforeGroupV0Encoder encoder = new AddCompositeBeforeGroupV0Encoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
         encoder.a(1).bCount(1).next().c(2);
+        encoder.checkEncodingIsComplete();
 
         modifyHeaderToLookLikeVersion0();
 
@@ -1640,6 +1700,7 @@ void allowsNewDecoderToSkipPresentButAddedCompositeFieldBeforeGroup()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
         encoder.a(1).d().x(-1).y(-2);
         encoder.bCount(1).next().c(2);
+        encoder.checkEncodingIsComplete();
 
         final AddCompositeBeforeGroupV1Decoder decoder = new AddCompositeBeforeGroupV1Decoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -1656,6 +1717,7 @@ void allowsOldDecoderToSkipAddedCompositeFieldBeforeGroup()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
         encoder.a(1).d().x(-1).y(-2);
         encoder.bCount(1).next().c(2);
+        encoder.checkEncodingIsComplete();
 
         modifyHeaderToLookLikeVersion1();
 
@@ -1676,6 +1738,7 @@ void allowsNewDecoderToDecodeAddedArrayFieldBeforeGroup()
             .putD((short)1, (short)2, (short)3, (short)4)
             .bCount(1)
             .next().c(2);
+        encoder.checkEncodingIsComplete();
 
         final AddArrayBeforeGroupV1Decoder decoder = new AddArrayBeforeGroupV1Decoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -1695,6 +1758,7 @@ void allowsNewDecoderToDecodeMissingArrayFieldBeforeGroupAsNullValue1()
         final AddArrayBeforeGroupV0Encoder encoder = new AddArrayBeforeGroupV0Encoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
         encoder.a(1).bCount(1).next().c(2);
+        encoder.checkEncodingIsComplete();
 
         modifyHeaderToLookLikeVersion0();
 
@@ -1716,6 +1780,7 @@ void allowsNewDecoderToDecodeMissingArrayFieldBeforeGroupAsNullValue2()
         final AddArrayBeforeGroupV0Encoder encoder = new AddArrayBeforeGroupV0Encoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
         encoder.a(1).bCount(1).next().c(2);
+        encoder.checkEncodingIsComplete();
 
         modifyHeaderToLookLikeVersion0();
 
@@ -1734,6 +1799,7 @@ void allowsNewDecoderToDecodeMissingArrayFieldBeforeGroupAsNullValue3()
         final AddArrayBeforeGroupV0Encoder encoder = new AddArrayBeforeGroupV0Encoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
         encoder.a(1).bCount(1).next().c(2);
+        encoder.checkEncodingIsComplete();
 
         modifyHeaderToLookLikeVersion0();
 
@@ -1752,6 +1818,7 @@ void allowsNewDecoderToDecodeMissingArrayFieldBeforeGroupAsNullValue4()
         final AddArrayBeforeGroupV0Encoder encoder = new AddArrayBeforeGroupV0Encoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
         encoder.a(1).bCount(1).next().c(2);
+        encoder.checkEncodingIsComplete();
 
         modifyHeaderToLookLikeVersion0();
 
@@ -1775,6 +1842,7 @@ void allowsNewDecoderToSkipPresentButAddedArrayFieldBeforeGroup()
             .putD((short)1, (short)2, (short)3, (short)4)
             .bCount(1)
             .next().c(2);
+        encoder.checkEncodingIsComplete();
 
         final AddArrayBeforeGroupV1Decoder decoder = new AddArrayBeforeGroupV1Decoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -1793,6 +1861,7 @@ void allowsOldDecoderToSkipAddedArrayFieldBeforeGroup()
             .putD((short)1, (short)2, (short)3, (short)4)
             .bCount(1)
             .next().c(2);
+        encoder.checkEncodingIsComplete();
 
         modifyHeaderToLookLikeVersion1();
 
@@ -1811,6 +1880,7 @@ void allowsNewDecoderToDecodeAddedBitSetFieldBeforeGroup()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
         encoder.a(1).d().guacamole(true).cheese(true);
         encoder.bCount(1).next().c(2);
+        encoder.checkEncodingIsComplete();
 
         final AddBitSetBeforeGroupV1Decoder decoder = new AddBitSetBeforeGroupV1Decoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -1831,6 +1901,7 @@ void allowsNewDecoderToDecodeMissingBitSetFieldBeforeGroupAsNullValue()
         final AddBitSetBeforeGroupV0Encoder encoder = new AddBitSetBeforeGroupV0Encoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
         encoder.a(1).bCount(1).next().c(2);
+        encoder.checkEncodingIsComplete();
 
         modifyHeaderToLookLikeVersion0();
 
@@ -1850,6 +1921,7 @@ void allowsNewDecoderToSkipPresentButAddedBitSetFieldBeforeGroup()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
         encoder.a(1).d().guacamole(true).cheese(true);
         encoder.bCount(1).next().c(2);
+        encoder.checkEncodingIsComplete();
 
         final AddBitSetBeforeGroupV1Decoder decoder = new AddBitSetBeforeGroupV1Decoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -1866,6 +1938,7 @@ void allowsOldDecoderToSkipAddedBitSetFieldBeforeGroup()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
         encoder.a(1).d().guacamole(true).cheese(true);
         encoder.bCount(1).next().c(2);
+        encoder.checkEncodingIsComplete();
 
         modifyHeaderToLookLikeVersion1();
 
@@ -1886,6 +1959,7 @@ void allowsEncodingAndDecodingEnumInsideGroupInSchemaDefinedOrder()
             .bCount(1)
             .next()
             .c(Direction.SELL);
+        encoder.checkEncodingIsComplete();
 
         final EnumInsideGroupDecoder decoder = new EnumInsideGroupDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -1917,6 +1991,7 @@ void disallowsDecodingEnumInsideGroupBeforeCallingNext()
             .bCount(1)
             .next()
             .c(Direction.SELL);
+        encoder.checkEncodingIsComplete();
 
         final EnumInsideGroupDecoder decoder = new EnumInsideGroupDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -1939,6 +2014,7 @@ void allowsReEncodingTopLevelEnum()
             .c(Direction.SELL);
 
         encoder.a(Direction.SELL);
+        encoder.checkEncodingIsComplete();
 
 
         final EnumInsideGroupDecoder decoder = new EnumInsideGroupDecoder()
@@ -1958,6 +2034,7 @@ void allowsEncodingAndDecodingBitSetInsideGroupInSchemaDefinedOrder()
         encoder.bCount(1)
             .next()
             .c().sourCream(true);
+        encoder.checkEncodingIsComplete();
 
         final BitSetInsideGroupDecoder decoder = new BitSetInsideGroupDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -1995,6 +2072,7 @@ void disallowsDecodingBitSetInsideGroupBeforeCallingNext()
         encoder.bCount(1)
             .next()
             .c().sourCream(true);
+        encoder.checkEncodingIsComplete();
 
         final BitSetInsideGroupDecoder decoder = new BitSetInsideGroupDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -2020,6 +2098,7 @@ void allowsReEncodingTopLevelBitSetViaReWrap()
             .c().sourCream(true);
 
         encoder.a().sourCream(true);
+        encoder.checkEncodingIsComplete();
 
         final BitSetInsideGroupDecoder decoder = new BitSetInsideGroupDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -2044,6 +2123,7 @@ void allowsEncodingAndDecodingArrayInsideGroupInSchemaDefinedOrder()
         encoder.bCount(1)
             .next()
             .putC((short)5, (short)6, (short)7, (short)8);
+        encoder.checkEncodingIsComplete();
 
         final ArrayInsideGroupDecoder decoder = new ArrayInsideGroupDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -2155,6 +2235,7 @@ private ArrayInsideGroupDecoder.BDecoder decodeUntilGroupWithArrayInside()
         encoder.bCount(1)
             .next()
             .putC((short)5, (short)6, (short)7, (short)8);
+        encoder.checkEncodingIsComplete();
 
         final ArrayInsideGroupDecoder decoder = new ArrayInsideGroupDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -2178,6 +2259,7 @@ void allowsReEncodingTopLevelArrayViaReWrap()
             .putC((short)5, (short)6, (short)7, (short)8);
 
         encoder.putA((short)9, (short)10, (short)11, (short)12);
+        encoder.checkEncodingIsComplete();
 
         final ArrayInsideGroupDecoder decoder = new ArrayInsideGroupDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -2202,6 +2284,7 @@ void allowsEncodingAndDecodingGroupFieldsInSchemaDefinedOrder1()
         encoder.a(42);
         encoder.bCount(0);
         encoder.dCount(1).next().e(43);
+        encoder.checkEncodingIsComplete();
 
         final MultipleGroupsDecoder decoder = new MultipleGroupsDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -2221,6 +2304,7 @@ void allowsEncodingAndDecodingGroupFieldsInSchemaDefinedOrder2()
         encoder.a(41);
         encoder.bCount(1).next().c(42);
         encoder.dCount(1).next().e(43);
+        encoder.checkEncodingIsComplete();
 
         final MultipleGroupsDecoder decoder = new MultipleGroupsDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -2242,6 +2326,7 @@ void allowsReEncodingTopLevelPrimitiveFieldsAfterGroups()
         encoder.bCount(1).next().c(42);
         encoder.dCount(1).next().e(43);
         encoder.a(44);
+        encoder.checkEncodingIsComplete();
 
         final MultipleGroupsDecoder decoder = new MultipleGroupsDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -2302,6 +2387,7 @@ void disallowsMissedDecodingOfGroupField()
         encoder.a(41);
         encoder.bCount(1).next().c(42);
         encoder.dCount(1).next().e(43);
+        encoder.checkEncodingIsComplete();
 
         final MultipleGroupsDecoder decoder = new MultipleGroupsDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -2320,6 +2406,7 @@ void disallowsReDecodingEarlierGroupField()
         encoder.a(41);
         encoder.bCount(1).next().c(42);
         encoder.dCount(1).next().e(43);
+        encoder.checkEncodingIsComplete();
 
         final MultipleGroupsDecoder decoder = new MultipleGroupsDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -2344,6 +2431,7 @@ void disallowsReDecodingLatestGroupField()
         encoder.a(41);
         encoder.bCount(1).next().c(42);
         encoder.dCount(1).next().e(43);
+        encoder.checkEncodingIsComplete();
 
         final MultipleGroupsDecoder decoder = new MultipleGroupsDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -2367,6 +2455,7 @@ void allowsNewDecoderToDecodeAddedVarData()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
         encoder.a(42);
         encoder.b("abc");
+        encoder.checkEncodingIsComplete();
 
         final AddVarDataV1Decoder decoder = new AddVarDataV1Decoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -2380,6 +2469,7 @@ void allowsNewDecoderToDecodeMissingAddedVarDataAsNullValue1()
         final AddVarDataV0Encoder encoder = new AddVarDataV0Encoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
         encoder.a(42);
+        encoder.checkEncodingIsComplete();
 
         modifyHeaderToLookLikeVersion0();
 
@@ -2396,6 +2486,7 @@ void allowsNewDecoderToDecodeMissingAddedVarDataAsNullValue2()
         final AddVarDataV0Encoder encoder = new AddVarDataV0Encoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
         encoder.a(42);
+        encoder.checkEncodingIsComplete();
 
         modifyHeaderToLookLikeVersion0();
 
@@ -2411,6 +2502,7 @@ void allowsNewDecoderToDecodeMissingAddedVarDataAsNullValue3()
         final AddVarDataV0Encoder encoder = new AddVarDataV0Encoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
         encoder.a(42);
+        encoder.checkEncodingIsComplete();
 
         modifyHeaderToLookLikeVersion0();
 
@@ -2426,6 +2518,7 @@ void allowsNewDecoderToDecodeMissingAddedVarDataAsNullValue4()
         final AddVarDataV0Encoder encoder = new AddVarDataV0Encoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
         encoder.a(42);
+        encoder.checkEncodingIsComplete();
 
         modifyHeaderToLookLikeVersion0();
 
@@ -2441,6 +2534,7 @@ void allowsNewDecoderToDecodeMissingAddedVarDataAsNullValue5()
         final AddVarDataV0Encoder encoder = new AddVarDataV0Encoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
         encoder.a(42);
+        encoder.checkEncodingIsComplete();
 
         modifyHeaderToLookLikeVersion0();
 
@@ -2459,6 +2553,7 @@ void allowsEncodingAndDecodingAsciiInsideGroupInSchemaDefinedOrder1()
         encoder.bCount(1)
             .next()
             .c("EURUSD");
+        encoder.checkEncodingIsComplete();
 
         final AsciiInsideGroupDecoder decoder = new AsciiInsideGroupDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -2485,6 +2580,7 @@ void allowsEncodingAndDecodingAsciiInsideGroupInSchemaDefinedOrder2()
             .c(3, (byte)'U')
             .c(4, (byte)'S')
             .c(5, (byte)'D');
+        encoder.checkEncodingIsComplete();
 
         final AsciiInsideGroupDecoder decoder = new AsciiInsideGroupDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -2582,6 +2678,7 @@ private AsciiInsideGroupDecoder.BDecoder decodeUntilGroupWithAsciiInside()
         encoder.bCount(1)
             .next()
             .c("EURUSD");
+        encoder.checkEncodingIsComplete();
 
         final AsciiInsideGroupDecoder decoder = new AsciiInsideGroupDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -2602,6 +2699,7 @@ void allowsReEncodingTopLevelAsciiViaReWrap()
             .c("EURUSD");
 
         encoder.a("CADUSD");
+        encoder.checkEncodingIsComplete();
 
         final AsciiInsideGroupDecoder decoder = new AsciiInsideGroupDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -2621,6 +2719,7 @@ void allowsNewDecoderToDecodeAddedAsciiFieldBeforeGroup1()
             .d("EURUSD")
             .bCount(1)
             .next().c(2);
+        encoder.checkEncodingIsComplete();
 
         final AddAsciiBeforeGroupV1Decoder decoder = new AddAsciiBeforeGroupV1Decoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -2640,6 +2739,7 @@ void allowsNewDecoderToDecodeAddedAsciiFieldBeforeGroup2()
             .d("EURUSD")
             .bCount(1)
             .next().c(2);
+        encoder.checkEncodingIsComplete();
 
         final AddAsciiBeforeGroupV1Decoder decoder = new AddAsciiBeforeGroupV1Decoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -2663,6 +2763,7 @@ void allowsNewDecoderToDecodeAddedAsciiFieldBeforeGroup3()
             .putD(eurUsdBytes, 0)
             .bCount(1)
             .next().c(2);
+        encoder.checkEncodingIsComplete();
 
         final AddAsciiBeforeGroupV1Decoder decoder = new AddAsciiBeforeGroupV1Decoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -2690,6 +2791,7 @@ void allowsNewDecoderToDecodeAddedAsciiFieldBeforeGroup4()
             .d(5, (byte)'D')
             .bCount(1)
             .next().c(2);
+        encoder.checkEncodingIsComplete();
 
         final AddAsciiBeforeGroupV1Decoder decoder = new AddAsciiBeforeGroupV1Decoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -2711,6 +2813,7 @@ void allowsNewDecoderToDecodeMissingAsciiFieldBeforeGroupAsNullValue1()
         final AddAsciiBeforeGroupV0Encoder encoder = new AddAsciiBeforeGroupV0Encoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
         encoder.a(1).bCount(1).next().c(2);
+        encoder.checkEncodingIsComplete();
 
         modifyHeaderToLookLikeVersion0();
 
@@ -2734,6 +2837,7 @@ void allowsNewDecoderToDecodeMissingAsciiFieldBeforeGroupAsNullValue2()
         final AddAsciiBeforeGroupV0Encoder encoder = new AddAsciiBeforeGroupV0Encoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
         encoder.a(1).bCount(1).next().c(2);
+        encoder.checkEncodingIsComplete();
 
         modifyHeaderToLookLikeVersion0();
 
@@ -2752,6 +2856,7 @@ void allowsNewDecoderToDecodeMissingAsciiFieldBeforeGroupAsNullValue3()
         final AddAsciiBeforeGroupV0Encoder encoder = new AddAsciiBeforeGroupV0Encoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
         encoder.a(1).bCount(1).next().c(2);
+        encoder.checkEncodingIsComplete();
 
         modifyHeaderToLookLikeVersion0();
 
@@ -2772,6 +2877,7 @@ void allowsNewDecoderToDecodeMissingAsciiFieldBeforeGroupAsNullValue4()
         final AddAsciiBeforeGroupV0Encoder encoder = new AddAsciiBeforeGroupV0Encoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
         encoder.a(1).bCount(1).next().c(2);
+        encoder.checkEncodingIsComplete();
 
         modifyHeaderToLookLikeVersion0();
 
@@ -2793,6 +2899,7 @@ void allowsNewDecoderToSkipPresentButAddedAsciiFieldBeforeGroup()
             .d("EURUSD")
             .bCount(1)
             .next().c(2);
+        encoder.checkEncodingIsComplete();
 
         final AddAsciiBeforeGroupV1Decoder decoder = new AddAsciiBeforeGroupV1Decoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -2811,6 +2918,7 @@ void allowsOldDecoderToSkipAddedAsciiFieldBeforeGroup()
             .d("EURUSD")
             .bCount(1)
             .next().c(2);
+        encoder.checkEncodingIsComplete();
 
         modifyHeaderToLookLikeVersion1();
 
@@ -2828,6 +2936,7 @@ void allowsEncodeAndDecodeOfMessagesWithNoABlock()
         final NoBlockEncoder encoder = new NoBlockEncoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
         encoder.a("abc");
+        encoder.checkEncodingIsComplete();
 
         final NoBlockDecoder decoder = new NoBlockDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -2840,6 +2949,7 @@ void allowsEncodeAndDecodeOfGroupsWithNoBlock()
         final GroupWithNoBlockEncoder encoder = new GroupWithNoBlockEncoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
         encoder.aCount(1).next().b("abc");
+        encoder.checkEncodingIsComplete();
 
         final GroupWithNoBlockDecoder decoder = new GroupWithNoBlockDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -2946,6 +3056,7 @@ void allowsEncodingAndDecodingNestedGroupWithVarDataInSchemaDefinedOrder()
         bEncoder.next().c(2).dCount(0);
         bEncoder.next().c(3).dCount(1).next().e(4).f("abc");
         bEncoder.next().c(5).dCount(2).next().e(6).f("def").next().e(7).f("ghi");
+        encoder.checkEncodingIsComplete();
 
         final NestedGroupWithVarLengthDecoder decoder = new NestedGroupWithVarLengthDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -3021,6 +3132,7 @@ void disallowsMissedDecodingOfVarLengthFieldInNestedGroupToNextInnerElement1()
         encoder.a(1);
         final NestedGroupWithVarLengthEncoder.BEncoder bEncoder = encoder.bCount(1);
         bEncoder.next().c(2).dCount(2).next().e(3).f("abc").next().e(4).f("def");
+        encoder.checkEncodingIsComplete();
 
         final NestedGroupWithVarLengthDecoder decoder = new NestedGroupWithVarLengthDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -3047,6 +3159,7 @@ void disallowsMissedDecodingOfVarLengthFieldInNestedGroupToNextInnerElement2()
         final NestedGroupWithVarLengthEncoder.BEncoder bEncoder = encoder.bCount(2);
         bEncoder.next().c(2).dCount(2).next().e(3).f("abc").next().e(4).f("def");
         bEncoder.next().c(5).dCount(0);
+        encoder.checkEncodingIsComplete();
 
         final NestedGroupWithVarLengthDecoder decoder = new NestedGroupWithVarLengthDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -3073,6 +3186,7 @@ void disallowsMissedDecodingOfVarLengthFieldInNestedGroupToNextOuterElement1()
         final NestedGroupWithVarLengthEncoder.BEncoder bEncoder = encoder.bCount(2);
         bEncoder.next().c(2).dCount(2).next().e(3).f("abc").next().e(4).f("def");
         bEncoder.next().c(5).dCount(0);
+        encoder.checkEncodingIsComplete();
 
         final NestedGroupWithVarLengthDecoder decoder = new NestedGroupWithVarLengthDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -3099,6 +3213,7 @@ void disallowsMissedDecodingOfVarLengthFieldInNestedGroupToNextOuterElement2()
         final NestedGroupWithVarLengthEncoder.BEncoder bEncoder = encoder.bCount(2);
         bEncoder.next().c(2).dCount(1).next().e(3).f("abc");
         bEncoder.next().c(5).dCount(0);
+        encoder.checkEncodingIsComplete();
 
         final NestedGroupWithVarLengthDecoder decoder = new NestedGroupWithVarLengthDecoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
@@ -3116,6 +3231,115 @@ void disallowsMissedDecodingOfVarLengthFieldInNestedGroupToNextOuterElement2()
             containsString("Expected one of these transitions: [\"b.d.e(?)\", \"b.d.f(?)\"]."));
     }
 
+    @Test
+    void disallowsIncompleteMessagesDueToMissingVarLengthField1()
+    {
+        final MultipleVarLengthEncoder encoder = new MultipleVarLengthEncoder()
+            .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+        encoder.a(1).b("abc");
+        final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, encoder::checkEncodingIsComplete);
+        assertThat(exception.getMessage(),
+            containsString("Not fully encoded, current state: V0_B_DONE, allowed transitions: \"c(?)\""));
+    }
+
+    @Test
+    void disallowsIncompleteMessagesDueToMissingVarLengthField2()
+    {
+        final NoBlockEncoder encoder = new NoBlockEncoder()
+            .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+        final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, encoder::checkEncodingIsComplete);
+        assertThat(exception.getMessage(),
+            containsString("Not fully encoded, current state: V0_BLOCK, allowed transitions: \"a(?)\""));
+    }
+
+    @Test
+    void disallowsIncompleteMessagesDueToMissingTopLevelGroup1()
+    {
+        final MultipleGroupsEncoder encoder = new MultipleGroupsEncoder()
+            .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+        encoder.a(1).bCount(0);
+        final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, encoder::checkEncodingIsComplete);
+        assertThat(exception.getMessage(), containsString(
+            "Not fully encoded, current state: V0_B_0, allowed transitions: \"dCount(0)\", \"dCount(>0)\""));
+    }
+
+    @Test
+    void disallowsIncompleteMessagesDueToMissingTopLevelGroup2()
+    {
+        final MultipleGroupsEncoder encoder = new MultipleGroupsEncoder()
+            .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+        encoder.a(1).bCount(1).next().c(2);
+        final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, encoder::checkEncodingIsComplete);
+        assertThat(exception.getMessage(), containsString(
+            "Not fully encoded, current state: V0_B_1_BLOCK, allowed transitions: " +
+            "\"b.c(?)\", \"dCount(0)\", \"dCount(>0)\""));
+    }
+
+    @Test
+    void disallowsIncompleteMessagesDueToMissingTopLevelGroup3()
+    {
+        final MultipleGroupsEncoder encoder = new MultipleGroupsEncoder()
+            .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+        encoder.a(1);
+        final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, encoder::checkEncodingIsComplete);
+        assertThat(exception.getMessage(), containsString(
+            "Not fully encoded, current state: V0_BLOCK, allowed transitions: " +
+            "\"a(?)\", \"bCount(0)\", \"bCount(>0)\""));
+    }
+
+    @CsvSource(value = {
+        "1,V0_B_1_BLOCK",
+        "2,V0_B_N_BLOCK",
+    })
+    @ParameterizedTest
+    void disallowsIncompleteMessagesDueToMissingNestedGroup1(final int bCount, final String expectedState)
+    {
+        final NestedGroupWithVarLengthEncoder encoder = new NestedGroupWithVarLengthEncoder()
+            .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+        encoder.a(1).bCount(bCount).next().c(2);
+        final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, encoder::checkEncodingIsComplete);
+        assertThat(exception.getMessage(), containsString("Not fully encoded, current state: " + expectedState));
+    }
+
+    @CsvSource(value = {
+        "1,1,V0_B_1_D_N",
+        "1,2,V0_B_1_D_N",
+        "2,0,V0_B_N_D_0",
+        "2,1,V0_B_N_D_N",
+        "2,2,V0_B_N_D_N",
+    })
+    @ParameterizedTest
+    void disallowsIncompleteMessagesDueToMissingNestedGroup2(
+        final int bCount,
+        final int dCount,
+        final String expectedState)
+    {
+        final NestedGroupWithVarLengthEncoder encoder = new NestedGroupWithVarLengthEncoder()
+            .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+        encoder.a(1).bCount(bCount).next().c(2).dCount(dCount);
+        final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, encoder::checkEncodingIsComplete);
+        assertThat(exception.getMessage(), containsString("Not fully encoded, current state: " + expectedState));
+    }
+
+    @CsvSource(value = {
+        "1,1,V0_B_1_D_1_BLOCK",
+        "1,2,V0_B_1_D_N_BLOCK",
+        "2,1,V0_B_N_D_1_BLOCK",
+        "2,2,V0_B_N_D_N_BLOCK",
+    })
+    @ParameterizedTest
+    void disallowsIncompleteMessagesDueToMissingVarDataInNestedGroup(
+        final int bCount,
+        final int dCount,
+        final String expectedState)
+    {
+        final NestedGroupWithVarLengthEncoder encoder = new NestedGroupWithVarLengthEncoder()
+            .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+        encoder.a(1).bCount(bCount).next().c(2).dCount(dCount).next().e(10);
+        final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, encoder::checkEncodingIsComplete);
+        assertThat(exception.getMessage(), containsString("Not fully encoded, current state: " + expectedState));
+    }
+
     private void modifyHeaderToLookLikeVersion0()
     {
         messageHeaderDecoder.wrap(buffer, OFFSET);

From f0958de089e47fa923337214028dadc460c7e6ad Mon Sep 17 00:00:00 2001
From: Zach Bray 
Date: Mon, 26 Jun 2023 18:56:02 +0100
Subject: [PATCH 28/50] [Java] Initialise buffer bytes before encoding in
 tests.

Check that null checks are actually working, rather than just returning
0s from the buffer.

Also, fix usage of bitsets to always set all options.
---
 .../sbe/FieldAccessOrderCheckTest.java        | 28 ++++++++++++-------
 1 file changed, 18 insertions(+), 10 deletions(-)

diff --git a/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldAccessOrderCheckTest.java b/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldAccessOrderCheckTest.java
index cbbe250187..0b55ca3d6e 100644
--- a/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldAccessOrderCheckTest.java
+++ b/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldAccessOrderCheckTest.java
@@ -21,12 +21,14 @@
 import org.agrona.MutableDirectBuffer;
 import org.agrona.concurrent.UnsafeBuffer;
 import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.CsvSource;
 
 import java.nio.charset.StandardCharsets;
+import java.util.Random;
 
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.*;
@@ -48,6 +50,12 @@ static void assumeDebugMode()
         assumeFalse(productionMode);
     }
 
+    @BeforeEach
+    void setUp()
+    {
+        new Random().nextBytes(buffer.byteArray());
+    }
+
     @Test
     void allowsEncodingAndDecodingVariableLengthFieldsInSchemaDefinedOrder()
     {
@@ -1878,7 +1886,7 @@ void allowsNewDecoderToDecodeAddedBitSetFieldBeforeGroup()
     {
         final AddBitSetBeforeGroupV1Encoder encoder = new AddBitSetBeforeGroupV1Encoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
-        encoder.a(1).d().guacamole(true).cheese(true);
+        encoder.a(1).d().guacamole(true).cheese(true).sourCream(false);
         encoder.bCount(1).next().c(2);
         encoder.checkEncodingIsComplete();
 
@@ -1919,7 +1927,7 @@ void allowsNewDecoderToSkipPresentButAddedBitSetFieldBeforeGroup()
     {
         final AddBitSetBeforeGroupV1Encoder encoder = new AddBitSetBeforeGroupV1Encoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
-        encoder.a(1).d().guacamole(true).cheese(true);
+        encoder.a(1).d().guacamole(true).cheese(true).sourCream(false);
         encoder.bCount(1).next().c(2);
         encoder.checkEncodingIsComplete();
 
@@ -1936,7 +1944,7 @@ void allowsOldDecoderToSkipAddedBitSetFieldBeforeGroup()
     {
         final AddBitSetBeforeGroupV1Encoder encoder = new AddBitSetBeforeGroupV1Encoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
-        encoder.a(1).d().guacamole(true).cheese(true);
+        encoder.a(1).d().guacamole(true).cheese(true).sourCream(false);
         encoder.bCount(1).next().c(2);
         encoder.checkEncodingIsComplete();
 
@@ -2030,10 +2038,10 @@ void allowsEncodingAndDecodingBitSetInsideGroupInSchemaDefinedOrder()
     {
         final BitSetInsideGroupEncoder encoder = new BitSetInsideGroupEncoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
-        encoder.a().cheese(true).guacamole(true);
+        encoder.a().cheese(true).guacamole(true).sourCream(false);
         encoder.bCount(1)
             .next()
-            .c().sourCream(true);
+            .c().cheese(false).guacamole(false).sourCream(true);
         encoder.checkEncodingIsComplete();
 
         final BitSetInsideGroupDecoder decoder = new BitSetInsideGroupDecoder()
@@ -2056,7 +2064,7 @@ void disallowsEncodingBitSetInsideGroupBeforeCallingNext()
     {
         final BitSetInsideGroupEncoder encoder = new BitSetInsideGroupEncoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
-        encoder.a().cheese(true).guacamole(true);
+        encoder.a().cheese(true).guacamole(true).sourCream(false);
         final BitSetInsideGroupEncoder.BEncoder bEncoder = encoder.bCount(1);
         final Exception exception =
             assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, bEncoder::c);
@@ -2068,10 +2076,10 @@ void disallowsDecodingBitSetInsideGroupBeforeCallingNext()
     {
         final BitSetInsideGroupEncoder encoder = new BitSetInsideGroupEncoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
-        encoder.a().cheese(true).guacamole(true);
+        encoder.a().cheese(true).guacamole(true).sourCream(false);
         encoder.bCount(1)
             .next()
-            .c().sourCream(true);
+            .c().guacamole(false).cheese(false).sourCream(true);
         encoder.checkEncodingIsComplete();
 
         final BitSetInsideGroupDecoder decoder = new BitSetInsideGroupDecoder()
@@ -2092,10 +2100,10 @@ void allowsReEncodingTopLevelBitSetViaReWrap()
     {
         final BitSetInsideGroupEncoder encoder = new BitSetInsideGroupEncoder()
             .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
-        encoder.a().cheese(true).guacamole(true);
+        encoder.a().cheese(true).guacamole(true).sourCream(false);
         encoder.bCount(1)
             .next()
-            .c().sourCream(true);
+            .c().cheese(false).guacamole(false).sourCream(true);
 
         encoder.a().sourCream(true);
         encoder.checkEncodingIsComplete();

From 1faedbbbcbd5d3b456141291c889431ef280b5a8 Mon Sep 17 00:00:00 2001
From: Zach Bray 
Date: Mon, 26 Jun 2023 19:14:08 +0100
Subject: [PATCH 29/50] [C#] Ignore personal Rider file.

---
 .gitignore | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.gitignore b/.gitignore
index 117022a886..ebdaada109 100644
--- a/.gitignore
+++ b/.gitignore
@@ -105,6 +105,7 @@ csharp/sbe-generated/mktdata/*.cs
 csharp/sbe-generated/uk_co_real_logic_sbe_benchmarks_fix
 csharp/sbe-tests/*.sbe
 csharp/nuget/
+csharp/csharp.sln.DotSettings.user
 
 # rust
 rust/target

From 3885a63d3103ac52406c660147ee929fa9aaee63 Mon Sep 17 00:00:00 2001
From: Zach Bray 
Date: Mon, 26 Jun 2023 19:00:41 +0100
Subject: [PATCH 30/50] [C#] Fix bug in string encoding method.

Previously it would generate something like:

```
buffer.PutByte(someOffset, (ushort) someValue);
//                         ^^^^^^^^
```

Where the source value was too wide for the destination.
---
 .../co/real_logic/sbe/generation/csharp/CSharpGenerator.java  | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java
index 5a607a8414..acb9821618 100644
--- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java
+++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java
@@ -513,9 +513,9 @@ private CharSequence generateVarData(final List tokens, final String inde
                         indent + INDENT + "int byteCount = _buffer.SetBytesFromString(encoding, value, " +
                         "limit + sizeOfLengthField);\n" +
                         indent + INDENT + "_parentMessage.Limit = limit + sizeOfLengthField + byteCount;\n" +
-                        indent + INDENT + "_buffer.%3$sPut%4$s(limit, (ushort)byteCount);\n" +
+                        indent + INDENT + "_buffer.%3$sPut%4$s(limit, (%5$s)byteCount);\n" +
                         indent + "}\n",
-                        propertyName, sizeOfLengthField, lengthTypePrefix, byteOrderStr));
+                        propertyName, sizeOfLengthField, lengthTypePrefix, byteOrderStr, lengthCSharpType));
                 }
             }
         }

From 8f74af90b6c1edad5af4419c75d43075b16b1682 Mon Sep 17 00:00:00 2001
From: Zach Bray 
Date: Mon, 26 Jun 2023 19:06:59 +0100
Subject: [PATCH 31/50] [C#] Port field access order tests.

In this commit, I've ported most of the tests from the Java suite of
tests for field access order checking to CSharp.

To get the tests to compile, I had to generate an empty method stub for
`CheckEncodingIsComplete()` on the codecs.

Some generated APIs are different, e.g., around strings and arrays.
Therefore, test cases and their numbers differ slightly in some places.
---
 .gitignore                                    |    1 +
 build.gradle                                  |    1 +
 .../sbe-tests/FieldAccessOrderCheckTests.cs   | 3092 +++++++++++++++++
 .../generation/csharp/CSharpGenerator.java    |   34 +-
 4 files changed, 3123 insertions(+), 5 deletions(-)
 create mode 100644 csharp/sbe-tests/FieldAccessOrderCheckTests.cs

diff --git a/.gitignore b/.gitignore
index ebdaada109..5d674b6f04 100644
--- a/.gitignore
+++ b/.gitignore
@@ -101,6 +101,7 @@ csharp/sbe-generated/issue483
 csharp/sbe-generated/issue560
 csharp/sbe-generated/issue661
 csharp/sbe-generated/since-deprecated
+csharp/sbe-generated/order_check
 csharp/sbe-generated/mktdata/*.cs
 csharp/sbe-generated/uk_co_real_logic_sbe_benchmarks_fix
 csharp/sbe-tests/*.sbe
diff --git a/build.gradle b/build.gradle
index 7f0c5264aa..1ccb8e95e7 100644
--- a/build.gradle
+++ b/build.gradle
@@ -739,6 +739,7 @@ tasks.register('generateCSharpCodecsTests', JavaExec) {
             'sbe-tool/src/test/resources/issue560.xml',
             'sbe-tool/src/test/resources/since-deprecated-test-schema.xml',
             'sbe-tool/src/test/resources/example-bigendian-test-schema.xml',
+            'sbe-tool/src/test/resources/field-order-check-schema.xml',
             'sbe-benchmarks/src/main/resources/fix-message-samples.xml']
 }
 
diff --git a/csharp/sbe-tests/FieldAccessOrderCheckTests.cs b/csharp/sbe-tests/FieldAccessOrderCheckTests.cs
new file mode 100644
index 0000000000..039ac759f1
--- /dev/null
+++ b/csharp/sbe-tests/FieldAccessOrderCheckTests.cs
@@ -0,0 +1,3092 @@
+using System;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Order.Check;
+using Org.SbeTool.Sbe.Dll;
+
+namespace Org.SbeTool.Sbe.Tests
+{
+    [TestClass, Ignore]
+    public class FieldAccessOrderCheckTests
+    {
+        private const int Offset = 0;
+        private readonly DirectBuffer _buffer = new DirectBuffer();
+        private readonly MessageHeader _messageHeader = new MessageHeader();
+
+        [TestInitialize]
+        public void SetUp()
+        {
+            var byteArray = new byte[128];
+            new Random().NextBytes(byteArray);
+            _buffer.Wrap(byteArray);
+        }
+
+        [TestMethod]
+        public void AllowsEncodingAndDecodingVariableLengthFieldsInSchemaDefinedOrder()
+        {
+            var encoder = new MultipleVarLength();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 42;
+            encoder.SetB("abc");
+            encoder.SetC("def");
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new MultipleVarLength();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(42, decoder.A);
+            Assert.AreEqual("abc", decoder.GetB());
+            Assert.AreEqual("def", decoder.GetC());
+            Assert.IsTrue(decoder.ToString().Contains("A=42|B='abc'|C='def'"));
+        }
+
+        [TestMethod]
+        public void AllowsReEncodingTopLevelPrimitiveFields()
+        {
+            var encoder = new MultipleVarLength();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 42;
+            encoder.SetB("abc");
+            encoder.SetC("def");
+            encoder.A = 43;
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new MultipleVarLength();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(43, decoder.A);
+            Assert.AreEqual("abc", decoder.GetB());
+            Assert.AreEqual("def", decoder.GetC());
+        }
+
+        [TestMethod]
+        public void DisallowsSkippingEncodingOfVariableLengthField1()
+        {
+            var encoder = new MultipleVarLength();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 42;
+            var exception = Assert.ThrowsException(() => encoder.SetC("def"));
+            Assert.IsTrue(exception.Message.Contains("Cannot access field \"c\" in state: V0_BLOCK"));
+        }
+
+        [TestMethod]
+        public void DisallowsSkippingEncodingOfVariableLengthField2()
+        {
+            var encoder = new MultipleVarLength();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 42;
+            var def = Encoding.ASCII.GetBytes("def");
+            var exception = Assert.ThrowsException(() => encoder.SetC(def));
+            Assert.IsTrue(exception.Message.Contains("Cannot access field \"c\" in state: V0_BLOCK"));
+        }
+
+        [TestMethod]
+        public void DisallowsReEncodingEarlierVariableLengthFields()
+        {
+            var encoder = new MultipleVarLength();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 42;
+            encoder.SetB("abc");
+            encoder.SetC("def");
+            var exception = Assert.ThrowsException(() => encoder.SetB("ghi"));
+            Assert.IsTrue(exception.Message.Contains("Cannot access field \"b\" in state: V0_C_DONE"));
+        }
+
+        [TestMethod]
+        public void DisallowsReEncodingLatestVariableLengthField()
+        {
+            var encoder = new MultipleVarLength();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 42;
+            encoder.SetB("abc");
+            encoder.SetC("def");
+            var exception = Assert.ThrowsException(() => encoder.SetC("ghi"));
+            Assert.IsTrue(exception.Message.Contains("Cannot access field \"c\" in state: V0_C_DONE"));
+        }
+
+        [TestMethod]
+        public void DisallowsSkippingDecodingOfVariableLengthField1()
+        {
+            var decoder = DecodeUntilVarLengthFields();
+
+            var exception = Assert.ThrowsException(() => decoder.GetC());
+            Assert.IsTrue(exception.Message.Contains("Cannot access field \"c\" in state: V0_BLOCK"));
+        }
+
+        [TestMethod]
+        public void DisallowsSkippingDecodingOfVariableLengthField2()
+        {
+            var decoder = DecodeUntilVarLengthFields();
+
+            var exception = Assert.ThrowsException(() => decoder.GetCBytes());
+            Assert.IsTrue(exception.Message.Contains("Cannot access field \"c\" in state: V0_BLOCK"));
+        }
+
+        [TestMethod]
+        public void DisallowsSkippingDecodingOfVariableLengthField3()
+        {
+            var decoder = DecodeUntilVarLengthFields();
+
+            var exception = Assert.ThrowsException(() => decoder.GetC(new byte[8]));
+            Assert.IsTrue(exception.Message.Contains("Cannot access field \"c\" in state: V0_BLOCK"));
+        }
+
+        [TestMethod]
+        public void DisallowsReDecodingEarlierVariableLengthField()
+        {
+            var decoder = DecodeUntilVarLengthFields();
+            Assert.AreEqual("abc", decoder.GetB());
+            Assert.AreEqual("def", decoder.GetC());
+
+            var exception = Assert.ThrowsException(() => decoder.GetB());
+            Assert.IsTrue(exception.Message.Contains("Cannot access field \"b\" in state: V0_C_DONE"));
+        }
+
+        [TestMethod]
+        public void DisallowsReDecodingLatestVariableLengthField()
+        {
+            var decoder = DecodeUntilVarLengthFields();
+            Assert.AreEqual("abc", decoder.GetB());
+            Assert.AreEqual("def", decoder.GetC());
+
+            var exception = Assert.ThrowsException(() => decoder.GetC());
+            Assert.IsTrue(exception.Message.Contains("Cannot access field \"c\" in state: V0_C_DONE"));
+        }
+
+        private MultipleVarLength DecodeUntilVarLengthFields()
+        {
+            var encoder = new MultipleVarLength();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 42;
+            encoder.SetB("abc");
+            encoder.SetC("def");
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new MultipleVarLength();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(42, decoder.A);
+
+            return decoder;
+        }
+
+        [TestMethod]
+        public void AllowsEncodingAndDecodingGroupAndVariableLengthFieldsInSchemaDefinedOrder()
+        {
+            var encoder = new GroupAndVarLength();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 42;
+            var bEncoder = encoder.BCount(2);
+            bEncoder.Next().C = 1;
+            bEncoder.Next().C = 2;
+            encoder.SetD("abc");
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new GroupAndVarLength();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(42, decoder.A);
+            var bDecoder = decoder.B;
+            Assert.AreEqual(2, bDecoder.Count);
+            Assert.AreEqual(1, bDecoder.Next().C);
+            Assert.AreEqual(2, bDecoder.Next().C);
+            Assert.AreEqual("abc", decoder.GetD());
+            Assert.IsTrue(decoder.ToString().Contains("A=42|B=[(C=1),(C=2)]|D='abc'"));
+        }
+
+        [TestMethod]
+        public void AllowsEncodingAndDecodingEmptyGroupAndVariableLengthFieldsInSchemaDefinedOrder()
+        {
+            var encoder = new GroupAndVarLength()
+                .WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 42;
+            encoder.BCount(0);
+            encoder.SetD("abc");
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new GroupAndVarLength()
+                .WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(42, decoder.A);
+            var bs = decoder.B;
+            Assert.AreEqual(0, bs.Count);
+            Assert.AreEqual("abc", decoder.GetD());
+            Assert.IsTrue(decoder.ToString().Contains("A=42|B=[]|D='abc'"));
+        }
+
+        [TestMethod]
+        public void DisallowsEncodingGroupElementBeforeCallingNext()
+        {
+            var encoder = new GroupAndVarLength()
+                .WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 42;
+            var bEncoder = encoder.BCount(1);
+            var exception = Assert.ThrowsException(() => bEncoder.C = 1);
+            Assert.IsTrue(exception.Message.Contains("Cannot access field \"b.c\" in state: V0_B_N"));
+        }
+
+        [TestMethod]
+        public void DisallowsDecodingGroupElementBeforeCallingNext()
+        {
+            var encoder = new GroupAndVarLength()
+                .WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 42;
+            var bEncoder = encoder.BCount(2);
+            bEncoder
+                .Next()
+                .C = 1;
+            bEncoder.Next()
+                .C = 2;
+            encoder.SetD("abc");
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new GroupAndVarLength()
+                .WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(42, decoder.A);
+            var bs = decoder.B;
+            Assert.AreEqual(2, bs.Count);
+            var exception = Assert.ThrowsException(() => _ = bs.C);
+            Assert.IsTrue(exception.Message.Contains("Cannot access field \"b.c\" in state: V0_B_N"));
+        }
+
+        [TestMethod]
+        public void DisallowsSkippingEncodingOfGroup()
+        {
+            var encoder = new GroupAndVarLength()
+                .WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 42;
+            var exception = Assert.ThrowsException(() => encoder.SetD("abc"));
+            Assert.IsTrue(exception.Message.Contains("Cannot access field \"d\" in state: V0_BLOCK"));
+        }
+
+        [TestMethod]
+        public void DisallowsReEncodingVariableLengthFieldAfterGroup()
+        {
+            var encoder = new GroupAndVarLength()
+                .WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 42;
+            var bEncoder = encoder.BCount(2);
+            bEncoder
+                .Next()
+                .C = 1;
+            bEncoder.Next()
+                .C = 2;
+            encoder.SetD("abc");
+            var exception = Assert.ThrowsException(() => encoder.SetD("def"));
+            Assert.IsTrue(exception.Message.Contains("Cannot access field \"d\" in state: V0_D_DONE"));
+        }
+
+        [TestMethod]
+        public void DisallowsReEncodingGroupCount()
+        {
+            var encoder = new GroupAndVarLength()
+                .WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 42;
+            var bEncoder = encoder.BCount(2);
+            bEncoder
+                .Next()
+                .C = 1;
+            bEncoder.Next()
+                .C = 2;
+            encoder.SetD("abc");
+            var exception = Assert.ThrowsException(() => encoder.BCount(1));
+            Assert.IsTrue(
+                exception.Message.Contains("Cannot encode count of repeating group \"b\" in state: V0_D_DONE"));
+        }
+
+        [TestMethod]
+        public void DisallowsMissedDecodingOfGroupBeforeVariableLengthField()
+        {
+            var encoder = new GroupAndVarLength()
+                .WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 42;
+            var bEncoder = encoder.BCount(2);
+            bEncoder
+                .Next()
+                .C = 1;
+            bEncoder
+                .Next()
+                .C = 2;
+            encoder.SetD("abc");
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new GroupAndVarLength()
+                .WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(42, decoder.A);
+            var exception = Assert.ThrowsException(() => decoder.GetD());
+            Assert.IsTrue(exception.Message.Contains("Cannot access field \"d\" in state: V0_BLOCK"));
+        }
+
+        [TestMethod]
+        public void DisallowsReDecodingVariableLengthFieldAfterGroup()
+        {
+            var encoder = new GroupAndVarLength()
+                .WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 42;
+            var bEncoder = encoder.BCount(2);
+            bEncoder
+                .Next()
+                .C = 1;
+            bEncoder
+                .Next()
+                .C = 2;
+            encoder.SetD("abc");
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new GroupAndVarLength()
+                .WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(42, decoder.A);
+            var bs = decoder.B;
+            Assert.AreEqual(2, bs.Count);
+            Assert.AreEqual(1, bs.Next().C);
+            Assert.AreEqual(2, bs.Next().C);
+            Assert.AreEqual("abc", decoder.GetD());
+            var exception = Assert.ThrowsException(() => decoder.GetD());
+            Assert.IsTrue(exception.Message.Contains("Cannot access field \"d\" in state: V0_D_DONE"));
+        }
+
+        [TestMethod]
+        public void DisallowsReDecodingGroupAfterVariableLengthField()
+        {
+            var encoder = new GroupAndVarLength()
+                .WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 42;
+            var bEncoder = encoder.BCount(2);
+            bEncoder
+                .Next()
+                .C = 1;
+            bEncoder
+                .Next()
+                .C = 2;
+            encoder.SetD("abc");
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new GroupAndVarLength()
+                .WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(42, decoder.A);
+            var bs = decoder.B;
+            Assert.AreEqual(2, bs.Count);
+            Assert.AreEqual(1, bs.Next().C);
+            Assert.AreEqual(2, bs.Next().C);
+            Assert.AreEqual("abc", decoder.GetD());
+            var exception = Assert.ThrowsException(() => decoder.B);
+            Assert.IsTrue(
+                exception.Message.Contains("Cannot decode count of repeating group \"b\" in state: V0_D_DONE"));
+        }
+
+        [TestMethod]
+        public void AllowsEncodingAndDecodingVariableLengthFieldInsideGroupInSchemaDefinedOrder()
+        {
+            var encoder = new VarLengthInsideGroup()
+                .WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 42;
+            var bEncoder = encoder.BCount(2);
+            bEncoder
+                .Next()
+                .C = 1;
+            bEncoder
+                .SetD("abc");
+            bEncoder
+                .Next()
+                .C = 2;
+            bEncoder
+                .SetD("def");
+            encoder.SetE("ghi");
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new VarLengthInsideGroup()
+                .WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(42, decoder.A);
+            var bs = decoder.B;
+            Assert.AreEqual(2, bs.Count);
+            Assert.AreEqual(1, bs.Next().C);
+            Assert.AreEqual("abc", bs.GetD());
+            Assert.AreEqual(2, bs.Next().C);
+            Assert.AreEqual("def", bs.GetD());
+            Assert.AreEqual("ghi", decoder.GetE());
+            Assert.IsTrue(decoder.ToString().Contains("A=42|B=[(C=1|D='abc'),(C=2|D='def')]|E='ghi'"));
+        }
+
+        [TestMethod]
+        public void DisallowsMissedGroupElementVariableLengthFieldToEncodeAtTopLevel()
+        {
+            var encoder = new VarLengthInsideGroup()
+                .WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 42;
+            encoder.BCount(1).Next().C = 1;
+            var exception = Assert.ThrowsException(() => encoder.SetE("abc"));
+            Assert.IsTrue(exception.Message.Contains("Cannot access field \"e\" in state: V0_B_1_BLOCK"));
+        }
+
+        [TestMethod]
+        public void DisallowsMissedGroupElementVariableLengthFieldToEncodeNextElement()
+        {
+            var encoder = new VarLengthInsideGroup()
+                .WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 42;
+            var bEncoder = encoder.BCount(2).Next();
+            var exception = Assert.ThrowsException(() => bEncoder.Next());
+            Assert.IsTrue(
+                exception.Message.Contains(
+                    "Cannot access next element in repeating group \"b\" in state: V0_B_N_BLOCK"));
+        }
+
+        [TestMethod]
+        public void DisallowsMissedGroupElementEncoding()
+        {
+            var encoder = new VarLengthInsideGroup()
+                .WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 42;
+            var bEncoder = encoder.BCount(2);
+            bEncoder.Next().C = 1;
+            bEncoder.SetD("abc");
+            var exception = Assert.ThrowsException(() => encoder.SetE("abc"));
+            Assert.IsTrue(exception.Message.Contains("Cannot access field \"e\" in state: V0_B_N_D_DONE"));
+        }
+
+        [TestMethod]
+        public void DisallowsReEncodingGroupElementVariableLengthField()
+        {
+            var encoder = new VarLengthInsideGroup()
+                .WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 42;
+            var bEncoder = encoder.BCount(1).Next();
+            bEncoder.C = 1;
+            bEncoder.SetD("abc");
+            encoder.SetE("def");
+            var exception = Assert.ThrowsException(() => bEncoder.SetD("ghi"));
+            Assert.IsTrue(exception.Message.Contains("Cannot access field \"b.d\" in state: V0_E_DONE"));
+        }
+
+        [TestMethod]
+        public void DisallowsReDecodingGroupElementVariableLengthField()
+        {
+            var encoder = new VarLengthInsideGroup()
+                .WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 42;
+            var bEncoder = encoder.BCount(2).Next();
+            bEncoder.C = 1;
+            bEncoder.SetD("abc");
+            bEncoder.Next().C = 2;
+            bEncoder.SetD("def");
+            encoder.SetE("ghi");
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new VarLengthInsideGroup()
+                .WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(42, decoder.A);
+            var bDecoder = decoder.B;
+            Assert.AreEqual(2, bDecoder.Count);
+            Assert.AreEqual(1, bDecoder.Next().C);
+            Assert.AreEqual("abc", bDecoder.GetD());
+            var exception = Assert.ThrowsException(() => bDecoder.GetD());
+            Assert.IsTrue(exception.Message.Contains("Cannot access field \"b.d\" in state: V0_B_N_D_DONE"));
+        }
+
+        [TestMethod]
+        public void DisallowsMissedDecodingOfGroupElementVariableLengthFieldToNextElement()
+        {
+            var encoder = new VarLengthInsideGroup()
+                .WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 42;
+            var bEncoder = encoder.BCount(2).Next();
+            bEncoder.C = 1;
+            bEncoder.SetD("abc");
+            bEncoder.Next().C = 2;
+            bEncoder.SetD("def");
+            encoder.SetE("ghi");
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new VarLengthInsideGroup()
+                .WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(42, decoder.A);
+            var bDecoder = decoder.B;
+            Assert.AreEqual(2, bDecoder.Count);
+            Assert.AreEqual(1, bDecoder.Next().C);
+            var exception = Assert.ThrowsException(() => bDecoder.Next());
+            Assert.IsTrue(
+                exception.Message.Contains(
+                    "Cannot access next element in repeating group \"b\" in state: V0_B_N_BLOCK"));
+        }
+
+        [TestMethod]
+        public void DisallowsMissedDecodingOfGroupElementVariableLengthFieldToTopLevel()
+        {
+            var encoder = new VarLengthInsideGroup()
+                .WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 42;
+            var bEncoder = encoder.BCount(1).Next();
+            bEncoder.C = 1;
+            bEncoder.SetD("abc");
+            encoder.SetE("ghi");
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new VarLengthInsideGroup()
+                .WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(42, decoder.A);
+            var bDecoder = decoder.B;
+            Assert.AreEqual(1, bDecoder.Count);
+            Assert.AreEqual(1, bDecoder.Next().C);
+            var exception = Assert.ThrowsException(() => decoder.GetE());
+            Assert.IsTrue(exception.Message.Contains("Cannot access field \"e\" in state: V0_B_1_BLOCK"));
+        }
+
+        [TestMethod]
+        public void DisallowsMissedDecodingOfGroupElement()
+        {
+            var encoder = new VarLengthInsideGroup()
+                .WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 42;
+            var bEncoder = encoder.BCount(2).Next();
+            bEncoder.C = 1;
+            bEncoder.SetD("abc");
+            bEncoder.Next().C = 2;
+            bEncoder.SetD("def");
+            encoder.SetE("ghi");
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new VarLengthInsideGroup()
+                .WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(42, decoder.A);
+            var bDecoder = decoder.B;
+            Assert.AreEqual(2, bDecoder.Count);
+            Assert.AreEqual(1, bDecoder.Next().C);
+            var exception = Assert.ThrowsException(() => decoder.GetE());
+            Assert.IsTrue(exception.Message.Contains("Cannot access field \"e\" in state: V0_B_N_BLOCK"));
+        }
+
+        [TestMethod]
+        public void AllowsEncodingNestedGroupsInSchemaDefinedOrder()
+        {
+            var encoder = new NestedGroups().WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 42;
+            var bEncoder = encoder.BCount(2).Next();
+            bEncoder.C = 1;
+            var dEncoder = bEncoder.DCount(2).Next();
+            dEncoder.E = 2;
+            dEncoder = dEncoder.Next();
+            dEncoder.E = 3;
+            var fEncoder = bEncoder.FCount(1).Next();
+            fEncoder.G = 4;
+            bEncoder = bEncoder.Next();
+            bEncoder.C = 5;
+            dEncoder = bEncoder.DCount(1).Next();
+            dEncoder.E = 6;
+            fEncoder = bEncoder.FCount(1).Next();
+            fEncoder.G = 7;
+            var hEncoder = encoder.HCount(1).Next();
+            hEncoder.I = 8;
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new NestedGroups().WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(42, decoder.A);
+            var bDecoder = decoder.B;
+            Assert.AreEqual(2, bDecoder.Count);
+            var b0 = bDecoder.Next();
+            Assert.AreEqual(1, b0.C);
+            var b0ds = b0.D;
+            Assert.AreEqual(2, b0ds.Count);
+            Assert.AreEqual(2, b0ds.Next().E);
+            Assert.AreEqual(3, b0ds.Next().E);
+            var b0Fs = b0.F;
+            Assert.AreEqual(1, b0Fs.Count);
+            Assert.AreEqual(4, b0Fs.Next().G);
+            var b1 = bDecoder.Next();
+            Assert.AreEqual(5, b1.C);
+            var b1ds = b1.D;
+            Assert.AreEqual(1, b1ds.Count);
+            Assert.AreEqual(6, b1ds.Next().E);
+            var b1Fs = b1.F;
+            Assert.AreEqual(1, b1Fs.Count);
+            Assert.AreEqual(7, b1Fs.Next().G);
+            var hs = decoder.H;
+            Assert.AreEqual(1, hs.Count);
+            Assert.AreEqual(8, hs.Next().I);
+            Assert.IsTrue(decoder.ToString()
+                .Contains("A=42|B=[(C=1|D=[(E=2),(E=3)]|F=[(G=4)]),(C=5|D=[(E=6)]|F=[(G=7)])]|H=[(I=8)]"));
+        }
+
+        [TestMethod]
+        public void AllowsEncodingEmptyNestedGroupsInSchemaDefinedOrder()
+        {
+            var encoder = new NestedGroups().WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 42;
+            encoder.BCount(0);
+            encoder.HCount(0);
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new NestedGroups().WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(42, decoder.A);
+            var bDecoder = decoder.B;
+            Assert.AreEqual(0, bDecoder.Count);
+            var hDecoder = decoder.H;
+            Assert.AreEqual(0, hDecoder.Count);
+            Assert.IsTrue(decoder.ToString().Contains("A=42|B=[]|H=[]"));
+        }
+
+        [TestMethod]
+        public void DisallowsMissedEncodingOfNestedGroup()
+        {
+            var encoder = new NestedGroups().WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 42;
+            var bEncoder = encoder.BCount(1).Next();
+            bEncoder.C = 1;
+            Exception exception = Assert.ThrowsException(() => bEncoder.FCount(1));
+            Assert.IsTrue(
+                exception.Message.Contains("Cannot encode count of repeating group \"b.f\" in state: V0_B_1_BLOCK"));
+        }
+
+        [TestMethod]
+        public void AllowsEncodingAndDecodingCompositeInsideGroupInSchemaDefinedOrder()
+        {
+            var encoder = new CompositeInsideGroup().WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            var aEncoder = encoder.A;
+            aEncoder.X = 1;
+            aEncoder.Y = 2;
+            var bEncoder = encoder.BCount(1).Next();
+            var cEncoder = bEncoder.C;
+            cEncoder.X = 3;
+            cEncoder.Y = 4;
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new CompositeInsideGroup().WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            var aDecoder = decoder.A;
+            Assert.AreEqual(1, aDecoder.X);
+            Assert.AreEqual(2, aDecoder.Y);
+            var bDecoder = decoder.B;
+            Assert.AreEqual(1, bDecoder.Count);
+            var cDecoder = bDecoder.Next().C;
+            Assert.AreEqual(3, cDecoder.X);
+            Assert.AreEqual(4, cDecoder.Y);
+            Assert.IsTrue(decoder.ToString().Contains("A=(X=1|Y=2)|B=[(C=(X=3|Y=4))]"));
+        }
+
+        [TestMethod]
+        public void DisallowsEncodingCompositeInsideGroupBeforeCallingNext()
+        {
+            var encoder = new CompositeInsideGroup().WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            var aEncoder = encoder.A;
+            aEncoder.X = 1;
+            aEncoder.Y = 2;
+            var bEncoder = encoder.BCount(1);
+            Exception exception = Assert.ThrowsException(() => bEncoder.C);
+            Assert.IsTrue(exception.Message.Contains("Cannot access field \"b.c\" in state: V0_B_N"));
+        }
+
+        [TestMethod]
+        public void AllowsReEncodingTopLevelCompositeViaReWrap()
+        {
+            var encoder = new CompositeInsideGroup();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            var aEncoder = encoder.A;
+            aEncoder.X = 1;
+            aEncoder.Y = 2;
+            var bEncoder = encoder.BCount(1).Next();
+            bEncoder.C.X = 3;
+            bEncoder.C.Y = 4;
+            aEncoder = encoder.A;
+            aEncoder.X = 5;
+            aEncoder.Y = 6;
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new CompositeInsideGroup();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            var a = decoder.A;
+            Assert.AreEqual(5, a.X);
+            Assert.AreEqual(6, a.Y);
+            var b = decoder.B;
+            Assert.AreEqual(1, b.Count);
+            var c = b.Next().C;
+            Assert.AreEqual(3, c.X);
+            Assert.AreEqual(4, c.Y);
+        }
+
+        [TestMethod]
+        public void AllowsReEncodingTopLevelCompositeViaEncoderReference()
+        {
+            var encoder = new CompositeInsideGroup();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            var aEncoder = encoder.A;
+            aEncoder.X = 1;
+            aEncoder.Y = 2;
+            var bEncoder = encoder.BCount(1).Next();
+            bEncoder.C.X = 3;
+            bEncoder.C.Y = 4;
+            aEncoder.X = 5;
+            aEncoder.Y = 6;
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new CompositeInsideGroup();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            var a = decoder.A;
+            Assert.AreEqual(5, a.X);
+            Assert.AreEqual(6, a.Y);
+            var b = decoder.B;
+            Assert.AreEqual(1, b.Count);
+            var c = b.Next().C;
+            Assert.AreEqual(3, c.X);
+            Assert.AreEqual(4, c.Y);
+        }
+
+        [TestMethod]
+        public void AllowsReEncodingGroupElementCompositeViaReWrap()
+        {
+            var encoder = new CompositeInsideGroup();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            var aEncoder = encoder.A;
+            aEncoder.X = 1;
+            aEncoder.Y = 2;
+            var bEncoder = encoder.BCount(1).Next();
+            var cEncoder = bEncoder.C;
+            cEncoder.X = 3;
+            cEncoder.Y = 4;
+            cEncoder = bEncoder.C;
+            cEncoder.X = 5;
+            cEncoder.Y = 6;
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new CompositeInsideGroup();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            var a = decoder.A;
+            Assert.AreEqual(1, a.X);
+            Assert.AreEqual(2, a.Y);
+            var b = decoder.B;
+            Assert.AreEqual(1, b.Count);
+            var c = b.Next().C;
+            Assert.AreEqual(5, c.X);
+            Assert.AreEqual(6, c.Y);
+        }
+
+        [TestMethod]
+        public void AllowsReEncodingGroupElementCompositeViaEncoderReference()
+        {
+            var encoder = new CompositeInsideGroup();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            var aEncoder = encoder.A;
+            aEncoder.X = 1;
+            aEncoder.Y = 2;
+            var bEncoder = encoder.BCount(1).Next();
+            var cEncoder = bEncoder.C;
+            cEncoder.X = 3;
+            cEncoder.Y = 4;
+            cEncoder.X = 5;
+            cEncoder.Y = 6;
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new CompositeInsideGroup();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            var a = decoder.A;
+            Assert.AreEqual(1, a.X);
+            Assert.AreEqual(2, a.Y);
+            var b = decoder.B;
+            Assert.AreEqual(1, b.Count);
+            var c = b.Next().C;
+            Assert.AreEqual(5, c.X);
+            Assert.AreEqual(6, c.Y);
+        }
+
+        [TestMethod]
+        public void AllowsReDecodingTopLevelCompositeViaReWrap()
+        {
+            var encoder = new CompositeInsideGroup();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            var aEncoder = encoder.A;
+            aEncoder.X = 1;
+            aEncoder.Y = 2;
+            var bEncoder = encoder.BCount(1).Next();
+            var cEncoder = bEncoder.C;
+            cEncoder.X = 3;
+            cEncoder.Y = 4;
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new CompositeInsideGroup();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            var a1 = decoder.A;
+            Assert.AreEqual(1, a1.X);
+            Assert.AreEqual(2, a1.Y);
+            var b = decoder.B;
+            Assert.AreEqual(1, b.Count);
+            var c = b.Next().C;
+            Assert.AreEqual(3, c.X);
+            Assert.AreEqual(4, c.Y);
+            var a2 = decoder.A;
+            Assert.AreEqual(1, a2.X);
+            Assert.AreEqual(2, a2.Y);
+        }
+
+        [TestMethod]
+        public void AllowsReDecodingTopLevelCompositeViaEncoderReference()
+        {
+            var encoder = new CompositeInsideGroup();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            var aEncoder = encoder.A;
+            aEncoder.X = 1;
+            aEncoder.Y = 2;
+            var bEncoder = encoder.BCount(1).Next();
+            var cEncoder = bEncoder.C;
+            cEncoder.X = 3;
+            cEncoder.Y = 4;
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new CompositeInsideGroup();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            var a = decoder.A;
+            Assert.AreEqual(1, a.X);
+            Assert.AreEqual(2, a.Y);
+            var b = decoder.B;
+            Assert.AreEqual(1, b.Count);
+            var c = b.Next().C;
+            Assert.AreEqual(3, c.X);
+            Assert.AreEqual(4, c.Y);
+            Assert.AreEqual(1, a.X);
+            Assert.AreEqual(2, a.Y);
+        }
+
+        [TestMethod]
+        public void AllowsReDecodingGroupElementCompositeViaEncoderReference()
+        {
+            var encoder = new CompositeInsideGroup();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            var aEncoder = encoder.A;
+            aEncoder.X = 1;
+            aEncoder.Y = 2;
+            var bEncoder = encoder.BCount(1).Next();
+            var cEncoder = bEncoder.C;
+            cEncoder.X = 3;
+            cEncoder.Y = 4;
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new CompositeInsideGroup();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            var a = decoder.A;
+            Assert.AreEqual(1, a.X);
+            Assert.AreEqual(2, a.Y);
+            var b = decoder.B;
+            Assert.AreEqual(1, b.Count);
+            var c = b.Next().C;
+            Assert.AreEqual(3, c.X);
+            Assert.AreEqual(4, c.Y);
+            Assert.AreEqual(3, c.X);
+            Assert.AreEqual(4, c.Y);
+        }
+
+        [TestMethod]
+        public void AllowsNewDecoderToDecodeAddedPrimitiveField()
+        {
+            var encoder = new AddPrimitiveV1();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            encoder.B = 2;
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new AddPrimitiveV1();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+            Assert.AreEqual(2, decoder.B);
+        }
+
+        [TestMethod]
+        public void AllowsNewDecoderToDecodeMissingPrimitiveFieldAsNullValue()
+        {
+            var encoder = new AddPrimitiveV0()
+                .WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            encoder.CheckEncodingIsComplete();
+
+            ModifyHeaderToLookLikeVersion0();
+
+            var decoder = new AddPrimitiveV1()
+                .WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+            Assert.AreEqual(AddPrimitiveV1.BNullValue, decoder.B);
+        }
+
+        [TestMethod]
+        public void AllowsNewDecoderToDecodeAddedPrimitiveFieldBeforeGroup()
+        {
+            var encoder = new AddPrimitiveBeforeGroupV1()
+                .WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            encoder.D = 3;
+            encoder.BCount(1).Next().C = 2;
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new AddPrimitiveBeforeGroupV1()
+                .WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+            Assert.AreEqual(3, decoder.D);
+            var b = decoder.B;
+            Assert.AreEqual(1, b.Count);
+            Assert.AreEqual(2, b.Next().C);
+        }
+
+        [TestMethod]
+        public void AllowsNewDecoderToDecodeMissingPrimitiveFieldBeforeGroupAsNullValue()
+        {
+            var encoder = new AddPrimitiveBeforeGroupV0()
+                .WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            encoder.BCount(1).Next().C = 2;
+            encoder.CheckEncodingIsComplete();
+
+            ModifyHeaderToLookLikeVersion0();
+
+            var decoder = new AddPrimitiveBeforeGroupV1()
+                .WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+            Assert.AreEqual(AddPrimitiveBeforeGroupV1.DNullValue, decoder.D);
+            var b = decoder.B;
+            Assert.AreEqual(1, b.Count);
+            Assert.AreEqual(2, b.Next().C);
+        }
+
+        [TestMethod]
+        public void AllowsNewDecoderToSkipPresentButAddedPrimitiveFieldBeforeGroup()
+        {
+            var encoder = new AddPrimitiveBeforeGroupV1()
+                .WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            encoder.D = 3;
+            encoder.BCount(1).Next().C = 2;
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new AddPrimitiveBeforeGroupV1()
+                .WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+            var b = decoder.B;
+            Assert.AreEqual(1, b.Count);
+            Assert.AreEqual(2, b.Next().C);
+        }
+
+        [TestMethod]
+        public void AllowsOldDecoderToSkipAddedPrimitiveFieldBeforeGroup()
+        {
+            var encoder = new AddPrimitiveBeforeGroupV1()
+                .WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            encoder.D = 3;
+            encoder.BCount(1).Next().C = 2;
+            encoder.CheckEncodingIsComplete();
+
+            ModifyHeaderToLookLikeVersion1();
+
+            var decoder = new AddPrimitiveBeforeGroupV0()
+                .WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+            var b = decoder.B;
+            Assert.AreEqual(1, b.Count);
+            Assert.AreEqual(2, b.Next().C);
+        }
+
+        [TestMethod]
+        public void AllowsNewDecoderToDecodeAddedPrimitiveFieldBeforeVarData()
+        {
+            var encoder = new AddPrimitiveBeforeVarDataV1()
+                .WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            encoder.C = 3;
+            encoder.SetB("abc");
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new AddPrimitiveBeforeVarDataV1()
+                .WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+            Assert.AreEqual(3, decoder.C);
+            Assert.AreEqual("abc", decoder.GetB());
+        }
+
+        [TestMethod]
+        public void AllowsNewDecoderToDecodeMissingPrimitiveFieldBeforeVarDataAsNullValue()
+        {
+            var encoder = new AddPrimitiveBeforeVarDataV0();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            encoder.SetB("abc");
+            encoder.CheckEncodingIsComplete();
+
+            ModifyHeaderToLookLikeVersion0();
+
+            var decoder = new AddPrimitiveBeforeVarDataV1();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+            Assert.AreEqual(AddPrimitiveBeforeVarDataV1.CNullValue, decoder.C);
+            Assert.AreEqual("abc", decoder.GetB());
+        }
+
+        [TestMethod]
+        public void AllowsNewDecoderToSkipPresentButAddedPrimitiveFieldBeforeVarData()
+        {
+            var encoder = new AddPrimitiveBeforeVarDataV1();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            encoder.C = 3;
+            encoder.SetB("abc");
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new AddPrimitiveBeforeVarDataV1();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+            Assert.AreEqual("abc", decoder.GetB());
+        }
+
+        [TestMethod]
+        public void AllowsOldDecoderToSkipAddedPrimitiveFieldBeforeVarData()
+        {
+            var encoder = new AddPrimitiveBeforeVarDataV1();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            encoder.C = 3;
+            encoder.SetB("abc");
+            encoder.CheckEncodingIsComplete();
+
+            ModifyHeaderToLookLikeVersion1();
+
+            var decoder = new AddPrimitiveBeforeVarDataV0();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+            Assert.AreEqual("abc", decoder.GetB());
+        }
+
+        [TestMethod]
+        public void AllowsNewDecoderToDecodeAddedPrimitiveFieldInsideGroup()
+        {
+            var encoder = new AddPrimitiveInsideGroupV1();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            var group1 = encoder.BCount(1);
+            group1.Next();
+            group1.C = 2;
+            group1.D = 3;
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new AddPrimitiveInsideGroupV1();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+            var group2 = decoder.B;
+            Assert.AreEqual(1, group2.Count);
+            Assert.AreEqual(2, group2.Next().C);
+            Assert.AreEqual(3, group2.D);
+        }
+
+
+        [TestMethod]
+        public void AllowsNewDecoderToDecodeMissingPrimitiveFieldInsideGroupAsNullValue()
+        {
+            var encoder = new AddPrimitiveInsideGroupV0();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            encoder.BCount(1).Next().C = 2;
+            encoder.CheckEncodingIsComplete();
+
+            ModifyHeaderToLookLikeVersion0();
+
+            var decoder = new AddPrimitiveInsideGroupV1();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+            var b = decoder.B;
+            Assert.AreEqual(1, b.Count);
+            Assert.AreEqual(2, b.Next().C);
+            Assert.AreEqual(AddPrimitiveInsideGroupV1.BGroup.DNullValue, b.D);
+        }
+
+        [TestMethod]
+        public void AllowsNewDecoderToSkipPresentButAddedPrimitiveFieldInsideGroup()
+        {
+            var encoder = new AddPrimitiveInsideGroupV1();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            var group = encoder.BCount(2);
+            group.Next().C = 2;
+            group.D = 3;
+            group.Next().C = 4;
+            group.D = 5;
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new AddPrimitiveInsideGroupV1();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+            var b = decoder.B;
+            Assert.AreEqual(2, b.Count);
+            Assert.AreEqual(2, b.Next().C);
+            Assert.AreEqual(4, b.Next().C);
+        }
+
+        [TestMethod]
+        public void AllowsOldDecoderToSkipAddedPrimitiveFieldInsideGroup()
+        {
+            var encoder = new AddPrimitiveInsideGroupV1();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            var group = encoder.BCount(2);
+            group.Next().C = 2;
+            group.D = 3;
+            group.Next().C = 4;
+            group.D = 5;
+            encoder.CheckEncodingIsComplete();
+
+            ModifyHeaderToLookLikeVersion1();
+
+            var decoder = new AddPrimitiveInsideGroupV0();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+            var b = decoder.B;
+            Assert.AreEqual(2, b.Count);
+            Assert.AreEqual(2, b.Next().C);
+            Assert.AreEqual(4, b.Next().C);
+        }
+
+        [TestMethod]
+        public void AllowsNewDecoderToDecodeAddedGroupBeforeVarData()
+        {
+            var encoder = new AddGroupBeforeVarDataV1();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            var groupEncoder = encoder.CCount(1);
+            groupEncoder.Next().D = 2;
+            encoder.SetB("abc");
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new AddGroupBeforeVarDataV1();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+            var groupDecoder = decoder.C;
+            Assert.AreEqual(1, groupDecoder.Count);
+            Assert.AreEqual(2, groupDecoder.Next().D);
+            Assert.AreEqual("abc", decoder.GetB());
+        }
+
+        [TestMethod]
+        public void AllowsNewDecoderToDecodeMissingGroupBeforeVarDataAsNullValue()
+        {
+            var encoder = new AddGroupBeforeVarDataV0();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            encoder.SetB("abc");
+            encoder.CheckEncodingIsComplete();
+
+            ModifyHeaderToLookLikeVersion0();
+
+            var decoder = new AddGroupBeforeVarDataV1();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+            var groupDecoder = decoder.C;
+            Assert.AreEqual(0, groupDecoder.Count);
+            Assert.AreEqual("abc", decoder.GetB());
+            Assert.IsTrue(decoder.ToString().Contains("A=1|C=[]|B='abc'"));
+        }
+
+        [TestMethod]
+        public void AllowsNewDecoderToSkipMissingGroupBeforeVarData()
+        {
+            var encoder = new AddGroupBeforeVarDataV0();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            encoder.SetB("abc");
+            encoder.CheckEncodingIsComplete();
+
+            ModifyHeaderToLookLikeVersion0();
+
+            var decoder = new AddGroupBeforeVarDataV1();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+            Assert.AreEqual("abc", decoder.GetB());
+        }
+
+        [TestMethod]
+        public void DisallowsNewDecoderToSkipPresentButAddedGroupBeforeVarData()
+        {
+            var encoder = new AddGroupBeforeVarDataV1();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            var groupEncoder = encoder.CCount(1);
+            groupEncoder.Next().D = 2;
+            encoder.SetB("abc");
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new AddGroupBeforeVarDataV1();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+
+            var exception = Assert.ThrowsException(() => decoder.GetB());
+            Assert.IsTrue(exception.Message.Contains("Cannot access field \"b\" in state: V1_BLOCK"));
+        }
+
+        [TestMethod]
+        public void AllowsOldDecoderToSkipAddedGroupBeforeVarData()
+        {
+            var encoder = new AddGroupBeforeVarDataV1();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            _messageHeader.NumGroups = 1;
+            encoder.A = 1;
+            var groupEncoder = encoder.CCount(1);
+            groupEncoder.Next().D = 2;
+            encoder.SetB("abc");
+            encoder.CheckEncodingIsComplete();
+
+            ModifyHeaderToLookLikeVersion1();
+
+            var decoder = new AddGroupBeforeVarDataV0();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+
+            for (int i = 0; i < _messageHeader.NumGroups; i++)
+            {
+                SkipGroup(decoder);
+            }
+
+            Assert.AreEqual("abc", decoder.GetB());
+        }
+
+        private void SkipGroup(AddGroupBeforeVarDataV0 decoder)
+        {
+            var groupSizeEncoding = new GroupSizeEncoding();
+            groupSizeEncoding.Wrap(_buffer, decoder.Limit, MessageHeader.SbeSchemaVersion);
+            var bytesToSkip = GroupSizeEncoding.Size +
+                              groupSizeEncoding.BlockLength * groupSizeEncoding.NumInGroup;
+            decoder.Limit += bytesToSkip;
+        }
+
+        [TestMethod]
+        public void AllowsNewDecoderToDecodeAddedEnumFieldBeforeGroup()
+        {
+            var encoder = new AddEnumBeforeGroupV1();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            encoder.D = Direction.BUY;
+            var groupEncoder = encoder.BCount(1);
+            groupEncoder.Next().C = 2;
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new AddEnumBeforeGroupV1();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+            Assert.AreEqual(Direction.BUY, decoder.D);
+            var groupDecoder = decoder.B;
+            Assert.AreEqual(1, groupDecoder.Count);
+            Assert.AreEqual(2, groupDecoder.Next().C);
+        }
+
+        [TestMethod]
+        public void AllowsNewDecoderToDecodeMissingEnumFieldBeforeGroupAsNullValue()
+        {
+            var encoder = new AddEnumBeforeGroupV0();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            var myGroup = encoder.BCount(1);
+            myGroup.Next();
+            myGroup.C = 2;
+            encoder.CheckEncodingIsComplete();
+
+            ModifyHeaderToLookLikeVersion0();
+
+            var decoder = new AddEnumBeforeGroupV1();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+            Assert.AreEqual(Direction.NULL_VALUE, decoder.D);
+            var b = decoder.B;
+            Assert.AreEqual(1, b.Count);
+            Assert.AreEqual(2, b.Next().C);
+        }
+
+        [TestMethod]
+        public void AllowsNewDecoderToSkipPresentButAddedEnumFieldBeforeGroup()
+        {
+            var encoder = new AddEnumBeforeGroupV1();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            encoder.D = Direction.SELL;
+            encoder.BCount(1).Next().C = 2;
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new AddEnumBeforeGroupV1();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+            var b = decoder.B;
+            Assert.AreEqual(1, b.Count);
+            Assert.AreEqual(2, b.Next().C);
+        }
+
+        [TestMethod]
+        public void AllowsOldDecoderToSkipAddedEnumFieldBeforeGroup()
+        {
+            var encoder = new AddEnumBeforeGroupV1();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            encoder.D = Direction.BUY;
+            encoder.BCount(1).Next().C = 2;
+            encoder.CheckEncodingIsComplete();
+
+            ModifyHeaderToLookLikeVersion1();
+
+            var decoder = new AddEnumBeforeGroupV0();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+            var b = decoder.B;
+            Assert.AreEqual(1, b.Count);
+            Assert.AreEqual(2, b.Next().C);
+        }
+
+        [TestMethod]
+        public void AllowsNewDecoderToDecodeAddedCompositeFieldBeforeGroup()
+        {
+            var encoder = new AddCompositeBeforeGroupV1();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            var dEncoder = encoder.D;
+            dEncoder.X = -1;
+            dEncoder.Y = -2;
+            var bEncoder = encoder.BCount(1);
+            bEncoder.Next().C = 2;
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new AddCompositeBeforeGroupV1();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+            var d = decoder.D;
+            Assert.IsNotNull(d);
+            Assert.AreEqual(-1, d.X);
+            Assert.AreEqual(-2, d.Y);
+            var bDecoder = decoder.B;
+            Assert.AreEqual(1, bDecoder.Count);
+            Assert.AreEqual(2, bDecoder.Next().C);
+        }
+
+        [TestMethod]
+        public void AllowsNewDecoderToDecodeMissingCompositeFieldBeforeGroupAsNullValue()
+        {
+            var encoder = new AddCompositeBeforeGroupV0();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            var bEncoder = encoder.BCount(1);
+            bEncoder.Next().C = 2;
+            encoder.CheckEncodingIsComplete();
+
+            ModifyHeaderToLookLikeVersion0();
+
+            var decoder = new AddCompositeBeforeGroupV1();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+            Assert.IsNull(decoder.D);
+            var bDecoder = decoder.B;
+            Assert.AreEqual(1, bDecoder.Count);
+            Assert.AreEqual(2, bDecoder.Next().C);
+        }
+
+        [TestMethod]
+        public void AllowsNewDecoderToSkipPresentButAddedCompositeFieldBeforeGroup()
+        {
+            var encoder = new AddCompositeBeforeGroupV1();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            var dEncoder = encoder.D;
+            dEncoder.X = -1;
+            dEncoder.Y = -2;
+            var bEncoder = encoder.BCount(1);
+            bEncoder.Next().C = 2;
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new AddCompositeBeforeGroupV1();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+            var bDecoder = decoder.B;
+            Assert.AreEqual(1, bDecoder.Count);
+            Assert.AreEqual(2, bDecoder.Next().C);
+        }
+
+        [TestMethod]
+        public void AllowsOldDecoderToSkipAddedCompositeFieldBeforeGroup()
+        {
+            var encoder = new AddCompositeBeforeGroupV1();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            var dEncoder = encoder.D;
+            dEncoder.X = -1;
+            dEncoder.Y = -2;
+            var bEncoder = encoder.BCount(1);
+            bEncoder.Next().C = 2;
+            encoder.CheckEncodingIsComplete();
+
+            ModifyHeaderToLookLikeVersion1();
+
+            var decoder = new AddCompositeBeforeGroupV0();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+            var bDecoder = decoder.B;
+            Assert.AreEqual(1, bDecoder.Count);
+            Assert.AreEqual(2, bDecoder.Next().C);
+        }
+
+        [TestMethod]
+        public void AllowsNewDecoderToDecodeAddedArrayFieldBeforeGroup1()
+        {
+            var encoder = new AddArrayBeforeGroupV1();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            var dEncoder = encoder.DAsSpan();
+            dEncoder[0] = 1;
+            dEncoder[1] = 2;
+            dEncoder[2] = 3;
+            dEncoder[3] = 4;
+            var bEncoder = encoder.BCount(1);
+            bEncoder.Next().C = 2;
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new AddArrayBeforeGroupV1();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+            Assert.AreEqual((short)1, decoder.GetD(0));
+            Assert.AreEqual((short)2, decoder.GetD(1));
+            Assert.AreEqual((short)3, decoder.GetD(2));
+            Assert.AreEqual((short)4, decoder.GetD(3));
+            var bDecoder = decoder.B;
+            Assert.AreEqual(1, bDecoder.Count);
+            Assert.AreEqual(2, bDecoder.Next().C);
+        }
+
+        [TestMethod]
+        public void AllowsNewDecoderToDecodeAddedArrayFieldBeforeGroup2()
+        {
+            var encoder = new AddArrayBeforeGroupV1();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            encoder.SetD(0, 1);
+            encoder.SetD(1, 2);
+            encoder.SetD(2, 3);
+            encoder.SetD(3, 4);
+            var bEncoder = encoder.BCount(1);
+            bEncoder.Next().C = 2;
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new AddArrayBeforeGroupV1();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+            Assert.AreEqual((short)1, decoder.GetD(0));
+            Assert.AreEqual((short)2, decoder.GetD(1));
+            Assert.AreEqual((short)3, decoder.GetD(2));
+            Assert.AreEqual((short)4, decoder.GetD(3));
+            var bDecoder = decoder.B;
+            Assert.AreEqual(1, bDecoder.Count);
+            Assert.AreEqual(2, bDecoder.Next().C);
+        }
+
+        [TestMethod]
+        public void AllowsNewDecoderToDecodeMissingArrayFieldBeforeGroupAsNullValue1()
+        {
+            var encoder = new AddArrayBeforeGroupV0();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            var bEncoder = encoder.BCount(1);
+            bEncoder.Next().C = 2;
+            encoder.CheckEncodingIsComplete();
+
+            ModifyHeaderToLookLikeVersion0();
+
+            var decoder = new AddArrayBeforeGroupV1();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+            Assert.AreEqual(AddArrayBeforeGroupV1.DNullValue, decoder.GetD(0));
+            Assert.AreEqual(AddArrayBeforeGroupV1.DNullValue, decoder.GetD(1));
+            Assert.AreEqual(AddArrayBeforeGroupV1.DNullValue, decoder.GetD(2));
+            Assert.AreEqual(AddArrayBeforeGroupV1.DNullValue, decoder.GetD(3));
+            var bDecoder = decoder.B;
+            Assert.AreEqual(1, bDecoder.Count);
+            Assert.AreEqual(2, bDecoder.Next().C);
+        }
+
+        [TestMethod]
+        public void AllowsNewDecoderToDecodeMissingArrayFieldBeforeGroupAsNullValue2()
+        {
+            var encoder = new AddArrayBeforeGroupV0();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            var bEncoder = encoder.BCount(1);
+            bEncoder.Next().C = 2;
+            encoder.CheckEncodingIsComplete();
+
+            ModifyHeaderToLookLikeVersion0();
+
+            var decoder = new AddArrayBeforeGroupV1();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+            Assert.AreEqual(0, decoder.D.Length);
+            var bDecoder = decoder.B;
+            Assert.AreEqual(1, bDecoder.Count);
+            Assert.AreEqual(2, bDecoder.Next().C);
+        }
+
+        [TestMethod]
+        public void AllowsNewDecoderToSkipPresentButAddedArrayFieldBeforeGroup()
+        {
+            var encoder = new AddArrayBeforeGroupV1();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            encoder.SetD(0, 1);
+            encoder.SetD(1, 2);
+            encoder.SetD(2, 3);
+            encoder.SetD(3, 4);
+            var bEncoder = encoder.BCount(1);
+            bEncoder.Next().C = 2;
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new AddArrayBeforeGroupV1();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+            var bDecoder = decoder.B;
+            Assert.AreEqual(1, bDecoder.Count);
+            Assert.AreEqual(2, bDecoder.Next().C);
+        }
+
+        [TestMethod]
+        public void AllowsOldDecoderToSkipAddedArrayFieldBeforeGroup()
+        {
+            var encoder = new AddArrayBeforeGroupV1();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            encoder.SetD(0, 1);
+            encoder.SetD(1, 2);
+            encoder.SetD(2, 3);
+            encoder.SetD(3, 4);
+            var bEncoder = encoder.BCount(1);
+            bEncoder.Next().C = 2;
+            encoder.CheckEncodingIsComplete();
+
+            ModifyHeaderToLookLikeVersion1();
+
+            var decoder = new AddArrayBeforeGroupV0();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+            var bDecoder = decoder.B;
+            Assert.AreEqual(1, bDecoder.Count);
+            Assert.AreEqual(2, bDecoder.Next().C);
+        }
+
+        [TestMethod]
+        public void AllowsNewDecoderToDecodeAddedBitSetFieldBeforeGroup()
+        {
+            var encoder = new AddBitSetBeforeGroupV1();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            encoder.D = Flags.Guacamole | Flags.Cheese;
+            var bEncoder = encoder.BCount(1);
+            bEncoder.Next().C = 2;
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new AddBitSetBeforeGroupV1();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+            Assert.IsNotNull(decoder.D);
+            Assert.AreEqual(true, decoder.D.HasFlag(Flags.Guacamole));
+            Assert.AreEqual(true, decoder.D.HasFlag(Flags.Cheese));
+            Assert.AreEqual(false, decoder.D.HasFlag(Flags.SourCream));
+            var bDecoder = decoder.B;
+            Assert.AreEqual(1, bDecoder.Count);
+            Assert.AreEqual(2, bDecoder.Next().C);
+        }
+
+        [TestMethod]
+        public void AllowsNewDecoderToDecodeMissingBitSetFieldBeforeGroupAsNullValue()
+        {
+            var encoder = new AddBitSetBeforeGroupV0();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            var bEncoder = encoder.BCount(1);
+            bEncoder.Next().C = 2;
+            encoder.CheckEncodingIsComplete();
+
+            ModifyHeaderToLookLikeVersion0();
+
+            var decoder = new AddBitSetBeforeGroupV1();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+            Assert.AreEqual((byte)decoder.D, 0);
+            var bDecoder = decoder.B;
+            Assert.AreEqual(1, bDecoder.Count);
+            Assert.AreEqual(2, bDecoder.Next().C);
+        }
+
+        [TestMethod]
+        public void AllowsNewDecoderToSkipPresentButAddedBitSetFieldBeforeGroup()
+        {
+            var encoder = new AddBitSetBeforeGroupV1();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            encoder.D = Flags.Guacamole | Flags.Cheese;
+            var bEncoder = encoder.BCount(1);
+            bEncoder.Next().C = 2;
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new AddBitSetBeforeGroupV1();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+            var bDecoder = decoder.B;
+            Assert.AreEqual(1, bDecoder.Count);
+            Assert.AreEqual(2, bDecoder.Next().C);
+        }
+
+        [TestMethod]
+        public void AllowsOldDecoderToSkipAddedBitSetFieldBeforeGroup()
+        {
+            var encoder = new AddBitSetBeforeGroupV1();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            encoder.D = Flags.Guacamole | Flags.Cheese;
+            var bEncoder = encoder.BCount(1);
+            bEncoder.Next().C = 2;
+            encoder.CheckEncodingIsComplete();
+
+            ModifyHeaderToLookLikeVersion1();
+
+            var decoder = new AddBitSetBeforeGroupV0();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+            var bDecoder = decoder.B;
+            Assert.AreEqual(1, bDecoder.Count);
+            Assert.AreEqual(2, bDecoder.Next().C);
+        }
+
+        [TestMethod]
+        public void AllowsEncodingAndDecodingEnumInsideGroupInSchemaDefinedOrder()
+        {
+            var encoder = new EnumInsideGroup();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = Direction.BUY;
+            var bEncoder = encoder.BCount(1);
+            bEncoder.Next().C = Direction.SELL;
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new EnumInsideGroup();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(Direction.BUY, decoder.A);
+            var bDecoder = decoder.B;
+            Assert.AreEqual(1, bDecoder.Count);
+            Assert.AreEqual(Direction.SELL, bDecoder.Next().C);
+            Assert.IsTrue(decoder.ToString().Contains("A=BUY|B=[(C=SELL)]"));
+        }
+
+        [TestMethod]
+        public void DisallowsEncodingEnumInsideGroupBeforeCallingNext()
+        {
+            var encoder = new EnumInsideGroup();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = Direction.BUY;
+            var bEncoder = encoder.BCount(1);
+            var exception = Assert.ThrowsException(() => bEncoder.C = Direction.SELL);
+            Assert.IsTrue(exception.Message.Contains("Cannot access field \"b.c\" in state: V0_B_N"));
+        }
+
+        [TestMethod]
+        public void DisallowsDecodingEnumInsideGroupBeforeCallingNext()
+        {
+            var encoder = new EnumInsideGroup();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = Direction.BUY;
+            var bEncoder = encoder.BCount(1);
+            bEncoder.Next().C = Direction.SELL;
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new EnumInsideGroup();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(Direction.BUY, decoder.A);
+            var bDecoder = decoder.B;
+            Assert.AreEqual(1, bDecoder.Count);
+            var exception = Assert.ThrowsException(() =>
+            {
+                var _ = bDecoder.C;
+            });
+            Assert.IsTrue(exception.Message.Contains("Cannot access field \"b.c\" in state: V0_B_N"));
+        }
+
+        [TestMethod]
+        public void AllowsReEncodingTopLevelEnum()
+        {
+            var encoder = new EnumInsideGroup();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = Direction.BUY;
+            var bEncoder = encoder.BCount(1);
+            bEncoder.Next().C = Direction.SELL;
+
+            encoder.A = Direction.SELL;
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new EnumInsideGroup();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(Direction.SELL, decoder.A);
+            var bDecoder = decoder.B;
+            Assert.AreEqual(1, bDecoder.Count);
+            Assert.AreEqual(Direction.SELL, bDecoder.Next().C);
+        }
+
+        [TestMethod]
+        public void AllowsEncodingAndDecodingBitSetInsideGroupInSchemaDefinedOrder()
+        {
+            var encoder = new BitSetInsideGroup();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = Flags.Cheese | Flags.Guacamole;
+            var bEncoder = encoder.BCount(1);
+            bEncoder.Next().C = Flags.SourCream;
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new BitSetInsideGroup();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(true, decoder.A.HasFlag((Flags.Guacamole)));
+            Assert.AreEqual(true, decoder.A.HasFlag(Flags.Cheese));
+            Assert.AreEqual(false, decoder.A.HasFlag(Flags.SourCream));
+            var bDecoder = decoder.B;
+            Assert.AreEqual(1, bDecoder.Count);
+            var cDecoder = bDecoder.Next().C;
+            Assert.AreEqual(false, cDecoder.HasFlag((Flags.Guacamole)));
+            Assert.AreEqual(false, cDecoder.HasFlag(Flags.Cheese));
+            Assert.AreEqual(true, cDecoder.HasFlag(Flags.SourCream));
+            Assert.IsTrue(decoder.ToString().Contains("A={Guacamole, Cheese}|B=[(C={SourCream})]"));
+        }
+
+        [TestMethod]
+        public void DisallowsEncodingBitSetInsideGroupBeforeCallingNext()
+        {
+            var encoder = new BitSetInsideGroup();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = Flags.Cheese | Flags.Guacamole;
+            var bEncoder = encoder.BCount(1);
+            var exception = Assert.ThrowsException(() =>
+            {
+                var _ = bEncoder.C;
+            });
+            Assert.IsTrue(exception.Message.Contains("Cannot access field \"b.c\" in state: V0_B_N"));
+        }
+
+        [TestMethod]
+        public void DisallowsDecodingBitSetInsideGroupBeforeCallingNext()
+        {
+            var encoder = new BitSetInsideGroup();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = Flags.Cheese | Flags.Guacamole;
+            var bEncoder = encoder.BCount(1);
+            bEncoder.Next().C = Flags.SourCream;
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new BitSetInsideGroup();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(true, decoder.A.HasFlag((Flags.Guacamole)));
+            Assert.AreEqual(true, decoder.A.HasFlag(Flags.Cheese));
+            Assert.AreEqual(false, decoder.A.HasFlag(Flags.SourCream));
+            var bDecoder = decoder.B;
+            Assert.AreEqual(1, bDecoder.Count);
+            var exception = Assert.ThrowsException(() =>
+            {
+                var _ = bDecoder.C;
+            });
+            Assert.IsTrue(exception.Message.Contains("Cannot access field \"b.c\" in state: V0_B_N"));
+        }
+
+        [TestMethod]
+        public void AllowsReEncodingTopLevelBitSetViaReWrap()
+        {
+            var encoder = new BitSetInsideGroup();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = Flags.Cheese | Flags.Guacamole;
+            var bEncoder = encoder.BCount(1);
+            bEncoder.Next().C = Flags.SourCream;
+
+            encoder.A |= Flags.SourCream;
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new BitSetInsideGroup();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(true, decoder.A.HasFlag((Flags.Guacamole)));
+            Assert.AreEqual(true, decoder.A.HasFlag(Flags.Cheese));
+            Assert.AreEqual(true, decoder.A.HasFlag(Flags.SourCream));
+            var bDecoder = decoder.B;
+            Assert.AreEqual(1, bDecoder.Count);
+            var cDecoder = bDecoder.Next().C;
+            Assert.AreEqual(false, cDecoder.HasFlag((Flags.Guacamole)));
+            Assert.AreEqual(false, cDecoder.HasFlag(Flags.Cheese));
+            Assert.AreEqual(true, cDecoder.HasFlag(Flags.SourCream));
+        }
+
+        [TestMethod]
+        public void AllowsEncodingAndDecodingArrayInsideGroupInSchemaDefinedOrder()
+        {
+            var encoder = new ArrayInsideGroup();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.SetA(0, 1);
+            encoder.SetA(1, 2);
+            encoder.SetA(2, 3);
+            encoder.SetA(3, 4);
+            var bEncoder = encoder.BCount(1);
+            bEncoder.Next();
+            bEncoder.SetC(0, 5);
+            bEncoder.SetC(1, 6);
+            bEncoder.SetC(2, 7);
+            bEncoder.SetC(3, 8);
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new ArrayInsideGroup();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A[0]);
+            Assert.AreEqual(2, decoder.A[1]);
+            Assert.AreEqual(3, decoder.A[2]);
+            Assert.AreEqual(4, decoder.A[3]);
+            var bDecoder = decoder.B;
+            Assert.AreEqual(1, bDecoder.Count);
+            bDecoder.Next();
+            Assert.AreEqual(5, bDecoder.C[0]);
+            Assert.AreEqual(6, bDecoder.C[1]);
+            Assert.AreEqual(7, bDecoder.C[2]);
+            Assert.AreEqual(8, bDecoder.C[3]);
+            StringAssert.Contains(decoder.ToString(), "A=[1,2,3,4]|B=[(C=[5,6,7,8])]");
+        }
+
+        [TestMethod]
+        public void DisallowsEncodingArrayInsideGroupBeforeCallingNext1()
+        {
+            var bEncoder = EncodeUntilGroupWithArrayInside();
+            var exception = Assert.ThrowsException(() => { bEncoder.SetC(0, 5); });
+            Assert.IsTrue(exception.Message.Contains("Cannot access field \"b.c\" in state: V0_B_N"));
+        }
+
+        [TestMethod]
+        public void DisallowsEncodingArrayInsideGroupBeforeCallingNext2()
+        {
+            var bEncoder = EncodeUntilGroupWithArrayInside();
+            var exception = Assert.ThrowsException(() => { bEncoder.CAsSpan()[0] = 1; });
+            Assert.IsTrue(exception.Message.Contains("Cannot access field \"b.c\" in state: V0_B_N"));
+        }
+
+        private ArrayInsideGroup.BGroup EncodeUntilGroupWithArrayInside()
+        {
+            var encoder = new ArrayInsideGroup();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.SetA(0, 1);
+            encoder.SetA(1, 2);
+            encoder.SetA(2, 3);
+            encoder.SetA(3, 4);
+            return encoder.BCount(1);
+        }
+
+        [TestMethod]
+        public void DisallowsDecodingArrayInsideGroupBeforeCallingNext1()
+        {
+            var bDecoder = DecodeUntilGroupWithArrayInside();
+            var exception = Assert.ThrowsException(() => bDecoder.C[0]);
+            Assert.IsTrue(exception.Message.Contains("Cannot access field \"b.c\" in state: V0_B_N"));
+        }
+
+        [TestMethod]
+        public void DisallowsDecodingArrayInsideGroupBeforeCallingNext2()
+        {
+            var bDecoder = DecodeUntilGroupWithArrayInside();
+            var exception = Assert.ThrowsException(() => bDecoder.GetC(0));
+            Assert.IsTrue(exception.Message.Contains("Cannot access field \"b.c\" in state: V0_B_N"));
+        }
+
+        private ArrayInsideGroup.BGroup DecodeUntilGroupWithArrayInside()
+        {
+            var encoder = new ArrayInsideGroup();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.SetA(0, 1);
+            encoder.SetA(1, 2);
+            encoder.SetA(2, 3);
+            encoder.SetA(3, 4);
+            var bEncoder = encoder.BCount(1).Next();
+            bEncoder.SetC(0, 5);
+            bEncoder.SetC(1, 6);
+            bEncoder.SetC(2, 7);
+            bEncoder.SetC(3, 8);
+
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new ArrayInsideGroup();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual((short)1, decoder.A[0]);
+            Assert.AreEqual((short)2, decoder.A[1]);
+            Assert.AreEqual((short)3, decoder.A[2]);
+            Assert.AreEqual((short)4, decoder.A[3]);
+            var bDecoder = decoder.B;
+            Assert.AreEqual(1, bDecoder.Count);
+            return bDecoder;
+        }
+
+        [TestMethod]
+        public void AllowsReEncodingTopLevelArrayViaReWrap()
+        {
+            var encoder = new ArrayInsideGroup();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.SetA(0, 1);
+            encoder.SetA(1, 2);
+            encoder.SetA(2, 3);
+            encoder.SetA(3, 4);
+            var bEncoder = encoder.BCount(1).Next();
+            bEncoder.SetC(0, 5);
+            bEncoder.SetC(1, 6);
+            bEncoder.SetC(2, 7);
+            bEncoder.SetC(3, 8);
+
+            encoder.SetA(0, 9);
+            encoder.SetA(1, 10);
+            encoder.SetA(2, 11);
+            encoder.SetA(3, 12);
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new ArrayInsideGroup();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(9, decoder.A[0]);
+            Assert.AreEqual(10, decoder.A[1]);
+            Assert.AreEqual(11, decoder.A[2]);
+            Assert.AreEqual(12, decoder.A[3]);
+            var bDecoder = decoder.B;
+            Assert.AreEqual(1, bDecoder.Count);
+            bDecoder.Next();
+            Assert.AreEqual(5, bDecoder.C[0]);
+            Assert.AreEqual(6, bDecoder.C[1]);
+            Assert.AreEqual(7, bDecoder.C[2]);
+            Assert.AreEqual(8, bDecoder.C[3]);
+        }
+
+        [TestMethod]
+        public void AllowsEncodingAndDecodingGroupFieldsInSchemaDefinedOrder1()
+        {
+            var encoder = new MultipleGroups();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 42;
+            encoder.BCount(0);
+            var dEncoder = encoder.DCount(1).Next();
+            dEncoder.E = 43;
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new MultipleGroups();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(42, decoder.A);
+            Assert.AreEqual(0, decoder.B.Count);
+            var dDecoder = decoder.D;
+            Assert.AreEqual(1, dDecoder.Count);
+            Assert.AreEqual(dDecoder.Next().E, 43);
+            Assert.IsTrue(decoder.ToString().Contains("A=42|B=[]|D=[(E=43)]"));
+        }
+
+        [TestMethod]
+        public void AllowsEncodingAndDecodingGroupFieldsInSchemaDefinedOrder2()
+        {
+            var encoder = new MultipleGroups();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 41;
+            var bEncoder = encoder.BCount(1).Next();
+            bEncoder.C = 42;
+            var dEncoder = encoder.DCount(1).Next();
+            dEncoder.E = 43;
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new MultipleGroups();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(41, decoder.A);
+            var bDecoder = decoder.B;
+            Assert.AreEqual(1, bDecoder.Count);
+            Assert.AreEqual(42, bDecoder.Next().C);
+            var dDecoder = decoder.D;
+            Assert.AreEqual(1, dDecoder.Count);
+            Assert.AreEqual(43, dDecoder.Next().E);
+        }
+
+        [TestMethod]
+        public void AllowsReEncodingTopLevelPrimitiveFieldsAfterGroups()
+        {
+            var encoder = new MultipleGroups();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 41;
+            var bEncoder = encoder.BCount(1).Next();
+            bEncoder.C = 42;
+            var dEncoder = encoder.DCount(1).Next();
+            dEncoder.E = 43;
+            encoder.A = 44;
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new MultipleGroups();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(44, decoder.A);
+            var bDecoder = decoder.B;
+            Assert.AreEqual(1, bDecoder.Count);
+            Assert.AreEqual(42, bDecoder.Next().C);
+            var dDecoder = decoder.D;
+            Assert.AreEqual(1, dDecoder.Count);
+            Assert.AreEqual(43, dDecoder.Next().E);
+        }
+
+        [TestMethod]
+        public void DisallowsMissedEncodingOfGroupField()
+        {
+            var encoder = new MultipleGroups();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 41;
+            Assert.ThrowsException(() => encoder.DCount(0));
+        }
+
+        [TestMethod]
+        public void DisallowsReEncodingEarlierGroupFields()
+        {
+            var encoder = new MultipleGroups();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 41;
+            var bEncoder = encoder.BCount(1).Next();
+            bEncoder.C = 42;
+            var dEncoder = encoder.DCount(1).Next();
+            dEncoder.E = 43;
+            Assert.ThrowsException(() => encoder.BCount(1));
+        }
+
+        [TestMethod]
+        public void DisallowsReEncodingLatestGroupField()
+        {
+            var encoder = new MultipleGroups();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 41;
+            var bEncoder = encoder.BCount(1).Next();
+            bEncoder.C = 42;
+            var dEncoder = encoder.DCount(1).Next();
+            dEncoder.E = 43;
+            Assert.ThrowsException(() => encoder.DCount(1));
+        }
+
+        [TestMethod]
+        public void DisallowsMissedDecodingOfGroupField()
+        {
+            var encoder = new MultipleGroups();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 41;
+            var bEncoder = encoder.BCount(1).Next();
+            bEncoder.C = 42;
+            var dEncoder = encoder.DCount(1).Next();
+            dEncoder.E = 43;
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new MultipleGroups();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(41, decoder.A);
+            Assert.ThrowsException(() =>
+            {
+                // ReSharper disable once UnusedVariable
+                var ignored = decoder.D;
+            });
+        }
+
+        [TestMethod]
+        public void DisallowsReDecodingEarlierGroupField()
+        {
+            var encoder = new MultipleGroups();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 41;
+            var bEncoder = encoder.BCount(1).Next();
+            bEncoder.C = 42;
+            var dEncoder = encoder.DCount(1).Next();
+            dEncoder.E = 43;
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new MultipleGroups();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(41, decoder.A);
+            var bDecoder = decoder.B;
+            Assert.AreEqual(1, bDecoder.Count);
+            Assert.AreEqual(42, bDecoder.Next().C);
+            var dDecoder = decoder.D;
+            Assert.AreEqual(1, dDecoder.Count);
+            Assert.AreEqual(43, dDecoder.Next().E);
+            Assert.ThrowsException(() =>
+            {
+                // ReSharper disable once UnusedVariable
+                var ignored = decoder.B;
+            });
+        }
+
+        [TestMethod]
+        public void DisallowsReDecodingLatestGroupField()
+        {
+            var encoder = new MultipleGroups();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 41;
+            var bEncoder = encoder.BCount(1).Next();
+            bEncoder.C = 42;
+            var dEncoder = encoder.DCount(1).Next();
+            dEncoder.E = 43;
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new MultipleGroups();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(41, decoder.A);
+            var bDecoder = decoder.B;
+            Assert.AreEqual(1, bDecoder.Count);
+            Assert.AreEqual(42, bDecoder.Next().C);
+            var dDecoder = decoder.D;
+            Assert.AreEqual(1, dDecoder.Count);
+            Assert.AreEqual(43, dDecoder.Next().E);
+            Assert.ThrowsException(() =>
+            {
+                // ReSharper disable once UnusedVariable
+                var ignored = decoder.D;
+            });
+        }
+
+        [TestMethod]
+        public void AllowsNewDecoderToDecodeAddedVarData()
+        {
+            var encoder = new AddVarDataV1();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 42;
+            encoder.SetB("abc");
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new AddVarDataV1();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(42, decoder.A);
+            Assert.AreEqual("abc", decoder.GetB());
+        }
+
+        [TestMethod]
+        public void AllowsNewDecoderToDecodeMissingAddedVarDataAsNullValue1()
+        {
+            var encoder = new AddVarDataV0();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 42;
+            encoder.CheckEncodingIsComplete();
+
+            ModifyHeaderToLookLikeVersion0();
+
+            var decoder = new AddVarDataV1();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(42, decoder.A);
+            Assert.AreEqual("", decoder.GetB());
+            Assert.IsTrue(decoder.ToString().Contains("A=42|B=''"));
+        }
+
+        [TestMethod]
+        public void AllowsNewDecoderToDecodeMissingAddedVarDataAsNullValue2()
+        {
+            var encoder = new AddVarDataV0();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 42;
+            encoder.CheckEncodingIsComplete();
+
+            ModifyHeaderToLookLikeVersion0();
+
+            var decoder = new AddVarDataV1();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(42, decoder.A);
+            Assert.AreEqual(0, decoder.BLength());
+        }
+
+        [TestMethod]
+        public void AllowsNewDecoderToDecodeMissingAddedVarDataAsNullValue3()
+        {
+            var encoder = new AddVarDataV0();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 42;
+            encoder.CheckEncodingIsComplete();
+
+            ModifyHeaderToLookLikeVersion0();
+
+            var decoder = new AddVarDataV1();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(42, decoder.A);
+            Assert.AreEqual(0, decoder.GetB(new byte[16]));
+        }
+
+        [TestMethod]
+        public void AllowsNewDecoderToDecodeMissingAddedVarDataAsNullValue4()
+        {
+            var encoder = new AddVarDataV0();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 42;
+            encoder.CheckEncodingIsComplete();
+
+            ModifyHeaderToLookLikeVersion0();
+
+            var decoder = new AddVarDataV1();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(42, decoder.A);
+            Assert.AreEqual(0, decoder.GetB(new byte[16], 0, 16));
+        }
+
+        [TestMethod]
+        public void AllowsEncodingAndDecodingAsciiInsideGroupInSchemaDefinedOrder1()
+        {
+            var encoder = new AsciiInsideGroup();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.SetA("GBPUSD");
+            var bEncoder = encoder.BCount(1);
+            bEncoder.Next();
+            bEncoder.SetC("EURUSD");
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new AsciiInsideGroup();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual("GBPUSD", decoder.GetA());
+            var bDecoder = decoder.B;
+            Assert.AreEqual(1, bDecoder.Count);
+            bDecoder.Next();
+            Assert.AreEqual("EURUSD", bDecoder.GetC());
+            Assert.IsTrue(decoder.ToString().Contains("A='GBPUSD'|B=[(C='EURUSD')]"));
+        }
+
+        [TestMethod]
+        public void AllowsEncodingAndDecodingAsciiInsideGroupInSchemaDefinedOrder2()
+        {
+            var encoder = new AsciiInsideGroup();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            var gbpUsdBytes = Encoding.ASCII.GetBytes("GBPUSD");
+            encoder.SetA(gbpUsdBytes, 0);
+            var bEncoder = encoder.BCount(1);
+            bEncoder.Next();
+            bEncoder.SetC(Encoding.ASCII.GetBytes("EURUSD"), 0);
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new AsciiInsideGroup();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            var aBytes = new byte[6];
+            decoder.GetA(aBytes, 0);
+            Assert.IsTrue(gbpUsdBytes.SequenceEqual(aBytes));
+            var bDecoder = decoder.B;
+            Assert.AreEqual(1, bDecoder.Count);
+            bDecoder.Next();
+            Assert.AreEqual((byte)'E', bDecoder.GetC(0));
+            Assert.AreEqual((byte)'U', bDecoder.GetC(1));
+            Assert.AreEqual((byte)'R', bDecoder.GetC(2));
+            Assert.AreEqual((byte)'U', bDecoder.GetC(3));
+            Assert.AreEqual((byte)'S', bDecoder.GetC(4));
+            Assert.AreEqual((byte)'D', bDecoder.GetC(5));
+        }
+
+        [TestMethod]
+        public void DisallowsEncodingAsciiInsideGroupBeforeCallingNext1()
+        {
+            var bEncoder = EncodeUntilGroupWithAsciiInside();
+
+            Exception exception = Assert.ThrowsException(() => bEncoder.SetC("EURUSD"));
+
+            Assert.IsTrue(exception.Message.Contains("Cannot access field \"b.c\" in state: V0_B_N"));
+        }
+
+        [TestMethod]
+        public void DisallowsEncodingAsciiInsideGroupBeforeCallingNext2()
+        {
+            var bEncoder = EncodeUntilGroupWithAsciiInside();
+
+            Exception exception = Assert.ThrowsException(() => bEncoder.SetC(0, (byte)'E'));
+
+            Assert.IsTrue(exception.Message.Contains("Cannot access field \"b.c\" in state: V0_B_N"));
+        }
+
+        [TestMethod]
+        public void DisallowsEncodingAsciiInsideGroupBeforeCallingNext3()
+        {
+            var bEncoder = EncodeUntilGroupWithAsciiInside();
+
+            Exception exception =
+                Assert.ThrowsException(() => bEncoder.CAsSpan()[0] = (byte)'E');
+
+            Assert.IsTrue(exception.Message.Contains("Cannot access field \"b.c\" in state: V0_B_N"));
+        }
+
+        [TestMethod]
+        public void DisallowsEncodingAsciiInsideGroupBeforeCallingNext4()
+        {
+            var bEncoder = EncodeUntilGroupWithAsciiInside();
+
+            Exception exception = Assert.ThrowsException(() =>
+            {
+                bEncoder.SetC(Encoding.ASCII.GetBytes("EURUSD"), 0);
+            });
+
+            Assert.IsTrue(exception.Message.Contains("Cannot access field \"b.c\" in state: V0_B_N"));
+        }
+
+        [TestMethod]
+        public void DisallowsEncodingAsciiInsideGroupBeforeCallingNext5()
+        {
+            var bEncoder = EncodeUntilGroupWithAsciiInside();
+
+            Exception exception = Assert.ThrowsException(() => 
+                bEncoder.SetC(Encoding.ASCII.GetBytes("EURUSD"), 0));
+
+            Assert.IsTrue(exception.Message.Contains("Cannot access field \"b.c\" in state: V0_B_N"));
+        }
+
+        [TestMethod]
+        public void DisallowsEncodingAsciiInsideGroupBeforeCallingNext6()
+        {
+            var bEncoder = EncodeUntilGroupWithAsciiInside();
+
+            Exception exception = Assert.ThrowsException(() => 
+                bEncoder.SetC(Encoding.ASCII.GetBytes("EURUSD")));
+
+            Assert.IsTrue(exception.Message.Contains("Cannot access field \"b.c\" in state: V0_B_N"));
+        }
+
+        private AsciiInsideGroup.BGroup EncodeUntilGroupWithAsciiInside()
+        {
+            var encoder = new AsciiInsideGroup();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.SetA("GBPUSD");
+            return encoder.BCount(1);
+        }
+
+        [TestMethod]
+        public void DisallowsDecodingAsciiInsideGroupBeforeCallingNext1()
+        {
+            var b = DecodeUntilGroupWithAsciiInside();
+
+            Exception exception = Assert.ThrowsException(() => b.GetC(0));
+
+            Assert.IsTrue(exception.Message.Contains("Cannot access field \"b.c\" in state: V0_B_N"));
+        }
+
+        [TestMethod]
+        public void DisallowsDecodingAsciiInsideGroupBeforeCallingNext2()
+        {
+            var b = DecodeUntilGroupWithAsciiInside();
+
+            Exception exception = Assert.ThrowsException(() => b.GetC());
+
+            Assert.IsTrue(exception.Message.Contains("Cannot access field \"b.c\" in state: V0_B_N"));
+        }
+
+        [TestMethod]
+        public void DisallowsDecodingAsciiInsideGroupBeforeCallingNext3()
+        {
+            var b = DecodeUntilGroupWithAsciiInside();
+
+            Exception exception = Assert.ThrowsException(() => b.C[0]);
+
+            Assert.IsTrue(exception.Message.Contains("Cannot access field \"b.c\" in state: V0_B_N"));
+        }
+
+        [TestMethod]
+        public void DisallowsDecodingAsciiInsideGroupBeforeCallingNext4()
+        {
+            var b = DecodeUntilGroupWithAsciiInside();
+
+            Exception exception = Assert.ThrowsException(() => b.GetC(new byte[16], 0));
+
+            Assert.IsTrue(exception.Message.Contains("Cannot access field \"b.c\" in state: V0_B_N"));
+        }
+
+        [TestMethod]
+        public void DisallowsDecodingAsciiInsideGroupBeforeCallingNext5()
+        {
+            var b = DecodeUntilGroupWithAsciiInside();
+
+            Exception exception = Assert.ThrowsException(() => b.GetC(new byte[16]));
+
+            Assert.IsTrue(exception.Message.Contains("Cannot access field \"b.c\" in state: V0_B_N"));
+        }
+
+        private AsciiInsideGroup.BGroup DecodeUntilGroupWithAsciiInside()
+        {
+            var encoder = new AsciiInsideGroup();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.SetA("GBPUSD");
+            var bEncoder = encoder.BCount(1);
+            bEncoder.Next();
+            bEncoder.SetC("EURUSD");
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new AsciiInsideGroup();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual("GBPUSD", decoder.GetA());
+            var bDecoder = decoder.B;
+            Assert.AreEqual(1, bDecoder.Count);
+            return bDecoder;
+        }
+
+        [TestMethod]
+        public void AllowsReEncodingTopLevelAsciiViaReWrap()
+        {
+            var encoder = new AsciiInsideGroup();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.SetA("GBPUSD");
+            var bEncoder = encoder.BCount(1);
+            bEncoder.Next();
+            bEncoder.SetC("EURUSD");
+
+            encoder.SetA("CADUSD");
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new AsciiInsideGroup();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual("CADUSD", decoder.GetA());
+            var bDecoder = decoder.B;
+            Assert.AreEqual(1, bDecoder.Count);
+            bDecoder.Next();
+            Assert.AreEqual("EURUSD", bDecoder.GetC());
+        }
+
+        [TestMethod]
+        public void AllowsNewDecoderToDecodeAddedAsciiFieldBeforeGroup1()
+        {
+            var encoder = new AddAsciiBeforeGroupV1();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            encoder.SetD("EURUSD");
+            var bEncoder = encoder.BCount(1);
+            bEncoder.Next();
+            bEncoder.C = 2;
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new AddAsciiBeforeGroupV1();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+            Assert.AreEqual("EURUSD", decoder.GetD());
+            var bDecoder = decoder.B;
+            Assert.AreEqual(1, bDecoder.Count);
+            Assert.AreEqual(2, bDecoder.Next().C);
+        }
+
+        [TestMethod]
+        public void AllowsNewDecoderToDecodeAddedAsciiFieldBeforeGroup2()
+        {
+            var encoder = new AddAsciiBeforeGroupV1();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            encoder.SetD("EURUSD");
+            var bEncoder = encoder.BCount(1);
+            bEncoder.Next();
+            bEncoder.C = 2;
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new AddAsciiBeforeGroupV1();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+            Assert.AreEqual((byte)'E', decoder.GetD(0));
+            Assert.AreEqual((byte)'U', decoder.GetD(1));
+            Assert.AreEqual((byte)'R', decoder.GetD(2));
+            Assert.AreEqual((byte)'U', decoder.GetD(3));
+            Assert.AreEqual((byte)'S', decoder.GetD(4));
+            Assert.AreEqual((byte)'D', decoder.GetD(5));
+            var bDecoder = decoder.B;
+            Assert.AreEqual(1, bDecoder.Count);
+            Assert.AreEqual(2, bDecoder.Next().C);
+        }
+
+        [TestMethod]
+        public void AllowsNewDecoderToDecodeAddedAsciiFieldBeforeGroup3()
+        {
+            var encoder = new AddAsciiBeforeGroupV1();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            encoder.SetD("EURUSD");
+            var bEncoder = encoder.BCount(1);
+            bEncoder.Next();
+            bEncoder.C = 2;
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new AddAsciiBeforeGroupV1();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+            Assert.AreEqual((byte)'E', decoder.D[0]);
+            Assert.AreEqual((byte)'U', decoder.D[1]);
+            Assert.AreEqual((byte)'R', decoder.D[2]);
+            Assert.AreEqual((byte)'U', decoder.D[3]);
+            Assert.AreEqual((byte)'S', decoder.D[4]);
+            Assert.AreEqual((byte)'D', decoder.D[5]);
+            var bDecoder = decoder.B;
+            Assert.AreEqual(1, bDecoder.Count);
+            Assert.AreEqual(2, bDecoder.Next().C);
+        }
+
+        [TestMethod]
+        public void AllowsNewDecoderToDecodeMissingAsciiFieldBeforeGroupAsNullValue1()
+        {
+            var encoder = new AddAsciiBeforeGroupV0();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            var bEncoder = encoder.BCount(1);
+            bEncoder.Next();
+            bEncoder.C = 2;
+            encoder.CheckEncodingIsComplete();
+
+            ModifyHeaderToLookLikeVersion0();
+
+            var decoder = new AddAsciiBeforeGroupV1();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+            Assert.AreEqual(AddAsciiBeforeGroupV1.DNullValue, decoder.GetD(0));
+            Assert.AreEqual(AddAsciiBeforeGroupV1.DNullValue, decoder.GetD(1));
+            Assert.AreEqual(AddAsciiBeforeGroupV1.DNullValue, decoder.GetD(2));
+            Assert.AreEqual(AddAsciiBeforeGroupV1.DNullValue, decoder.GetD(3));
+            Assert.AreEqual(AddAsciiBeforeGroupV1.DNullValue, decoder.GetD(4));
+            Assert.AreEqual(AddAsciiBeforeGroupV1.DNullValue, decoder.GetD(5));
+            var bDecoder = decoder.B;
+            Assert.AreEqual(1, bDecoder.Count);
+            Assert.AreEqual(2, bDecoder.Next().C);
+        }
+
+        [TestMethod]
+        public void AllowsNewDecoderToDecodeMissingAsciiFieldBeforeGroupAsNullValue2()
+        {
+            var encoder = new AddAsciiBeforeGroupV0();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            var bEncoder = encoder.BCount(1);
+            bEncoder.Next();
+            bEncoder.C = 2;
+            encoder.CheckEncodingIsComplete();
+
+            ModifyHeaderToLookLikeVersion0();
+
+            var decoder = new AddAsciiBeforeGroupV1();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+            Assert.AreEqual(0, decoder.D.Length);
+            var bDecoder = decoder.B;
+            Assert.AreEqual(1, bDecoder.Count);
+            Assert.AreEqual(2, bDecoder.Next().C);
+        }
+
+        [TestMethod]
+        public void AllowsNewDecoderToDecodeMissingAsciiFieldBeforeGroupAsNullValue3()
+        {
+            var encoder = new AddAsciiBeforeGroupV0();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            var bEncoder = encoder.BCount(1);
+            bEncoder.Next();
+            bEncoder.C = 2;
+            encoder.CheckEncodingIsComplete();
+
+            ModifyHeaderToLookLikeVersion0();
+
+            var decoder = new AddAsciiBeforeGroupV1();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+            Assert.AreEqual(0, decoder.GetD(new byte[16]));
+            var bDecoder = decoder.B;
+            Assert.AreEqual(1, bDecoder.Count);
+            Assert.AreEqual(2, bDecoder.Next().C);
+        }
+
+        [TestMethod]
+        public void AllowsNewDecoderToDecodeMissingAsciiFieldBeforeGroupAsNullValue4()
+        {
+            var encoder = new AddAsciiBeforeGroupV0();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            var bEncoder = encoder.BCount(1);
+            bEncoder.Next();
+            bEncoder.C = 2;
+            encoder.CheckEncodingIsComplete();
+
+            ModifyHeaderToLookLikeVersion0();
+
+            var decoder = new AddAsciiBeforeGroupV1();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+            Assert.AreEqual(0, decoder.GetD(new byte[16], 0));
+            var bDecoder = decoder.B;
+            Assert.AreEqual(1, bDecoder.Count);
+            Assert.AreEqual(2, bDecoder.Next().C);
+        }
+
+        [TestMethod]
+        public void AllowsNewDecoderToSkipPresentButAddedAsciiFieldBeforeGroup()
+        {
+            var encoder = new AddAsciiBeforeGroupV1();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            encoder.SetD("EURUSD");
+            var bEncoder = encoder.BCount(1);
+            bEncoder.Next();
+            bEncoder.C = 2;
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new AddAsciiBeforeGroupV1();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+            var bDecoder = decoder.B;
+            Assert.AreEqual(1, bDecoder.Count);
+            Assert.AreEqual(2, bDecoder.Next().C);
+        }
+
+        [TestMethod]
+        public void AllowsOldDecoderToSkipAddedAsciiFieldBeforeGroup()
+        {
+            var encoder = new AddAsciiBeforeGroupV1();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            encoder.SetD("EURUSD");
+            var bEncoder = encoder.BCount(1);
+            bEncoder.Next();
+            bEncoder.C = 2;
+            encoder.CheckEncodingIsComplete();
+
+            ModifyHeaderToLookLikeVersion1();
+
+            var decoder = new AddAsciiBeforeGroupV0();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+            var bDecoder = decoder.B;
+            Assert.AreEqual(1, bDecoder.Count);
+            Assert.AreEqual(2, bDecoder.Next().C);
+        }
+
+        [TestMethod]
+        public void AllowsEncodeAndDecodeOfMessagesWithNoABlock()
+        {
+            var encoder = new NoBlock();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.SetA("abc");
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new NoBlock();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual("abc", decoder.GetA());
+        }
+
+        [TestMethod]
+        public void AllowsEncodeAndDecodeOfGroupsWithNoBlock()
+        {
+            var encoder = new GroupWithNoBlock();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            var aEncoder = encoder.ACount(1);
+            aEncoder.Next();
+            aEncoder.SetB("abc");
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new GroupWithNoBlock();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            var aDecoder = decoder.A;
+            Assert.AreEqual(1, aDecoder.Count);
+            Assert.AreEqual("abc", aDecoder.Next().GetB());
+        }
+
+        [TestMethod]
+        public void DisallowsEncodingElementOfEmptyGroup1()
+        {
+            var encoder = new MultipleGroups();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 42;
+            var bEncoder = encoder.BCount(0);
+            var dEncoder = encoder.DCount(1);
+            dEncoder.Next();
+            dEncoder.E = 43;
+
+            var ex = Assert.ThrowsException(() => bEncoder.C = 44);
+            Assert.IsTrue(ex.Message.Contains("Cannot access field \"b.c\" in state: V0_D_1_BLOCK"));
+        }
+
+        [TestMethod]
+        public void DisallowsEncodingElementOfEmptyGroup2()
+        {
+            var encoder = new NestedGroups();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 42;
+            var bEncoder = encoder.BCount(1);
+            bEncoder.Next();
+            bEncoder.C = 43;
+            var dEncoder = bEncoder.DCount(0);
+            bEncoder.FCount(0);
+            encoder.HCount(0);
+
+            var ex = Assert.ThrowsException(() => dEncoder.E = 44);
+            Assert.IsTrue(ex.Message.Contains("Cannot access field \"b.d.e\" in state: V0_H_0"));
+        }
+
+        [TestMethod]
+        public void DisallowsEncodingElementOfEmptyGroup3()
+        {
+            var encoder = new NestedGroups();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 42;
+            var bEncoder = encoder.BCount(1);
+            bEncoder.Next();
+            bEncoder.C = 43;
+            var dEncoder = bEncoder.DCount(0);
+            bEncoder.FCount(0);
+            encoder.HCount(0);
+
+            var ex = Assert.ThrowsException(() => dEncoder.E = 44);
+            Assert.IsTrue(ex.Message.Contains("Cannot access field \"b.d.e\" in state: V0_H_0"));
+        }
+
+        [TestMethod]
+        public void DisallowsEncodingElementOfEmptyGroup4()
+        {
+            var encoder = new NestedGroups();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 42;
+            var bEncoder = encoder.BCount(1);
+            bEncoder.Next();
+            bEncoder.C = 43;
+            var dEncoder = bEncoder.DCount(0);
+            bEncoder.FCount(0);
+
+            var ex = Assert.ThrowsException(() => dEncoder.E = 44);
+            Assert.IsTrue(ex.Message.Contains("Cannot access field \"b.d.e\" in state: V0_B_1_F_0"));
+        }
+
+
+        [TestMethod]
+        public void DisallowsEncodingElementOfEmptyGroup5()
+        {
+            var encoder = new AddPrimitiveInsideGroupV1();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 42;
+            var bEncoder = encoder.BCount(0);
+            var exception = Assert.ThrowsException(() => bEncoder.C = 43);
+            Assert.IsTrue(exception.Message.Contains("Cannot access field \"b.c\" in state: V1_B_0"));
+        }
+
+        [TestMethod]
+        public void DisallowsEncodingElementOfEmptyGroup6()
+        {
+            var encoder = new GroupAndVarLength();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 42;
+            var bEncoder = encoder.BCount(0);
+            encoder.SetD("abc");
+            var exception = Assert.ThrowsException(() => bEncoder.C = 43);
+            Assert.IsTrue(exception.Message.Contains("Cannot access field \"b.c\" in state: V0_D_DONE"));
+        }
+
+        [TestMethod]
+        public void AllowsEncodingAndDecodingNestedGroupWithVarDataInSchemaDefinedOrder()
+        {
+            var encoder = new NestedGroupWithVarLength();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            var bEncoder = encoder.BCount(3);
+            var bNext = bEncoder.Next();
+            bNext.C = 2;
+            bNext.DCount(0);
+            bNext = bEncoder.Next();
+            bNext.C = 3;
+            var dEncoder = bNext.DCount(1);
+            var dNext = dEncoder.Next();
+            dNext.E = 4;
+            dEncoder.SetF("abc");
+            bNext = bEncoder.Next();
+            bNext.C = 5;
+            dEncoder = bNext.DCount(2);
+            dNext = dEncoder.Next();
+            dNext.E = 6;
+            dEncoder.SetF("def");
+            dNext = dEncoder.Next();
+            dNext.E = 7;
+            dEncoder.SetF("ghi");
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new NestedGroupWithVarLength();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+            var bDecoder = decoder.B;
+            Assert.AreEqual(3, bDecoder.Count);
+            Assert.AreEqual(2, bDecoder.Next().C);
+            Assert.AreEqual(0, bDecoder.D.Count);
+            Assert.AreEqual(3, bDecoder.Next().C);
+            var dDecoder = bDecoder.D;
+            Assert.AreEqual(1, dDecoder.Count);
+            Assert.AreEqual(4, dDecoder.Next().E);
+            Assert.AreEqual("abc", dDecoder.GetF());
+            Assert.AreEqual(5, bDecoder.Next().C);
+            Assert.AreEqual(dDecoder, bDecoder.D);
+            Assert.AreEqual(2, dDecoder.Count);
+            Assert.AreEqual(6, dDecoder.Next().E);
+            Assert.AreEqual("def", dDecoder.GetF());
+            Assert.AreEqual(7, dDecoder.Next().E);
+            Assert.AreEqual("ghi", dDecoder.GetF());
+        }
+
+        [DataTestMethod]
+        [DataRow(1, "V0_B_N_D_1_BLOCK")]
+        [DataRow(2, "V0_B_N_D_N_BLOCK")]
+        public void DisallowsMissedEncodingOfVarLengthFieldInNestedGroupToNextOuterElement(int dCount,
+            string expectedState)
+        {
+            var encoder = new NestedGroupWithVarLength();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            var bEncoder = encoder.BCount(2);
+            var bNext = bEncoder.Next();
+            bNext.C = 3;
+            var dEncoder = bNext.DCount(dCount);
+            dEncoder.Next().E = 4;
+            var exception = Assert.ThrowsException(bEncoder.Next);
+            Assert.IsTrue(exception.Message.Contains("Cannot access next element in repeating group \"b\" in state: " +
+                                                     expectedState));
+            Assert.IsTrue(
+                exception.Message.Contains("Expected one of these transitions: [\"b.d.e(?)\", \"b.d.f(?)\"]."));
+        }
+
+        [TestMethod]
+        public void DisallowsMissedDecodingOfVarLengthFieldInNestedGroupToNextInnerElement1()
+        {
+            var encoder = new NestedGroupWithVarLength();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            var bEncoder = encoder.BCount(1);
+            var bNext = bEncoder.Next();
+            bNext.C = 2;
+            var dEncoder = bNext.DCount(2);
+            var dNext = dEncoder.Next();
+            dNext.E = 3;
+            dEncoder.SetF("abc");
+            dNext = dEncoder.Next();
+            dNext.E = 4;
+            dEncoder.SetF("def");
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new NestedGroupWithVarLength();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+            var bDecoder = decoder.B;
+            Assert.AreEqual(1, bDecoder.Count);
+            Assert.AreEqual(2, bDecoder.Next().C);
+            var dDecoder = bDecoder.D;
+            Assert.AreEqual(2, dDecoder.Count);
+            Assert.AreEqual(3, dDecoder.Next().E);
+            var exception = Assert.ThrowsException(dDecoder.Next);
+            Assert.IsTrue(
+                exception.Message.Contains(
+                    "Cannot access next element in repeating group \"b.d\" in state: V0_B_1_D_N_BLOCK."));
+            Assert.IsTrue(
+                exception.Message.Contains("Expected one of these transitions: [\"b.d.e(?)\", \"b.d.f(?)\"]."));
+        }
+
+        [TestMethod]
+        public void DisallowsMissedDecodingOfVarLengthFieldInNestedGroupToNextInnerElement2()
+        {
+            var encoder = new NestedGroupWithVarLength();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            var bEncoder = encoder.BCount(2);
+            var bNext = bEncoder.Next();
+            bNext.C = 2;
+            var dEncoder = bNext.DCount(2);
+            var dNext = dEncoder.Next();
+            dNext.E = 3;
+            dEncoder.SetF("abc");
+            dNext = dEncoder.Next();
+            dNext.E = 4;
+            dEncoder.SetF("def");
+            bEncoder.Next().C = 5;
+            bEncoder.DCount(0);
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new NestedGroupWithVarLength();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+            var bDecoder = decoder.B;
+            Assert.AreEqual(2, bDecoder.Count);
+            Assert.AreEqual(2, bDecoder.Next().C);
+            var dDecoder = bDecoder.D;
+            Assert.AreEqual(2, dDecoder.Count);
+            Assert.AreEqual(3, dDecoder.Next().E);
+            var exception = Assert.ThrowsException(dDecoder.Next);
+            Assert.IsTrue(
+                exception.Message.Contains(
+                    "Cannot access next element in repeating group \"b.d\" in state: V0_B_N_D_N_BLOCK."));
+            Assert.IsTrue(
+                exception.Message.Contains("Expected one of these transitions: [\"b.d.e(?)\", \"b.d.f(?)\"]."));
+        }
+
+        [TestMethod]
+        public void DisallowsMissedDecodingOfVarLengthFieldInNestedGroupToNextOuterElement1()
+        {
+            var encoder = new NestedGroupWithVarLength();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            var bEncoder = encoder.BCount(2);
+            var bNext = bEncoder.Next();
+            bNext.C = 2;
+            var dEncoder = bNext.DCount(2);
+            var dNext = dEncoder.Next();
+            dNext.E = 3;
+            dEncoder.SetF("abc");
+            dNext = dEncoder.Next();
+            dNext.E = 4;
+            dEncoder.SetF("def");
+            bEncoder.Next().C = 5;
+            bEncoder.DCount(0);
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new NestedGroupWithVarLength();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+            var bDecoder = decoder.B;
+            Assert.AreEqual(2, bDecoder.Count);
+            Assert.AreEqual(2, bDecoder.Next().C);
+            var dDecoder = bDecoder.D;
+            Assert.AreEqual(2, dDecoder.Count);
+            Assert.AreEqual(3, dDecoder.Next().E);
+            var exception = Assert.ThrowsException(bDecoder.Next);
+            Assert.IsTrue(
+                exception.Message.Contains(
+                    "Cannot access next element in repeating group \"b\" in state: V0_B_N_D_N_BLOCK."));
+            Assert.IsTrue(
+                exception.Message.Contains("Expected one of these transitions: [\"b.d.e(?)\", \"b.d.f(?)\"]."));
+        }
+
+        [TestMethod]
+        public void DisallowsMissedDecodingOfVarLengthFieldInNestedGroupToNextOuterElement2()
+        {
+            var encoder = new NestedGroupWithVarLength();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            var bEncoder = encoder.BCount(2);
+            var bNext = bEncoder.Next();
+            bNext.C = 2;
+            var dEncoder = bNext.DCount(1);
+            var dNext = dEncoder.Next();
+            dNext.E = 3;
+            dEncoder.SetF("abc");
+            bEncoder.Next().C = 5;
+            bEncoder.DCount(0);
+            encoder.CheckEncodingIsComplete();
+
+            var decoder = new NestedGroupWithVarLength();
+            decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            Assert.AreEqual(1, decoder.A);
+            var bDecoder = decoder.B;
+            Assert.AreEqual(2, bDecoder.Count);
+            Assert.AreEqual(2, bDecoder.Next().C);
+            var dDecoder = bDecoder.D;
+            Assert.AreEqual(1, dDecoder.Count);
+            Assert.AreEqual(3, dDecoder.Next().E);
+            var exception = Assert.ThrowsException(() => bDecoder.Next());
+            Assert.IsTrue(
+                exception.Message.Contains(
+                    "Cannot access next element in repeating group \"b\" in state: V0_B_N_D_1_BLOCK."));
+            Assert.IsTrue(
+                exception.Message.Contains("Expected one of these transitions: [\"b.d.e(?)\", \"b.d.f(?)\"]."));
+        }
+
+        [TestMethod]
+        public void DisallowsIncompleteMessagesDueToMissingVarLengthField1()
+        {
+            var encoder = new MultipleVarLength();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            encoder.SetB("abc");
+            var exception = Assert.ThrowsException(() => encoder.CheckEncodingIsComplete());
+            Assert.IsTrue(
+                exception.Message.Contains(
+                    "Not fully encoded, current state: V0_B_DONE, allowed transitions: \"c(?)\""));
+        }
+
+        [TestMethod]
+        public void DisallowsIncompleteMessagesDueToMissingVarLengthField2()
+        {
+            var encoder = new NoBlock();
+            encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            var exception = Assert.ThrowsException(() => encoder.CheckEncodingIsComplete());
+            Assert.IsTrue(
+                exception.Message.Contains(
+                    "Not fully encoded, current state: V0_BLOCK, allowed transitions: \"a(?)\""));
+        }
+
+        [TestMethod]
+        public void DisallowsIncompleteMessagesDueToMissingTopLevelGroup1()
+        {
+            var encoder = new MultipleGroups()
+                .WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            encoder.BCount(0);
+            var exception = Assert.ThrowsException(encoder.CheckEncodingIsComplete);
+            StringAssert.Contains(exception.Message,
+                "Not fully encoded, current state: V0_B_0, allowed transitions: \"dCount(0)\", \"dCount(>0)\"");
+        }
+
+        [TestMethod]
+        public void DisallowsIncompleteMessagesDueToMissingTopLevelGroup2()
+        {
+            var encoder = new MultipleGroups()
+                .WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            var bEncoder = encoder.BCount(1).Next();
+            bEncoder.C = 2;
+            var exception = Assert.ThrowsException(encoder.CheckEncodingIsComplete);
+            StringAssert.Contains(exception.Message,
+                "Not fully encoded, current state: V0_B_1_BLOCK, allowed transitions: \"b.c(?)\", \"dCount(0)\", \"dCount(>0)\"");
+        }
+
+        [TestMethod]
+        public void DisallowsIncompleteMessagesDueToMissingTopLevelGroup3()
+        {
+            var encoder = new MultipleGroups()
+                .WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            var exception = Assert.ThrowsException(encoder.CheckEncodingIsComplete);
+            StringAssert.Contains(exception.Message,
+                "Not fully encoded, current state: V0_BLOCK, allowed transitions: \"a(?)\", \"bCount(0)\", \"bCount(>0)\"");
+        }
+
+        [DataTestMethod]
+        [DataRow(1, "V0_B_1_BLOCK")]
+        [DataRow(2, "V0_B_N_BLOCK")]
+        public void DisallowsIncompleteMessagesDueToMissingNestedGroup1(int bCount, string expectedState)
+        {
+            var encoder = new NestedGroupWithVarLength()
+                .WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            var bEncoder = encoder.BCount(bCount).Next();
+            bEncoder.C = 2;
+            var exception = Assert.ThrowsException(encoder.CheckEncodingIsComplete);
+            StringAssert.Contains(exception.Message, $"Not fully encoded, current state: {expectedState}");
+        }
+
+        [DataTestMethod]
+        [DataRow(1, 1, "V0_B_1_D_N")]
+        [DataRow(1, 2, "V0_B_1_D_N")]
+        [DataRow(2, 0, "V0_B_N_D_0")]
+        [DataRow(2, 1, "V0_B_N_D_N")]
+        [DataRow(2, 2, "V0_B_N_D_N")]
+        public void DisallowsIncompleteMessagesDueToMissingNestedGroup2(
+            int bCount,
+            int dCount,
+            string expectedState)
+        {
+            var encoder = new NestedGroupWithVarLength()
+                .WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            var bEncoder = encoder.BCount(bCount).Next();
+            bEncoder.C = 2;
+            bEncoder.DCount(dCount);
+            var exception = Assert.ThrowsException(encoder.CheckEncodingIsComplete);
+            StringAssert.Contains(exception.Message, $"Not fully encoded, current state: {expectedState}");
+        }
+
+        [DataTestMethod]
+        [DataRow(1, 1, "V0_B_1_D_1_BLOCK")]
+        [DataRow(1, 2, "V0_B_1_D_N_BLOCK")]
+        [DataRow(2, 1, "V0_B_N_D_1_BLOCK")]
+        [DataRow(2, 2, "V0_B_N_D_N_BLOCK")]
+        public void DisallowsIncompleteMessagesDueToMissingVarDataInNestedGroup(
+            int bCount,
+            int dCount,
+            string expectedState)
+        {
+            var encoder = new NestedGroupWithVarLength()
+                .WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
+            encoder.A = 1;
+            var bEncoder = encoder.BCount(bCount).Next();
+            bEncoder.C = 2;
+            bEncoder.DCount(dCount).Next().E = 10;
+            var exception = Assert.ThrowsException(encoder.CheckEncodingIsComplete);
+            StringAssert.Contains(exception.Message, $"Not fully encoded, current state: {expectedState}");
+        }
+
+        private void ModifyHeaderToLookLikeVersion0()
+        {
+            var messageHeaderDecoder = new MessageHeader();
+            messageHeaderDecoder.Wrap(_buffer, Offset, MessageHeader.SbeSchemaVersion);
+            int v1TemplateId = messageHeaderDecoder.TemplateId + 1_000;
+            var messageHeaderEncoder = new MessageHeader();
+            messageHeaderEncoder.Wrap(_buffer, Offset, MessageHeader.SbeSchemaVersion);
+            messageHeaderEncoder.TemplateId = (ushort)v1TemplateId;
+            messageHeaderEncoder.Version = 0;
+        }
+
+        private void ModifyHeaderToLookLikeVersion1()
+        {
+            var messageHeaderDecoder = new MessageHeader();
+            messageHeaderDecoder.Wrap(_buffer, Offset, MessageHeader.SbeSchemaVersion);
+            Debug.Assert(messageHeaderDecoder.Version == 1);
+            int v0TemplateId = messageHeaderDecoder.TemplateId - 1_000;
+            var messageHeaderEncoder = new MessageHeader();
+            messageHeaderEncoder.Wrap(_buffer, Offset, MessageHeader.SbeSchemaVersion);
+            messageHeaderEncoder.TemplateId = (ushort)v0TemplateId;
+        }
+    }
+}
\ No newline at end of file
diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java
index acb9821618..85ec82a458 100644
--- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java
+++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java
@@ -130,6 +130,7 @@ public void generate() throws IOException
                 out.append(generateDocumentation(BASE_INDENT, msgToken));
                 out.append(generateClassDeclaration(className));
                 out.append(generateMessageFlyweightCode(className, msgToken, BASE_INDENT));
+                out.append(generateFullyEncodedCheck(BASE_INDENT + INDENT));
 
                 final List messageBody = tokens.subList(1, tokens.size() - 1);
                 int offset = 0;
@@ -1184,16 +1185,17 @@ private CharSequence generateMessageFlyweightCode(final String className, final
             indent + INDENT + "{\n" +
             indent + INDENT + INDENT + "_parentMessage = this;\n" +
             indent + INDENT + "}\n\n" +
-            indent + INDENT + "public void WrapForEncode(DirectBuffer buffer, int offset)\n" +
+            indent + INDENT + "public %10$s WrapForEncode(DirectBuffer buffer, int offset)\n" +
             indent + INDENT + "{\n" +
             indent + INDENT + INDENT + "_buffer = buffer;\n" +
             indent + INDENT + INDENT + "_offset = offset;\n" +
             indent + INDENT + INDENT + "_actingBlockLength = BlockLength;\n" +
             indent + INDENT + INDENT + "_actingVersion = SchemaVersion;\n" +
             indent + INDENT + INDENT + "Limit = offset + _actingBlockLength;\n" +
+            indent + INDENT + INDENT + "return this;\n" +
             indent + INDENT + "}\n\n" +
-            indent + INDENT + "public void WrapForEncodeAndApplyHeader(DirectBuffer buffer, int offset, " +
-                " MessageHeader headerEncoder)\n" +
+            indent + INDENT + "public %10$s WrapForEncodeAndApplyHeader(DirectBuffer buffer, int offset, " +
+                "MessageHeader headerEncoder)\n" +
             indent + INDENT + "{\n" +
             indent + INDENT + INDENT + "headerEncoder.Wrap(buffer, offset, SchemaVersion);\n" +
             indent + INDENT + INDENT + "headerEncoder.BlockLength = BlockLength;\n" +
@@ -1201,9 +1203,9 @@ private CharSequence generateMessageFlyweightCode(final String className, final
             indent + INDENT + INDENT + "headerEncoder.SchemaId = SchemaId;\n" +
             indent + INDENT + INDENT + "headerEncoder.Version = SchemaVersion;\n" +
             indent + INDENT + INDENT + "\n" +
-            indent + INDENT + INDENT + "WrapForEncode(buffer, offset + MessageHeader.Size);\n" +
+            indent + INDENT + INDENT + "return WrapForEncode(buffer, offset + MessageHeader.Size);\n" +
             indent + INDENT + "}\n\n" +
-            indent + INDENT + "public void WrapForDecode(DirectBuffer buffer, int offset, " +
+            indent + INDENT + "public %10$s WrapForDecode(DirectBuffer buffer, int offset, " +
                 "int actingBlockLength, int actingVersion)\n" +
             indent + INDENT + "{\n" +
             indent + INDENT + INDENT + "_buffer = buffer;\n" +
@@ -1211,6 +1213,15 @@ private CharSequence generateMessageFlyweightCode(final String className, final
             indent + INDENT + INDENT + "_actingBlockLength = actingBlockLength;\n" +
             indent + INDENT + INDENT + "_actingVersion = actingVersion;\n" +
             indent + INDENT + INDENT + "Limit = offset + _actingBlockLength;\n" +
+            indent + INDENT + INDENT + "return this;\n" +
+            indent + INDENT + "}\n\n" +
+            indent + INDENT + "public %10$s WrapForDecodeAndApplyHeader(DirectBuffer buffer, int offset, " +
+                "MessageHeader headerDecoder)\n" +
+            indent + INDENT + "{\n" +
+            indent + INDENT + INDENT + "headerDecoder.Wrap(buffer, offset, SchemaVersion);\n" +
+            indent + INDENT + INDENT + "\n" +
+            indent + INDENT + INDENT + "return WrapForDecode(buffer, offset + MessageHeader.Size, " +
+            " headerDecoder.BlockLength, headerDecoder.Version);\n" +
             indent + INDENT + "}\n\n" +
             indent + INDENT + "public int Size\n" +
             indent + INDENT + "{\n" +
@@ -1244,6 +1255,17 @@ private CharSequence generateMessageFlyweightCode(final String className, final
             semanticVersion);
     }
 
+    private CharSequence generateFullyEncodedCheck(final String indent)
+    {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("\n");
+        sb.append(indent).append("public void CheckEncodingIsComplete()\n");
+        sb.append(indent).append("{\n");
+        sb.append(indent).append("    // TODO\n");
+        sb.append(indent).append("}\n");
+        return sb;
+    }
+
     private CharSequence generateFields(final List tokens, final String indent)
     {
         final StringBuilder sb = new StringBuilder();
@@ -1715,11 +1737,13 @@ private int writeTokenDisplay(
                     {
                         if (typeToken.encoding().primitiveType() == PrimitiveType.CHAR)
                         {
+                            append(sb, indent, "builder.Append(\"'\");");
                             append(sb, indent, "for (int i = 0; i < " + fieldName +
                                 "Length && this.Get" + fieldName + "(i) > 0; ++i)");
                             append(sb, indent, "{");
                             append(sb, indent, "    builder.Append((char)this.Get" + fieldName + "(i));");
                             append(sb, indent, "}");
+                            append(sb, indent, "builder.Append(\"'\");");
                         }
                         else
                         {

From f8763476dcb885e34139888da49bf113d45d5932 Mon Sep 17 00:00:00 2001
From: Zach Bray 
Date: Tue, 27 Jun 2023 17:19:33 +0100
Subject: [PATCH 32/50] [Java] Fix indent.

---
 .../uk/co/real_logic/sbe/generation/java/JavaGenerator.java     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java
index f001e84480..0bc3bd1494 100644
--- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java
+++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java
@@ -633,7 +633,7 @@ private static void generateAccessOrderListenerMethodForNextGroupElement(
             accessOrderModel,
             selectNextElementInGroup);
 
-        sb.append(indent).append("   }\n")
+        sb.append(indent).append("    }\n")
             .append(indent).append("    else if (remaining == 1)\n")
             .append(indent).append("    {\n");
 

From 18967f6911cc3eea0daa27155bfdde118c902b91 Mon Sep 17 00:00:00 2001
From: Zach Bray 
Date: Tue, 27 Jun 2023 17:20:10 +0100
Subject: [PATCH 33/50] [C#,Java] Port access order checking implementation to
 C#.

Tests remain ignored due to two pre-existing problems in the C# codecs:

1. `ToString()` alters the codec state, e.g., `_limit`.
2. Some accessors do not check the field is present in the version being
decoded.
---
 csharp/sbe-generated/sbe-generated.csproj     |   8 +
 .../sbe-tests/FieldAccessOrderCheckTests.cs   |  32 +-
 .../{java => common}/AccessOrderModel.java    | 255 +++++--
 .../generation/csharp/CSharpGenerator.java    | 640 ++++++++++++++++--
 .../sbe/generation/java/JavaGenerator.java    |   8 +-
 5 files changed, 833 insertions(+), 110 deletions(-)
 rename sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/{java => common}/AccessOrderModel.java (74%)

diff --git a/csharp/sbe-generated/sbe-generated.csproj b/csharp/sbe-generated/sbe-generated.csproj
index 6b4fc7bdbc..362d85ba05 100644
--- a/csharp/sbe-generated/sbe-generated.csproj
+++ b/csharp/sbe-generated/sbe-generated.csproj
@@ -7,6 +7,14 @@
     Copyright (C) Bill Segall 2018, MarketFactory Inc 2017, Adaptive 2014. All rights reserved.
   
 
+  
+    TRACE,ENABLE_ACCESS_ORDER_CHECKS
+  
+
+  
+    TRACE,ENABLE_ACCESS_ORDER_CHECKS
+  
+
   
     
   
diff --git a/csharp/sbe-tests/FieldAccessOrderCheckTests.cs b/csharp/sbe-tests/FieldAccessOrderCheckTests.cs
index 039ac759f1..5cb475bfc7 100644
--- a/csharp/sbe-tests/FieldAccessOrderCheckTests.cs
+++ b/csharp/sbe-tests/FieldAccessOrderCheckTests.cs
@@ -218,7 +218,7 @@ public void DisallowsEncodingGroupElementBeforeCallingNext()
                 .WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
             encoder.A = 42;
             var bEncoder = encoder.BCount(1);
-            var exception = Assert.ThrowsException(() => bEncoder.C = 1);
+            var exception = Assert.ThrowsException(() => bEncoder.C = 1);
             Assert.IsTrue(exception.Message.Contains("Cannot access field \"b.c\" in state: V0_B_N"));
         }
 
@@ -242,7 +242,7 @@ public void DisallowsDecodingGroupElementBeforeCallingNext()
             Assert.AreEqual(42, decoder.A);
             var bs = decoder.B;
             Assert.AreEqual(2, bs.Count);
-            var exception = Assert.ThrowsException(() => _ = bs.C);
+            var exception = Assert.ThrowsException(() => _ = bs.C);
             Assert.IsTrue(exception.Message.Contains("Cannot access field \"b.c\" in state: V0_B_N"));
         }
 
@@ -252,7 +252,7 @@ public void DisallowsSkippingEncodingOfGroup()
             var encoder = new GroupAndVarLength()
                 .WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
             encoder.A = 42;
-            var exception = Assert.ThrowsException(() => encoder.SetD("abc"));
+            var exception = Assert.ThrowsException(() => encoder.SetD("abc"));
             Assert.IsTrue(exception.Message.Contains("Cannot access field \"d\" in state: V0_BLOCK"));
         }
 
@@ -269,7 +269,7 @@ public void DisallowsReEncodingVariableLengthFieldAfterGroup()
             bEncoder.Next()
                 .C = 2;
             encoder.SetD("abc");
-            var exception = Assert.ThrowsException(() => encoder.SetD("def"));
+            var exception = Assert.ThrowsException(() => encoder.SetD("def"));
             Assert.IsTrue(exception.Message.Contains("Cannot access field \"d\" in state: V0_D_DONE"));
         }
 
@@ -286,7 +286,7 @@ public void DisallowsReEncodingGroupCount()
             bEncoder.Next()
                 .C = 2;
             encoder.SetD("abc");
-            var exception = Assert.ThrowsException(() => encoder.BCount(1));
+            var exception = Assert.ThrowsException(() => encoder.BCount(1));
             Assert.IsTrue(
                 exception.Message.Contains("Cannot encode count of repeating group \"b\" in state: V0_D_DONE"));
         }
@@ -310,7 +310,7 @@ public void DisallowsMissedDecodingOfGroupBeforeVariableLengthField()
             var decoder = new GroupAndVarLength()
                 .WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader);
             Assert.AreEqual(42, decoder.A);
-            var exception = Assert.ThrowsException(() => decoder.GetD());
+            var exception = Assert.ThrowsException(() => decoder.GetD());
             Assert.IsTrue(exception.Message.Contains("Cannot access field \"d\" in state: V0_BLOCK"));
         }
 
@@ -338,7 +338,7 @@ public void DisallowsReDecodingVariableLengthFieldAfterGroup()
             Assert.AreEqual(1, bs.Next().C);
             Assert.AreEqual(2, bs.Next().C);
             Assert.AreEqual("abc", decoder.GetD());
-            var exception = Assert.ThrowsException(() => decoder.GetD());
+            var exception = Assert.ThrowsException(() => decoder.GetD());
             Assert.IsTrue(exception.Message.Contains("Cannot access field \"d\" in state: V0_D_DONE"));
         }
 
@@ -366,7 +366,7 @@ public void DisallowsReDecodingGroupAfterVariableLengthField()
             Assert.AreEqual(1, bs.Next().C);
             Assert.AreEqual(2, bs.Next().C);
             Assert.AreEqual("abc", decoder.GetD());
-            var exception = Assert.ThrowsException(() => decoder.B);
+            var exception = Assert.ThrowsException(() => decoder.B);
             Assert.IsTrue(
                 exception.Message.Contains("Cannot decode count of repeating group \"b\" in state: V0_D_DONE"));
         }
@@ -411,7 +411,7 @@ public void DisallowsMissedGroupElementVariableLengthFieldToEncodeAtTopLevel()
                 .WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
             encoder.A = 42;
             encoder.BCount(1).Next().C = 1;
-            var exception = Assert.ThrowsException(() => encoder.SetE("abc"));
+            var exception = Assert.ThrowsException(() => encoder.SetE("abc"));
             Assert.IsTrue(exception.Message.Contains("Cannot access field \"e\" in state: V0_B_1_BLOCK"));
         }
 
@@ -422,7 +422,7 @@ public void DisallowsMissedGroupElementVariableLengthFieldToEncodeNextElement()
                 .WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader);
             encoder.A = 42;
             var bEncoder = encoder.BCount(2).Next();
-            var exception = Assert.ThrowsException(() => bEncoder.Next());
+            var exception = Assert.ThrowsException(() => bEncoder.Next());
             Assert.IsTrue(
                 exception.Message.Contains(
                     "Cannot access next element in repeating group \"b\" in state: V0_B_N_BLOCK"));
@@ -437,7 +437,7 @@ public void DisallowsMissedGroupElementEncoding()
             var bEncoder = encoder.BCount(2);
             bEncoder.Next().C = 1;
             bEncoder.SetD("abc");
-            var exception = Assert.ThrowsException(() => encoder.SetE("abc"));
+            var exception = Assert.ThrowsException(() => encoder.SetE("abc"));
             Assert.IsTrue(exception.Message.Contains("Cannot access field \"e\" in state: V0_B_N_D_DONE"));
         }
 
@@ -451,7 +451,7 @@ public void DisallowsReEncodingGroupElementVariableLengthField()
             bEncoder.C = 1;
             bEncoder.SetD("abc");
             encoder.SetE("def");
-            var exception = Assert.ThrowsException(() => bEncoder.SetD("ghi"));
+            var exception = Assert.ThrowsException(() => bEncoder.SetD("ghi"));
             Assert.IsTrue(exception.Message.Contains("Cannot access field \"b.d\" in state: V0_E_DONE"));
         }
 
@@ -476,7 +476,7 @@ public void DisallowsReDecodingGroupElementVariableLengthField()
             Assert.AreEqual(2, bDecoder.Count);
             Assert.AreEqual(1, bDecoder.Next().C);
             Assert.AreEqual("abc", bDecoder.GetD());
-            var exception = Assert.ThrowsException(() => bDecoder.GetD());
+            var exception = Assert.ThrowsException(() => bDecoder.GetD());
             Assert.IsTrue(exception.Message.Contains("Cannot access field \"b.d\" in state: V0_B_N_D_DONE"));
         }
 
@@ -500,7 +500,7 @@ public void DisallowsMissedDecodingOfGroupElementVariableLengthFieldToNextElemen
             var bDecoder = decoder.B;
             Assert.AreEqual(2, bDecoder.Count);
             Assert.AreEqual(1, bDecoder.Next().C);
-            var exception = Assert.ThrowsException(() => bDecoder.Next());
+            var exception = Assert.ThrowsException(() => bDecoder.Next());
             Assert.IsTrue(
                 exception.Message.Contains(
                     "Cannot access next element in repeating group \"b\" in state: V0_B_N_BLOCK"));
@@ -524,7 +524,7 @@ public void DisallowsMissedDecodingOfGroupElementVariableLengthFieldToTopLevel()
             var bDecoder = decoder.B;
             Assert.AreEqual(1, bDecoder.Count);
             Assert.AreEqual(1, bDecoder.Next().C);
-            var exception = Assert.ThrowsException(() => decoder.GetE());
+            var exception = Assert.ThrowsException(() => decoder.GetE());
             Assert.IsTrue(exception.Message.Contains("Cannot access field \"e\" in state: V0_B_1_BLOCK"));
         }
 
@@ -548,7 +548,7 @@ public void DisallowsMissedDecodingOfGroupElement()
             var bDecoder = decoder.B;
             Assert.AreEqual(2, bDecoder.Count);
             Assert.AreEqual(1, bDecoder.Next().C);
-            var exception = Assert.ThrowsException(() => decoder.GetE());
+            var exception = Assert.ThrowsException(() => decoder.GetE());
             Assert.IsTrue(exception.Message.Contains("Cannot access field \"e\" in state: V0_B_N_BLOCK"));
         }
 
diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/AccessOrderModel.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/AccessOrderModel.java
similarity index 74%
rename from sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/AccessOrderModel.java
rename to sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/AccessOrderModel.java
index 846ed49fdd..b00f631978 100644
--- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/AccessOrderModel.java
+++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/AccessOrderModel.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package uk.co.real_logic.sbe.generation.java;
+package uk.co.real_logic.sbe.generation.common;
 
 import uk.co.real_logic.sbe.generation.Generators;
 import uk.co.real_logic.sbe.ir.Signal;
@@ -32,10 +32,13 @@
 import static uk.co.real_logic.sbe.ir.GenerationUtil.collectGroups;
 import static uk.co.real_logic.sbe.ir.GenerationUtil.collectVarData;
 
+/**
+ * A state machine that models whether codec interactions are safe.
+ */
+@SuppressWarnings("CodeBlock2Expr")
 // Lambdas without braces tend to conflict with the indentation Checkstyle expects.
 // Therefore, we allow lambdas with code blocks even when a lambda expression is possible.
-@SuppressWarnings("CodeBlock2Expr")
-final class AccessOrderModel
+public final class AccessOrderModel
 {
     private static final boolean GENERATE_ACCESS_ORDER_CHECKS = Boolean.parseBoolean(
         System.getProperty("sbe.generate.access.order.checks", "true"));
@@ -50,12 +53,26 @@ final class AccessOrderModel
     private State encoderWrappedState;
     private Set terminalEncoderStates;
 
-    static boolean generateAccessOrderChecks()
+    /**
+     * Whether to generate access order checks.
+     * @return whether to generate access order checks.
+     */
+    public static boolean generateAccessOrderChecks()
     {
         return GENERATE_ACCESS_ORDER_CHECKS;
     }
 
-    static AccessOrderModel newInstance(
+    /**
+     * Builds a state machine that models whether codec interactions are safe.
+     *
+     * @param msgToken the message token
+     * @param fields the fields in the message
+     * @param groups the groups in the message
+     * @param varData the varData in the message
+     * @param versionsSelector a function that selects the versions to model in the state machine
+     * @return the access order model
+     */
+    public static AccessOrderModel newInstance(
         final Token msgToken,
         final List fields,
         final List groups,
@@ -67,17 +84,29 @@ static AccessOrderModel newInstance(
         return model;
     }
 
-    State notWrappedState()
+    /**
+     * The initial state before a codec is wrapped.
+     * @return the initial state before a codec is wrapped.
+     */
+    public State notWrappedState()
     {
         return notWrappedState;
     }
 
-    State latestVersionWrappedState()
+    /**
+     * The state after a codec is wrapped in the latest version.
+     * @return the state after a codec is wrapped in the latest version.
+     */
+    public State latestVersionWrappedState()
     {
         return encoderWrappedState;
     }
 
-    void forEachDecoderWrappedState(final IntObjConsumer consumer)
+    /**
+     * Iterates over the states after a codec is wrapped over a particular version of data.
+     * @param consumer the consumer of the states.
+     */
+    public void forEachWrappedStateByVersion(final IntObjConsumer consumer)
     {
         final Int2ObjectHashMap.EntryIterator iterator = versionWrappedStates.entrySet().iterator();
         while (iterator.hasNext())
@@ -87,35 +116,59 @@ void forEachDecoderWrappedState(final IntObjConsumer consumer)
         }
     }
 
-    void forEachTerminalEncoderState(final Consumer consumer)
+    /**
+     * Iterates over the states in which a codec is fully encoded.
+     * @param consumer the consumer of the states.
+     */
+    public void forEachTerminalEncoderState(final Consumer consumer)
     {
         terminalEncoderStates.forEach(consumer);
     }
 
-    void forEachStateOrderedByStateNumber(final Consumer consumer)
+    /**
+     * Iterates over the codec states in the order of their state numbers.
+     * @param consumer the consumer of the states.
+     */
+    public void forEachStateOrderedByStateNumber(final Consumer consumer)
     {
         transitionsByState.keySet().stream()
             .sorted(Comparator.comparingInt(s -> s.number))
             .forEach(consumer);
     }
 
-    CodecInteraction.HashConsingFactory interactionFactory()
+    /**
+     * Returns a hash-consing factory for codec interactions.
+     * These interactions are the transitions in the state machine.
+     * @see CodecInteraction
+     * @return a hash-consing factory for codec interactions.
+     */
+    public CodecInteraction.HashConsingFactory interactionFactory()
     {
         return interactionFactory;
     }
 
-    void getTransitions(
-        final List transitionsOut,
-        final CodecInteraction interaction)
+    /**
+     * Iterates over the possible state machine transitions due to the supplied codec interaction.
+     * @param interaction a codec interaction.
+     * @param consumer the consumer of the transitions.
+     */
+    public void forEachTransition(
+        final CodecInteraction interaction,
+        final Consumer consumer)
     {
         final List transitionsForContext = transitionsByInteraction.get(interaction);
         if (null != transitionsForContext)
         {
-            transitionsOut.addAll(transitionsForContext);
+            transitionsForContext.forEach(consumer);
         }
     }
 
-    void forEachTransitionFrom(final State state, final Consumer consumer)
+    /**
+     * Finds the possible transitions from a given state.
+     * @param state the state to find transitions from.
+     * @param consumer the consumer of the transitions.
+     */
+    public void forEachTransitionFrom(final State state, final Consumer consumer)
     {
         final List transitionGroups = transitionsByState.get(state);
         if (null != transitionGroups)
@@ -124,8 +177,13 @@ void forEachTransitionFrom(final State state, final Consumer co
         }
     }
 
+    /**
+     * Encodes the state machine as a graphviz dot diagram.
+     * @param sb the string builder to append to.
+     * @param indent the indentation to use.
+     */
     @SuppressWarnings("SameParameterValue")
-    void generateGraph(final StringBuilder sb, final String indent)
+    public void generateGraph(final StringBuilder sb, final String indent)
     {
         sb.append(indent).append("digraph G {\n");
         transitionsByInteraction.values().forEach(transitionsForContext ->
@@ -344,6 +402,7 @@ private String currentGroupPath()
         }
     }
 
+    @SuppressWarnings("ClassCanBeRecord")
     private static final class VersionCollector implements SchemaConsumer
     {
         private final IntHashSet versions;
@@ -496,7 +555,10 @@ private Set exitStates()
         }
     }
 
-    static final class State
+    /**
+     * A state in which a codec may reside.
+     */
+    public static final class State
     {
         private final int number;
         private final String name;
@@ -512,12 +574,16 @@ private State(final int number, final String name)
          * and start at 0. This numbering scheme allows easy generation of lookup tables.
          * @return the state number
          */
-        int number()
+        public int number()
         {
             return number;
         }
 
-        String name()
+        /**
+         * Returns the name of the state.
+         * @return the name of the state.
+         */
+        public String name()
         {
             return name;
         }
@@ -532,7 +598,10 @@ public String toString()
         }
     }
 
-    static final class TransitionGroup
+    /**
+     * A group of transitions that share the same end state.
+     */
+    public static final class TransitionGroup
     {
         private final CodecInteraction interaction;
         private final Set from;
@@ -548,17 +617,30 @@ private TransitionGroup(
             this.to = to;
         }
 
-        void forEachStartState(final Consumer consumer)
+        /**
+         * Iterate over the start states of the transitions in this group.
+         * @param consumer the consumer of the start states.
+         */
+        public void forEachStartState(final Consumer consumer)
         {
             from.forEach(consumer);
         }
 
-        State endState()
+        /**
+         * Returns the end state of the transitions in this group.
+         * @return the end state of the transitions in this group.
+         */
+        public State endState()
         {
             return to;
         }
 
-        String exampleCode()
+        /**
+         * Returns some example code for the codec interaction that the transitions in this group share.
+         * Useful for producing error messages.
+         * @return some example code for the codec interaction that the transitions in this group share.
+         */
+        public String exampleCode()
         {
             return interaction.exampleCode();
         }
@@ -574,18 +656,30 @@ public String toString()
         }
     }
 
-    abstract static class CodecInteraction
+    /**
+     * Represents an interaction against a codec, e.g., {@code encoder.wrap(...)} or {@code decoder.myVarData()}.
+     */
+    public abstract static class CodecInteraction
     {
-        abstract String groupQualifiedName();
+        /**
+         * Returns a name for the interaction qualified by any groups that it is nested within.
+         * @return a name for the interaction qualified by any groups that it is nested within.
+         */
+        public abstract String groupQualifiedName();
 
         abstract String exampleCode();
 
         abstract String exampleConditions();
 
-        final boolean isTopLevelBlockFieldAccess()
+        /**
+         * Returns {@code true} if this interaction is a top-level block field access; {@code false} otherwise.
+         * @return {@code true} if this interaction is a top-level block field access; {@code false} otherwise.
+         */
+        public final boolean isTopLevelBlockFieldAccess()
         {
             if (this instanceof AccessField)
             {
+                //noinspection PatternVariableCanBeUsed
                 final AccessField accessField = (AccessField)this;
                 return accessField.isTopLevelBlockField();
             }
@@ -606,19 +700,19 @@ private static final class Wrap extends CodecInteraction
             }
 
             @Override
-            String groupQualifiedName()
+            public String groupQualifiedName()
             {
                 return "wrap";
             }
 
             @Override
-            String exampleCode()
+            public String exampleCode()
             {
                 return "wrap(version=" + version + ")";
             }
 
             @Override
-            String exampleConditions()
+            public String exampleConditions()
             {
                 return "";
             }
@@ -648,19 +742,19 @@ boolean isTopLevelBlockField()
             }
 
             @Override
-            String groupQualifiedName()
+            public String groupQualifiedName()
             {
                 return groupPath + token.name();
             }
 
             @Override
-            String exampleCode()
+            public String exampleCode()
             {
                 return groupPath + token.name() + "(?)";
             }
 
             @Override
-            String exampleConditions()
+            public String exampleConditions()
             {
                 return "";
             }
@@ -683,7 +777,7 @@ private DetermineGroupIsEmpty(final String groupPath, final Token token)
             }
 
             @Override
-            String groupQualifiedName()
+            public String groupQualifiedName()
             {
                 return groupPath + token.name();
             }
@@ -718,7 +812,7 @@ private DetermineGroupHasElements(final String groupPath, final Token token)
             }
 
             @Override
-            String groupQualifiedName()
+            public String groupQualifiedName()
             {
                 return groupPath + token.name();
             }
@@ -754,7 +848,7 @@ private MoveToNextElement(final String groupPath, final Token token)
             }
 
             @Override
-            String groupQualifiedName()
+            public String groupQualifiedName()
             {
                 return groupPath + token.name();
             }
@@ -790,7 +884,7 @@ private MoveToLastElement(final String groupPath, final Token token)
             }
 
             @Override
-            String groupQualifiedName()
+            public String groupQualifiedName()
             {
                 return groupPath + token.name();
             }
@@ -808,7 +902,12 @@ String exampleConditions()
             }
         }
 
-        static final class HashConsingFactory
+        /**
+         * Factory for creating {@link CodecInteraction} instances. This factory
+         * is used to hash-cons the instances, so that they can be compared by
+         * reference.
+         */
+        public static final class HashConsingFactory
         {
             private final Int2ObjectHashMap wrapInteractions = new Int2ObjectHashMap<>();
             private final Map accessFieldInteractions = new HashMap<>();
@@ -828,36 +927,104 @@ static final class HashConsingFactory
                 this.topLevelBlockFields = topLevelBlockFields;
             }
 
-            CodecInteraction wrap(final int version)
+            /**
+             * Find or create a {@link CodecInteraction} to represent wrapping a codec around
+             * the given version of data.
+             * @param version the version of data to wrap
+             * @return the {@link CodecInteraction} instance
+             */
+            public CodecInteraction wrap(final int version)
             {
                 return wrapInteractions.computeIfAbsent(version, Wrap::new);
             }
 
-            CodecInteraction accessField(final Token token)
+            /**
+             * Find or create a {@link CodecInteraction} to represent accessing the field identified
+             * by the given token.
+             *
+             * 

The supplied token must carry a {@link Signal#BEGIN_FIELD} + * or {@link Signal#BEGIN_VAR_DATA} signal. + * + * @param token the token identifying the field + * @return the {@link CodecInteraction} instance + */ + public CodecInteraction accessField(final Token token) { return accessFieldInteractions.computeIfAbsent(token, t -> new AccessField(groupPathsByField.get(t), t, topLevelBlockFields.contains(t))); } - CodecInteraction determineGroupIsEmpty(final Token token) + /** + * Find or create a {@link CodecInteraction} to represent determining a + * repeating group is empty. + * + *

For encoding, this will be when the group count is supplied, e.g., + * {@code encoder.myGroupCount(0)}. + * + *

For decoding, this will be when the group is read, e.g., + * {@code decoder.myGroup()}. + * + *

The supplied token must carry a {@link Signal#BEGIN_GROUP} signal. + * + * @param token the token identifying the group + * @return the {@link CodecInteraction} instance + */ + public CodecInteraction determineGroupIsEmpty(final Token token) { return determineGroupIsEmptyInteractions.computeIfAbsent(token, t -> new DetermineGroupIsEmpty(groupPathsByField.get(t), t)); } - CodecInteraction determineGroupHasElements(final Token token) + /** + * Find or create a {@link CodecInteraction} to represent determining a + * repeating group has elements. + * + *

For encoding, this will be when the group count is supplied, e.g., + * {@code encoder.myGroupCount(1)}. + * + *

For decoding, this will be when the group is read, e.g., + * {@code decoder.myGroup()}. + * + *

The supplied token must carry a {@link Signal#BEGIN_GROUP} signal. + * + * @param token the token identifying the group + * @return the {@link CodecInteraction} instance + */ + public CodecInteraction determineGroupHasElements(final Token token) { return determineGroupHasElementsInteractions.computeIfAbsent(token, t -> new DetermineGroupHasElements(groupPathsByField.get(t), t)); } - CodecInteraction moveToNextElement(final Token token) + /** + * Find or create a {@link CodecInteraction} to represent moving to the next + * element in a repeating group. + * + *

For encoders, decoders, and codecs, this will be when the next element + * is accessed, e.g., {@code myGroup.next()} when {@code myGroup.count - myGroup.index > 1}. + * + *

The supplied token must carry a {@link Signal#BEGIN_GROUP} signal. + * + * @param token the token identifying the group + * @return the {@link CodecInteraction} instance + */ + public CodecInteraction moveToNextElement(final Token token) { return moveToNextElementInteractions.computeIfAbsent(token, t -> new MoveToNextElement(groupPathsByField.get(t), t)); } - CodecInteraction moveToLastElement(final Token token) + /** + * Find or create a {@link CodecInteraction} to represent moving to the last + * element in a repeating group. + * + *

For encoders, decoders, and codecs, this will be when the last element + * is accessed, e.g., {@code myGroup.next()} when {@code myGroup.count - myGroup.index == 1}. + * + * @param token the token identifying the group + * @return the {@link CodecInteraction} instance + */ + public CodecInteraction moveToLastElement(final Token token) { return moveToLastElementInteractions.computeIfAbsent(token, t -> new MoveToLastElement(groupPathsByField.get(t), t)); diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java index 85ec82a458..0739a592ad 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java @@ -19,8 +19,10 @@ import uk.co.real_logic.sbe.PrimitiveType; import uk.co.real_logic.sbe.PrimitiveValue; import uk.co.real_logic.sbe.generation.CodeGenerator; +import org.agrona.collections.MutableBoolean; import org.agrona.generation.OutputManager; import uk.co.real_logic.sbe.generation.Generators; +import uk.co.real_logic.sbe.generation.common.AccessOrderModel; import uk.co.real_logic.sbe.ir.*; import org.agrona.Verify; @@ -28,7 +30,10 @@ import java.io.Writer; import java.nio.ByteOrder; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; +import java.util.function.Function; import static java.lang.System.lineSeparator; @@ -126,28 +131,41 @@ public void generate() throws IOException try (Writer out = outputManager.createOutput(className)) { - out.append(generateFileHeader(ir.applicableNamespace())); - out.append(generateDocumentation(BASE_INDENT, msgToken)); - out.append(generateClassDeclaration(className)); - out.append(generateMessageFlyweightCode(className, msgToken, BASE_INDENT)); - out.append(generateFullyEncodedCheck(BASE_INDENT + INDENT)); - final List messageBody = tokens.subList(1, tokens.size() - 1); int offset = 0; - final List fields = new ArrayList<>(); offset = collectFields(messageBody, offset, fields); - out.append(generateFields(fields, BASE_INDENT)); - final List groups = new ArrayList<>(); offset = collectGroups(messageBody, offset, groups); + final List varData = new ArrayList<>(); + collectVarData(messageBody, offset, varData); + + AccessOrderModel accessOrderModel = null; + if (AccessOrderModel.generateAccessOrderChecks()) + { + accessOrderModel = AccessOrderModel.newInstance( + msgToken, + fields, + groups, + varData, + Function.identity()); + } + + out.append(generateFileHeader(ir.applicableNamespace())); + out.append(generateDocumentation(BASE_INDENT, msgToken)); + out.append(generateClassDeclaration(className)); + out.append(generateMessageFlyweightCode(className, msgToken, accessOrderModel, BASE_INDENT)); + + out.append(generateFieldOrderStates(BASE_INDENT, accessOrderModel)); + out.append(generateFullyEncodedCheck(BASE_INDENT, accessOrderModel)); + + out.append(generateFields(accessOrderModel, fields, BASE_INDENT)); + final StringBuilder sb = new StringBuilder(); - generateGroups(sb, className, groups, BASE_INDENT); + generateGroups(sb, className, groups, accessOrderModel, BASE_INDENT); out.append(sb); - final List varData = new ArrayList<>(); - collectVarData(messageBody, offset, varData); - out.append(generateVarData(varData, BASE_INDENT + INDENT)); + out.append(generateVarData(accessOrderModel, varData, BASE_INDENT + INDENT)); out.append(generateDisplay(toUpperFirstChar(msgToken.name()), fields, groups, varData)); @@ -161,6 +179,7 @@ private void generateGroups( final StringBuilder sb, final String parentMessageClassName, final List tokens, + final AccessOrderModel accessOrderModel, final String indent) { for (int i = 0, size = tokens.size(); i < size; i++) @@ -171,23 +190,24 @@ private void generateGroups( throw new IllegalStateException("tokens must begin with BEGIN_GROUP: token=" + groupToken); } final String groupName = groupToken.name(); - sb.append(generateGroupProperty(groupName, groupToken, indent + INDENT)); + sb.append(generateGroupProperty(groupName, accessOrderModel, groupToken, indent + INDENT)); - generateGroupClassHeader(sb, groupName, parentMessageClassName, tokens, i, indent + INDENT); + generateGroupClassHeader(sb, groupName, parentMessageClassName, tokens, + accessOrderModel, i, indent + INDENT); i++; i += tokens.get(i).componentTokenCount(); final List fields = new ArrayList<>(); i = collectFields(tokens, i, fields); - sb.append(generateFields(fields, indent + INDENT)); + sb.append(generateFields(accessOrderModel, fields, indent + INDENT)); final List groups = new ArrayList<>(); i = collectGroups(tokens, i, groups); - generateGroups(sb, parentMessageClassName, groups, indent + INDENT); + generateGroups(sb, parentMessageClassName, groups, accessOrderModel, indent + INDENT); final List varData = new ArrayList<>(); i = collectVarData(tokens, i, varData); - sb.append(generateVarData(varData, indent + INDENT + INDENT)); + sb.append(generateVarData(accessOrderModel, varData, indent + INDENT + INDENT)); appendGroupInstanceDisplay(sb, fields, groups, varData, indent + TWO_INDENT); @@ -200,6 +220,7 @@ private void generateGroupClassHeader( final String groupName, final String parentMessageClassName, final List tokens, + final AccessOrderModel accessOrderModel, final int index, final String indent) { @@ -285,15 +306,35 @@ private void generateGroupClassHeader( blockLength, dimensionHeaderLength)); - generateGroupEnumerator(sb, groupName, typeForNumInGroup, indent); + if (null != accessOrderModel) + { + sb.append("\n") + .append(indent).append(" private CodecState codecState()\n") + .append(indent).append(" {\n") + .append(indent).append(" return _parentMessage.codecState();\n") + .append(indent).append(" }\n"); + + sb.append("\n") + .append(indent).append(" private void codecState(CodecState newState)\n") + .append(indent).append(" {\n") + .append(indent).append(" _parentMessage.codecState(newState);\n") + .append(indent).append(" }\n"); + } + + final Token groupToken = tokens.get(index); + generateGroupEnumerator(sb, accessOrderModel, groupToken, groupName, typeForNumInGroup, indent); } private void generateGroupEnumerator( final StringBuilder sb, + final AccessOrderModel accessOrderModel, + final Token groupToken, final String groupName, final String typeForNumInGroup, final String indent) { + generateAccessOrderListenerMethodForNextGroupElement(sb, accessOrderModel, indent + INDENT, groupToken); + sb.append( indent + INDENT + "public int ActingBlockLength { get { return _blockLength; } }\n\n" + indent + INDENT + "public int Count { get { return _count; } }\n\n" + @@ -315,6 +356,7 @@ private void generateGroupEnumerator( indent + INDENT + INDENT + "{\n" + indent + INDENT + INDENT + INDENT + "ThrowHelper.ThrowInvalidOperationException();\n" + indent + INDENT + INDENT + "}\n\n" + + generateAccessOrderListenerCall(accessOrderModel, indent + TWO_INDENT, "OnNextElementAccessed") + indent + INDENT + INDENT + "_offset = _parentMessage.Limit;\n" + indent + INDENT + INDENT + "_parentMessage.Limit = _offset + _blockLength;\n" + indent + INDENT + INDENT + "++_index;\n\n" + @@ -339,7 +381,11 @@ private boolean isRepresentableByInt32(final Encoding encoding) encoding.applicableMaxValue().longValue() <= Integer.MAX_VALUE; } - private CharSequence generateGroupProperty(final String groupName, final Token token, final String indent) + private CharSequence generateGroupProperty( + final String groupName, + final AccessOrderModel accessOrderModel, + final Token token, + final String indent) { final StringBuilder sb = new StringBuilder(); @@ -356,6 +402,8 @@ private CharSequence generateGroupProperty(final String groupName, final Token t toUpperFirstChar(groupName), token.id())); + generateAccessOrderListenerMethodForGroupWrap(sb, accessOrderModel, indent + INDENT, token); + generateSinceActingDeprecated(sb, indent, toUpperFirstChar(groupName), token); sb.append(String.format("\n" + @@ -365,6 +413,8 @@ private CharSequence generateGroupProperty(final String groupName, final Token t indent + INDENT + "get\n" + indent + INDENT + "{\n" + indent + INDENT + INDENT + "_%4$s.WrapForDecode(_parentMessage, _buffer, _actingVersion);\n" + + generateAccessOrderListenerCall(accessOrderModel, indent + TWO_INDENT, token, + "_" + groupName + ".Count", "\"decode\"") + indent + INDENT + INDENT + "return _%4$s;\n" + indent + INDENT + "}\n" + indent + "}\n", @@ -376,6 +426,7 @@ private CharSequence generateGroupProperty(final String groupName, final Token t sb.append(String.format("\n" + indent + "public %1$sGroup %2$sCount(int count)\n" + indent + "{\n" + + generateAccessOrderListenerCall(accessOrderModel, indent + INDENT, token, "count", "\"encode\"") + indent + INDENT + "_%3$s.WrapForEncode(_parentMessage, _buffer, count);\n" + indent + INDENT + "return _%3$s;\n" + indent + "}\n", @@ -386,7 +437,10 @@ private CharSequence generateGroupProperty(final String groupName, final Token t return sb; } - private CharSequence generateVarData(final List tokens, final String indent) + private CharSequence generateVarData( + final AccessOrderModel accessOrderModel, + final List tokens, + final String indent) { final StringBuilder sb = new StringBuilder(); @@ -413,6 +467,10 @@ private CharSequence generateVarData(final List tokens, final String inde final ByteOrder byteOrder = lengthEncoding.byteOrder(); final String byteOrderStr = generateByteOrder(byteOrder, lengthEncoding.primitiveType().size()); + generateAccessOrderListenerMethod(sb, accessOrderModel, indent, token); + final CharSequence accessOrderListenerCall = + generateAccessOrderListenerCall(accessOrderModel, indent + INDENT, token); + sb.append(String.format("\n" + indent + "public const int %sHeaderSize = %d;\n", propertyName, @@ -421,6 +479,7 @@ private CharSequence generateVarData(final List tokens, final String inde sb.append(String.format(indent + "\n" + indent + "public int %1$sLength()\n" + indent + "{\n" + + accessOrderListenerCall + indent + INDENT + "_buffer.CheckLimit(_parentMessage.Limit + %2$d);\n" + indent + INDENT + "return (int)_buffer.%3$sGet%4$s(_parentMessage.Limit);\n" + indent + "}\n", @@ -438,6 +497,7 @@ private CharSequence generateVarData(final List tokens, final String inde indent + "public int Get%1$s(Span dst)\n" + indent + "{\n" + "%2$s" + + accessOrderListenerCall + indent + INDENT + "const int sizeOfLengthField = %3$d;\n" + indent + INDENT + "int limit = _parentMessage.Limit;\n" + indent + INDENT + "_buffer.CheckLimit(limit + sizeOfLengthField);\n" + @@ -457,6 +517,7 @@ private CharSequence generateVarData(final List tokens, final String inde indent + "// Allocates and returns a new byte array\n" + indent + "public byte[] Get%1$sBytes()\n" + indent + "{\n" + + accessOrderListenerCall + indent + INDENT + "const int sizeOfLengthField = %2$d;\n" + indent + INDENT + "int limit = _parentMessage.Limit;\n" + indent + INDENT + "_buffer.CheckLimit(limit + sizeOfLengthField);\n" + @@ -479,6 +540,7 @@ private CharSequence generateVarData(final List tokens, final String inde sb.append(String.format("\n" + indent + "public int Set%1$s(ReadOnlySpan src)\n" + indent + "{\n" + + accessOrderListenerCall + indent + INDENT + "const int sizeOfLengthField = %2$d;\n" + indent + INDENT + "int limit = _parentMessage.Limit;\n" + indent + INDENT + "_parentMessage.Limit = limit + sizeOfLengthField + src.Length;\n" + @@ -498,6 +560,7 @@ private CharSequence generateVarData(final List tokens, final String inde .append(String.format( indent + "public string Get%1$s()\n" + indent + "{\n" + + accessOrderListenerCall + indent + INDENT + "const int sizeOfLengthField = %2$d;\n" + indent + INDENT + "int limit = _parentMessage.Limit;\n" + indent + INDENT + "_buffer.CheckLimit(limit + sizeOfLengthField);\n" + @@ -508,6 +571,7 @@ private CharSequence generateVarData(final List tokens, final String inde indent + "}\n\n" + indent + "public void Set%1$s(string value)\n" + indent + "{\n" + + accessOrderListenerCall + indent + INDENT + "var encoding = %1$sResolvedCharacterEncoding;\n" + indent + INDENT + "const int sizeOfLengthField = %2$d;\n" + indent + INDENT + "int limit = _parentMessage.Limit;\n" + @@ -589,23 +653,24 @@ private CharSequence generateCompositePropertyElements(final List tokens, { final Token token = tokens.get(i); final String propertyName = formatPropertyName(token.name()); + final AccessOrderModel accessOrderModel = null; switch (token.signal()) { case ENCODING: - sb.append(generatePrimitiveProperty(propertyName, token, token, indent)); + sb.append(generatePrimitiveProperty(propertyName, token, token, accessOrderModel, indent)); break; case BEGIN_ENUM: - sb.append(generateEnumProperty(propertyName, token, token, indent)); + sb.append(generateEnumProperty(propertyName, token, token, accessOrderModel, indent)); break; case BEGIN_SET: - sb.append(generateBitSetProperty(propertyName, token, token, indent)); + sb.append(generateBitSetProperty(propertyName, token, token, accessOrderModel, indent)); break; case BEGIN_COMPOSITE: - sb.append(generateCompositeProperty(propertyName, token, token, indent)); + sb.append(generateCompositeProperty(propertyName, token, token, accessOrderModel, indent)); break; default: @@ -748,7 +813,11 @@ private CharSequence generateEnumDeclaration( } private CharSequence generatePrimitiveProperty( - final String propertyName, final Token fieldToken, final Token typeToken, final String indent) + final String propertyName, + final Token fieldToken, + final Token typeToken, + final AccessOrderModel accessOrderModel, + final String indent) { final StringBuilder sb = new StringBuilder(); @@ -760,7 +829,8 @@ private CharSequence generatePrimitiveProperty( } else { - sb.append(generatePrimitivePropertyMethods(propertyName, fieldToken, typeToken, indent)); + sb.append(generatePrimitivePropertyMethods(propertyName, fieldToken, typeToken, + accessOrderModel, indent)); } return sb; @@ -770,17 +840,20 @@ private CharSequence generatePrimitivePropertyMethods( final String propertyName, final Token fieldToken, final Token typeToken, + final AccessOrderModel accessOrderModel, final String indent) { final int arrayLength = typeToken.arrayLength(); if (arrayLength == 1) { - return generateSingleValueProperty(propertyName, fieldToken, typeToken, indent + INDENT); + return generateSingleValueProperty(propertyName, fieldToken, typeToken, + accessOrderModel, indent + INDENT); } else if (arrayLength > 1) { - return generateArrayProperty(propertyName, fieldToken, typeToken, indent + INDENT); + return generateArrayProperty(propertyName, fieldToken, typeToken, + accessOrderModel, indent + INDENT); } return ""; @@ -810,6 +883,7 @@ private CharSequence generateSingleValueProperty( final String propertyName, final Token fieldToken, final Token typeToken, + final AccessOrderModel accessOrderModel, final String indent) { final String typeName = cSharpTypeName(typeToken.encoding().primitiveType()); @@ -818,6 +892,10 @@ private CharSequence generateSingleValueProperty( final ByteOrder byteOrder = typeToken.encoding().byteOrder(); final String byteOrderStr = generateByteOrder(byteOrder, typeToken.encoding().primitiveType().size()); + final CharSequence accessOrderListenerCall = + generateAccessOrderListenerCall(accessOrderModel, indent + TWO_INDENT, fieldToken); + + return String.format("\n" + "%1$s" + indent + "public %2$s %3$s\n" + @@ -825,10 +903,12 @@ private CharSequence generateSingleValueProperty( indent + INDENT + "get\n" + indent + INDENT + "{\n" + "%4$s" + + accessOrderListenerCall + indent + INDENT + INDENT + "return _buffer.%5$sGet%7$s(_offset + %6$d);\n" + indent + INDENT + "}\n" + indent + INDENT + "set\n" + indent + INDENT + "{\n" + + accessOrderListenerCall + indent + INDENT + INDENT + "_buffer.%5$sPut%7$s(_offset + %6$d, value);\n" + indent + INDENT + "}\n" + indent + "}\n\n", @@ -913,7 +993,7 @@ private CharSequence generateArrayProperty( final String propertyName, final Token fieldToken, final Token typeToken, - final String indent) + final AccessOrderModel accessOrderModel, final String indent) { final String typeName = cSharpTypeName(typeToken.encoding().primitiveType()); final String typePrefix = toUpperFirstChar(typeToken.encoding().primitiveType().primitiveName()); @@ -924,6 +1004,11 @@ private CharSequence generateArrayProperty( final int typeSize = typeToken.encoding().primitiveType().size(); final String propName = toUpperFirstChar(propertyName); + final CharSequence accessOrderListenerCall = + generateAccessOrderListenerCall(accessOrderModel, indent + INDENT, fieldToken); + final CharSequence accessOrderListenerCallDoubleIndent = + generateAccessOrderListenerCall(accessOrderModel, indent + TWO_INDENT, fieldToken); + final StringBuilder sb = new StringBuilder(); sb.append(String.format("\n" + @@ -939,6 +1024,7 @@ private CharSequence generateArrayProperty( indent + INDENT + INDENT + "ThrowHelper.ThrowIndexOutOfRangeException(index);\n" + indent + INDENT + "}\n\n" + "%5$s" + + accessOrderListenerCall + indent + INDENT + "return _buffer.%6$sGet%9$s(_offset + %7$d + (index * %8$d));\n" + indent + "}\n", generateDocumentation(indent, fieldToken), @@ -954,6 +1040,7 @@ private CharSequence generateArrayProperty( indent + INDENT + "{\n" + indent + INDENT + INDENT + "ThrowHelper.ThrowIndexOutOfRangeException(index);\n" + indent + INDENT + "}\n\n" + + accessOrderListenerCall + indent + INDENT + "_buffer.%5$sPut%8$s(_offset + %6$d + (index * %7$d), value);\n" + indent + "}\n", generateDocumentation(indent, fieldToken), @@ -963,8 +1050,16 @@ private CharSequence generateArrayProperty( "%1$s" + indent + "public ReadOnlySpan<%2$s> %3$s\n" + indent + "{\n" + - indent + INDENT + "get => _buffer.AsReadOnlySpan<%2$s>(_offset + %4$s, %3$sLength);\n" + - indent + INDENT + "set => value.CopyTo(_buffer.AsSpan<%2$s>(_offset + %4$s, %3$sLength));\n" + + indent + INDENT + "get\n" + + indent + INDENT + "{\n" + + accessOrderListenerCallDoubleIndent + + indent + INDENT + INDENT + "return _buffer.AsReadOnlySpan<%2$s>(_offset + %4$s, %3$sLength);\n" + + indent + INDENT + "}\n" + + indent + INDENT + "set\n" + + indent + INDENT + "{\n" + + accessOrderListenerCallDoubleIndent + + indent + INDENT + INDENT + "value.CopyTo(_buffer.AsSpan<%2$s>(_offset + %4$s, %3$sLength));\n" + + indent + INDENT + "}\n" + indent + "}\n", generateDocumentation(indent, fieldToken), typeName, propName, offset)); @@ -973,6 +1068,7 @@ private CharSequence generateArrayProperty( "%1$s" + indent + "public Span<%2$s> %3$sAsSpan()\n" + indent + "{\n" + + accessOrderListenerCall + indent + INDENT + "return _buffer.AsSpan<%2$s>(_offset + %4$s, %3$sLength);\n" + indent + "}\n", generateDocumentation(indent, fieldToken), @@ -987,6 +1083,7 @@ private CharSequence generateArrayProperty( indent + "{\n" + indent + INDENT + "const int length = %2$d;\n" + "%3$s" + + accessOrderListenerCall + indent + INDENT + "return Get%1$s(new Span(dst, dstOffset, length));\n" + indent + "}\n", propName, fieldLength, generateArrayFieldNotPresentCondition(fieldToken.version(), indent), offset)); @@ -1000,6 +1097,7 @@ private CharSequence generateArrayProperty( indent + INDENT + INDENT + "ThrowHelper.ThrowWhenSpanLengthTooSmall(dst.Length);\n" + indent + INDENT + "}\n\n" + "%3$s" + + accessOrderListenerCall + indent + INDENT + "_buffer.GetBytes(_offset + %4$d, dst);\n" + indent + INDENT + "return length;\n" + indent + "}\n", @@ -1020,6 +1118,7 @@ private CharSequence generateArrayProperty( indent + INDENT + "{\n" + indent + INDENT + INDENT + "ThrowHelper.ThrowWhenSpanLengthTooLarge(src.Length);\n" + indent + INDENT + "}\n\n" + + accessOrderListenerCall + indent + INDENT + "_buffer.SetBytes(_offset + %3$d, src);\n" + indent + "}\n", propName, fieldLength, offset)); @@ -1027,11 +1126,13 @@ private CharSequence generateArrayProperty( sb.append(String.format("\n" + indent + "public void Set%1$s(string value)\n" + indent + "{\n" + + accessOrderListenerCall + indent + INDENT + "_buffer.SetNullTerminatedBytesFromString(%1$sResolvedCharacterEncoding, " + "value, _offset + %2$s, %1$sLength, %1$sNullValue);\n" + indent + "}\n" + indent + "public string Get%1$s()\n" + indent + "{\n" + + accessOrderListenerCall + indent + INDENT + "return _buffer.GetStringFromNullTerminatedBytes(%1$sResolvedCharacterEncoding, " + "_offset + %2$s, %1$sLength, %1$sNullValue);\n" + indent + "}\n", @@ -1157,7 +1258,11 @@ private CharSequence generateFixedFlyweightCode(final int size) size); } - private CharSequence generateMessageFlyweightCode(final String className, final Token token, final String indent) + private CharSequence generateMessageFlyweightCode( + final String className, + final Token token, + final AccessOrderModel accessOrderModel, + final String indent) { final String blockLengthType = cSharpTypeName(ir.headerStructure().blockLengthType()); final String templateIdType = cSharpTypeName(ir.headerStructure().templateIdType()); @@ -1187,6 +1292,7 @@ private CharSequence generateMessageFlyweightCode(final String className, final indent + INDENT + "}\n\n" + indent + INDENT + "public %10$s WrapForEncode(DirectBuffer buffer, int offset)\n" + indent + INDENT + "{\n" + + generateEncoderWrapListener(accessOrderModel, indent + TWO_INDENT) + indent + INDENT + INDENT + "_buffer = buffer;\n" + indent + INDENT + INDENT + "_offset = offset;\n" + indent + INDENT + INDENT + "_actingBlockLength = BlockLength;\n" + @@ -1205,9 +1311,11 @@ private CharSequence generateMessageFlyweightCode(final String className, final indent + INDENT + INDENT + "\n" + indent + INDENT + INDENT + "return WrapForEncode(buffer, offset + MessageHeader.Size);\n" + indent + INDENT + "}\n\n" + + generateDecoderWrapListener(accessOrderModel, indent + INDENT) + indent + INDENT + "public %10$s WrapForDecode(DirectBuffer buffer, int offset, " + "int actingBlockLength, int actingVersion)\n" + indent + INDENT + "{\n" + + generateAccessOrderListenerCall(accessOrderModel, indent + TWO_INDENT, "OnWrapForDecode", "actingVersion") + indent + INDENT + INDENT + "_buffer = buffer;\n" + indent + INDENT + INDENT + "_offset = offset;\n" + indent + INDENT + INDENT + "_actingBlockLength = actingBlockLength;\n" + @@ -1255,18 +1363,433 @@ private CharSequence generateMessageFlyweightCode(final String className, final semanticVersion); } - private CharSequence generateFullyEncodedCheck(final String indent) + private static CharSequence qualifiedStateCase(final AccessOrderModel.State state) + { + return "CodecState." + state.name(); + } + + private static CharSequence stateCaseForSwitchCase(final AccessOrderModel.State state) { + return qualifiedStateCase(state); + } + + private static CharSequence unqualifiedStateCase(final AccessOrderModel.State state) + { + return state.name(); + } + + private static CharSequence generateFieldOrderStates(final String indent, final AccessOrderModel accessOrderModel) + { + if (null == accessOrderModel) + { + return ""; + } + + final StringBuilder sb = new StringBuilder(); + + sb.append(indent).append("///\n"); + + sb.append(indent).append("///

\n"); + sb.append(indent).append("/// \n"); + sb.append(indent).append("/// The states in which a encoder/decoder/codec can live.\n"); + sb.append(indent).append("/// \n"); + sb.append(indent).append("/// \n"); + sb.append(indent).append("/// The state machine diagram below, encoded in the dot language, describes\n"); + sb.append(indent).append("/// the valid state transitions according to the order in which fields may be\n"); + sb.append(indent).append("/// accessed safely. Tools such as PlantUML and Graphviz can render it.\n"); + sb.append(indent).append("/// \n"); + sb.append(indent).append("/// \n"); + accessOrderModel.generateGraph(sb, indent + "/// "); + sb.append(indent).append("/// \n"); + sb.append(indent).append("/// \n"); + sb.append(indent).append("private enum CodecState\n") + .append(indent).append("{\n"); + accessOrderModel.forEachStateOrderedByStateNumber(state -> + { + sb.append(indent).append(INDENT).append(unqualifiedStateCase(state)) + .append(" = ").append(state.number()) + .append(",\n"); + }); + sb.append(indent).append("}\n\n"); + + sb.append("\n").append(indent).append("private static readonly string[] StateNameLookup = new []\n") + .append(indent).append("{\n"); + accessOrderModel.forEachStateOrderedByStateNumber(state -> + { + sb.append(indent).append(INDENT).append("\"").append(state.name()).append("\",\n"); + }); + sb.append(indent).append("};\n\n"); + + sb.append(indent).append("private static readonly string[] StateTransitionsLookup = new []\n") + .append(indent).append("{\n"); + accessOrderModel.forEachStateOrderedByStateNumber(state -> + { + sb.append(indent).append(INDENT).append("\""); + final MutableBoolean isFirst = new MutableBoolean(true); + final Set transitionDescriptions = new HashSet<>(); + accessOrderModel.forEachTransitionFrom(state, transitionGroup -> + { + if (transitionDescriptions.add(transitionGroup.exampleCode())) + { + if (isFirst.get()) + { + isFirst.set(false); + } + else + { + sb.append(", "); + } + + sb.append("\\\"").append(transitionGroup.exampleCode()).append("\\\""); + } + }); + sb.append("\",\n"); + }); + sb.append(indent).append("};\n\n"); + + sb.append(indent).append("private static string codecStateName(CodecState state)\n") + .append(indent).append("{\n") + .append(indent).append(INDENT).append("return StateNameLookup[(int) state];\n") + .append(indent).append("}\n\n"); + + sb.append(indent).append("private static string codecStateTransitions(CodecState state)\n") + .append(indent).append("{\n") + .append(indent).append(INDENT).append("return StateTransitionsLookup[(int) state];\n") + .append(indent).append("}\n\n"); + + sb.append(indent).append("private CodecState _codecState = ") + .append(qualifiedStateCase(accessOrderModel.notWrappedState())) + .append(";\n\n"); + + sb.append(indent).append("private CodecState codecState()\n") + .append(indent).append("{\n") + .append(indent).append(INDENT).append("return _codecState;\n") + .append(indent).append("}\n\n"); + + sb.append(indent).append("private void codecState(CodecState newState)\n") + .append(indent).append("{\n") + .append(indent).append(INDENT).append("_codecState = newState;\n") + .append(indent).append("}\n"); + + return sb; + } + + private static CharSequence generateFullyEncodedCheck( + final String indent, + final AccessOrderModel accessOrderModel) + { + if (null == accessOrderModel) + { + return ""; + } + final StringBuilder sb = new StringBuilder(); sb.append("\n"); - sb.append(indent).append("public void CheckEncodingIsComplete()\n"); - sb.append(indent).append("{\n"); - sb.append(indent).append(" // TODO\n"); + + sb.append(indent).append("public void CheckEncodingIsComplete()\n") + .append(indent).append("{\n") + .append(indent).append(INDENT).append("switch (_codecState)\n") + .append(indent).append(INDENT).append("{\n"); + + accessOrderModel.forEachTerminalEncoderState(state -> + { + sb.append(indent).append(TWO_INDENT).append("case ").append(stateCaseForSwitchCase(state)).append(":\n") + .append(indent).append(THREE_INDENT).append("return;\n"); + }); + + sb.append(indent).append(TWO_INDENT).append("default:\n") + .append(indent).append(THREE_INDENT) + .append("throw new InvalidOperationException(\"Not fully encoded, current state: \" +\n") + .append(indent).append(THREE_INDENT) + .append(INDENT).append("codecStateName(_codecState) + \", allowed transitions: \" +\n") + .append(indent).append(THREE_INDENT) + .append(INDENT).append("codecStateTransitions(_codecState));\n") + .append(indent).append(INDENT).append("}\n") + .append(indent).append("}\n\n"); + + return sb; + } + + private static String accessOrderListenerMethodName(final Token token) + { + return "On" + Generators.toUpperFirstChar(token.name()) + "Accessed"; + } + + private static void generateAccessOrderListenerMethod( + final StringBuilder sb, + final AccessOrderModel accessOrderModel, + final String indent, + final Token token) + { + if (null == accessOrderModel) + { + return; + } + + sb.append("\n") + .append(indent).append("private void ").append(accessOrderListenerMethodName(token)).append("()\n") + .append(indent).append("{\n"); + + final AccessOrderModel.CodecInteraction fieldAccess = + accessOrderModel.interactionFactory().accessField(token); + + generateAccessOrderListener( + sb, + indent + INDENT, + "access field", + accessOrderModel, + fieldAccess); + sb.append(indent).append("}\n"); + } + + private static CharSequence generateAccessOrderListenerCall( + final AccessOrderModel accessOrderModel, + final String indent, + final Token token, + final String... arguments) + { + return generateAccessOrderListenerCall( + accessOrderModel, + indent, + accessOrderListenerMethodName(token), + arguments); + } + + private static CharSequence generateAccessOrderListenerCall( + final AccessOrderModel accessOrderModel, + final String indent, + final String methodName, + final String... arguments) + { + if (null == accessOrderModel) + { + return ""; + } + + final StringBuilder sb = new StringBuilder(); + sb.append("#if ENABLE_ACCESS_ORDER_CHECKS\n") + .append(indent).append(methodName).append("("); + + for (int i = 0; i < arguments.length; i++) + { + if (i > 0) + { + sb.append(", "); + } + sb.append(arguments[i]); + } + sb.append(");\n"); + + sb.append("#endif\n"); + return sb; } - private CharSequence generateFields(final List tokens, final String indent) + private static void generateAccessOrderListenerMethodForGroupWrap( + final StringBuilder sb, + final AccessOrderModel accessOrderModel, + final String indent, + final Token token) + { + if (null == accessOrderModel) + { + return; + } + + sb.append("\n") + .append(indent).append("private void ").append(accessOrderListenerMethodName(token)) + .append("(int remaining, string action)\n") + .append(indent).append("{\n") + .append(indent).append(INDENT).append("if (remaining == 0)\n") + .append(indent).append(INDENT).append("{\n"); + + final AccessOrderModel.CodecInteraction selectEmptyGroup = + accessOrderModel.interactionFactory().determineGroupIsEmpty(token); + + generateAccessOrderListener( + sb, + indent + TWO_INDENT, + "\" + action + \" count of repeating group", + accessOrderModel, + selectEmptyGroup); + + sb.append(indent).append(INDENT).append("}\n") + .append(indent).append(INDENT).append("else\n") + .append(indent).append(INDENT).append("{\n"); + + final AccessOrderModel.CodecInteraction selectNonEmptyGroup = + accessOrderModel.interactionFactory().determineGroupHasElements(token); + + generateAccessOrderListener( + sb, + indent + TWO_INDENT, + "\" + action + \" count of repeating group", + accessOrderModel, + selectNonEmptyGroup); + + sb.append(indent).append(INDENT).append("}\n") + .append(indent).append("}\n"); + } + + private static void generateAccessOrderListener( + final StringBuilder sb, + final String indent, + final String action, + final AccessOrderModel accessOrderModel, + final AccessOrderModel.CodecInteraction interaction) + { + if (interaction.isTopLevelBlockFieldAccess()) + { + sb.append(indent).append("if (codecState() == ") + .append(qualifiedStateCase(accessOrderModel.notWrappedState())) + .append(")\n") + .append(indent).append("{\n"); + generateAccessOrderException(sb, indent + INDENT, action, interaction); + sb.append(indent).append("}\n"); + } + else + { + sb.append(indent).append("switch (codecState())\n") + .append(indent).append("{\n"); + + accessOrderModel.forEachTransition(interaction, transitionGroup -> + { + transitionGroup.forEachStartState(startState -> + { + sb.append(indent).append(INDENT) + .append("case ").append(stateCaseForSwitchCase(startState)).append(":\n"); + }); + sb.append(indent).append(TWO_INDENT).append("codecState(") + .append(qualifiedStateCase(transitionGroup.endState())).append(");\n") + .append(indent).append(TWO_INDENT).append("break;\n"); + }); + + sb.append(indent).append(INDENT).append("default:\n"); + generateAccessOrderException(sb, indent + TWO_INDENT, action, interaction); + sb.append(indent).append("}\n"); + } + } + + private static void generateAccessOrderException( + final StringBuilder sb, + final String indent, + final String action, + final AccessOrderModel.CodecInteraction interaction) + { + sb.append(indent).append("throw new InvalidOperationException(") + .append("\"Illegal field access order. \" +\n") + .append(indent).append(INDENT) + .append("\"Cannot ").append(action).append(" \\\"").append(interaction.groupQualifiedName()) + .append("\\\" in state: \" + codecStateName(codecState()) +\n") + .append(indent).append(INDENT) + .append("\". Expected one of these transitions: [\" + codecStateTransitions(codecState()) +\n") + .append(indent).append(INDENT) + .append("\"]. Please see the diagram in the docs of the inner enum #CodecState.\");\n"); + } + + private static void generateAccessOrderListenerMethodForNextGroupElement( + final StringBuilder sb, + final AccessOrderModel accessOrderModel, + final String indent, + final Token token) + { + if (null == accessOrderModel) + { + return; + } + + sb.append("\n"); + + sb.append(indent).append("private void OnNextElementAccessed()\n") + .append(indent).append("{\n") + .append(indent).append(INDENT).append("int remaining = ").append("_count - _index").append(";\n") + .append(indent).append(INDENT).append("if (remaining > 1)\n") + .append(indent).append(INDENT).append("{\n"); + + final AccessOrderModel.CodecInteraction selectNextElementInGroup = + accessOrderModel.interactionFactory().moveToNextElement(token); + + generateAccessOrderListener( + sb, + indent + TWO_INDENT, + "access next element in repeating group", + accessOrderModel, + selectNextElementInGroup); + + sb.append(indent).append(INDENT).append("}\n") + .append(indent).append(INDENT).append("else if (remaining == 1)\n") + .append(indent).append(INDENT).append("{\n"); + + final AccessOrderModel.CodecInteraction selectLastElementInGroup = + accessOrderModel.interactionFactory().moveToLastElement(token); + + generateAccessOrderListener( + sb, + indent + TWO_INDENT, + "access next element in repeating group", + accessOrderModel, + selectLastElementInGroup); + + sb.append(indent).append(INDENT).append("}\n") + .append(indent).append("}\n"); + } + + private static CharSequence generateDecoderWrapListener( + final AccessOrderModel accessOrderModel, + final String indent) + { + if (null == accessOrderModel) + { + return ""; + } + + final StringBuilder sb = new StringBuilder(); + sb.append(indent).append("private void OnWrapForDecode(int actingVersion)\n") + .append(indent).append("{\n") + .append(indent).append(INDENT).append("switch(actingVersion)\n") + .append(indent).append(INDENT).append("{\n"); + + accessOrderModel.forEachWrappedStateByVersion((version, state) -> + { + sb.append(indent).append(TWO_INDENT).append("case ").append(version).append(":\n") + .append(indent).append(THREE_INDENT).append("codecState(") + .append(qualifiedStateCase(state)).append(");\n") + .append(indent).append(THREE_INDENT).append("break;\n"); + }); + + sb.append(indent).append(TWO_INDENT).append("default:\n") + .append(indent).append(THREE_INDENT).append("codecState(") + .append(qualifiedStateCase(accessOrderModel.latestVersionWrappedState())).append(");\n") + .append(indent).append(THREE_INDENT).append("break;\n") + .append(indent).append(INDENT).append("}\n") + .append(indent).append("}\n\n"); + + return sb; + } + + private CharSequence generateEncoderWrapListener( + final AccessOrderModel accessOrderModel, + final String indent) + { + if (null == accessOrderModel) + { + return ""; + } + + final StringBuilder sb = new StringBuilder(); + sb.append("#if ENABLE_ACCESS_ORDER_CHECKS\n") + .append(indent).append("codecState(") + .append(qualifiedStateCase(accessOrderModel.latestVersionWrappedState())) + .append(");\n") + .append("#endif\n"); + return sb; + } + + private CharSequence generateFields( + final AccessOrderModel accessOrderModel, + final List tokens, + final String indent) { final StringBuilder sb = new StringBuilder(); @@ -1278,28 +1801,34 @@ private CharSequence generateFields(final List tokens, final String inden final Token encodingToken = tokens.get(i + 1); final String propertyName = signalToken.name(); - generateFieldIdMethod(sb, signalToken, indent + INDENT); + generateFieldIdMethod(sb, signalToken, indent); generateSinceActingDeprecated( sb, indent, CSharpUtil.formatPropertyName(signalToken.name()), signalToken); generateOffsetMethod(sb, signalToken, indent + INDENT); generateFieldMetaAttributeMethod(sb, signalToken, indent + INDENT); + generateAccessOrderListenerMethod(sb, accessOrderModel, indent, signalToken); + switch (encodingToken.signal()) { case ENCODING: - sb.append(generatePrimitiveProperty(propertyName, signalToken, encodingToken, indent)); + sb.append(generatePrimitiveProperty(propertyName, signalToken, encodingToken, + accessOrderModel, indent)); break; case BEGIN_ENUM: - sb.append(generateEnumProperty(propertyName, signalToken, encodingToken, indent)); + sb.append(generateEnumProperty(propertyName, signalToken, encodingToken, + accessOrderModel, indent)); break; case BEGIN_SET: - sb.append(generateBitSetProperty(propertyName, signalToken, encodingToken, indent)); + sb.append(generateBitSetProperty(propertyName, signalToken, encodingToken, + accessOrderModel, indent)); break; case BEGIN_COMPOSITE: - sb.append(generateCompositeProperty(propertyName, signalToken, encodingToken, indent)); + sb.append(generateCompositeProperty(propertyName, signalToken, encodingToken, + accessOrderModel, indent)); break; default: @@ -1374,6 +1903,7 @@ private CharSequence generateEnumProperty( final String propertyName, final Token fieldToken, final Token typeToken, + final AccessOrderModel accessOrderModel, final String indent) { final String enumName = formatClassName(typeToken.applicableTypeName()); @@ -1403,6 +1933,9 @@ private CharSequence generateEnumProperty( } else { + final CharSequence accessOrderListenerCall = generateAccessOrderListenerCall( + accessOrderModel, indent + TWO_INDENT, fieldToken); + return String.format("\n" + "%1$s" + indent + INDENT + "public %2$s %3$s\n" + @@ -1410,10 +1943,12 @@ private CharSequence generateEnumProperty( indent + INDENT + INDENT + "get\n" + indent + INDENT + INDENT + "{\n" + "%4$s" + + accessOrderListenerCall + indent + INDENT + INDENT + INDENT + "return (%5$s)_buffer.%6$sGet%8$s(_offset + %7$d);\n" + indent + INDENT + INDENT + "}\n" + indent + INDENT + INDENT + "set\n" + indent + INDENT + INDENT + "{\n" + + accessOrderListenerCall + indent + INDENT + INDENT + INDENT + "_buffer.%6$sPut%8$s(_offset + %7$d, (%9$s)value);\n" + indent + INDENT + INDENT + "}\n" + indent + INDENT + "}\n\n", @@ -1430,7 +1965,11 @@ private CharSequence generateEnumProperty( } private String generateBitSetProperty( - final String propertyName, final Token fieldToken, final Token typeToken, final String indent) + final String propertyName, + final Token fieldToken, + final Token typeToken, + final AccessOrderModel accessOrderModel, + final String indent) { final String bitSetName = formatClassName(typeToken.applicableTypeName()); final int offset = typeToken.offset(); @@ -1438,6 +1977,8 @@ private String generateBitSetProperty( final ByteOrder byteOrder = typeToken.encoding().byteOrder(); final String byteOrderStr = generateByteOrder(byteOrder, typeToken.encoding().primitiveType().size()); final String typeName = cSharpTypeName(typeToken.encoding().primitiveType()); + final CharSequence accessOrderListenerCall = generateAccessOrderListenerCall( + accessOrderModel, indent + TWO_INDENT, fieldToken); return String.format("\n" + "%1$s" + @@ -1446,10 +1987,12 @@ private String generateBitSetProperty( indent + INDENT + INDENT + "get\n" + indent + INDENT + INDENT + "{\n" + "%4$s" + + accessOrderListenerCall + indent + INDENT + INDENT + INDENT + "return (%5$s)_buffer.%6$sGet%8$s(_offset + %7$d);\n" + indent + INDENT + INDENT + "}\n" + indent + INDENT + INDENT + "set\n" + indent + INDENT + INDENT + "{\n" + + accessOrderListenerCall + indent + INDENT + INDENT + INDENT + "_buffer.%6$sPut%8$s(_offset + %7$d, (%9$s)value);\n" + indent + INDENT + INDENT + "}\n" + indent + INDENT + "}\n", @@ -1465,10 +2008,16 @@ private String generateBitSetProperty( } private Object generateCompositeProperty( - final String propertyName, final Token fieldToken, final Token typeToken, final String indent) + final String propertyName, + final Token fieldToken, + final Token typeToken, + final AccessOrderModel accessOrderModel, + final String indent) { final String compositeName = CSharpUtil.formatClassName(typeToken.applicableTypeName()); final int offset = typeToken.offset(); + final CharSequence accessOrderListenerCall = generateAccessOrderListenerCall( + accessOrderModel, indent + THREE_INDENT, fieldToken); final StringBuilder sb = new StringBuilder(); sb.append(String.format("\n" + @@ -1484,6 +2033,7 @@ private Object generateCompositeProperty( indent + INDENT + INDENT + "get\n" + indent + INDENT + INDENT + "{\n" + "%4$s" + + accessOrderListenerCall + indent + INDENT + INDENT + INDENT + "_%5$s.Wrap(_buffer, _offset + %6$d, _actingVersion);\n" + indent + INDENT + INDENT + INDENT + "return _%5$s;\n" + indent + INDENT + INDENT + "}\n" + diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java index 0bc3bd1494..47dd08d4d9 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java @@ -18,6 +18,7 @@ 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.generation.common.AccessOrderModel; import uk.co.real_logic.sbe.ir.*; import org.agrona.DirectBuffer; import org.agrona.MutableDirectBuffer; @@ -571,10 +572,7 @@ private static void generateAccessOrderListener( sb.append(indent).append("switch (codecState())\n") .append(indent).append("{\n"); - final List transitionGroups = new ArrayList<>(); - accessOrderModel.getTransitions(transitionGroups, interaction); - - transitionGroups.forEach(transitionGroup -> + accessOrderModel.forEachTransition(interaction, transitionGroup -> { transitionGroup.forEachStartState(startState -> sb.append(indent).append(" case ").append(stateCaseForSwitchCase(startState)).append(":\n")); @@ -666,7 +664,7 @@ private static CharSequence generateDecoderWrapListener( .append(indent).append(" switch(actingVersion)\n") .append(indent).append(" {\n"); - accessOrderModel.forEachDecoderWrappedState((version, state) -> + accessOrderModel.forEachWrappedStateByVersion((version, state) -> { sb.append(indent).append(" case ").append(version).append(":\n") .append(indent).append(" codecState(") From cc18cac738c62be2b61989be89da7bdc9da214f0 Mon Sep 17 00:00:00 2001 From: Zach Bray Date: Tue, 27 Jun 2023 19:02:30 +0100 Subject: [PATCH 34/50] [C#] Recover codec state after `ToString()`. Previously, the `ToString()` call would fail or complete but alter the state of the codec such that it was in a terminal state. Now, we treat `codecState` much like `limit` and recover the original value before returning control from `ToString()`. --- .../sbe/generation/csharp/CSharpGenerator.java | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java index 0739a592ad..0edb6fac47 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java @@ -167,7 +167,8 @@ public void generate() throws IOException out.append(generateVarData(accessOrderModel, varData, BASE_INDENT + INDENT)); - out.append(generateDisplay(toUpperFirstChar(msgToken.name()), fields, groups, varData)); + out.append(generateDisplay(toUpperFirstChar(msgToken.name()), + fields, groups, varData, accessOrderModel)); out.append(INDENT + "}\n"); out.append("}\n"); @@ -2378,7 +2379,8 @@ private CharSequence generateDisplay( final String name, final List tokens, final List groups, - final List varData) + final List varData, + final AccessOrderModel accessOrderModel) { final StringBuilder sb = new StringBuilder(100); @@ -2392,6 +2394,12 @@ private CharSequence generateDisplay( append(sb, TWO_INDENT, " }"); sb.append('\n'); append(sb, TWO_INDENT, " int originalLimit = this.Limit;"); + sb.append("#if ENABLE_ACCESS_ORDER_CHECKS\n"); + append(sb, TWO_INDENT, " CodecState originalState = _codecState;"); + sb.append(THREE_INDENT).append("_codecState = ") + .append(qualifiedStateCase(accessOrderModel.notWrappedState())).append(";\n"); + append(sb, TWO_INDENT, " OnWrapForDecode(_actingVersion);"); + sb.append("#endif\n"); append(sb, TWO_INDENT, " this.Limit = _offset + _actingBlockLength;"); append(sb, TWO_INDENT, " builder.Append(\"[" + name + "](sbeTemplateId=\");"); append(sb, TWO_INDENT, " builder.Append(" + name + ".TemplateId);"); @@ -2415,6 +2423,9 @@ private CharSequence generateDisplay( sb.append('\n'); appendDisplay(sb, tokens, groups, varData, THREE_INDENT); sb.append('\n'); + sb.append("#if ENABLE_ACCESS_ORDER_CHECKS\n"); + append(sb, TWO_INDENT, " _codecState = originalState;"); + sb.append("#endif\n"); append(sb, TWO_INDENT, " this.Limit = originalLimit;"); sb.append('\n'); append(sb, TWO_INDENT, "}"); From 9bc676c3997773aab9a35320da6627e9f2489323 Mon Sep 17 00:00:00 2001 From: Zach Bray Date: Wed, 28 Jun 2023 17:33:07 +0100 Subject: [PATCH 35/50] [C#] Generate (more) presence checks in accessors. Previously, some accessors did not check that the acting version (being decoded) had the field/group/varData being accessed. Therefore, it might return garbage. But other accessors did check. In this commit, I've attempted to make more of the accessors (hopefully all) check the acting version before attempting to read data from the underlying buffer. This change should make the C# codecs closer to the Java decoders. --- .../generation/csharp/CSharpGenerator.java | 57 ++++++++++++++++--- 1 file changed, 49 insertions(+), 8 deletions(-) diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java index 0edb6fac47..5dee07b9d8 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java @@ -255,6 +255,15 @@ private void generateGroupClassHeader( groupName)); } + sb.append("\n") + .append(indent).append(INDENT).append("public void NotPresent()\n") + .append(indent).append(INDENT).append("{\n") + .append(indent).append(TWO_INDENT).append("_count = 0;\n") + .append(indent).append(TWO_INDENT).append("_index = 0;\n") + .append(indent).append(TWO_INDENT).append("_buffer = null;\n") + .append(indent).append(TWO_INDENT).append("_offset = 0;\n") + .append(indent).append(INDENT).append("}\n"); + sb.append(String.format("\n" + indent + INDENT + "public void WrapForDecode(%s parentMessage, DirectBuffer buffer, int actingVersion)\n" + indent + INDENT + "{\n" + @@ -407,15 +416,18 @@ private CharSequence generateGroupProperty( generateSinceActingDeprecated(sb, indent, toUpperFirstChar(groupName), token); + final String groupField = "_" + toLowerFirstChar(groupName); + sb.append(String.format("\n" + "%1$s" + indent + "public %2$sGroup %3$s\n" + indent + "{\n" + indent + INDENT + "get\n" + indent + INDENT + "{\n" + + generateGroupNotPresentCondition(token.version(), indent + INDENT + INDENT, groupField) + indent + INDENT + INDENT + "_%4$s.WrapForDecode(_parentMessage, _buffer, _actingVersion);\n" + generateAccessOrderListenerCall(accessOrderModel, indent + TWO_INDENT, token, - "_" + groupName + ".Count", "\"decode\"") + + groupField + ".Count", "\"decode\"") + indent + INDENT + INDENT + "return _%4$s;\n" + indent + INDENT + "}\n" + indent + "}\n", @@ -480,6 +492,7 @@ private CharSequence generateVarData( sb.append(String.format(indent + "\n" + indent + "public int %1$sLength()\n" + indent + "{\n" + + generateArrayFieldNotPresentCondition(token.version(), indent, "0") + accessOrderListenerCall + indent + INDENT + "_buffer.CheckLimit(_parentMessage.Limit + %2$d);\n" + indent + INDENT + "return (int)_buffer.%3$sGet%4$s(_parentMessage.Limit);\n" + @@ -509,7 +522,7 @@ private CharSequence generateVarData( indent + INDENT + "return bytesCopied;\n" + indent + "}\n", propertyName, - generateArrayFieldNotPresentCondition(token.version(), indent), + generateArrayFieldNotPresentCondition(token.version(), indent, "0"), sizeOfLengthField, lengthTypePrefix, byteOrderStr)); @@ -518,6 +531,7 @@ private CharSequence generateVarData( indent + "// Allocates and returns a new byte array\n" + indent + "public byte[] Get%1$sBytes()\n" + indent + "{\n" + + generateArrayFieldNotPresentCondition(token.version(), indent, "new byte[0]") + accessOrderListenerCall + indent + INDENT + "const int sizeOfLengthField = %2$d;\n" + indent + INDENT + "int limit = _parentMessage.Limit;\n" + @@ -561,6 +575,7 @@ private CharSequence generateVarData( .append(String.format( indent + "public string Get%1$s()\n" + indent + "{\n" + + generateArrayFieldNotPresentCondition(token.version(), indent, "\"\"") + accessOrderListenerCall + indent + INDENT + "const int sizeOfLengthField = %2$d;\n" + indent + INDENT + "int limit = _parentMessage.Limit;\n" + @@ -948,16 +963,34 @@ private CharSequence generateFieldNotPresentCondition( literal); } - private CharSequence generateArrayFieldNotPresentCondition(final int sinceVersion, final String indent) + private CharSequence generateGroupNotPresentCondition( + final int sinceVersion, + final String indent, + final String groupInstanceField) { if (0 == sinceVersion) { return ""; } - return String.format( - indent + INDENT + INDENT + "if (_actingVersion < %d) return 0;\n\n", - sinceVersion); + return indent + "if (_actingVersion < " + sinceVersion + ")" + + indent + "{\n" + + indent + INDENT + groupInstanceField + ".NotPresent();\n" + + indent + INDENT + "return " + groupInstanceField + ";\n" + + indent + "}\n\n"; + } + + private CharSequence generateArrayFieldNotPresentCondition( + final int sinceVersion, + final String indent, + final String defaultValue) + { + if (0 == sinceVersion) + { + return ""; + } + + return indent + INDENT + "if (_actingVersion < " + sinceVersion + ") return " + defaultValue + ";\n\n"; } private CharSequence generateBitSetNotPresentCondition( @@ -1053,6 +1086,7 @@ private CharSequence generateArrayProperty( indent + "{\n" + indent + INDENT + "get\n" + indent + INDENT + "{\n" + + generateArrayFieldNotPresentCondition(fieldToken.version(), indent + INDENT + INDENT, "new %2$s[0]") + accessOrderListenerCallDoubleIndent + indent + INDENT + INDENT + "return _buffer.AsReadOnlySpan<%2$s>(_offset + %4$s, %3$sLength);\n" + indent + INDENT + "}\n" + @@ -1069,6 +1103,7 @@ private CharSequence generateArrayProperty( "%1$s" + indent + "public Span<%2$s> %3$sAsSpan()\n" + indent + "{\n" + + generateArrayFieldNotPresentCondition(fieldToken.version(), indent + INDENT + INDENT, "new %2$s[0]") + accessOrderListenerCall + indent + INDENT + "return _buffer.AsSpan<%2$s>(_offset + %4$s, %3$sLength);\n" + indent + "}\n", @@ -1087,7 +1122,10 @@ private CharSequence generateArrayProperty( accessOrderListenerCall + indent + INDENT + "return Get%1$s(new Span(dst, dstOffset, length));\n" + indent + "}\n", - propName, fieldLength, generateArrayFieldNotPresentCondition(fieldToken.version(), indent), offset)); + propName, + fieldLength, + generateArrayFieldNotPresentCondition(fieldToken.version(), indent, "0"), + offset)); sb.append(String.format("\n" + indent + "public int Get%1$s(Span dst)\n" + @@ -1102,7 +1140,10 @@ private CharSequence generateArrayProperty( indent + INDENT + "_buffer.GetBytes(_offset + %4$d, dst);\n" + indent + INDENT + "return length;\n" + indent + "}\n", - propName, fieldLength, generateArrayFieldNotPresentCondition(fieldToken.version(), indent), offset)); + propName, + fieldLength, + generateArrayFieldNotPresentCondition(fieldToken.version(), indent, "0"), + offset)); sb.append(String.format("\n" + indent + "public void Set%1$s(byte[] src, int srcOffset)\n" + From 03d605c1802293eacd4ab5d60e2a5939ea6ce284 Mon Sep 17 00:00:00 2001 From: Zach Bray Date: Wed, 28 Jun 2023 17:38:04 +0100 Subject: [PATCH 36/50] [C#] Enable access order checking tests. Having ported the implementation from Java and fixed some issues where the C# codecs were not checking the acting version when decoding data, the tests now pass. --- csharp/sbe-tests/FieldAccessOrderCheckTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/csharp/sbe-tests/FieldAccessOrderCheckTests.cs b/csharp/sbe-tests/FieldAccessOrderCheckTests.cs index 5cb475bfc7..1852628a67 100644 --- a/csharp/sbe-tests/FieldAccessOrderCheckTests.cs +++ b/csharp/sbe-tests/FieldAccessOrderCheckTests.cs @@ -8,7 +8,7 @@ namespace Org.SbeTool.Sbe.Tests { - [TestClass, Ignore] + [TestClass] public class FieldAccessOrderCheckTests { private const int Offset = 0; From 749962204b6810f5022c8edc0aef71bfc960205b Mon Sep 17 00:00:00 2001 From: Zach Bray Date: Mon, 31 Jul 2023 17:28:00 +0100 Subject: [PATCH 37/50] [C++] Port access order checking tests and implementation to C++. In this commit, I also addressed these issues (1-3): --- 1. Tidy up string format usage. --- For convenience and readability, I had inlined some concatenation into `string.format(...)` templates; however, this makes it unclear whether the variable/method-output being concatenated uses template parameters. Therefore, I have adjusted to inject these strings via template parameters. FWIW I'd prefer to use `StringBuilder` everywhere, as I think it is hard to read large string format templates. --- 2. Explcitly delete copy constructors of flyweights. --- I found it easy to accidentally write sub-optimal code where I relied on copy constructors. For example: ``` MyMessage::MyGroup myGroup = myMessage.myGroup(10); ``` rather than: ``` MyMessage::MyGroup &myGroup = myMessage.myGroup(10); ``` Copying semantics are a bit strange when it comes to flyweights and trees of flyweights. Therefore, I think it is best to avoid entirely. This change is behind a feature flag. To re-enable implicit copy constructors specify the system property `-Dsbe.generate.implicit.copy.constructors=true`. --- 3. Adjust state machines to allow repeated decoding of variable-length --- data lengths, as accesses do not advance `limit`. --- .gitignore | 3 + .../sbe-tests/FieldAccessOrderCheckTests.cs | 32 +- .../generation/common/AccessOrderModel.java | 88 + .../sbe/generation/cpp/CppGenerator.java | 1195 +++- .../generation/csharp/CSharpGenerator.java | 203 +- .../sbe/generation/java/JavaGenerator.java | 187 +- .../sbe/ir/generated/FrameCodecDecoder.java | 60 +- .../sbe/ir/generated/FrameCodecEncoder.java | 26 +- .../sbe/ir/generated/TokenCodecDecoder.java | 220 +- .../sbe/ir/generated/TokenCodecEncoder.java | 50 +- sbe-tool/src/test/cpp/BoundsCheckTest.cpp | 61 +- sbe-tool/src/test/cpp/CMakeLists.txt | 6 + sbe-tool/src/test/cpp/CodeGenTest.cpp | 80 +- .../test/cpp/CompositeOffsetsCodeGenTest.cpp | 2 +- .../test/cpp/FieldAccessOrderCheckTest.cpp | 4837 +++++++++++++++++ .../sbe/FieldAccessOrderCheckTest.java | 57 +- 16 files changed, 6699 insertions(+), 408 deletions(-) create mode 100644 sbe-tool/src/test/cpp/FieldAccessOrderCheckTest.cpp diff --git a/.gitignore b/.gitignore index 5d674b6f04..6769c8db69 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,9 @@ out # cmake cmake-build-debug codecs +.cmake +CMakeFiles +thirdparty # cpp build linux cppbuild/CMakeCache.txt diff --git a/csharp/sbe-tests/FieldAccessOrderCheckTests.cs b/csharp/sbe-tests/FieldAccessOrderCheckTests.cs index 1852628a67..6a2912cc08 100644 --- a/csharp/sbe-tests/FieldAccessOrderCheckTests.cs +++ b/csharp/sbe-tests/FieldAccessOrderCheckTests.cs @@ -131,6 +131,19 @@ public void DisallowsSkippingDecodingOfVariableLengthField3() Assert.IsTrue(exception.Message.Contains("Cannot access field \"c\" in state: V0_BLOCK")); } + [TestMethod] + public void AllowsRepeatedDecodingOfVariableLengthDataLength() + { + var decoder = DecodeUntilVarLengthFields(); + Assert.AreEqual(3, decoder.BLength()); + Assert.AreEqual(3, decoder.BLength()); + Assert.AreEqual(3, decoder.BLength()); + Assert.AreEqual("abc", decoder.GetB()); + Assert.AreEqual(3, decoder.CLength()); + Assert.AreEqual(3, decoder.CLength()); + Assert.AreEqual(3, decoder.CLength()); + } + [TestMethod] public void DisallowsReDecodingEarlierVariableLengthField() { @@ -2806,7 +2819,8 @@ public void DisallowsMissedEncodingOfVarLengthFieldInNestedGroupToNextOuterEleme Assert.IsTrue(exception.Message.Contains("Cannot access next element in repeating group \"b\" in state: " + expectedState)); Assert.IsTrue( - exception.Message.Contains("Expected one of these transitions: [\"b.d.e(?)\", \"b.d.f(?)\"].")); + exception.Message.Contains( + "Expected one of these transitions: [\"b.d.e(?)\", \"b.d.fLength()\", \"b.d.f(?)\"].")); } [TestMethod] @@ -2841,7 +2855,8 @@ public void DisallowsMissedDecodingOfVarLengthFieldInNestedGroupToNextInnerEleme exception.Message.Contains( "Cannot access next element in repeating group \"b.d\" in state: V0_B_1_D_N_BLOCK.")); Assert.IsTrue( - exception.Message.Contains("Expected one of these transitions: [\"b.d.e(?)\", \"b.d.f(?)\"].")); + exception.Message.Contains( + "Expected one of these transitions: [\"b.d.e(?)\", \"b.d.fLength()\", \"b.d.f(?)\"].")); } [TestMethod] @@ -2878,7 +2893,8 @@ public void DisallowsMissedDecodingOfVarLengthFieldInNestedGroupToNextInnerEleme exception.Message.Contains( "Cannot access next element in repeating group \"b.d\" in state: V0_B_N_D_N_BLOCK.")); Assert.IsTrue( - exception.Message.Contains("Expected one of these transitions: [\"b.d.e(?)\", \"b.d.f(?)\"].")); + exception.Message.Contains( + "Expected one of these transitions: [\"b.d.e(?)\", \"b.d.fLength()\", \"b.d.f(?)\"].")); } [TestMethod] @@ -2915,7 +2931,8 @@ public void DisallowsMissedDecodingOfVarLengthFieldInNestedGroupToNextOuterEleme exception.Message.Contains( "Cannot access next element in repeating group \"b\" in state: V0_B_N_D_N_BLOCK.")); Assert.IsTrue( - exception.Message.Contains("Expected one of these transitions: [\"b.d.e(?)\", \"b.d.f(?)\"].")); + exception.Message.Contains( + "Expected one of these transitions: [\"b.d.e(?)\", \"b.d.fLength()\", \"b.d.f(?)\"].")); } [TestMethod] @@ -2949,7 +2966,8 @@ public void DisallowsMissedDecodingOfVarLengthFieldInNestedGroupToNextOuterEleme exception.Message.Contains( "Cannot access next element in repeating group \"b\" in state: V0_B_N_D_1_BLOCK.")); Assert.IsTrue( - exception.Message.Contains("Expected one of these transitions: [\"b.d.e(?)\", \"b.d.f(?)\"].")); + exception.Message.Contains( + "Expected one of these transitions: [\"b.d.e(?)\", \"b.d.fLength()\", \"b.d.f(?)\"].")); } [TestMethod] @@ -2962,7 +2980,7 @@ public void DisallowsIncompleteMessagesDueToMissingVarLengthField1() var exception = Assert.ThrowsException(() => encoder.CheckEncodingIsComplete()); Assert.IsTrue( exception.Message.Contains( - "Not fully encoded, current state: V0_B_DONE, allowed transitions: \"c(?)\"")); + "Not fully encoded, current state: V0_B_DONE, allowed transitions: \"cLength()\", \"c(?)\"")); } [TestMethod] @@ -2973,7 +2991,7 @@ public void DisallowsIncompleteMessagesDueToMissingVarLengthField2() var exception = Assert.ThrowsException(() => encoder.CheckEncodingIsComplete()); Assert.IsTrue( exception.Message.Contains( - "Not fully encoded, current state: V0_BLOCK, allowed transitions: \"a(?)\"")); + "Not fully encoded, current state: V0_BLOCK, allowed transitions: \"aLength()\", \"a(?)\"")); } [TestMethod] diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/AccessOrderModel.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/AccessOrderModel.java index b00f631978..d8465f400d 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/AccessOrderModel.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/AccessOrderModel.java @@ -116,6 +116,15 @@ public void forEachWrappedStateByVersion(final IntObjConsumer consumer) } } + /** + * Returns the number of schema versions. + * @return the number of schema versions. + */ + public int versionCount() + { + return versionWrappedStates.size(); + } + /** * Iterates over the states in which a codec is fully encoded. * @param consumer the consumer of the states. @@ -136,6 +145,15 @@ public void forEachStateOrderedByStateNumber(final Consumer consumer) .forEach(consumer); } + /** + * Returns the number of states in the state machine. + * @return the number of states in the state machine. + */ + public int stateCount() + { + return transitionsByState.size(); + } + /** * Returns a hash-consing factory for codec interactions. * These interactions are the transitions in the state machine. @@ -541,6 +559,12 @@ public void onVarData(final Token token) { if (filter.test(token)) { + final CodecInteraction lengthAccessInteraction = interactionFactory.accessVarDataLength(token); + currentStates.forEach(state -> + { + allocateTransitions(lengthAccessInteraction, Collections.singleton(state), state); + }); + final CodecInteraction codecInteraction = interactionFactory.accessField(token); final State accessedState = allocateState(statePrefix + token.name().toUpperCase() + "_DONE"); allocateTransitions(codecInteraction, currentStates, accessedState); @@ -635,6 +659,15 @@ public State endState() return to; } + /** + * Returns {@code true} if the transitions in this group do not change state. + * @return {@code true} if the transitions in this group do not change state. + */ + public boolean alwaysEndsInStartState() + { + return from.size() == 1 && from.contains(to); + } + /** * Returns some example code for the codec interaction that the transitions in this group share. * Useful for producing error messages. @@ -902,6 +935,42 @@ String exampleConditions() } } + /** + * When the length of a variable length field is accessed without adjusting the position. + */ + private static final class AccessVarDataLength extends CodecInteraction + { + private final String groupPath; + private final Token token; + + private AccessVarDataLength(final String groupPath, final Token token) + { + assert groupPath != null; + assert token.signal() == Signal.BEGIN_VAR_DATA; + this.groupPath = groupPath; + this.token = token; + } + + @Override + public String groupQualifiedName() + { + return groupPath + token.name(); + } + + @Override + String exampleCode() + { + return groupPath + token.name() + "Length()"; + } + + @Override + String exampleConditions() + { + return ""; + } + } + + /** * Factory for creating {@link CodecInteraction} instances. This factory * is used to hash-cons the instances, so that they can be compared by @@ -915,6 +984,7 @@ public static final class HashConsingFactory private final Map determineGroupHasElementsInteractions = new HashMap<>(); private final Map moveToNextElementInteractions = new HashMap<>(); private final Map moveToLastElementInteractions = new HashMap<>(); + private final Map accessVarDataLengthInteractions = new HashMap<>(); private final Map groupPathsByField; private final Set topLevelBlockFields; @@ -1029,6 +1099,24 @@ public CodecInteraction moveToLastElement(final Token token) return moveToLastElementInteractions.computeIfAbsent(token, t -> new MoveToLastElement(groupPathsByField.get(t), t)); } + + /** + * Find or create a {@link CodecInteraction} to represent accessing the length + * of a variable-length data field without advancing the codec position. + * + *

For decoders and codecs, this will be when the length is accessed, e.g., + * {@code decoder.myVarDataLength()}. + * + *

The supplied token must carry a {@link Signal#BEGIN_VAR_DATA} signal. + * + * @param token the token identifying the field + * @return the {@link CodecInteraction} instance + */ + public CodecInteraction accessVarDataLength(final Token token) + { + return accessVarDataLengthInteractions.computeIfAbsent(token, + t -> new AccessVarDataLength(groupPathsByField.get(t), t)); + } } } } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java index 482335b269..35556c6faa 100755 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java @@ -15,23 +15,24 @@ */ package uk.co.real_logic.sbe.generation.cpp; -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.generation.common.AccessOrderModel; 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 org.agrona.Strings; +import org.agrona.Verify; +import org.agrona.collections.MutableBoolean; +import org.agrona.generation.OutputManager; import java.io.IOException; import java.io.Writer; import java.nio.ByteOrder; -import java.util.ArrayList; -import java.util.Formatter; -import java.util.List; +import java.util.*; +import java.util.function.Function; import static uk.co.real_logic.sbe.generation.Generators.toLowerFirstChar; import static uk.co.real_logic.sbe.generation.Generators.toUpperFirstChar; @@ -45,8 +46,12 @@ @SuppressWarnings("MethodLength") public class CppGenerator implements CodeGenerator { + private static final boolean DISABLE_IMPLICIT_COPYING = Boolean.parseBoolean( + System.getProperty("sbe.disable.implicit.copying", "true")); private static final String BASE_INDENT = ""; private static final String INDENT = " "; + private static final String TWO_INDENT = INDENT + INDENT; + private static final String THREE_INDENT = TWO_INDENT + INDENT; private final Ir ir; private final OutputManager outputManager; @@ -146,9 +151,6 @@ public void generate() throws IOException try (Writer out = outputManager.createOutput(className)) { - out.append(generateFileHeader(ir.namespaces(), className, typesToInclude)); - out.append(generateClassDeclaration(className)); - out.append(generateMessageFlyweightCode(className, msgToken)); final List messageBody = tokens.subList(1, tokens.size() - 1); int i = 0; @@ -162,20 +164,368 @@ public void generate() throws IOException final List varData = new ArrayList<>(); collectVarData(messageBody, i, varData); + AccessOrderModel accessOrderModel = null; + if (AccessOrderModel.generateAccessOrderChecks()) + { + accessOrderModel = AccessOrderModel.newInstance( + msgToken, + fields, + groups, + varData, + Function.identity()); + } + + out.append(generateFileHeader(ir.namespaces(), className, typesToInclude)); + out.append(generateClassDeclaration(className)); + out.append(generateMessageFlyweightCode(className, msgToken, accessOrderModel)); + out.append(generateFullyEncodedCheck(accessOrderModel)); + final StringBuilder sb = new StringBuilder(); - generateFields(sb, className, fields, BASE_INDENT); - generateGroups(sb, groups, BASE_INDENT); - generateVarData(sb, className, varData, BASE_INDENT); + generateFields(sb, className, fields, accessOrderModel, BASE_INDENT); + generateGroups(sb, groups, accessOrderModel, BASE_INDENT); + generateVarData(sb, className, varData, accessOrderModel, BASE_INDENT); generateDisplay(sb, msgToken.name(), fields, groups, varData); sb.append(generateMessageLength(groups, varData, BASE_INDENT)); sb.append("};\n"); + generateLookupTableDefinitions(sb, className, accessOrderModel); sb.append(CppUtil.closingBraces(ir.namespaces().length)).append("#endif\n"); out.append(sb); } } } - private void generateGroups(final StringBuilder sb, final List tokens, final String indent) + private static CharSequence generateFullyEncodedCheck(final AccessOrderModel accessOrderModel) + { + if (null == accessOrderModel) + { + return ""; + } + + final String indent = " "; + final StringBuilder sb = new StringBuilder(); + sb.append("\n"); + + sb.append(indent).append("void checkEncodingIsComplete()\n") + .append(indent).append("{\n") + .append("#if defined(ENABLE_ACCESS_ORDER_CHECKS)\n") + .append(indent).append(INDENT).append("switch (m_codecState)\n") + .append(indent).append(INDENT).append("{\n"); + + accessOrderModel.forEachTerminalEncoderState(state -> + { + sb.append(indent).append(TWO_INDENT).append("case ").append(stateCaseForSwitchCase(state)).append(":\n") + .append(indent).append(THREE_INDENT).append("return;\n"); + }); + + sb.append(indent).append(TWO_INDENT).append("default:\n") + .append(indent).append(THREE_INDENT) + .append("throw AccessOrderError(std::string(\"Not fully encoded, current state: \") +\n") + .append(indent).append(THREE_INDENT) + .append(INDENT).append("codecStateName(m_codecState) + \", allowed transitions: \" +\n") + .append(indent).append(THREE_INDENT) + .append(INDENT).append("codecStateTransitions(m_codecState));\n") + .append(indent).append(INDENT).append("}\n") + .append("#endif\n"); + + sb.append(indent).append("}\n\n"); + + return sb; + } + + private static String accessOrderListenerMethodName(final Token token) + { + return "on" + Generators.toUpperFirstChar(token.name()) + "Accessed"; + } + + private static String accessOrderListenerMethodName(final Token token, final String suffix) + { + return "on" + Generators.toUpperFirstChar(token.name()) + suffix + "Accessed"; + } + + private static void generateAccessOrderListenerMethod( + final StringBuilder sb, + final AccessOrderModel accessOrderModel, + final String indent, + final Token token) + { + if (null == accessOrderModel) + { + return; + } + + final AccessOrderModel.CodecInteraction fieldAccess = + accessOrderModel.interactionFactory().accessField(token); + + final String constDeclaration = canChangeState(accessOrderModel, fieldAccess) ? "" : " const"; + + sb.append("\n") + .append(indent).append("void ").append(accessOrderListenerMethodName(token)).append("()") + .append(constDeclaration).append("\n") + .append(indent).append("{\n"); + + generateAccessOrderListener( + sb, + indent + INDENT, + "access field", + accessOrderModel, + fieldAccess); + + sb.append(indent).append("}\n"); + } + + private static boolean canChangeState( + final AccessOrderModel accessOrderModel, + final AccessOrderModel.CodecInteraction fieldAccess) + { + if (fieldAccess.isTopLevelBlockFieldAccess()) + { + return false; + } + + final MutableBoolean canChangeState = new MutableBoolean(false); + accessOrderModel.forEachTransition(fieldAccess, transition -> + { + if (!transition.alwaysEndsInStartState()) + { + canChangeState.set(true); + } + }); + + return canChangeState.get(); + } + + private static CharSequence generateAccessOrderListenerCall( + final AccessOrderModel accessOrderModel, + final String indent, + final Token token, + final String... arguments) + { + return generateAccessOrderListenerCall( + accessOrderModel, + indent, + accessOrderListenerMethodName(token), + arguments); + } + + private static CharSequence generateAccessOrderListenerCall( + final AccessOrderModel accessOrderModel, + final String indent, + final String methodName, + final String... arguments) + { + if (null == accessOrderModel) + { + return ""; + } + + final StringBuilder sb = new StringBuilder(); + sb.append("#if defined(ENABLE_ACCESS_ORDER_CHECKS)\n") + .append(indent).append(methodName).append("("); + + for (int i = 0; i < arguments.length; i++) + { + if (i > 0) + { + sb.append(", "); + } + sb.append(arguments[i]); + } + sb.append(");\n"); + + sb.append("#endif\n"); + + return sb; + } + + private static void generateAccessOrderListenerMethodForGroupWrap( + final StringBuilder sb, + final AccessOrderModel accessOrderModel, + final String indent, + final Token token) + { + if (null == accessOrderModel) + { + return; + } + + sb.append("\n") + .append(indent).append("void ").append(accessOrderListenerMethodName(token)) + .append("(std::uint64_t remaining, std::string action)\n") + .append(indent).append("{\n") + .append(indent).append(INDENT).append("if (0 == remaining)\n") + .append(indent).append(INDENT).append("{\n"); + + final AccessOrderModel.CodecInteraction selectEmptyGroup = + accessOrderModel.interactionFactory().determineGroupIsEmpty(token); + + generateAccessOrderListener( + sb, + indent + TWO_INDENT, + "\" + action + \" count of repeating group", + accessOrderModel, + selectEmptyGroup); + + sb.append(indent).append(INDENT).append("}\n") + .append(indent).append(INDENT).append("else\n") + .append(indent).append(INDENT).append("{\n"); + + final AccessOrderModel.CodecInteraction selectNonEmptyGroup = + accessOrderModel.interactionFactory().determineGroupHasElements(token); + + generateAccessOrderListener( + sb, + indent + TWO_INDENT, + "\" + action + \" count of repeating group", + accessOrderModel, + selectNonEmptyGroup); + + sb.append(indent).append(INDENT).append("}\n") + .append(indent).append("}\n"); + } + + private static void generateAccessOrderListenerMethodForVarDataLength( + final StringBuilder sb, + final AccessOrderModel accessOrderModel, + final String indent, + final Token token) + { + if (null == accessOrderModel) + { + return; + } + + sb.append("\n") + .append(indent).append("void ").append(accessOrderListenerMethodName(token, "Length")) + .append("() const\n") + .append(indent).append("{\n"); + + final AccessOrderModel.CodecInteraction accessLength = + accessOrderModel.interactionFactory().accessVarDataLength(token); + + generateAccessOrderListener( + sb, + indent + INDENT, + "decode length of var data", + accessOrderModel, + accessLength); + + sb.append(indent).append("}\n"); + } + + private static void generateAccessOrderListener( + final StringBuilder sb, + final String indent, + final String action, + final AccessOrderModel accessOrderModel, + final AccessOrderModel.CodecInteraction interaction) + { + if (interaction.isTopLevelBlockFieldAccess()) + { + sb.append(indent).append("if (codecState() == ") + .append(qualifiedStateCase(accessOrderModel.notWrappedState())) + .append(")\n") + .append(indent).append("{\n"); + generateAccessOrderException(sb, indent + INDENT, action, interaction); + sb.append(indent).append("}\n"); + } + else + { + sb.append(indent).append("switch (codecState())\n") + .append(indent).append("{\n"); + + accessOrderModel.forEachTransition(interaction, transitionGroup -> + { + + transitionGroup.forEachStartState(startState -> + { + sb.append(indent).append(INDENT) + .append("case ").append(stateCaseForSwitchCase(startState)).append(":\n"); + }); + + if (!transitionGroup.alwaysEndsInStartState()) + { + sb.append(indent).append(TWO_INDENT).append("codecState(") + .append(qualifiedStateCase(transitionGroup.endState())).append(");\n"); + } + + sb.append(indent).append(TWO_INDENT).append("break;\n"); + }); + + sb.append(indent).append(INDENT).append("default:\n"); + generateAccessOrderException(sb, indent + TWO_INDENT, action, interaction); + sb.append(indent).append("}\n"); + } + } + + private static void generateAccessOrderException( + final StringBuilder sb, + final String indent, + final String action, + final AccessOrderModel.CodecInteraction interaction) + { + sb.append(indent).append("throw AccessOrderError(") + .append("std::string(\"Illegal field access order. \") +\n") + .append(indent).append(INDENT) + .append("\"Cannot ").append(action).append(" \\\"").append(interaction.groupQualifiedName()) + .append("\\\" in state: \" + codecStateName(codecState()) +\n") + .append(indent).append(INDENT) + .append("\". Expected one of these transitions: [\" + codecStateTransitions(codecState()) +\n") + .append(indent).append(INDENT) + .append("\"]. Please see the diagram in the docs of the inner enum #CodecState.\");\n"); + } + + private static void generateAccessOrderListenerMethodForNextGroupElement( + final StringBuilder sb, + final AccessOrderModel accessOrderModel, + final String indent, + final Token token) + { + if (null == accessOrderModel) + { + return; + } + + sb.append("\n"); + + sb.append(indent).append("void onNextElementAccessed()\n") + .append(indent).append("{\n") + .append(indent).append(INDENT).append("std::uint64_t remaining = m_count - m_index;\n") + .append(indent).append(INDENT).append("if (remaining > 1)\n") + .append(indent).append(INDENT).append("{\n"); + + final AccessOrderModel.CodecInteraction selectNextElementInGroup = + accessOrderModel.interactionFactory().moveToNextElement(token); + + generateAccessOrderListener( + sb, + indent + TWO_INDENT, + "access next element in repeating group", + accessOrderModel, + selectNextElementInGroup); + + sb.append(indent).append(INDENT).append("}\n") + .append(indent).append(INDENT).append("else if (1 == remaining)\n") + .append(indent).append(INDENT).append("{\n"); + + final AccessOrderModel.CodecInteraction selectLastElementInGroup = + accessOrderModel.interactionFactory().moveToLastElement(token); + + generateAccessOrderListener( + sb, + indent + TWO_INDENT, + "access next element in repeating group", + accessOrderModel, + selectLastElementInGroup); + + sb.append(indent).append(INDENT).append("}\n") + .append(indent).append("}\n"); + } + + private void generateGroups( + final StringBuilder sb, + final List tokens, + final AccessOrderModel accessOrderModel, + final String indent) { for (int i = 0, size = tokens.size(); i < size; i++) { @@ -189,7 +539,7 @@ private void generateGroups(final StringBuilder sb, final List tokens, fi final Token numInGroupToken = Generators.findFirst("numInGroup", tokens, i); final String cppTypeForNumInGroup = cppTypeName(numInGroupToken.encoding().primitiveType()); - generateGroupClassHeader(sb, groupName, tokens, i, indent + INDENT); + generateGroupClassHeader(sb, groupName, groupToken, tokens, accessOrderModel, i, indent + INDENT); ++i; final int groupHeaderTokenCount = tokens.get(i).componentTokenCount(); @@ -197,26 +547,32 @@ private void generateGroups(final StringBuilder sb, final List tokens, fi final List fields = new ArrayList<>(); i = collectFields(tokens, i, fields); - generateFields(sb, formatClassName(groupName), fields, indent + INDENT); + generateFields(sb, formatClassName(groupName), fields, accessOrderModel, indent + INDENT); final List groups = new ArrayList<>(); i = collectGroups(tokens, i, groups); - generateGroups(sb, groups, indent + INDENT); + generateGroups(sb, groups, accessOrderModel, indent + INDENT); final List varData = new ArrayList<>(); i = collectVarData(tokens, i, varData); - generateVarData(sb, formatClassName(groupName), varData, indent + INDENT); + generateVarData(sb, formatClassName(groupName), varData, accessOrderModel, indent + INDENT); sb.append(generateGroupDisplay(groupName, fields, groups, varData, indent + INDENT + INDENT)); sb.append(generateMessageLength(groups, varData, indent + INDENT + INDENT)); sb.append(indent).append(" };\n"); - generateGroupProperty(sb, groupName, groupToken, cppTypeForNumInGroup, indent); + generateGroupProperty(sb, groupName, groupToken, cppTypeForNumInGroup, accessOrderModel, indent); } } private static void generateGroupClassHeader( - final StringBuilder sb, final String groupName, final List tokens, final int index, final String indent) + final StringBuilder sb, + final String groupName, + final Token groupToken, + final List tokens, + final AccessOrderModel accessOrderModel, + final int index, + final String indent) { final String dimensionsClassName = formatClassName(tokens.get(index + 1).name()); final int dimensionHeaderLength = tokens.get(index + 1).encodedLength(); @@ -226,6 +582,8 @@ private static void generateGroupClassHeader( final String cppTypeBlockLength = cppTypeName(blockLengthToken.encoding().primitiveType()); final String cppTypeNumInGroup = cppTypeName(numInGroupToken.encoding().primitiveType()); + final String groupClassName = formatClassName(groupName); + new Formatter(sb).format("\n" + indent + "class %1$s\n" + indent + "{\n" + @@ -243,17 +601,50 @@ private static void generateGroupClassHeader( indent + " SBE_NODISCARD std::uint64_t *sbePositionPtr() SBE_NOEXCEPT\n" + indent + " {\n" + indent + " return m_positionPtr;\n" + - indent + " }\n\n" + + indent + " }\n\n", + groupClassName); - indent + "public:\n", - formatClassName(groupName)); + if (null != accessOrderModel) + { + new Formatter(sb).format( + indent + " CodecState *m_codecStatePtr = nullptr;\n\n" + + + indent + " CodecState codecState() const SBE_NOEXCEPT\n" + + indent + " {\n" + + indent + " return *m_codecStatePtr;\n" + + indent + " }\n\n" + + + indent + " CodecState *codecStatePtr()\n" + + indent + " {\n" + + indent + " return m_codecStatePtr;\n" + + indent + " }\n\n" + + + indent + " void codecState(CodecState codecState)\n" + + indent + " {\n" + + indent + " *m_codecStatePtr = codecState;\n" + + indent + " }\n\n" + ); + } + + sb.append(generateHiddenCopyConstructor(indent + " ", groupClassName)); + + final String codecStateParameter = null == accessOrderModel ? + ")\n" : + ",\n " + indent + " CodecState *codecState)\n"; + + final String codecStateAssignment = null == accessOrderModel ? + "" : + indent + " m_codecStatePtr = codecState;\n"; new Formatter(sb).format( + indent + "public:\n" + + indent + " %5$s() = default;\n\n" + + indent + " inline void wrapForDecode(\n" + indent + " char *buffer,\n" + indent + " std::uint64_t *pos,\n" + indent + " const std::uint64_t actingVersion,\n" + - indent + " const std::uint64_t bufferLength)\n" + + indent + " const std::uint64_t bufferLength%3$s" + indent + " {\n" + indent + " %2$s dimensions(buffer, *pos, bufferLength, actingVersion);\n" + indent + " m_buffer = buffer;\n" + @@ -265,8 +656,13 @@ private static void generateGroupClassHeader( indent + " m_initialPosition = *pos;\n" + indent + " m_positionPtr = pos;\n" + indent + " *m_positionPtr = *m_positionPtr + %1$d;\n" + + "%4$s" + indent + " }\n", - dimensionHeaderLength, dimensionsClassName); + dimensionHeaderLength, + dimensionsClassName, + codecStateParameter, + codecStateAssignment, + groupClassName); final long minCount = numInGroupToken.encoding().applicableMinValue().longValue(); final String minCheck = minCount > 0 ? "count < " + minCount + " || " : ""; @@ -277,7 +673,7 @@ private static void generateGroupClassHeader( indent + " const %3$s count,\n" + indent + " std::uint64_t *pos,\n" + indent + " const std::uint64_t actingVersion,\n" + - indent + " const std::uint64_t bufferLength)\n" + + indent + " const std::uint64_t bufferLength%8$s" + indent + " {\n" + indent + "#if defined(__GNUG__) && !defined(__clang__)\n" + indent + "#pragma GCC diagnostic push\n" + @@ -302,6 +698,7 @@ private static void generateGroupClassHeader( indent + " m_initialPosition = *pos;\n" + indent + " m_positionPtr = pos;\n" + indent + " *m_positionPtr = *m_positionPtr + %4$d;\n" + + "%9$s" + indent + " }\n", cppTypeBlockLength, blockLength, @@ -309,7 +706,36 @@ private static void generateGroupClassHeader( dimensionHeaderLength, minCheck, numInGroupToken.encoding().applicableMaxValue().longValue(), - dimensionsClassName); + dimensionsClassName, + codecStateParameter, + codecStateAssignment); + + if (groupToken.version() > 0) + { + final String codecStateNullAssignment = null == accessOrderModel ? + "" : + indent + " m_codecStatePtr = nullptr;\n"; + + new Formatter(sb).format( + indent + " inline void notPresent(std::uint64_t actingVersion)\n" + + indent + " {\n" + + indent + " m_buffer = nullptr;\n" + + indent + " m_bufferLength = 0;\n" + + indent + " m_blockLength = 0;\n" + + indent + " m_count = 0;\n" + + indent + " m_index = 0;\n" + + indent + " m_actingVersion = actingVersion;\n" + + indent + " m_initialPosition = 0;\n" + + indent + " m_positionPtr = nullptr;\n" + + "%1$s" + + indent + " }\n", + codecStateNullAssignment); + } + + generateAccessOrderListenerMethodForNextGroupElement(sb, accessOrderModel, indent + INDENT, groupToken); + + final CharSequence onNextAccessOrderCall = null == accessOrderModel ? "" : + generateAccessOrderListenerCall(accessOrderModel, indent + TWO_INDENT, "onNextElementAccessed"); new Formatter(sb).format("\n" + indent + " static SBE_CONSTEXPR std::uint64_t sbeHeaderSize() SBE_NOEXCEPT\n" + @@ -358,6 +784,7 @@ private static void generateGroupClassHeader( indent + " {\n" + indent + " throw std::runtime_error(\"index >= count [E108]\");\n" + indent + " }\n" + + "%4$s" + indent + " m_offset = *m_positionPtr;\n" + indent + " if (SBE_BOUNDS_CHECK_EXPECT(((m_offset + m_blockLength) > m_bufferLength), false))\n" + indent + " {\n" + @@ -370,7 +797,8 @@ private static void generateGroupClassHeader( indent + " }\n", dimensionHeaderLength, blockLength, - formatClassName(groupName)); + groupClassName, + onNextAccessOrderCall); sb.append("\n") .append(indent).append(" inline std::uint64_t resetCountToIndex()\n") @@ -399,6 +827,7 @@ private static void generateGroupProperty( final String groupName, final Token token, final String cppTypeForNumInGroup, + final AccessOrderModel accessOrderModel, final String indent) { final String className = formatClassName(groupName); @@ -420,25 +849,76 @@ private static void generateGroupProperty( groupName, token.id()); - new Formatter(sb).format("\n" + - indent + " SBE_NODISCARD inline %1$s &%2$s()\n" + - indent + " {\n" + - indent + " m_%2$s.wrapForDecode(m_buffer, sbePositionPtr(), m_actingVersion, m_bufferLength);\n" + - indent + " return m_%2$s;\n" + - indent + " }\n", - className, - propertyName); + if (null != accessOrderModel) + { + generateAccessOrderListenerMethodForGroupWrap( + sb, + accessOrderModel, + indent + INDENT, + token + ); + } + + final String codecStateArgument = null == accessOrderModel ? "" : ", codecStatePtr()"; + + final CharSequence onDecodeAccessOrderCall = null == accessOrderModel ? "" : + generateAccessOrderListenerCall(accessOrderModel, indent + TWO_INDENT, token, + "m_" + propertyName + ".count()", "\"decode\""); + + if (token.version() > 0) + { + new Formatter(sb).format("\n" + + indent + " SBE_NODISCARD inline %1$s &%2$s()\n" + + indent + " {\n" + + indent + " if (m_actingVersion < %5$du)\n" + + indent + " {\n" + + indent + " m_%2$s.notPresent(m_actingVersion);\n" + + indent + " return m_%2$s;\n" + + indent + " }\n\n" + + + indent + " m_%2$s.wrapForDecode(" + + "m_buffer, sbePositionPtr(), m_actingVersion, m_bufferLength%3$s);\n" + + "%4$s" + + indent + " return m_%2$s;\n" + + indent + " }\n", + className, + propertyName, + codecStateArgument, + onDecodeAccessOrderCall, + token.version()); + } + else + { + new Formatter(sb).format("\n" + + indent + " SBE_NODISCARD inline %1$s &%2$s()\n" + + indent + " {\n" + + indent + " m_%2$s.wrapForDecode(" + + "m_buffer, sbePositionPtr(), m_actingVersion, m_bufferLength%3$s);\n" + + "%4$s" + + indent + " return m_%2$s;\n" + + indent + " }\n", + className, + propertyName, + codecStateArgument, + onDecodeAccessOrderCall); + } + + final CharSequence onEncodeAccessOrderCall = null == accessOrderModel ? "" : + generateAccessOrderListenerCall(accessOrderModel, indent + TWO_INDENT, token, "count", "\"encode\""); new Formatter(sb).format("\n" + indent + " %1$s &%2$sCount(const %3$s count)\n" + indent + " {\n" + indent + " m_%2$s.wrapForEncode(" + - "m_buffer, count, sbePositionPtr(), m_actingVersion, m_bufferLength);\n" + + "m_buffer, count, sbePositionPtr(), m_actingVersion, m_bufferLength%4$s);\n" + + "%5$s" + indent + " return m_%2$s;\n" + indent + " }\n", className, propertyName, - cppTypeForNumInGroup); + cppTypeForNumInGroup, + codecStateArgument, + onEncodeAccessOrderCall); final int version = token.version(); final String versionCheck = 0 == version ? @@ -458,7 +938,11 @@ private static void generateGroupProperty( } private void generateVarData( - final StringBuilder sb, final String className, final List tokens, final String indent) + final StringBuilder sb, + final String className, + final List tokens, + final AccessOrderModel accessOrderModel, + final String indent) { for (int i = 0, size = tokens.size(); i < size;) { @@ -480,12 +964,39 @@ private void generateVarData( generateFieldMetaAttributeMethod(sb, token, indent); generateVarDataDescriptors( - sb, token, propertyName, characterEncoding, lengthToken, lengthOfLengthField, lengthCppType, indent); + sb, token, propertyName, characterEncoding, lengthOfLengthField, indent); + + generateAccessOrderListenerMethodForVarDataLength(sb, accessOrderModel, indent + INDENT, token); + + final CharSequence lengthAccessListenerCall = generateAccessOrderListenerCall( + accessOrderModel, indent + TWO_INDENT, + accessOrderListenerMethodName(token, "Length")); + + new Formatter(sb).format("\n" + + indent + " SBE_NODISCARD %4$s %1$sLength() const\n" + + indent + " {\n" + + "%2$s" + + "%5$s" + + indent + " %4$s length;\n" + + indent + " std::memcpy(&length, m_buffer + sbePosition(), sizeof(%4$s));\n" + + indent + " return %3$s(length);\n" + + indent + " }\n", + toLowerFirstChar(propertyName), + generateArrayFieldNotPresentCondition(token.version(), BASE_INDENT), + formatByteOrderEncoding(lengthToken.encoding().byteOrder(), lengthToken.encoding().primitiveType()), + lengthCppType, + lengthAccessListenerCall); + + generateAccessOrderListenerMethod(sb, accessOrderModel, indent + INDENT, token); + + final CharSequence accessOrderListenerCall = + generateAccessOrderListenerCall(accessOrderModel, indent + TWO_INDENT, token); new Formatter(sb).format("\n" + indent + " std::uint64_t skip%1$s()\n" + indent + " {\n" + "%2$s" + + "%6$s" + indent + " std::uint64_t lengthOfLengthField = %3$d;\n" + indent + " std::uint64_t lengthPosition = sbePosition();\n" + indent + " %5$s lengthFieldValue;\n" + @@ -498,12 +1009,14 @@ private void generateVarData( generateArrayFieldNotPresentCondition(token.version(), indent), lengthOfLengthField, lengthByteOrderStr, - lengthCppType); + lengthCppType, + accessOrderListenerCall); new Formatter(sb).format("\n" + indent + " SBE_NODISCARD const char *%1$s()\n" + indent + " {\n" + "%2$s" + + "%6$s" + indent + " %4$s lengthFieldValue;\n" + indent + " std::memcpy(&lengthFieldValue, m_buffer + sbePosition(), sizeof(%4$s));\n" + indent + " const char *fieldPtr = m_buffer + sbePosition() + %3$d;\n" + @@ -514,12 +1027,14 @@ private void generateVarData( generateTypeFieldNotPresentCondition(token.version(), indent), lengthOfLengthField, lengthCppType, - lengthByteOrderStr); + lengthByteOrderStr, + accessOrderListenerCall); new Formatter(sb).format("\n" + indent + " std::uint64_t get%1$s(char *dst, const std::uint64_t length)\n" + indent + " {\n" + "%2$s" + + "%6$s" + indent + " std::uint64_t lengthOfLengthField = %3$d;\n" + indent + " std::uint64_t lengthPosition = sbePosition();\n" + indent + " sbePosition(lengthPosition + lengthOfLengthField);\n" + @@ -536,11 +1051,13 @@ private void generateVarData( generateArrayFieldNotPresentCondition(token.version(), indent), lengthOfLengthField, lengthByteOrderStr, - lengthCppType); + lengthCppType, + accessOrderListenerCall); new Formatter(sb).format("\n" + indent + " %5$s &put%1$s(const char *src, const %3$s length)\n" + indent + " {\n" + + "%6$s" + indent + " std::uint64_t lengthOfLengthField = %2$d;\n" + indent + " std::uint64_t lengthPosition = sbePosition();\n" + indent + " %3$s lengthFieldValue = %4$s(length);\n" + @@ -558,12 +1075,14 @@ private void generateVarData( lengthOfLengthField, lengthCppType, lengthByteOrderStr, - className); + className, + accessOrderListenerCall); new Formatter(sb).format("\n" + indent + " std::string get%1$sAsString()\n" + indent + " {\n" + "%2$s" + + "%6$s" + indent + " std::uint64_t lengthOfLengthField = %3$d;\n" + indent + " std::uint64_t lengthPosition = sbePosition();\n" + indent + " sbePosition(lengthPosition + lengthOfLengthField);\n" + @@ -579,15 +1098,17 @@ private void generateVarData( generateStringNotPresentCondition(token.version(), indent), lengthOfLengthField, lengthByteOrderStr, - lengthCppType); + lengthCppType, + accessOrderListenerCall); - generateJsonEscapedStringGetter(sb, token, indent, propertyName); + generateJsonEscapedStringGetter(sb, token, indent, propertyName, accessOrderListenerCall); new Formatter(sb).format("\n" + indent + " #if __cplusplus >= 201703L\n" + indent + " std::string_view get%1$sAsStringView()\n" + indent + " {\n" + "%2$s" + + "%6$s" + indent + " std::uint64_t lengthOfLengthField = %3$d;\n" + indent + " std::uint64_t lengthPosition = sbePosition();\n" + indent + " sbePosition(lengthPosition + lengthOfLengthField);\n" + @@ -604,7 +1125,8 @@ private void generateVarData( generateStringViewNotPresentCondition(token.version(), indent), lengthOfLengthField, lengthByteOrderStr, - lengthCppType); + lengthCppType, + accessOrderListenerCall); new Formatter(sb).format("\n" + indent + " %1$s &put%2$s(const std::string &str)\n" + @@ -645,9 +1167,7 @@ private void generateVarDataDescriptors( final Token token, final String propertyName, final String characterEncoding, - final Token lengthToken, final Integer sizeOfLengthField, - final String lengthCppType, final String indent) { new Formatter(sb).format("\n" + @@ -687,19 +1207,6 @@ private void generateVarDataDescriptors( indent + " }\n", toLowerFirstChar(propertyName), sizeOfLengthField); - - new Formatter(sb).format("\n" + - indent + " SBE_NODISCARD %4$s %1$sLength() const\n" + - indent + " {\n" + - "%2$s" + - indent + " %4$s length;\n" + - indent + " std::memcpy(&length, m_buffer + sbePosition(), sizeof(%4$s));\n" + - indent + " return %3$s(length);\n" + - indent + " }\n", - toLowerFirstChar(propertyName), - generateArrayFieldNotPresentCondition(version, BASE_INDENT), - formatByteOrderEncoding(lengthToken.encoding().byteOrder(), lengthToken.encoding().primitiveType()), - lengthCppType); } private void generateChoiceSet(final List tokens) throws IOException @@ -1226,19 +1733,21 @@ private CharSequence generateCompositePropertyElements( switch (fieldToken.signal()) { case ENCODING: - generatePrimitiveProperty(sb, containingClassName, propertyName, fieldToken, fieldToken, indent); + generatePrimitiveProperty(sb, containingClassName, propertyName, fieldToken, fieldToken, + null, indent); break; case BEGIN_ENUM: - generateEnumProperty(sb, containingClassName, fieldToken, propertyName, fieldToken, indent); + generateEnumProperty(sb, containingClassName, fieldToken, propertyName, fieldToken, + null, indent); break; case BEGIN_SET: - generateBitsetProperty(sb, propertyName, fieldToken, indent); + generateBitsetProperty(sb, propertyName, fieldToken, fieldToken, null, indent); break; case BEGIN_COMPOSITE: - generateCompositeProperty(sb, propertyName, fieldToken, indent); + generateCompositeProperty(sb, propertyName, fieldToken, fieldToken, null, indent); break; default: @@ -1257,6 +1766,7 @@ private void generatePrimitiveProperty( final String propertyName, final Token propertyToken, final Token encodingToken, + final AccessOrderModel accessOrderModel, final String indent) { generatePrimitiveFieldMetaData(sb, propertyName, encodingToken, indent); @@ -1268,7 +1778,7 @@ private void generatePrimitiveProperty( else { generatePrimitivePropertyMethods( - sb, containingClassName, propertyName, propertyToken, encodingToken, indent); + sb, containingClassName, propertyName, propertyToken, encodingToken, accessOrderModel, indent); } } @@ -1278,16 +1788,19 @@ private void generatePrimitivePropertyMethods( final String propertyName, final Token propertyToken, final Token encodingToken, + final AccessOrderModel accessOrderModel, final String indent) { final int arrayLength = encodingToken.arrayLength(); if (arrayLength == 1) { - generateSingleValueProperty(sb, containingClassName, propertyName, propertyToken, encodingToken, indent); + generateSingleValueProperty(sb, containingClassName, propertyName, propertyToken, encodingToken, + accessOrderModel, indent); } else if (arrayLength > 1) { - generateArrayProperty(sb, containingClassName, propertyName, propertyToken, encodingToken, indent); + generateArrayProperty(sb, containingClassName, propertyName, propertyToken, encodingToken, + accessOrderModel, indent); } } @@ -1415,42 +1928,59 @@ private CharSequence generateStoreValue( return sb; } + private static String noexceptDeclaration(final AccessOrderModel accessOrderModel) + { + return accessOrderModel == null ? " SBE_NOEXCEPT" : ""; + } + private void generateSingleValueProperty( final StringBuilder sb, final String containingClassName, final String propertyName, final Token propertyToken, final Token encodingToken, + final AccessOrderModel accessOrderModel, final String indent) { final PrimitiveType primitiveType = encodingToken.encoding().primitiveType(); final String cppTypeName = cppTypeName(primitiveType); final int offset = encodingToken.offset(); + final CharSequence accessOrderListenerCall = + generateAccessOrderListenerCall(accessOrderModel, indent + TWO_INDENT, propertyToken); + + final String noexceptDeclaration = noexceptDeclaration(accessOrderModel); + new Formatter(sb).format("\n" + - indent + " SBE_NODISCARD %1$s %2$s() const SBE_NOEXCEPT\n" + + indent + " SBE_NODISCARD %1$s %2$s() const%6$s\n" + indent + " {\n" + "%3$s" + "%4$s" + + "%5$s" + indent + " }\n", cppTypeName, propertyName, generateFieldNotPresentCondition(propertyToken.version(), encodingToken.encoding(), indent), - generateLoadValue(primitiveType, Integer.toString(offset), encodingToken.encoding().byteOrder(), indent)); + accessOrderListenerCall, + generateLoadValue(primitiveType, Integer.toString(offset), encodingToken.encoding().byteOrder(), indent), + noexceptDeclaration); final CharSequence storeValue = generateStoreValue( primitiveType, "", Integer.toString(offset), encodingToken.encoding().byteOrder(), indent); new Formatter(sb).format("\n" + - indent + " %1$s &%2$s(const %3$s value) SBE_NOEXCEPT\n" + + indent + " %1$s &%2$s(const %3$s value)%6$s\n" + indent + " {\n" + "%4$s" + + "%5$s" + indent + " return *this;\n" + indent + " }\n", formatClassName(containingClassName), propertyName, cppTypeName, - storeValue); + storeValue, + accessOrderListenerCall, + noexceptDeclaration); } private void generateArrayProperty( @@ -1459,12 +1989,18 @@ private void generateArrayProperty( final String propertyName, final Token propertyToken, final Token encodingToken, + final AccessOrderModel accessOrderModel, final String indent) { final PrimitiveType primitiveType = encodingToken.encoding().primitiveType(); final String cppTypeName = cppTypeName(primitiveType); final int offset = encodingToken.offset(); + final CharSequence accessOrderListenerCall = + generateAccessOrderListenerCall(accessOrderModel, indent + TWO_INDENT, propertyToken); + + final String noexceptDeclaration = noexceptDeclaration(accessOrderModel); + final int arrayLength = encodingToken.arrayLength(); new Formatter(sb).format("\n" + indent + " static SBE_CONSTEXPR std::uint64_t %1$sLength() SBE_NOEXCEPT\n" + @@ -1475,24 +2011,30 @@ private void generateArrayProperty( arrayLength); new Formatter(sb).format("\n" + - indent + " SBE_NODISCARD const char *%1$s() const SBE_NOEXCEPT\n" + + indent + " SBE_NODISCARD const char *%1$s() const%5$s\n" + indent + " {\n" + "%2$s" + + "%4$s" + indent + " return m_buffer + m_offset + %3$d;\n" + indent + " }\n", propertyName, generateTypeFieldNotPresentCondition(propertyToken.version(), indent), - offset); + offset, + accessOrderListenerCall, + noexceptDeclaration); new Formatter(sb).format("\n" + - indent + " SBE_NODISCARD char *%1$s() SBE_NOEXCEPT\n" + + indent + " SBE_NODISCARD char *%1$s()%5$s\n" + indent + " {\n" + "%2$s" + + "%4$s" + indent + " return m_buffer + m_offset + %3$d;\n" + indent + " }\n", propertyName, generateTypeFieldNotPresentCondition(propertyToken.version(), indent), - offset); + offset, + accessOrderListenerCall, + noexceptDeclaration); final CharSequence loadValue = generateLoadValue( primitiveType, @@ -1508,13 +2050,15 @@ private void generateArrayProperty( indent + " throw std::runtime_error(\"index out of range for %2$s [E104]\");\n" + indent + " }\n\n" + "%4$s" + + "%6$s" + "%5$s" + indent + " }\n", cppTypeName, propertyName, arrayLength, generateFieldNotPresentCondition(propertyToken.version(), encodingToken.encoding(), indent), - loadValue); + loadValue, + accessOrderListenerCall); final CharSequence storeValue = generateStoreValue( primitiveType, @@ -1531,6 +2075,7 @@ private void generateArrayProperty( indent + " throw std::runtime_error(\"index out of range for %2$s [E105]\");\n" + indent + " }\n\n" + + "%6$s" + "%5$s" + indent + " return *this;\n" + indent + " }\n", @@ -1538,7 +2083,8 @@ private void generateArrayProperty( propertyName, cppTypeName, arrayLength, - storeValue); + storeValue, + accessOrderListenerCall); new Formatter(sb).format("\n" + indent + " std::uint64_t get%1$s(char *const dst, const std::uint64_t length) const\n" + @@ -1549,6 +2095,7 @@ private void generateArrayProperty( indent + " }\n\n" + "%3$s" + + "%6$s" + indent + " std::memcpy(dst, m_buffer + m_offset + %4$d, " + "sizeof(%5$s) * static_cast(length));\n" + indent + " return length;\n" + @@ -1557,11 +2104,13 @@ private void generateArrayProperty( arrayLength, generateArrayFieldNotPresentCondition(propertyToken.version(), indent), offset, - cppTypeName); + cppTypeName, + accessOrderListenerCall); new Formatter(sb).format("\n" + - indent + " %1$s &put%2$s(const char *const src) SBE_NOEXCEPT\n" + + indent + " %1$s &put%2$s(const char *const src)%7$s\n" + indent + " {\n" + + "%6$s" + indent + " std::memcpy(m_buffer + m_offset + %3$d, src, sizeof(%4$s) * %5$d);\n" + indent + " return *this;\n" + indent + " }\n", @@ -1569,7 +2118,9 @@ private void generateArrayProperty( toUpperFirstChar(propertyName), offset, cppTypeName, - arrayLength); + arrayLength, + accessOrderListenerCall, + noexceptDeclaration); if (arrayLength > 1 && arrayLength <= 4) { @@ -1588,8 +2139,9 @@ private void generateArrayProperty( } } - sb.append(") SBE_NOEXCEPT\n"); + sb.append(")").append(noexceptDeclaration).append("\n"); sb.append(indent).append(" {\n"); + sb.append(accessOrderListenerCall); for (int i = 0; i < arrayLength; i++) { @@ -1611,6 +2163,8 @@ private void generateArrayProperty( new Formatter(sb).format("\n" + indent + " SBE_NODISCARD std::string get%1$sAsString() const\n" + indent + " {\n" + + "%4$s" + + "%5$s" + indent + " const char *buffer = m_buffer + m_offset + %2$d;\n" + indent + " std::size_t length = 0;\n\n" + @@ -1621,14 +2175,18 @@ private void generateArrayProperty( indent + " }\n", toUpperFirstChar(propertyName), offset, - arrayLength); + arrayLength, + generateStringNotPresentCondition(propertyToken.version(), indent), + accessOrderListenerCall); - generateJsonEscapedStringGetter(sb, encodingToken, indent, propertyName); + generateJsonEscapedStringGetter(sb, encodingToken, indent, propertyName, accessOrderListenerCall); new Formatter(sb).format("\n" + indent + " #if __cplusplus >= 201703L\n" + - indent + " SBE_NODISCARD std::string_view get%1$sAsStringView() const SBE_NOEXCEPT\n" + + indent + " SBE_NODISCARD std::string_view get%1$sAsStringView() const %6$s\n" + indent + " {\n" + + "%4$s" + + "%5$s" + indent + " const char *buffer = m_buffer + m_offset + %2$d;\n" + indent + " std::size_t length = 0;\n\n" + @@ -1640,7 +2198,10 @@ private void generateArrayProperty( indent + " #endif\n", toUpperFirstChar(propertyName), offset, - arrayLength); + arrayLength, + generateStringViewNotPresentCondition(propertyToken.version(), indent), + accessOrderListenerCall, + noexceptDeclaration); new Formatter(sb).format("\n" + indent + " #if __cplusplus >= 201703L\n" + @@ -1652,6 +2213,7 @@ private void generateArrayProperty( indent + " throw std::runtime_error(\"string too large for put%2$s [E106]\");\n" + indent + " }\n\n" + + "%5$s" + indent + " std::memcpy(m_buffer + m_offset + %3$d, str.data(), srcLength);\n" + indent + " for (std::size_t start = srcLength; start < %4$d; ++start)\n" + indent + " {\n" + @@ -1669,6 +2231,7 @@ private void generateArrayProperty( indent + " throw std::runtime_error(\"string too large for put%2$s [E106]\");\n" + indent + " }\n\n" + + "%5$s" + indent + " std::memcpy(m_buffer + m_offset + %3$d, str.c_str(), srcLength);\n" + indent + " for (std::size_t start = srcLength; start < %4$d; ++start)\n" + indent + " {\n" + @@ -1681,17 +2244,23 @@ private void generateArrayProperty( containingClassName, toUpperFirstChar(propertyName), offset, - arrayLength); + arrayLength, + accessOrderListenerCall); } } private void generateJsonEscapedStringGetter( - final StringBuilder sb, final Token token, final String indent, final String propertyName) + final StringBuilder sb, + final Token token, + final String indent, + final String propertyName, + final CharSequence accessOrderListenerCall) { new Formatter(sb).format("\n" + indent + " std::string get%1$sAsJsonEscapedString()\n" + indent + " {\n" + "%2$s" + + "%3$s" + indent + " std::ostringstream oss;\n" + indent + " std::string s = get%1$sAsString();\n\n" + indent + " for (const auto c : s)\n" + @@ -1720,7 +2289,8 @@ private void generateJsonEscapedStringGetter( indent + " return oss.str();\n" + indent + " }\n", toUpperFirstChar(propertyName), - generateStringNotPresentCondition(token.version(), indent)); + generateStringNotPresentCondition(token.version(), indent), + accessOrderListenerCall); } private void generateConstPropertyMethods( @@ -1807,7 +2377,7 @@ private void generateConstPropertyMethods( values, constantValue.length); - generateJsonEscapedStringGetter(sb, token, indent, propertyName); + generateJsonEscapedStringGetter(sb, token, indent, propertyName, ""); } private CharSequence generateFixedFlyweightCode(final String className, final int size) @@ -1821,6 +2391,7 @@ private CharSequence generateFixedFlyweightCode(final String className, final in " std::uint64_t m_bufferLength = 0;\n" + " std::uint64_t m_offset = 0;\n" + " std::uint64_t m_actingVersion = 0;\n\n" + + "%7$s" + "public:\n" + " enum MetaAttribute\n" + @@ -1879,7 +2450,17 @@ private CharSequence generateFixedFlyweightCode(final String className, final in " const std::uint64_t actingVersion,\n" + " const std::uint64_t bufferLength)\n" + " {\n" + - " return *this = %1$s(buffer, offset, bufferLength, actingVersion);\n" + + " m_buffer = buffer;\n" + + " m_bufferLength = bufferLength;\n" + + " m_offset = offset;\n" + + " m_actingVersion = actingVersion;\n\n" + + + " if (SBE_BOUNDS_CHECK_EXPECT(((m_offset + %2$s) > m_bufferLength), false))\n" + + " {\n" + + " throw std::runtime_error(\"buffer too short for flyweight [E107]\");\n" + + " }\n\n" + + + " return *this;\n" + " }\n\n" + " SBE_NODISCARD static SBE_CONSTEXPR std::uint64_t encodedLength() SBE_NOEXCEPT\n" + @@ -1926,14 +2507,53 @@ private CharSequence generateFixedFlyweightCode(final String className, final in schemaIdType, generateLiteral(ir.headerStructure().schemaIdType(), Integer.toString(ir.id())), schemaVersionType, - generateLiteral(ir.headerStructure().schemaVersionType(), Integer.toString(ir.version()))); + generateLiteral(ir.headerStructure().schemaVersionType(), Integer.toString(ir.version())), + generateHiddenCopyConstructor(" ", className)); } - private static CharSequence generateConstructorsAndOperators(final String className) + private static String generateHiddenCopyConstructor(final String indent, final String className) + { + final String ctorAndCopyAssignmentDeletion = String.format( + "#if __cplusplus >= 201103L\n" + + "%1$s%2$s(const %2$s&) = delete;\n" + + "%1$s%2$s& operator=(const %2$s&) = delete;\n" + + "#else\n" + + "%1$s%2$s(const %2$s&);\n" + + "%1$s%2$s& operator=(const %2$s&);\n" + + "#endif\n\n", + indent, className); + + return DISABLE_IMPLICIT_COPYING ? ctorAndCopyAssignmentDeletion : ""; + } + + private static CharSequence generateConstructorsAndOperators( + final String className, + final AccessOrderModel accessOrderModel) { + final String constructorWithCodecState = null == accessOrderModel ? "" : String.format( + " %1$s(\n" + + " char *buffer,\n" + + " const std::uint64_t offset,\n" + + " const std::uint64_t bufferLength,\n" + + " const std::uint64_t actingBlockLength,\n" + + " const std::uint64_t actingVersion,\n" + + " CodecState codecState) :\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" + + " m_codecState(codecState)\n" + + " {\n" + + " }\n\n", + className); + return String.format( " %1$s() = default;\n\n" + + "%2$s" + + " %1$s(\n" + " char *buffer,\n" + " const std::uint64_t offset,\n" + @@ -1962,10 +2582,14 @@ private static CharSequence generateConstructorsAndOperators(final String classN " %1$s(buffer, 0, bufferLength, actingBlockLength, actingVersion)\n" + " {\n" + " }\n\n", - className); + className, + constructorWithCodecState); } - private CharSequence generateMessageFlyweightCode(final String className, final Token token) + private CharSequence generateMessageFlyweightCode( + final String className, + final Token token, + final AccessOrderModel accessOrderModel) { final String blockLengthType = cppTypeName(ir.headerStructure().blockLengthType()); final String templateIdType = cppTypeName(ir.headerStructure().templateIdType()); @@ -1975,20 +2599,28 @@ private CharSequence generateMessageFlyweightCode(final String className, final final String headerType = ir.headerStructure().tokens().get(0).name(); final String semanticVersion = ir.semanticVersion() == null ? "" : ir.semanticVersion(); + + final String codecStateArgument = null == accessOrderModel ? "" : ", m_codecState"; + return String.format( "private:\n" + + "%15$s" + + "%16$s" + " char *m_buffer = nullptr;\n" + " std::uint64_t m_bufferLength = 0;\n" + " std::uint64_t m_offset = 0;\n" + " std::uint64_t m_position = 0;\n" + " std::uint64_t m_actingBlockLength = 0;\n" + - " std::uint64_t m_actingVersion = 0;\n\n" + + " std::uint64_t m_actingVersion = 0;\n" + + "%17$s" + " inline std::uint64_t *sbePositionPtr() SBE_NOEXCEPT\n" + " {\n" + " return &m_position;\n" + " }\n\n" + + "%22$s" + + "public:\n" + " static const %1$s SBE_BLOCK_LENGTH = %2$s;\n" + " static const %3$s SBE_TEMPLATE_ID = %4$s;\n" + @@ -2015,7 +2647,9 @@ private CharSequence generateMessageFlyweightCode(final String className, final " using messageHeader = %12$s;\n\n" + + "%18$s" + "%11$s" + + " SBE_NODISCARD static SBE_CONSTEXPR %1$s sbeBlockLength() SBE_NOEXCEPT\n" + " {\n" + " return %2$s;\n" + @@ -2058,7 +2692,14 @@ private CharSequence generateMessageFlyweightCode(final String className, final " %10$s &wrapForEncode(char *buffer, const std::uint64_t offset, const std::uint64_t bufferLength)\n" + " {\n" + - " return *this = %10$s(buffer, offset, bufferLength, sbeBlockLength(), sbeSchemaVersion());\n" + + " m_buffer = buffer;\n" + + " m_bufferLength = bufferLength;\n" + + " m_offset = offset;\n" + + " m_actingBlockLength = sbeBlockLength();\n" + + " m_actingVersion = sbeSchemaVersion();\n" + + " m_position = sbeCheckPosition(m_offset + m_actingBlockLength);\n" + + "%19$s" + + " return *this;\n" + " }\n\n" + " %10$s &wrapAndApplyHeader(" + @@ -2072,14 +2713,18 @@ private CharSequence generateMessageFlyweightCode(final String className, final " .schemaId(sbeSchemaId())\n" + " .version(sbeSchemaVersion());\n\n" + - " return *this = %10$s(\n" + - " buffer,\n" + - " offset + messageHeader::encodedLength(),\n" + - " bufferLength,\n" + - " sbeBlockLength(),\n" + - " sbeSchemaVersion());\n" + + " m_buffer = buffer;\n" + + " m_bufferLength = bufferLength;\n" + + " m_offset = offset + messageHeader::encodedLength();\n" + + " m_actingBlockLength = sbeBlockLength();\n" + + " m_actingVersion = sbeSchemaVersion();\n" + + " m_position = sbeCheckPosition(m_offset + m_actingBlockLength);\n" + + "%19$s" + + " return *this;\n" + " }\n\n" + + "%20$s" + + " %10$s &wrapForDecode(\n" + " char *buffer,\n" + " const std::uint64_t offset,\n" + @@ -2087,7 +2732,14 @@ private CharSequence generateMessageFlyweightCode(final String className, final " const std::uint64_t actingVersion,\n" + " const std::uint64_t bufferLength)\n" + " {\n" + - " return *this = %10$s(buffer, offset, bufferLength, actingBlockLength, actingVersion);\n" + + " m_buffer = buffer;\n" + + " m_bufferLength = bufferLength;\n" + + " m_offset = offset;\n" + + " m_actingBlockLength = actingBlockLength;\n" + + " m_actingVersion = actingVersion;\n" + + " m_position = sbeCheckPosition(m_offset + m_actingBlockLength);\n" + + "%21$s" + + " return *this;\n" + " }\n\n" + " %10$s &sbeRewind()\n" + @@ -2123,7 +2775,7 @@ private CharSequence generateMessageFlyweightCode(final String className, final " SBE_NODISCARD std::uint64_t decodeLength() const\n" + " {\n" + - " %10$s skipper(m_buffer, m_offset, m_bufferLength, sbeBlockLength(), m_actingVersion);\n" + + " %10$s skipper(m_buffer, m_offset, m_bufferLength, sbeBlockLength(), m_actingVersion%14$s);\n" + " skipper.skip();\n" + " return skipper.encodedLength();\n" + " }\n\n" + @@ -2157,15 +2809,269 @@ private CharSequence generateMessageFlyweightCode(final String className, final generateLiteral(ir.headerStructure().schemaVersionType(), Integer.toString(ir.version())), semanticType, className, - generateConstructorsAndOperators(className), + generateConstructorsAndOperators(className, accessOrderModel), formatClassName(headerType), - semanticVersion); + semanticVersion, + codecStateArgument, + generateFieldOrderStateEnum(accessOrderModel), + generateLookupTableDeclarations(accessOrderModel), + generateFieldOrderStateMember(accessOrderModel), + generateAccessOrderErrorType(accessOrderModel), + generateEncoderWrapListener(accessOrderModel), + generateDecoderWrapListener(accessOrderModel), + generateDecoderWrapListenerCall(accessOrderModel), + generateHiddenCopyConstructor(" ", className)); + } + + private CharSequence generateAccessOrderErrorType(final AccessOrderModel accessOrderModel) + { + if (null == accessOrderModel) + { + return ""; + } + + final StringBuilder sb = new StringBuilder(); + sb.append(INDENT).append("class AccessOrderError : public std::logic_error\n") + .append(INDENT).append("{\n") + .append(INDENT).append("public:\n") + .append(INDENT).append(" explicit AccessOrderError(const std::string &msg) : std::logic_error(msg) {}\n") + .append(INDENT).append("};\n\n"); + return sb; + } + + private static CharSequence generateLookupTableDeclarations(final AccessOrderModel accessOrderModel) + { + if (null == accessOrderModel) + { + return ""; + } + + final StringBuilder sb = new StringBuilder(); + sb.append(INDENT).append("static const std::string STATE_NAME_LOOKUP[") + .append(accessOrderModel.stateCount()) + .append("];\n"); + sb.append(INDENT).append("static const std::string STATE_TRANSITIONS_LOOKUP[") + .append(accessOrderModel.stateCount()) + .append("];\n\n"); + + sb.append(INDENT).append("static std::string codecStateName(CodecState state)\n") + .append(INDENT).append("{\n") + .append(TWO_INDENT).append("return STATE_NAME_LOOKUP[static_cast(state)];\n") + .append(INDENT).append("}\n\n"); + + sb.append(INDENT).append("static std::string codecStateTransitions(CodecState state)\n") + .append(INDENT).append("{\n") + .append(TWO_INDENT).append("return STATE_TRANSITIONS_LOOKUP[static_cast(state)];\n") + .append(INDENT).append("}\n\n"); + + return sb; + } + + private static void generateLookupTableDefinitions( + final StringBuilder sb, + final String className, + final AccessOrderModel accessOrderModel) + { + if (null == accessOrderModel) + { + return; + } + + sb.append("\n").append("const std::string ").append(className).append("::STATE_NAME_LOOKUP[") + .append(accessOrderModel.stateCount()).append("] =\n") + .append("{\n"); + accessOrderModel.forEachStateOrderedByStateNumber(state -> + { + sb.append(INDENT).append("\"").append(state.name()).append("\",\n"); + }); + sb.append("};\n\n"); + + sb.append("const std::string ").append(className).append("::STATE_TRANSITIONS_LOOKUP[") + .append(accessOrderModel.stateCount()).append("] =\n") + .append("{\n"); + accessOrderModel.forEachStateOrderedByStateNumber(state -> + { + sb.append(INDENT).append("\""); + final MutableBoolean isFirst = new MutableBoolean(true); + final Set transitionDescriptions = new HashSet<>(); + accessOrderModel.forEachTransitionFrom(state, transitionGroup -> + { + if (transitionDescriptions.add(transitionGroup.exampleCode())) + { + if (isFirst.get()) + { + isFirst.set(false); + } + else + { + sb.append(", "); + } + + sb.append("\\\"").append(transitionGroup.exampleCode()).append("\\\""); + } + }); + sb.append("\",\n"); + }); + sb.append("};\n\n"); + } + + private static CharSequence qualifiedStateCase(final AccessOrderModel.State state) + { + return "CodecState::" + state.name(); + } + + private static CharSequence stateCaseForSwitchCase(final AccessOrderModel.State state) + { + return qualifiedStateCase(state); + } + + private static CharSequence unqualifiedStateCase(final AccessOrderModel.State state) + { + return state.name(); + } + + private static CharSequence generateFieldOrderStateEnum(final AccessOrderModel accessOrderModel) + { + if (null == accessOrderModel) + { + return ""; + } + + final StringBuilder sb = new StringBuilder(); + + sb.append(" /**\n"); + sb.append(" * The states in which a encoder/decoder/codec can live.\n"); + sb.append(" *\n"); + sb.append(" *

The state machine diagram below, encoded in the dot language, describes\n"); + sb.append(" * the valid state transitions according to the order in which fields may be\n"); + sb.append(" * accessed safely. Tools such as PlantUML and Graphviz can render it.\n"); + sb.append(" *\n"); + sb.append(" *

{@code\n");
+        accessOrderModel.generateGraph(sb, "     *   ");
+        sb.append("     * }
\n"); + sb.append(" */\n"); + sb.append(INDENT).append("enum class CodecState\n") + .append(INDENT).append("{\n"); + accessOrderModel.forEachStateOrderedByStateNumber(state -> + { + sb.append(INDENT).append(INDENT).append(unqualifiedStateCase(state)) + .append(" = ").append(state.number()) + .append(",\n"); + }); + sb.append(INDENT).append("};\n\n"); + + return sb; + } + + private static CharSequence generateFieldOrderStateMember(final AccessOrderModel accessOrderModel) + { + if (null == accessOrderModel) + { + return "\n"; + } + + final StringBuilder sb = new StringBuilder(); + + sb.append(INDENT).append("CodecState m_codecState = ") + .append(qualifiedStateCase(accessOrderModel.notWrappedState())) + .append(";\n\n"); + + sb.append(INDENT).append("CodecState codecState() const\n") + .append(INDENT).append("{\n") + .append(INDENT).append(INDENT).append("return m_codecState;\n") + .append(INDENT).append("}\n\n"); + + sb.append(INDENT).append("CodecState *codecStatePtr()\n") + .append(INDENT).append("{\n") + .append(INDENT).append(INDENT).append("return &m_codecState;\n") + .append(INDENT).append("}\n\n"); + + sb.append(INDENT).append("void codecState(CodecState newState)\n") + .append(INDENT).append("{\n") + .append(INDENT).append(INDENT).append("m_codecState = newState;\n") + .append(INDENT).append("}\n\n"); + + return sb; + } + + private static CharSequence generateDecoderWrapListener(final AccessOrderModel accessOrderModel) + { + if (null == accessOrderModel) + { + return ""; + } + + if (accessOrderModel.versionCount() == 1) + { + return ""; + } + + final StringBuilder sb = new StringBuilder(); + sb.append(INDENT).append("void onWrapForDecode(std::uint64_t actingVersion)\n") + .append(INDENT).append("{\n") + .append(INDENT).append(INDENT).append("switch(actingVersion)\n") + .append(INDENT).append(INDENT).append("{\n"); + + accessOrderModel.forEachWrappedStateByVersion((version, state) -> + { + sb.append(INDENT).append(TWO_INDENT).append("case ").append(version).append(":\n") + .append(INDENT).append(THREE_INDENT).append("codecState(") + .append(qualifiedStateCase(state)).append(");\n") + .append(INDENT).append(THREE_INDENT).append("break;\n"); + }); + + sb.append(INDENT).append(TWO_INDENT).append("default:\n") + .append(INDENT).append(THREE_INDENT).append("codecState(") + .append(qualifiedStateCase(accessOrderModel.latestVersionWrappedState())).append(");\n") + .append(INDENT).append(THREE_INDENT).append("break;\n") + .append(INDENT).append(INDENT).append("}\n") + .append(INDENT).append("}\n\n"); + + return sb; + } + + + private CharSequence generateDecoderWrapListenerCall(final AccessOrderModel accessOrderModel) + { + if (null == accessOrderModel) + { + return ""; + } + + if (accessOrderModel.versionCount() == 1) + { + final StringBuilder sb = new StringBuilder(); + sb.append("#if defined(ENABLE_ACCESS_ORDER_CHECKS)\n") + .append(TWO_INDENT).append("codecState(") + .append(qualifiedStateCase(accessOrderModel.latestVersionWrappedState())).append(");\n") + .append("#endif\n"); + return sb; + } + + return generateAccessOrderListenerCall(accessOrderModel, TWO_INDENT, "onWrapForDecode", "actingVersion"); + } + + private CharSequence generateEncoderWrapListener(final AccessOrderModel accessOrderModel) + { + if (null == accessOrderModel) + { + return ""; + } + + final StringBuilder sb = new StringBuilder(); + sb.append("#if defined(ENABLE_ACCESS_ORDER_CHECKS)\n") + .append(TWO_INDENT).append("codecState(") + .append(qualifiedStateCase(accessOrderModel.latestVersionWrappedState())) + .append(");\n") + .append("#endif\n"); + return sb; } private void generateFields( final StringBuilder sb, final String containingClassName, final List tokens, + final AccessOrderModel accessOrderModel, final String indent) { for (int i = 0, size = tokens.size(); i < size; i++) @@ -2179,23 +3085,29 @@ private void generateFields( generateFieldMetaAttributeMethod(sb, signalToken, indent); generateFieldCommonMethods(indent, sb, signalToken, encodingToken, propertyName); + generateAccessOrderListenerMethod(sb, accessOrderModel, indent + INDENT, signalToken); + switch (encodingToken.signal()) { case ENCODING: generatePrimitiveProperty( - sb, containingClassName, propertyName, signalToken, encodingToken, indent); + sb, containingClassName, propertyName, signalToken, encodingToken, + accessOrderModel, indent); break; case BEGIN_ENUM: - generateEnumProperty(sb, containingClassName, signalToken, propertyName, encodingToken, indent); + generateEnumProperty(sb, containingClassName, signalToken, propertyName, encodingToken, + accessOrderModel, indent); break; case BEGIN_SET: - generateBitsetProperty(sb, propertyName, encodingToken, indent); + generateBitsetProperty(sb, propertyName, signalToken, encodingToken, + accessOrderModel, indent); break; case BEGIN_COMPOSITE: - generateCompositeProperty(sb, propertyName, encodingToken, indent); + generateCompositeProperty(sb, propertyName, signalToken, encodingToken, + accessOrderModel, indent); break; default: @@ -2310,6 +3222,7 @@ private void generateEnumProperty( final Token fieldToken, final String propertyName, final Token encodingToken, + final AccessOrderModel accessOrderModel, final String indent) { final String enumName = formatClassName(encodingToken.applicableTypeName()); @@ -2362,21 +3275,31 @@ private void generateEnumProperty( else { final String offsetStr = Integer.toString(offset); + + final CharSequence accessOrderListenerCall = generateAccessOrderListenerCall( + accessOrderModel, indent + TWO_INDENT, fieldToken); + + final String noexceptDeclaration = noexceptDeclaration(accessOrderModel); + new Formatter(sb).format("\n" + - indent + " SBE_NODISCARD %1$s %2$sRaw() const SBE_NOEXCEPT\n" + + indent + " SBE_NODISCARD %1$s %2$sRaw() const%6$s\n" + indent + " {\n" + "%3$s" + "%4$s" + + "%5$s" + indent + " }\n", typeName, propertyName, generateFieldNotPresentCondition(fieldToken.version(), encodingToken.encoding(), indent), - generateLoadValue(primitiveType, offsetStr, encodingToken.encoding().byteOrder(), indent)); + accessOrderListenerCall, + generateLoadValue(primitiveType, offsetStr, encodingToken.encoding().byteOrder(), indent), + noexceptDeclaration); new Formatter(sb).format("\n" + indent + " SBE_NODISCARD %1$s::Value %2$s() const\n" + indent + " {\n" + "%3$s" + + "%7$s" + indent + " %5$s val;\n" + indent + " std::memcpy(&val, m_buffer + m_offset + %6$d, sizeof(%5$s));\n" + indent + " return %1$s::get(%4$s(val));\n" + @@ -2386,11 +3309,13 @@ private void generateEnumProperty( generateEnumFieldNotPresentCondition(fieldToken.version(), enumName, indent), formatByteOrderEncoding(encodingToken.encoding().byteOrder(), primitiveType), typeName, - offset); + offset, + accessOrderListenerCall); new Formatter(sb).format("\n" + - indent + " %1$s &%2$s(const %3$s::Value value) SBE_NOEXCEPT\n" + + indent + " %1$s &%2$s(const %3$s::Value value)%8$s\n" + indent + " {\n" + + "%7$s" + indent + " %4$s val = %6$s(value);\n" + indent + " std::memcpy(m_buffer + m_offset + %5$d, &val, sizeof(%4$s));\n" + indent + " return *this;\n" + @@ -2400,15 +3325,22 @@ private void generateEnumProperty( enumName, typeName, offset, - formatByteOrderEncoding(encodingToken.encoding().byteOrder(), primitiveType)); + formatByteOrderEncoding(encodingToken.encoding().byteOrder(), primitiveType), + accessOrderListenerCall, + noexceptDeclaration); } } private static void generateBitsetProperty( - final StringBuilder sb, final String propertyName, final Token token, final String indent) + final StringBuilder sb, + final String propertyName, + final Token fieldToken, + final Token encodingToken, + final AccessOrderModel accessOrderModel, + final String indent) { - final String bitsetName = formatClassName(token.applicableTypeName()); - final int offset = token.offset(); + final String bitsetName = formatClassName(encodingToken.applicableTypeName()); + final int offset = encodingToken.offset(); new Formatter(sb).format("\n" + indent + "private:\n" + @@ -2418,15 +3350,20 @@ private static void generateBitsetProperty( bitsetName, propertyName); + final CharSequence accessOrderListenerCall = generateAccessOrderListenerCall( + accessOrderModel, indent + TWO_INDENT, fieldToken); + new Formatter(sb).format( indent + " SBE_NODISCARD %1$s &%2$s()\n" + indent + " {\n" + + "%4$s" + 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); + offset, + accessOrderListenerCall); new Formatter(sb).format("\n" + indent + " static SBE_CONSTEXPR std::size_t %1$sEncodingLength() SBE_NOEXCEPT\n" + @@ -2434,13 +3371,18 @@ private static void generateBitsetProperty( indent + " return %2$d;\n" + indent + " }\n", propertyName, - token.encoding().primitiveType().size()); + encodingToken.encoding().primitiveType().size()); } private static void generateCompositeProperty( - final StringBuilder sb, final String propertyName, final Token token, final String indent) + final StringBuilder sb, + final String propertyName, + final Token fieldToken, + final Token encodingToken, + final AccessOrderModel accessOrderModel, + final String indent) { - final String compositeName = formatClassName(token.applicableTypeName()); + final String compositeName = formatClassName(encodingToken.applicableTypeName()); new Formatter(sb).format("\n" + "private:\n" + @@ -2450,15 +3392,20 @@ private static void generateCompositeProperty( compositeName, propertyName); + final CharSequence accessOrderListenerCall = generateAccessOrderListenerCall( + accessOrderModel, indent + TWO_INDENT, fieldToken); + new Formatter(sb).format( indent + " SBE_NODISCARD %1$s &%2$s()\n" + indent + " {\n" + + "%4$s" + 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()); + encodingToken.offset(), + accessOrderListenerCall); } private CharSequence generateNullValueLiteral(final PrimitiveType primitiveType, final Encoding encoding) diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java index 5dee07b9d8..41cf6c8fb0 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java @@ -418,34 +418,51 @@ private CharSequence generateGroupProperty( final String groupField = "_" + toLowerFirstChar(groupName); + final CharSequence accessOrderListenerCallOnDecode = generateAccessOrderListenerCall( + accessOrderModel, + indent + TWO_INDENT, + token, + groupField + ".Count", + "\"decode\""); + sb.append(String.format("\n" + "%1$s" + indent + "public %2$sGroup %3$s\n" + indent + "{\n" + indent + INDENT + "get\n" + indent + INDENT + "{\n" + - generateGroupNotPresentCondition(token.version(), indent + INDENT + INDENT, groupField) + + "%5$s" + + indent + INDENT + INDENT + "_%4$s.WrapForDecode(_parentMessage, _buffer, _actingVersion);\n" + - generateAccessOrderListenerCall(accessOrderModel, indent + TWO_INDENT, token, - groupField + ".Count", "\"decode\"") + + "%6$s" + + indent + INDENT + INDENT + "return _%4$s;\n" + indent + INDENT + "}\n" + indent + "}\n", generateDocumentation(indent, token), className, toUpperFirstChar(groupName), - toLowerFirstChar(groupName))); + toLowerFirstChar(groupName), + generateGroupNotPresentCondition(token.version(), indent + INDENT + INDENT, groupField), + accessOrderListenerCallOnDecode)); + final CharSequence accessOrderListenerCallOnEncode = generateAccessOrderListenerCall( + accessOrderModel, + indent + INDENT, + token, + "count", + "\"encode\""); sb.append(String.format("\n" + indent + "public %1$sGroup %2$sCount(int count)\n" + indent + "{\n" + - generateAccessOrderListenerCall(accessOrderModel, indent + INDENT, token, "count", "\"encode\"") + + "%4$s" + indent + INDENT + "_%3$s.WrapForEncode(_parentMessage, _buffer, count);\n" + indent + INDENT + "return _%3$s;\n" + indent + "}\n", className, toUpperFirstChar(groupName), - toLowerFirstChar(groupName))); + toLowerFirstChar(groupName), + accessOrderListenerCallOnEncode)); return sb; } @@ -480,27 +497,34 @@ private CharSequence generateVarData( final ByteOrder byteOrder = lengthEncoding.byteOrder(); final String byteOrderStr = generateByteOrder(byteOrder, lengthEncoding.primitiveType().size()); + generateAccessOrderListenerMethodForVarDataLength(sb, accessOrderModel, indent, token); generateAccessOrderListenerMethod(sb, accessOrderModel, indent, token); - final CharSequence accessOrderListenerCall = - generateAccessOrderListenerCall(accessOrderModel, indent + INDENT, token); sb.append(String.format("\n" + indent + "public const int %sHeaderSize = %d;\n", propertyName, sizeOfLengthField)); + final CharSequence lengthAccessOrderListenerCall = generateAccessOrderListenerCall( + accessOrderModel, indent + INDENT, accessOrderListenerMethodName(token, "Length")); + sb.append(String.format(indent + "\n" + indent + "public int %1$sLength()\n" + indent + "{\n" + - generateArrayFieldNotPresentCondition(token.version(), indent, "0") + - accessOrderListenerCall + + "%5$s" + + "%6$s" + indent + INDENT + "_buffer.CheckLimit(_parentMessage.Limit + %2$d);\n" + indent + INDENT + "return (int)_buffer.%3$sGet%4$s(_parentMessage.Limit);\n" + indent + "}\n", propertyName, sizeOfLengthField, lengthTypePrefix, - byteOrderStr)); + byteOrderStr, + generateArrayFieldNotPresentCondition(token.version(), indent, "0"), + lengthAccessOrderListenerCall)); + + final CharSequence accessOrderListenerCall = + generateAccessOrderListenerCall(accessOrderModel, indent + INDENT, token); sb.append(String.format("\n" + indent + "public int Get%1$s(byte[] dst, int dstOffset, int length) =>\n" + @@ -511,7 +535,7 @@ private CharSequence generateVarData( indent + "public int Get%1$s(Span dst)\n" + indent + "{\n" + "%2$s" + - accessOrderListenerCall + + "%6$s" + indent + INDENT + "const int sizeOfLengthField = %3$d;\n" + indent + INDENT + "int limit = _parentMessage.Limit;\n" + indent + INDENT + "_buffer.CheckLimit(limit + sizeOfLengthField);\n" + @@ -525,14 +549,15 @@ private CharSequence generateVarData( generateArrayFieldNotPresentCondition(token.version(), indent, "0"), sizeOfLengthField, lengthTypePrefix, - byteOrderStr)); + byteOrderStr, + accessOrderListenerCall)); sb.append(String.format(indent + "\n" + indent + "// Allocates and returns a new byte array\n" + indent + "public byte[] Get%1$sBytes()\n" + indent + "{\n" + - generateArrayFieldNotPresentCondition(token.version(), indent, "new byte[0]") + - accessOrderListenerCall + + "%5$s" + + "%6$s" + indent + INDENT + "const int sizeOfLengthField = %2$d;\n" + indent + INDENT + "int limit = _parentMessage.Limit;\n" + indent + INDENT + "_buffer.CheckLimit(limit + sizeOfLengthField);\n" + @@ -545,7 +570,9 @@ private CharSequence generateVarData( propertyName, sizeOfLengthField, lengthTypePrefix, - byteOrderStr)); + byteOrderStr, + generateArrayFieldNotPresentCondition(token.version(), indent, "new byte[0]"), + accessOrderListenerCall)); sb.append(String.format("\n" + indent + "public int Set%1$s(byte[] src, int srcOffset, int length) =>\n" + @@ -555,7 +582,7 @@ private CharSequence generateVarData( sb.append(String.format("\n" + indent + "public int Set%1$s(ReadOnlySpan src)\n" + indent + "{\n" + - accessOrderListenerCall + + "%6$s" + indent + INDENT + "const int sizeOfLengthField = %2$d;\n" + indent + INDENT + "int limit = _parentMessage.Limit;\n" + indent + INDENT + "_parentMessage.Limit = limit + sizeOfLengthField + src.Length;\n" + @@ -567,7 +594,8 @@ private CharSequence generateVarData( sizeOfLengthField, lengthTypePrefix, lengthCSharpType, - byteOrderStr)); + byteOrderStr, + accessOrderListenerCall)); if (characterEncoding != null) // only generate these string based methods if there is an encoding { @@ -575,8 +603,8 @@ private CharSequence generateVarData( .append(String.format( indent + "public string Get%1$s()\n" + indent + "{\n" + - generateArrayFieldNotPresentCondition(token.version(), indent, "\"\"") + - accessOrderListenerCall + + "%6$s" + + "%7$s" + indent + INDENT + "const int sizeOfLengthField = %2$d;\n" + indent + INDENT + "int limit = _parentMessage.Limit;\n" + indent + INDENT + "_buffer.CheckLimit(limit + sizeOfLengthField);\n" + @@ -587,7 +615,7 @@ private CharSequence generateVarData( indent + "}\n\n" + indent + "public void Set%1$s(string value)\n" + indent + "{\n" + - accessOrderListenerCall + + "%7$s" + indent + INDENT + "var encoding = %1$sResolvedCharacterEncoding;\n" + indent + INDENT + "const int sizeOfLengthField = %2$d;\n" + indent + INDENT + "int limit = _parentMessage.Limit;\n" + @@ -596,7 +624,13 @@ private CharSequence generateVarData( indent + INDENT + "_parentMessage.Limit = limit + sizeOfLengthField + byteCount;\n" + indent + INDENT + "_buffer.%3$sPut%4$s(limit, (%5$s)byteCount);\n" + indent + "}\n", - propertyName, sizeOfLengthField, lengthTypePrefix, byteOrderStr, lengthCSharpType)); + propertyName, + sizeOfLengthField, + lengthTypePrefix, + byteOrderStr, + lengthCSharpType, + generateArrayFieldNotPresentCondition(token.version(), indent, "\"\""), + accessOrderListenerCall)); } } } @@ -911,7 +945,6 @@ private CharSequence generateSingleValueProperty( final CharSequence accessOrderListenerCall = generateAccessOrderListenerCall(accessOrderModel, indent + TWO_INDENT, fieldToken); - return String.format("\n" + "%1$s" + indent + "public %2$s %3$s\n" + @@ -919,12 +952,12 @@ private CharSequence generateSingleValueProperty( indent + INDENT + "get\n" + indent + INDENT + "{\n" + "%4$s" + - accessOrderListenerCall + + "%8$s" + indent + INDENT + INDENT + "return _buffer.%5$sGet%7$s(_offset + %6$d);\n" + indent + INDENT + "}\n" + indent + INDENT + "set\n" + indent + INDENT + "{\n" + - accessOrderListenerCall + + "%8$s" + indent + INDENT + INDENT + "_buffer.%5$sPut%7$s(_offset + %6$d, value);\n" + indent + INDENT + "}\n" + indent + "}\n\n", @@ -934,7 +967,8 @@ private CharSequence generateSingleValueProperty( generateFieldNotPresentCondition(fieldToken.version(), typeToken.encoding(), indent), typePrefix, offset, - byteOrderStr); + byteOrderStr, + accessOrderListenerCall); } private CharSequence generateFieldNotPresentCondition( @@ -1058,13 +1092,14 @@ private CharSequence generateArrayProperty( indent + INDENT + INDENT + "ThrowHelper.ThrowIndexOutOfRangeException(index);\n" + indent + INDENT + "}\n\n" + "%5$s" + - accessOrderListenerCall + + "%10$s" + indent + INDENT + "return _buffer.%6$sGet%9$s(_offset + %7$d + (index * %8$d));\n" + indent + "}\n", generateDocumentation(indent, fieldToken), typeName, propName, fieldLength, generateFieldNotPresentCondition(fieldToken.version(), typeToken.encoding(), indent), - typePrefix, offset, typeSize, byteOrderStr)); + typePrefix, offset, typeSize, byteOrderStr, + accessOrderListenerCall)); sb.append(String.format("\n" + "%1$s" + @@ -1074,11 +1109,12 @@ private CharSequence generateArrayProperty( indent + INDENT + "{\n" + indent + INDENT + INDENT + "ThrowHelper.ThrowIndexOutOfRangeException(index);\n" + indent + INDENT + "}\n\n" + - accessOrderListenerCall + + "%9$s" + indent + INDENT + "_buffer.%5$sPut%8$s(_offset + %6$d + (index * %7$d), value);\n" + indent + "}\n", generateDocumentation(indent, fieldToken), - propName, typeName, fieldLength, typePrefix, offset, typeSize, byteOrderStr)); + propName, typeName, fieldLength, typePrefix, offset, typeSize, byteOrderStr, + accessOrderListenerCall)); sb.append(String.format("\n" + "%1$s" + @@ -1086,29 +1122,35 @@ private CharSequence generateArrayProperty( indent + "{\n" + indent + INDENT + "get\n" + indent + INDENT + "{\n" + - generateArrayFieldNotPresentCondition(fieldToken.version(), indent + INDENT + INDENT, "new %2$s[0]") + - accessOrderListenerCallDoubleIndent + + "%5$s" + + "%6$s" + indent + INDENT + INDENT + "return _buffer.AsReadOnlySpan<%2$s>(_offset + %4$s, %3$sLength);\n" + indent + INDENT + "}\n" + indent + INDENT + "set\n" + indent + INDENT + "{\n" + - accessOrderListenerCallDoubleIndent + + "%6$s" + indent + INDENT + INDENT + "value.CopyTo(_buffer.AsSpan<%2$s>(_offset + %4$s, %3$sLength));\n" + indent + INDENT + "}\n" + indent + "}\n", generateDocumentation(indent, fieldToken), - typeName, propName, offset)); + typeName, propName, offset, + generateArrayFieldNotPresentCondition(fieldToken.version(), + indent + INDENT + INDENT, "new " + typeName + "[0]"), + accessOrderListenerCallDoubleIndent)); sb.append(String.format("\n" + "%1$s" + indent + "public Span<%2$s> %3$sAsSpan()\n" + indent + "{\n" + - generateArrayFieldNotPresentCondition(fieldToken.version(), indent + INDENT + INDENT, "new %2$s[0]") + - accessOrderListenerCall + + "%5$s" + + "%6$s" + indent + INDENT + "return _buffer.AsSpan<%2$s>(_offset + %4$s, %3$sLength);\n" + indent + "}\n", generateDocumentation(indent, fieldToken), - typeName, propName, offset)); + typeName, propName, offset, + generateArrayFieldNotPresentCondition(fieldToken.version(), + indent + INDENT + INDENT, "new " + typeName + "[0]"), + accessOrderListenerCall)); if (typeToken.encoding().primitiveType() == PrimitiveType.CHAR) { @@ -1119,13 +1161,13 @@ private CharSequence generateArrayProperty( indent + "{\n" + indent + INDENT + "const int length = %2$d;\n" + "%3$s" + - accessOrderListenerCall + + "%4$s" + indent + INDENT + "return Get%1$s(new Span(dst, dstOffset, length));\n" + indent + "}\n", propName, fieldLength, generateArrayFieldNotPresentCondition(fieldToken.version(), indent, "0"), - offset)); + accessOrderListenerCall)); sb.append(String.format("\n" + indent + "public int Get%1$s(Span dst)\n" + @@ -1136,14 +1178,15 @@ private CharSequence generateArrayProperty( indent + INDENT + INDENT + "ThrowHelper.ThrowWhenSpanLengthTooSmall(dst.Length);\n" + indent + INDENT + "}\n\n" + "%3$s" + - accessOrderListenerCall + + "%5$s" + indent + INDENT + "_buffer.GetBytes(_offset + %4$d, dst);\n" + indent + INDENT + "return length;\n" + indent + "}\n", propName, fieldLength, generateArrayFieldNotPresentCondition(fieldToken.version(), indent, "0"), - offset)); + offset, + accessOrderListenerCall)); sb.append(String.format("\n" + indent + "public void Set%1$s(byte[] src, int srcOffset)\n" + @@ -1160,25 +1203,28 @@ private CharSequence generateArrayProperty( indent + INDENT + "{\n" + indent + INDENT + INDENT + "ThrowHelper.ThrowWhenSpanLengthTooLarge(src.Length);\n" + indent + INDENT + "}\n\n" + - accessOrderListenerCall + + "%4$s" + indent + INDENT + "_buffer.SetBytes(_offset + %3$d, src);\n" + indent + "}\n", - propName, fieldLength, offset)); + propName, fieldLength, offset, + accessOrderListenerCall)); sb.append(String.format("\n" + indent + "public void Set%1$s(string value)\n" + indent + "{\n" + - accessOrderListenerCall + + "%3$s" + indent + INDENT + "_buffer.SetNullTerminatedBytesFromString(%1$sResolvedCharacterEncoding, " + "value, _offset + %2$s, %1$sLength, %1$sNullValue);\n" + indent + "}\n" + indent + "public string Get%1$s()\n" + indent + "{\n" + - accessOrderListenerCall + + "%3$s" + indent + INDENT + "return _buffer.GetStringFromNullTerminatedBytes(%1$sResolvedCharacterEncoding, " + "_offset + %2$s, %1$sLength, %1$sNullValue);\n" + indent + "}\n", - propName, offset)); + propName, + offset, + accessOrderListenerCall)); } return sb; @@ -1334,7 +1380,7 @@ private CharSequence generateMessageFlyweightCode( indent + INDENT + "}\n\n" + indent + INDENT + "public %10$s WrapForEncode(DirectBuffer buffer, int offset)\n" + indent + INDENT + "{\n" + - generateEncoderWrapListener(accessOrderModel, indent + TWO_INDENT) + + "%12$s" + indent + INDENT + INDENT + "_buffer = buffer;\n" + indent + INDENT + INDENT + "_offset = offset;\n" + indent + INDENT + INDENT + "_actingBlockLength = BlockLength;\n" + @@ -1353,11 +1399,11 @@ private CharSequence generateMessageFlyweightCode( indent + INDENT + INDENT + "\n" + indent + INDENT + INDENT + "return WrapForEncode(buffer, offset + MessageHeader.Size);\n" + indent + INDENT + "}\n\n" + - generateDecoderWrapListener(accessOrderModel, indent + INDENT) + + "%13$s" + indent + INDENT + "public %10$s WrapForDecode(DirectBuffer buffer, int offset, " + "int actingBlockLength, int actingVersion)\n" + indent + INDENT + "{\n" + - generateAccessOrderListenerCall(accessOrderModel, indent + TWO_INDENT, "OnWrapForDecode", "actingVersion") + + "%14$s" + indent + INDENT + INDENT + "_buffer = buffer;\n" + indent + INDENT + INDENT + "_offset = offset;\n" + indent + INDENT + INDENT + "_actingBlockLength = actingBlockLength;\n" + @@ -1402,7 +1448,10 @@ private CharSequence generateMessageFlyweightCode( generateLiteral(ir.headerStructure().schemaVersionType(), Integer.toString(ir.version())), semanticType, className, - semanticVersion); + semanticVersion, + generateEncoderWrapListener(accessOrderModel, indent + TWO_INDENT), + generateDecoderWrapListener(accessOrderModel, indent + INDENT), + generateAccessOrderListenerCall(accessOrderModel, indent + TWO_INDENT, "OnWrapForDecode", "actingVersion")); } private static CharSequence qualifiedStateCase(final AccessOrderModel.State state) @@ -1530,6 +1579,7 @@ private static CharSequence generateFullyEncodedCheck( sb.append(indent).append("public void CheckEncodingIsComplete()\n") .append(indent).append("{\n") + .append("#if ENABLE_ACCESS_ORDER_CHECKS\n") .append(indent).append(INDENT).append("switch (_codecState)\n") .append(indent).append(INDENT).append("{\n"); @@ -1547,6 +1597,7 @@ private static CharSequence generateFullyEncodedCheck( .append(indent).append(THREE_INDENT) .append(INDENT).append("codecStateTransitions(_codecState));\n") .append(indent).append(INDENT).append("}\n") + .append("#endif\n") .append(indent).append("}\n\n"); return sb; @@ -1557,6 +1608,11 @@ private static String accessOrderListenerMethodName(final Token token) return "On" + Generators.toUpperFirstChar(token.name()) + "Accessed"; } + private static String accessOrderListenerMethodName(final Token token, final String suffix) + { + return "On" + Generators.toUpperFirstChar(token.name()) + suffix + "Accessed"; + } + private static void generateAccessOrderListenerMethod( final StringBuilder sb, final AccessOrderModel accessOrderModel, @@ -1777,6 +1833,34 @@ private static void generateAccessOrderListenerMethodForNextGroupElement( .append(indent).append("}\n"); } + private static void generateAccessOrderListenerMethodForVarDataLength( + final StringBuilder sb, + final AccessOrderModel accessOrderModel, + final String indent, + final Token token) + { + if (null == accessOrderModel) + { + return; + } + + sb.append("\n") + .append(indent).append("void ").append(accessOrderListenerMethodName(token, "Length")).append("()\n") + .append(indent).append("{\n"); + + final AccessOrderModel.CodecInteraction accessLength = + accessOrderModel.interactionFactory().accessVarDataLength(token); + + generateAccessOrderListener( + sb, + indent + INDENT, + "decode length of var data", + accessOrderModel, + accessLength); + + sb.append(indent).append("}\n"); + } + private static CharSequence generateDecoderWrapListener( final AccessOrderModel accessOrderModel, final String indent) @@ -1985,12 +2069,12 @@ private CharSequence generateEnumProperty( indent + INDENT + INDENT + "get\n" + indent + INDENT + INDENT + "{\n" + "%4$s" + - accessOrderListenerCall + + "%10$s" + indent + INDENT + INDENT + INDENT + "return (%5$s)_buffer.%6$sGet%8$s(_offset + %7$d);\n" + indent + INDENT + INDENT + "}\n" + indent + INDENT + INDENT + "set\n" + indent + INDENT + INDENT + "{\n" + - accessOrderListenerCall + + "%10$s" + indent + INDENT + INDENT + INDENT + "_buffer.%6$sPut%8$s(_offset + %7$d, (%9$s)value);\n" + indent + INDENT + INDENT + "}\n" + indent + INDENT + "}\n\n", @@ -2002,7 +2086,8 @@ private CharSequence generateEnumProperty( typePrefix, offset, byteOrderStr, - enumUnderlyingType); + enumUnderlyingType, + accessOrderListenerCall); } } @@ -2029,12 +2114,12 @@ private String generateBitSetProperty( indent + INDENT + INDENT + "get\n" + indent + INDENT + INDENT + "{\n" + "%4$s" + - accessOrderListenerCall + + "%10$s" + indent + INDENT + INDENT + INDENT + "return (%5$s)_buffer.%6$sGet%8$s(_offset + %7$d);\n" + indent + INDENT + INDENT + "}\n" + indent + INDENT + INDENT + "set\n" + indent + INDENT + INDENT + "{\n" + - accessOrderListenerCall + + "%10$s" + indent + INDENT + INDENT + INDENT + "_buffer.%6$sPut%8$s(_offset + %7$d, (%9$s)value);\n" + indent + INDENT + INDENT + "}\n" + indent + INDENT + "}\n", @@ -2046,7 +2131,8 @@ private String generateBitSetProperty( typePrefix, offset, byteOrderStr, - typeName); + typeName, + accessOrderListenerCall); } private Object generateCompositeProperty( @@ -2075,7 +2161,7 @@ private Object generateCompositeProperty( indent + INDENT + INDENT + "get\n" + indent + INDENT + INDENT + "{\n" + "%4$s" + - accessOrderListenerCall + + "%7$s" + indent + INDENT + INDENT + INDENT + "_%5$s.Wrap(_buffer, _offset + %6$d, _actingVersion);\n" + indent + INDENT + INDENT + INDENT + "return _%5$s;\n" + indent + INDENT + INDENT + "}\n" + @@ -2085,7 +2171,8 @@ private Object generateCompositeProperty( toUpperFirstChar(propertyName), generateTypeFieldNotPresentCondition(fieldToken.version(), indent), toLowerFirstChar(propertyName), - offset)); + offset, + accessOrderListenerCall)); return sb; } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java index 47dd08d4d9..31260779d2 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java @@ -410,19 +410,22 @@ private static void generateFullyEncodedCheck( sb.append(" public void checkEncodingIsComplete()\n") .append(" {\n") - .append(" switch (codecState)\n") - .append(" {\n"); + .append(" if (ENABLE_ACCESS_ORDER_CHECKS)\n") + .append(" {\n") + .append(" switch (codecState)\n") + .append(" {\n"); accessOrderModel.forEachTerminalEncoderState(state -> { - sb.append(" case ").append(stateCaseForSwitchCase(state)).append(":\n") - .append(" return;\n"); + sb.append(" case ").append(stateCaseForSwitchCase(state)).append(":\n") + .append(" return;\n"); }); - sb.append(" default:\n") - .append(" throw new IllegalStateException(\"Not fully encoded, current state: \" +\n") - .append(" CodecStates.name(codecState) + \", allowed transitions: \" +\n") - .append(" CodecStates.transitions(codecState));\n") + sb.append(" default:\n") + .append(" throw new IllegalStateException(\"Not fully encoded, current state: \" +\n") + .append(" CodecStates.name(codecState) + \", allowed transitions: \" +\n") + .append(" CodecStates.transitions(codecState));\n") + .append(" }\n") .append(" }\n") .append(" }\n\n"); } @@ -432,6 +435,11 @@ private static String accessOrderListenerMethodName(final Token token) return "on" + Generators.toUpperFirstChar(token.name()) + "Accessed"; } + private static String accessOrderListenerMethodName(final Token token, final String suffix) + { + return "on" + Generators.toUpperFirstChar(token.name()) + suffix + "Accessed"; + } + private static void generateAccessOrderListenerMethod( final StringBuilder sb, final AccessOrderModel accessOrderModel, @@ -649,6 +657,34 @@ private static void generateAccessOrderListenerMethodForNextGroupElement( .append(indent).append("}\n"); } + private static void generateAccessOrderListenerMethodForVarDataLength( + final StringBuilder sb, + final AccessOrderModel accessOrderModel, + final String indent, + final Token token) + { + if (null == accessOrderModel) + { + return; + } + + sb.append("\n") + .append(indent).append("void ").append(accessOrderListenerMethodName(token, "Length")).append("()\n") + .append(indent).append("{\n"); + + final AccessOrderModel.CodecInteraction accessLength = + accessOrderModel.interactionFactory().accessVarDataLength(token); + + generateAccessOrderListener( + sb, + indent + INDENT, + "decode length of var data", + accessOrderModel, + accessLength); + + sb.append(indent).append("}\n"); + } + private static CharSequence generateDecoderWrapListener( final AccessOrderModel accessOrderModel, final String indent) @@ -1214,7 +1250,8 @@ private void generateGroupEncoderClassDeclaration( private static void generateGroupDecoderProperty( final StringBuilder sb, final String groupName, - final AccessOrderModel accessOrderModel, final Token token, + final AccessOrderModel accessOrderModel, + final Token token, final String indent, final boolean isSubGroup) { @@ -1263,12 +1300,13 @@ private static void generateGroupDecoderProperty( indent + " {\n" + "%3$s" + indent + " %2$s.wrap(buffer);\n" + - generateAccessOrderListenerCall(accessOrderModel, indent + " ", token, propertyName + ".count") + + "%4$s" + indent + " return %2$s;\n" + indent + " }\n", className, propertyName, - actingVersionGuard); + actingVersionGuard, + generateAccessOrderListenerCall(accessOrderModel, indent + " ", token, propertyName + ".count")); } private void generateGroupEncoderProperty( @@ -1305,12 +1343,13 @@ private void generateGroupEncoderProperty( new Formatter(sb).format("\n" + indent + " public %1$s %2$sCount(final int count)\n" + indent + " {\n" + - generateAccessOrderListenerCall(accessOrderModel, indent + " ", token, "count") + + "%3$s" + indent + " %2$s.wrap(buffer, count);\n" + indent + " return %2$s;\n" + indent + " }\n", className, - propertyName); + propertyName, + generateAccessOrderListenerCall(accessOrderModel, indent + " ", token, "count")); } private void generateDecoderVarData( @@ -1348,20 +1387,26 @@ private void generateDecoderVarData( .append(indent).append(" return ").append(sizeOfLengthField).append(";\n") .append(indent).append(" }\n"); + generateAccessOrderListenerMethodForVarDataLength(sb, accessOrderModel, indent + " ", token); + + final CharSequence lengthAccessOrderListenerCall = generateAccessOrderListenerCall( + accessOrderModel, indent + " ", accessOrderListenerMethodName(token, "Length")); + generateAccessOrderListenerMethod(sb, accessOrderModel, indent + " ", token); - final CharSequence accessOrderListenerCall = - generateAccessOrderListenerCall(accessOrderModel, indent + " ", token); sb.append("\n") .append(indent).append(" public int ").append(methodPropName).append("Length()\n") .append(indent).append(" {\n") .append(generateArrayFieldNotPresentCondition(token.version(), indent)) - .append(accessOrderListenerCall) + .append(lengthAccessOrderListenerCall) .append(indent).append(" final int limit = parentMessage.limit();\n") .append(indent).append(" return ").append(PrimitiveType.UINT32 == lengthType ? "(int)" : "") .append(generateGet(lengthType, "limit", byteOrderStr)).append(";\n") .append(indent).append(" }\n"); + final CharSequence accessOrderListenerCall = + generateAccessOrderListenerCall(accessOrderModel, indent + " ", token); + generateDataDecodeMethods(sb, token, propertyName, sizeOfLengthField, lengthType, byteOrderStr, characterEncoding, accessOrderListenerCall, indent); @@ -1440,7 +1485,7 @@ private void generateDataDecodeMethods( indent + " public int skip%1$s()\n" + indent + " {\n" + "%2$s" + - accessOrderListenerCall + + "%6$s" + indent + " final int headerLength = %3$d;\n" + indent + " final int limit = parentMessage.limit();\n" + indent + " final int dataLength = %4$s%5$s;\n" + @@ -1452,7 +1497,8 @@ private void generateDataDecodeMethods( generateStringNotPresentConditionForAppendable(token.version(), indent), sizeOfLengthField, PrimitiveType.UINT32 == lengthType ? "(int)" : "", - generateGet(lengthType, "limit", byteOrderStr)); + generateGet(lengthType, "limit", byteOrderStr), + accessOrderListenerCall); generateVarDataTypedDecoder( sb, @@ -1492,7 +1538,7 @@ private void generateDataDecodeMethods( indent + " public String %1$s()\n" + indent + " {\n" + "%2$s" + - accessOrderListenerCall + + "%7$s" + indent + " final int headerLength = %3$d;\n" + indent + " final int limit = parentMessage.limit();\n" + indent + " final int dataLength = %4$s%5$s;\n" + @@ -1510,7 +1556,8 @@ private void generateDataDecodeMethods( sizeOfLengthField, PrimitiveType.UINT32 == lengthType ? "(int)" : "", generateGet(lengthType, "limit", byteOrderStr), - charset(characterEncoding)); + charset(characterEncoding), + accessOrderListenerCall); if (isAsciiEncoding(characterEncoding)) { @@ -1518,7 +1565,7 @@ private void generateDataDecodeMethods( indent + " public int get%1$s(final Appendable appendable)\n" + indent + " {\n" + "%2$s" + - accessOrderListenerCall + + "%6$s" + indent + " final int headerLength = %3$d;\n" + indent + " final int limit = parentMessage.limit();\n" + indent + " final int dataLength = %4$s%5$s;\n" + @@ -1531,7 +1578,8 @@ private void generateDataDecodeMethods( generateStringNotPresentConditionForAppendable(token.version(), indent), sizeOfLengthField, PrimitiveType.UINT32 == lengthType ? "(int)" : "", - generateGet(lengthType, "limit", byteOrderStr)); + generateGet(lengthType, "limit", byteOrderStr), + accessOrderListenerCall); } } } @@ -1550,7 +1598,7 @@ private void generateVarDataWrapDecoder( indent + " public void wrap%s(final %s wrapBuffer)\n" + indent + " {\n" + "%s" + - accessOrderListenerCall + + "%s" + indent + " final int headerLength = %d;\n" + indent + " final int limit = parentMessage.limit();\n" + indent + " final int dataLength = %s%s;\n" + @@ -1560,6 +1608,7 @@ private void generateVarDataWrapDecoder( propertyName, readOnlyBuffer, generateWrapFieldNotPresentCondition(token.version(), indent), + accessOrderListenerCall, sizeOfLengthField, PrimitiveType.UINT32 == lengthType ? "(int)" : "", generateGet(lengthType, "limit", byteOrderStr)); @@ -1641,7 +1690,7 @@ private void generateCharArrayEncodeMethods( indent + " {\n" + indent + " throw new IllegalStateException(\"length > maxValue for type: \" + length);\n" + indent + " }\n\n" + - accessOrderListenerCall + + "%6$s" + indent + " final int headerLength = %4$d;\n" + indent + " final int limit = parentMessage.limit();\n" + indent + " parentMessage.limit(limit + headerLength + length);\n" + @@ -1653,7 +1702,8 @@ private void generateCharArrayEncodeMethods( formatPropertyName(propertyName), maxLengthValue, sizeOfLengthField, - generatePut(lengthPutType, "limit", "length", byteOrderStr)); + generatePut(lengthPutType, "limit", "length", byteOrderStr), + accessOrderListenerCall); new Formatter(sb).format("\n" + indent + " public %1$s %2$s(final CharSequence value)\n" + @@ -1663,7 +1713,7 @@ private void generateCharArrayEncodeMethods( indent + " {\n" + indent + " throw new IllegalStateException(\"length > maxValue for type: \" + length);\n" + indent + " }\n\n" + - accessOrderListenerCall + + "%6$s" + indent + " final int headerLength = %4$d;\n" + indent + " final int limit = parentMessage.limit();\n" + indent + " parentMessage.limit(limit + headerLength + length);\n" + @@ -1675,7 +1725,8 @@ private void generateCharArrayEncodeMethods( formatPropertyName(propertyName), maxLengthValue, sizeOfLengthField, - generatePut(lengthPutType, "limit", "length", byteOrderStr)); + generatePut(lengthPutType, "limit", "length", byteOrderStr), + accessOrderListenerCall); } else { @@ -1689,7 +1740,7 @@ private void generateCharArrayEncodeMethods( indent + " {\n" + indent + " throw new IllegalStateException(\"length > maxValue for type: \" + length);\n" + indent + " }\n\n" + - accessOrderListenerCall + + "%7$s" + indent + " final int headerLength = %5$d;\n" + indent + " final int limit = parentMessage.limit();\n" + indent + " parentMessage.limit(limit + headerLength + length);\n" + @@ -1702,7 +1753,8 @@ private void generateCharArrayEncodeMethods( charset(characterEncoding), maxLengthValue, sizeOfLengthField, - generatePut(lengthPutType, "limit", "length", byteOrderStr)); + generatePut(lengthPutType, "limit", "length", byteOrderStr), + accessOrderListenerCall); } } @@ -1721,7 +1773,7 @@ private void generateVarDataTypedDecoder( indent + " public int get%s(final %s dst, final int dstOffset, final int length)\n" + indent + " {\n" + "%s" + - accessOrderListenerCall + + "%s" + indent + " final int headerLength = %d;\n" + indent + " final int limit = parentMessage.limit();\n" + indent + " final int dataLength = %s%s;\n" + @@ -1733,6 +1785,7 @@ private void generateVarDataTypedDecoder( propertyName, exchangeType, generateArrayFieldNotPresentCondition(token.version(), indent), + accessOrderListenerCall, sizeOfLengthField, PrimitiveType.UINT32 == lengthType ? "(int)" : "", generateGet(lengthType, "limit", byteOrderStr)); @@ -1759,7 +1812,7 @@ private void generateDataTypedEncoder( indent + " {\n" + indent + " throw new IllegalStateException(\"length > maxValue for type: \" + length);\n" + indent + " }\n\n" + - accessOrderListenerCall + + "%7$s" + indent + " final int headerLength = %5$d;\n" + indent + " final int limit = parentMessage.limit();\n" + indent + " parentMessage.limit(limit + headerLength + length);\n" + @@ -1772,7 +1825,8 @@ private void generateDataTypedEncoder( exchangeType, maxLengthValue, sizeOfLengthField, - generatePut(lengthPutType, "limit", "length", byteOrderStr)); + generatePut(lengthPutType, "limit", "length", byteOrderStr), + accessOrderListenerCall); } private void generateBitSet(final List tokens) throws IOException @@ -2522,12 +2576,13 @@ private CharSequence generatePrimitivePropertyDecode( indent + " public %s %s()\n" + indent + " {\n" + "%s" + - accessOrderListenerCall + + "%s" + indent + " return %s;\n" + indent + " }\n\n", javaTypeName, formatPropertyName(propertyName), generateFieldNotPresentCondition(inComposite, propertyToken.version(), encoding, indent), + accessOrderListenerCall, generateGet(encoding.primitiveType(), "offset + " + offset, byteOrderStr)); } @@ -2547,13 +2602,14 @@ private CharSequence generatePrimitivePropertyEncode( "\n" + indent + " public %s %s(final %s value)\n" + indent + " {\n" + - accessOrderListenerCall + + "%s" + indent + " %s;\n" + indent + " return this;\n" + indent + " }\n\n", formatClassName(containingClassName), formatPropertyName(propertyName), javaTypeName, + accessOrderListenerCall, generatePut(encoding.primitiveType(), "offset + " + offset, "value", byteOrderStr)); } @@ -2678,7 +2734,7 @@ private CharSequence generatePrimitiveArrayPropertyDecode( indent + " throw new IndexOutOfBoundsException(\"index out of range: index=\" + index);\n" + indent + " }\n\n" + "%s" + - accessOrderListenerCall + + "%s" + indent + " final int pos = offset + %d + (index * %d);\n\n" + indent + " return %s;\n" + indent + " }\n\n", @@ -2686,6 +2742,7 @@ private CharSequence generatePrimitiveArrayPropertyDecode( propertyName, fieldLength, generateFieldNotPresentCondition(inComposite, propertyToken.version(), encoding, indent), + accessOrderListenerCall, offset, typeSize, generateGet(encoding.primitiveType(), "pos", byteOrderStr)); @@ -2704,20 +2761,21 @@ private CharSequence generatePrimitiveArrayPropertyDecode( "\"Copy will go out of range: offset=\" + dstOffset);\n" + indent + " }\n\n" + "%s" + - accessOrderListenerCall + + "%s" + indent + " buffer.getBytes(offset + %d, dst, dstOffset, length);\n\n" + indent + " return length;\n" + indent + " }\n", Generators.toUpperFirstChar(propertyName), fieldLength, generateArrayFieldNotPresentCondition(propertyToken.version(), indent), + accessOrderListenerCall, offset); new Formatter(sb).format("\n" + indent + " public String %s()\n" + indent + " {\n" + "%s" + - accessOrderListenerCall + + "%s" + indent + " final byte[] dst = new byte[%d];\n" + indent + " buffer.getBytes(offset + %d, dst, 0, %d);\n\n" + indent + " int end = 0;\n" + @@ -2726,6 +2784,7 @@ private CharSequence generatePrimitiveArrayPropertyDecode( indent + " }\n\n", propertyName, generateStringNotPresentCondition(propertyToken.version(), indent), + accessOrderListenerCall, fieldLength, offset, fieldLength, @@ -2738,7 +2797,7 @@ private CharSequence generatePrimitiveArrayPropertyDecode( indent + " public int get%1$s(final Appendable value)\n" + indent + " {\n" + "%2$s" + - accessOrderListenerCall + + "%5$s" + indent + " for (int i = 0; i < %3$d; ++i)\n" + indent + " {\n" + indent + " final int c = buffer.getByte(offset + %4$d + i) & 0xFF;\n" + @@ -2760,7 +2819,8 @@ private CharSequence generatePrimitiveArrayPropertyDecode( Generators.toUpperFirstChar(propertyName), generateStringNotPresentConditionForAppendable(propertyToken.version(), indent), fieldLength, - offset); + offset, + accessOrderListenerCall); } } else if (encoding.primitiveType() == PrimitiveType.UINT8) @@ -2769,13 +2829,14 @@ else if (encoding.primitiveType() == PrimitiveType.UINT8) indent + " public int get%s(final byte[] dst, final int dstOffset, final int length)\n" + indent + " {\n" + "%s" + - accessOrderListenerCall + + "%s" + indent + " final int bytesCopied = Math.min(length, %d);\n" + indent + " buffer.getBytes(offset + %d, dst, dstOffset, bytesCopied);\n\n" + indent + " return bytesCopied;\n" + indent + " }\n", Generators.toUpperFirstChar(propertyName), generateArrayFieldNotPresentCondition(propertyToken.version(), indent), + accessOrderListenerCall, fieldLength, offset); @@ -2783,7 +2844,7 @@ else if (encoding.primitiveType() == PrimitiveType.UINT8) indent + " public int get%s(final %s dst, final int dstOffset, final int length)\n" + indent + " {\n" + "%s" + - accessOrderListenerCall + + "%s" + indent + " final int bytesCopied = Math.min(length, %d);\n" + indent + " buffer.getBytes(offset + %d, dst, dstOffset, bytesCopied);\n\n" + indent + " return bytesCopied;\n" + @@ -2791,6 +2852,7 @@ else if (encoding.primitiveType() == PrimitiveType.UINT8) Generators.toUpperFirstChar(propertyName), fqMutableBuffer, generateArrayFieldNotPresentCondition(propertyToken.version(), indent), + accessOrderListenerCall, fieldLength, offset); @@ -2798,12 +2860,13 @@ else if (encoding.primitiveType() == PrimitiveType.UINT8) indent + " public void wrap%s(final %s wrapBuffer)\n" + indent + " {\n" + "%s" + - accessOrderListenerCall + + "%s" + indent + " wrapBuffer.wrap(buffer, offset + %d, %d);\n" + indent + " }\n", Generators.toUpperFirstChar(propertyName), readOnlyBuffer, generateWrapFieldNotPresentCondition(propertyToken.version(), indent), + accessOrderListenerCall, offset, fieldLength); } @@ -2854,7 +2917,7 @@ private CharSequence generatePrimitiveArrayPropertyEncode( indent + " {\n" + indent + " throw new IndexOutOfBoundsException(\"index out of range: index=\" + index);\n" + indent + " }\n\n" + - accessOrderListenerCall + + "%s" + indent + " final int pos = offset + %d + (index * %d);\n" + indent + " %s;\n\n" + indent + " return this;\n" + @@ -2863,6 +2926,7 @@ private CharSequence generatePrimitiveArrayPropertyEncode( propertyName, javaTypeName, arrayLength, + accessOrderListenerCall, offset, typeSize, generatePut(primitiveType, "pos", "value", byteOrderStr)); @@ -2947,13 +3011,14 @@ private void generateCharArrayEncodeMethods( indent + " throw new IndexOutOfBoundsException(" + "\"Copy will go out of range: offset=\" + srcOffset);\n" + indent + " }\n\n" + - accessOrderListenerCall + + "%s" + indent + " buffer.putBytes(offset + %d, src, srcOffset, length);\n\n" + indent + " return this;\n" + indent + " }\n", formatClassName(containingClassName), Generators.toUpperFirstChar(propertyName), fieldLength, + accessOrderListenerCall, offset); if (isAsciiEncoding(encoding.characterEncoding())) @@ -2968,7 +3033,7 @@ private void generateCharArrayEncodeMethods( indent + " throw new IndexOutOfBoundsException(" + "\"String too large for copy: byte length=\" + srcLength);\n" + indent + " }\n\n" + - accessOrderListenerCall + + "%5$s" + indent + " buffer.putStringWithoutLengthAscii(offset + %4$d, src);\n\n" + indent + " for (int start = srcLength; start < length; ++start)\n" + indent + " {\n" + @@ -2979,7 +3044,8 @@ private void generateCharArrayEncodeMethods( formatClassName(containingClassName), propertyName, fieldLength, - offset); + offset, + accessOrderListenerCall); new Formatter(sb).format("\n" + indent + " public %1$s %2$s(final CharSequence src)\n" + @@ -2991,7 +3057,7 @@ private void generateCharArrayEncodeMethods( indent + " throw new IndexOutOfBoundsException(" + "\"CharSequence too large for copy: byte length=\" + srcLength);\n" + indent + " }\n\n" + - accessOrderListenerCall + + "%5$s" + indent + " buffer.putStringWithoutLengthAscii(offset + %4$d, src);\n\n" + indent + " for (int start = srcLength; start < length; ++start)\n" + indent + " {\n" + @@ -3002,7 +3068,8 @@ private void generateCharArrayEncodeMethods( formatClassName(containingClassName), propertyName, fieldLength, - offset); + offset, + accessOrderListenerCall); } else { @@ -3017,7 +3084,7 @@ private void generateCharArrayEncodeMethods( indent + " throw new IndexOutOfBoundsException(" + "\"String too large for copy: byte length=\" + bytes.length);\n" + indent + " }\n\n" + - accessOrderListenerCall + + "%s" + indent + " buffer.putBytes(offset + %d, bytes, 0, bytes.length);\n\n" + indent + " for (int start = bytes.length; start < length; ++start)\n" + indent + " {\n" + @@ -3029,6 +3096,7 @@ private void generateCharArrayEncodeMethods( propertyName, fieldLength, charset(encoding.characterEncoding()), + accessOrderListenerCall, offset, offset); } @@ -3051,7 +3119,7 @@ private void generateByteArrayEncodeMethods( indent + " throw new IllegalStateException(" + "\"length > maxValue for type: \" + length);\n" + indent + " }\n\n" + - accessOrderListenerCall + + "%s" + indent + " buffer.putBytes(offset + %d, src, srcOffset, length);\n" + indent + " for (int i = length; i < %d; i++)\n" + indent + " {\n" + @@ -3062,6 +3130,7 @@ private void generateByteArrayEncodeMethods( formatClassName(containingClassName), Generators.toUpperFirstChar(propertyName), fieldLength, + accessOrderListenerCall, offset, fieldLength, offset); @@ -3074,7 +3143,7 @@ private void generateByteArrayEncodeMethods( indent + " throw new IllegalStateException(" + "\"length > maxValue for type: \" + length);\n" + indent + " }\n\n" + - accessOrderListenerCall + + "%s" + indent + " buffer.putBytes(offset + %d, src, srcOffset, length);\n" + indent + " for (int i = length; i < %d; i++)\n" + indent + " {\n" + @@ -3086,6 +3155,7 @@ private void generateByteArrayEncodeMethods( Generators.toUpperFirstChar(propertyName), fqReadOnlyBuffer, fieldLength, + accessOrderListenerCall, offset, fieldLength, offset); @@ -3753,12 +3823,13 @@ private void generateEnumDecoder( indent + " public %s %sRaw()\n" + indent + " {\n" + "%s" + - accessOrderListenerCall + + "%s" + indent + " return %s;\n" + indent + " }\n", javaTypeName, formatPropertyName(propertyName), generateFieldNotPresentCondition(inComposite, fieldToken.version(), encoding, indent), + accessOrderListenerCall, rawGetStr); new Formatter(sb).format( @@ -3766,12 +3837,13 @@ private void generateEnumDecoder( indent + " public %s %s()\n" + indent + " {\n" + "%s" + - accessOrderListenerCall + + "%s" + indent + " return %s.get(%s);\n" + indent + " }\n\n", enumName, propertyName, generatePropertyNotPresentCondition(inComposite, DECODER, fieldToken, enumName, indent), + accessOrderListenerCall, enumName, rawGetStr); } @@ -3796,13 +3868,14 @@ private void generateEnumEncoder( new Formatter(sb).format("\n" + indent + " public %s %s(final %s value)\n" + indent + " {\n" + - accessOrderListenerCall + + "%s" + indent + " %s;\n" + indent + " return this;\n" + indent + " }\n", formatClassName(containingClassName), propertyName, enumName, + accessOrderListenerCall, generatePut(encoding.primitiveType(), "offset + " + offset, "value.value()", byteOrderString)); } } @@ -3829,13 +3902,14 @@ private void generateBitSetProperty( indent + " public %s %s()\n" + indent + " {\n" + "%s" + - accessOrderListenerCall + + "%s" + indent + " %s.wrap(buffer, offset + %d);\n" + indent + " return %s;\n" + indent + " }\n", bitSetName, propertyName, generatePropertyNotPresentCondition(inComposite, codecType, propertyToken, null, indent), + accessOrderListenerCall, propertyName, bitsetToken.offset(), propertyName); @@ -3863,13 +3937,14 @@ private void generateCompositeProperty( indent + " public %s %s()\n" + indent + " {\n" + "%s" + - accessOrderListenerCall + + "%s" + indent + " %s.wrap(buffer, offset + %d);\n" + indent + " return %s;\n" + indent + " }\n", compositeName, propertyName, generatePropertyNotPresentCondition(inComposite, codecType, propertyToken, null, indent), + accessOrderListenerCall, propertyName, compositeToken.offset(), propertyName); diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java index 8b702c0236..a65b6520bc 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java @@ -30,8 +30,11 @@ public final class FrameCodecDecoder * V0_BLOCK -> V0_BLOCK [label=" irId(?) "]; * V0_BLOCK -> V0_BLOCK [label=" irVersion(?) "]; * V0_BLOCK -> V0_BLOCK [label=" schemaVersion(?) "]; + * V0_BLOCK -> V0_BLOCK [label=" packageNameLength() "]; * V0_BLOCK -> V0_PACKAGENAME_DONE [label=" packageName(?) "]; + * V0_PACKAGENAME_DONE -> V0_PACKAGENAME_DONE [label=" namespaceNameLength() "]; * V0_PACKAGENAME_DONE -> V0_NAMESPACENAME_DONE [label=" namespaceName(?) "]; + * V0_NAMESPACENAME_DONE -> V0_NAMESPACENAME_DONE [label=" semanticVersionLength() "]; * V0_NAMESPACENAME_DONE -> V0_SEMANTICVERSION_DONE [label=" semanticVersion(?) "]; * } * }
@@ -56,9 +59,9 @@ private static class CodecStates private static final String[] STATE_TRANSITIONS_LOOKUP = { "\"wrap(version=0)\"", - "\"irId(?)\", \"irVersion(?)\", \"schemaVersion(?)\", \"packageName(?)\"", - "\"namespaceName(?)\"", - "\"semanticVersion(?)\"", + "\"irId(?)\", \"irVersion(?)\", \"schemaVersion(?)\", \"packageNameLength()\", \"packageName(?)\"", + "\"namespaceNameLength()\", \"namespaceName(?)\"", + "\"semanticVersionLength()\", \"semanticVersion(?)\"", "", }; @@ -469,6 +472,21 @@ public static int packageNameHeaderLength() return 2; } + void onPackageNameLengthAccessed() + { + switch (codecState()) + { + case CodecStates.V0_BLOCK: + codecState(CodecStates.V0_BLOCK); + break; + default: + throw new IllegalStateException("Illegal field access order. " + + "Cannot decode length of var data \"packageName\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + } + } + private void onPackageNameAccessed() { switch (codecState()) @@ -488,7 +506,7 @@ public int packageNameLength() { if (ENABLE_ACCESS_ORDER_CHECKS) { - onPackageNameAccessed(); + onPackageNameLengthAccessed(); } final int limit = parentMessage.limit(); @@ -612,6 +630,21 @@ public static int namespaceNameHeaderLength() return 2; } + void onNamespaceNameLengthAccessed() + { + switch (codecState()) + { + case CodecStates.V0_PACKAGENAME_DONE: + codecState(CodecStates.V0_PACKAGENAME_DONE); + break; + default: + throw new IllegalStateException("Illegal field access order. " + + "Cannot decode length of var data \"namespaceName\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + } + } + private void onNamespaceNameAccessed() { switch (codecState()) @@ -631,7 +664,7 @@ public int namespaceNameLength() { if (ENABLE_ACCESS_ORDER_CHECKS) { - onNamespaceNameAccessed(); + onNamespaceNameLengthAccessed(); } final int limit = parentMessage.limit(); @@ -755,6 +788,21 @@ public static int semanticVersionHeaderLength() return 2; } + void onSemanticVersionLengthAccessed() + { + switch (codecState()) + { + case CodecStates.V0_NAMESPACENAME_DONE: + codecState(CodecStates.V0_NAMESPACENAME_DONE); + break; + default: + throw new IllegalStateException("Illegal field access order. " + + "Cannot decode length of var data \"semanticVersion\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + } + } + private void onSemanticVersionAccessed() { switch (codecState()) @@ -774,7 +822,7 @@ public int semanticVersionLength() { if (ENABLE_ACCESS_ORDER_CHECKS) { - onSemanticVersionAccessed(); + onSemanticVersionLengthAccessed(); } final int limit = parentMessage.limit(); diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java index 5c1b582445..5d866e6dfc 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java @@ -30,8 +30,11 @@ public final class FrameCodecEncoder * V0_BLOCK -> V0_BLOCK [label=" irId(?) "]; * V0_BLOCK -> V0_BLOCK [label=" irVersion(?) "]; * V0_BLOCK -> V0_BLOCK [label=" schemaVersion(?) "]; + * V0_BLOCK -> V0_BLOCK [label=" packageNameLength() "]; * V0_BLOCK -> V0_PACKAGENAME_DONE [label=" packageName(?) "]; + * V0_PACKAGENAME_DONE -> V0_PACKAGENAME_DONE [label=" namespaceNameLength() "]; * V0_PACKAGENAME_DONE -> V0_NAMESPACENAME_DONE [label=" namespaceName(?) "]; + * V0_NAMESPACENAME_DONE -> V0_NAMESPACENAME_DONE [label=" semanticVersionLength() "]; * V0_NAMESPACENAME_DONE -> V0_SEMANTICVERSION_DONE [label=" semanticVersion(?) "]; * } * }
@@ -56,9 +59,9 @@ private static class CodecStates private static final String[] STATE_TRANSITIONS_LOOKUP = { "\"wrap(version=0)\"", - "\"irId(?)\", \"irVersion(?)\", \"schemaVersion(?)\", \"packageName(?)\"", - "\"namespaceName(?)\"", - "\"semanticVersion(?)\"", + "\"irId(?)\", \"irVersion(?)\", \"schemaVersion(?)\", \"packageNameLength()\", \"packageName(?)\"", + "\"namespaceNameLength()\", \"namespaceName(?)\"", + "\"semanticVersionLength()\", \"semanticVersion(?)\"", "", }; @@ -731,14 +734,17 @@ public StringBuilder appendTo(final StringBuilder builder) public void checkEncodingIsComplete() { - switch (codecState) + if (ENABLE_ACCESS_ORDER_CHECKS) { - case CodecStates.V0_SEMANTICVERSION_DONE: - return; - default: - throw new IllegalStateException("Not fully encoded, current state: " + - CodecStates.name(codecState) + ", allowed transitions: " + - CodecStates.transitions(codecState)); + switch (codecState) + { + case CodecStates.V0_SEMANTICVERSION_DONE: + return; + default: + throw new IllegalStateException("Not fully encoded, current state: " + + CodecStates.name(codecState) + ", allowed transitions: " + + CodecStates.transitions(codecState)); + } } } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java index ab75328b6e..51a321ea99 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java @@ -37,16 +37,27 @@ public final class TokenCodecDecoder * V0_BLOCK -> V0_BLOCK [label=" byteOrder(?) "]; * V0_BLOCK -> V0_BLOCK [label=" presence(?) "]; * V0_BLOCK -> V0_BLOCK [label=" deprecated(?) "]; + * V0_BLOCK -> V0_BLOCK [label=" nameLength() "]; * V0_BLOCK -> V0_NAME_DONE [label=" name(?) "]; + * V0_NAME_DONE -> V0_NAME_DONE [label=" constValueLength() "]; * V0_NAME_DONE -> V0_CONSTVALUE_DONE [label=" constValue(?) "]; + * V0_CONSTVALUE_DONE -> V0_CONSTVALUE_DONE [label=" minValueLength() "]; * V0_CONSTVALUE_DONE -> V0_MINVALUE_DONE [label=" minValue(?) "]; + * V0_MINVALUE_DONE -> V0_MINVALUE_DONE [label=" maxValueLength() "]; * V0_MINVALUE_DONE -> V0_MAXVALUE_DONE [label=" maxValue(?) "]; + * V0_MAXVALUE_DONE -> V0_MAXVALUE_DONE [label=" nullValueLength() "]; * V0_MAXVALUE_DONE -> V0_NULLVALUE_DONE [label=" nullValue(?) "]; + * V0_NULLVALUE_DONE -> V0_NULLVALUE_DONE [label=" characterEncodingLength() "]; * V0_NULLVALUE_DONE -> V0_CHARACTERENCODING_DONE [label=" characterEncoding(?) "]; + * V0_CHARACTERENCODING_DONE -> V0_CHARACTERENCODING_DONE [label=" epochLength() "]; * V0_CHARACTERENCODING_DONE -> V0_EPOCH_DONE [label=" epoch(?) "]; + * V0_EPOCH_DONE -> V0_EPOCH_DONE [label=" timeUnitLength() "]; * V0_EPOCH_DONE -> V0_TIMEUNIT_DONE [label=" timeUnit(?) "]; + * V0_TIMEUNIT_DONE -> V0_TIMEUNIT_DONE [label=" semanticTypeLength() "]; * V0_TIMEUNIT_DONE -> V0_SEMANTICTYPE_DONE [label=" semanticType(?) "]; + * V0_SEMANTICTYPE_DONE -> V0_SEMANTICTYPE_DONE [label=" descriptionLength() "]; * V0_SEMANTICTYPE_DONE -> V0_DESCRIPTION_DONE [label=" description(?) "]; + * V0_DESCRIPTION_DONE -> V0_DESCRIPTION_DONE [label=" referencedNameLength() "]; * V0_DESCRIPTION_DONE -> V0_REFERENCEDNAME_DONE [label=" referencedName(?) "]; * } * }
@@ -87,17 +98,17 @@ private static class CodecStates private static final String[] STATE_TRANSITIONS_LOOKUP = { "\"wrap(version=0)\"", - "\"tokenOffset(?)\", \"tokenSize(?)\", \"fieldId(?)\", \"tokenVersion(?)\", \"componentTokenCount(?)\", \"signal(?)\", \"primitiveType(?)\", \"byteOrder(?)\", \"presence(?)\", \"deprecated(?)\", \"name(?)\"", - "\"constValue(?)\"", - "\"minValue(?)\"", - "\"maxValue(?)\"", - "\"nullValue(?)\"", - "\"characterEncoding(?)\"", - "\"epoch(?)\"", - "\"timeUnit(?)\"", - "\"semanticType(?)\"", - "\"description(?)\"", - "\"referencedName(?)\"", + "\"tokenOffset(?)\", \"tokenSize(?)\", \"fieldId(?)\", \"tokenVersion(?)\", \"componentTokenCount(?)\", \"signal(?)\", \"primitiveType(?)\", \"byteOrder(?)\", \"presence(?)\", \"deprecated(?)\", \"nameLength()\", \"name(?)\"", + "\"constValueLength()\", \"constValue(?)\"", + "\"minValueLength()\", \"minValue(?)\"", + "\"maxValueLength()\", \"maxValue(?)\"", + "\"nullValueLength()\", \"nullValue(?)\"", + "\"characterEncodingLength()\", \"characterEncoding(?)\"", + "\"epochLength()\", \"epoch(?)\"", + "\"timeUnitLength()\", \"timeUnit(?)\"", + "\"semanticTypeLength()\", \"semanticType(?)\"", + "\"descriptionLength()\", \"description(?)\"", + "\"referencedNameLength()\", \"referencedName(?)\"", "", }; @@ -957,6 +968,21 @@ public static int nameHeaderLength() return 2; } + void onNameLengthAccessed() + { + switch (codecState()) + { + case CodecStates.V0_BLOCK: + codecState(CodecStates.V0_BLOCK); + break; + default: + throw new IllegalStateException("Illegal field access order. " + + "Cannot decode length of var data \"name\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + } + } + private void onNameAccessed() { switch (codecState()) @@ -976,7 +1002,7 @@ public int nameLength() { if (ENABLE_ACCESS_ORDER_CHECKS) { - onNameAccessed(); + onNameLengthAccessed(); } final int limit = parentMessage.limit(); @@ -1100,6 +1126,21 @@ public static int constValueHeaderLength() return 2; } + void onConstValueLengthAccessed() + { + switch (codecState()) + { + case CodecStates.V0_NAME_DONE: + codecState(CodecStates.V0_NAME_DONE); + break; + default: + throw new IllegalStateException("Illegal field access order. " + + "Cannot decode length of var data \"constValue\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + } + } + private void onConstValueAccessed() { switch (codecState()) @@ -1119,7 +1160,7 @@ public int constValueLength() { if (ENABLE_ACCESS_ORDER_CHECKS) { - onConstValueAccessed(); + onConstValueLengthAccessed(); } final int limit = parentMessage.limit(); @@ -1243,6 +1284,21 @@ public static int minValueHeaderLength() return 2; } + void onMinValueLengthAccessed() + { + switch (codecState()) + { + case CodecStates.V0_CONSTVALUE_DONE: + codecState(CodecStates.V0_CONSTVALUE_DONE); + break; + default: + throw new IllegalStateException("Illegal field access order. " + + "Cannot decode length of var data \"minValue\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + } + } + private void onMinValueAccessed() { switch (codecState()) @@ -1262,7 +1318,7 @@ public int minValueLength() { if (ENABLE_ACCESS_ORDER_CHECKS) { - onMinValueAccessed(); + onMinValueLengthAccessed(); } final int limit = parentMessage.limit(); @@ -1386,6 +1442,21 @@ public static int maxValueHeaderLength() return 2; } + void onMaxValueLengthAccessed() + { + switch (codecState()) + { + case CodecStates.V0_MINVALUE_DONE: + codecState(CodecStates.V0_MINVALUE_DONE); + break; + default: + throw new IllegalStateException("Illegal field access order. " + + "Cannot decode length of var data \"maxValue\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + } + } + private void onMaxValueAccessed() { switch (codecState()) @@ -1405,7 +1476,7 @@ public int maxValueLength() { if (ENABLE_ACCESS_ORDER_CHECKS) { - onMaxValueAccessed(); + onMaxValueLengthAccessed(); } final int limit = parentMessage.limit(); @@ -1529,6 +1600,21 @@ public static int nullValueHeaderLength() return 2; } + void onNullValueLengthAccessed() + { + switch (codecState()) + { + case CodecStates.V0_MAXVALUE_DONE: + codecState(CodecStates.V0_MAXVALUE_DONE); + break; + default: + throw new IllegalStateException("Illegal field access order. " + + "Cannot decode length of var data \"nullValue\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + } + } + private void onNullValueAccessed() { switch (codecState()) @@ -1548,7 +1634,7 @@ public int nullValueLength() { if (ENABLE_ACCESS_ORDER_CHECKS) { - onNullValueAccessed(); + onNullValueLengthAccessed(); } final int limit = parentMessage.limit(); @@ -1672,6 +1758,21 @@ public static int characterEncodingHeaderLength() return 2; } + void onCharacterEncodingLengthAccessed() + { + switch (codecState()) + { + case CodecStates.V0_NULLVALUE_DONE: + codecState(CodecStates.V0_NULLVALUE_DONE); + break; + default: + throw new IllegalStateException("Illegal field access order. " + + "Cannot decode length of var data \"characterEncoding\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + } + } + private void onCharacterEncodingAccessed() { switch (codecState()) @@ -1691,7 +1792,7 @@ public int characterEncodingLength() { if (ENABLE_ACCESS_ORDER_CHECKS) { - onCharacterEncodingAccessed(); + onCharacterEncodingLengthAccessed(); } final int limit = parentMessage.limit(); @@ -1815,6 +1916,21 @@ public static int epochHeaderLength() return 2; } + void onEpochLengthAccessed() + { + switch (codecState()) + { + case CodecStates.V0_CHARACTERENCODING_DONE: + codecState(CodecStates.V0_CHARACTERENCODING_DONE); + break; + default: + throw new IllegalStateException("Illegal field access order. " + + "Cannot decode length of var data \"epoch\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + } + } + private void onEpochAccessed() { switch (codecState()) @@ -1834,7 +1950,7 @@ public int epochLength() { if (ENABLE_ACCESS_ORDER_CHECKS) { - onEpochAccessed(); + onEpochLengthAccessed(); } final int limit = parentMessage.limit(); @@ -1958,6 +2074,21 @@ public static int timeUnitHeaderLength() return 2; } + void onTimeUnitLengthAccessed() + { + switch (codecState()) + { + case CodecStates.V0_EPOCH_DONE: + codecState(CodecStates.V0_EPOCH_DONE); + break; + default: + throw new IllegalStateException("Illegal field access order. " + + "Cannot decode length of var data \"timeUnit\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + } + } + private void onTimeUnitAccessed() { switch (codecState()) @@ -1977,7 +2108,7 @@ public int timeUnitLength() { if (ENABLE_ACCESS_ORDER_CHECKS) { - onTimeUnitAccessed(); + onTimeUnitLengthAccessed(); } final int limit = parentMessage.limit(); @@ -2101,6 +2232,21 @@ public static int semanticTypeHeaderLength() return 2; } + void onSemanticTypeLengthAccessed() + { + switch (codecState()) + { + case CodecStates.V0_TIMEUNIT_DONE: + codecState(CodecStates.V0_TIMEUNIT_DONE); + break; + default: + throw new IllegalStateException("Illegal field access order. " + + "Cannot decode length of var data \"semanticType\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + } + } + private void onSemanticTypeAccessed() { switch (codecState()) @@ -2120,7 +2266,7 @@ public int semanticTypeLength() { if (ENABLE_ACCESS_ORDER_CHECKS) { - onSemanticTypeAccessed(); + onSemanticTypeLengthAccessed(); } final int limit = parentMessage.limit(); @@ -2244,6 +2390,21 @@ public static int descriptionHeaderLength() return 2; } + void onDescriptionLengthAccessed() + { + switch (codecState()) + { + case CodecStates.V0_SEMANTICTYPE_DONE: + codecState(CodecStates.V0_SEMANTICTYPE_DONE); + break; + default: + throw new IllegalStateException("Illegal field access order. " + + "Cannot decode length of var data \"description\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + } + } + private void onDescriptionAccessed() { switch (codecState()) @@ -2263,7 +2424,7 @@ public int descriptionLength() { if (ENABLE_ACCESS_ORDER_CHECKS) { - onDescriptionAccessed(); + onDescriptionLengthAccessed(); } final int limit = parentMessage.limit(); @@ -2387,6 +2548,21 @@ public static int referencedNameHeaderLength() return 2; } + void onReferencedNameLengthAccessed() + { + switch (codecState()) + { + case CodecStates.V0_DESCRIPTION_DONE: + codecState(CodecStates.V0_DESCRIPTION_DONE); + break; + default: + throw new IllegalStateException("Illegal field access order. " + + "Cannot decode length of var data \"referencedName\" in state: " + CodecStates.name(codecState()) + + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + + "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + } + } + private void onReferencedNameAccessed() { switch (codecState()) @@ -2406,7 +2582,7 @@ public int referencedNameLength() { if (ENABLE_ACCESS_ORDER_CHECKS) { - onReferencedNameAccessed(); + onReferencedNameLengthAccessed(); } final int limit = parentMessage.limit(); diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java index 0ba727fc80..4aa64244ca 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java @@ -37,16 +37,27 @@ public final class TokenCodecEncoder * V0_BLOCK -> V0_BLOCK [label=" byteOrder(?) "]; * V0_BLOCK -> V0_BLOCK [label=" presence(?) "]; * V0_BLOCK -> V0_BLOCK [label=" deprecated(?) "]; + * V0_BLOCK -> V0_BLOCK [label=" nameLength() "]; * V0_BLOCK -> V0_NAME_DONE [label=" name(?) "]; + * V0_NAME_DONE -> V0_NAME_DONE [label=" constValueLength() "]; * V0_NAME_DONE -> V0_CONSTVALUE_DONE [label=" constValue(?) "]; + * V0_CONSTVALUE_DONE -> V0_CONSTVALUE_DONE [label=" minValueLength() "]; * V0_CONSTVALUE_DONE -> V0_MINVALUE_DONE [label=" minValue(?) "]; + * V0_MINVALUE_DONE -> V0_MINVALUE_DONE [label=" maxValueLength() "]; * V0_MINVALUE_DONE -> V0_MAXVALUE_DONE [label=" maxValue(?) "]; + * V0_MAXVALUE_DONE -> V0_MAXVALUE_DONE [label=" nullValueLength() "]; * V0_MAXVALUE_DONE -> V0_NULLVALUE_DONE [label=" nullValue(?) "]; + * V0_NULLVALUE_DONE -> V0_NULLVALUE_DONE [label=" characterEncodingLength() "]; * V0_NULLVALUE_DONE -> V0_CHARACTERENCODING_DONE [label=" characterEncoding(?) "]; + * V0_CHARACTERENCODING_DONE -> V0_CHARACTERENCODING_DONE [label=" epochLength() "]; * V0_CHARACTERENCODING_DONE -> V0_EPOCH_DONE [label=" epoch(?) "]; + * V0_EPOCH_DONE -> V0_EPOCH_DONE [label=" timeUnitLength() "]; * V0_EPOCH_DONE -> V0_TIMEUNIT_DONE [label=" timeUnit(?) "]; + * V0_TIMEUNIT_DONE -> V0_TIMEUNIT_DONE [label=" semanticTypeLength() "]; * V0_TIMEUNIT_DONE -> V0_SEMANTICTYPE_DONE [label=" semanticType(?) "]; + * V0_SEMANTICTYPE_DONE -> V0_SEMANTICTYPE_DONE [label=" descriptionLength() "]; * V0_SEMANTICTYPE_DONE -> V0_DESCRIPTION_DONE [label=" description(?) "]; + * V0_DESCRIPTION_DONE -> V0_DESCRIPTION_DONE [label=" referencedNameLength() "]; * V0_DESCRIPTION_DONE -> V0_REFERENCEDNAME_DONE [label=" referencedName(?) "]; * } * }
@@ -87,17 +98,17 @@ private static class CodecStates private static final String[] STATE_TRANSITIONS_LOOKUP = { "\"wrap(version=0)\"", - "\"tokenOffset(?)\", \"tokenSize(?)\", \"fieldId(?)\", \"tokenVersion(?)\", \"componentTokenCount(?)\", \"signal(?)\", \"primitiveType(?)\", \"byteOrder(?)\", \"presence(?)\", \"deprecated(?)\", \"name(?)\"", - "\"constValue(?)\"", - "\"minValue(?)\"", - "\"maxValue(?)\"", - "\"nullValue(?)\"", - "\"characterEncoding(?)\"", - "\"epoch(?)\"", - "\"timeUnit(?)\"", - "\"semanticType(?)\"", - "\"description(?)\"", - "\"referencedName(?)\"", + "\"tokenOffset(?)\", \"tokenSize(?)\", \"fieldId(?)\", \"tokenVersion(?)\", \"componentTokenCount(?)\", \"signal(?)\", \"primitiveType(?)\", \"byteOrder(?)\", \"presence(?)\", \"deprecated(?)\", \"nameLength()\", \"name(?)\"", + "\"constValueLength()\", \"constValue(?)\"", + "\"minValueLength()\", \"minValue(?)\"", + "\"maxValueLength()\", \"maxValue(?)\"", + "\"nullValueLength()\", \"nullValue(?)\"", + "\"characterEncodingLength()\", \"characterEncoding(?)\"", + "\"epochLength()\", \"epoch(?)\"", + "\"timeUnitLength()\", \"timeUnit(?)\"", + "\"semanticTypeLength()\", \"semanticType(?)\"", + "\"descriptionLength()\", \"description(?)\"", + "\"referencedNameLength()\", \"referencedName(?)\"", "", }; @@ -2030,14 +2041,17 @@ public StringBuilder appendTo(final StringBuilder builder) public void checkEncodingIsComplete() { - switch (codecState) + if (ENABLE_ACCESS_ORDER_CHECKS) { - case CodecStates.V0_REFERENCEDNAME_DONE: - return; - default: - throw new IllegalStateException("Not fully encoded, current state: " + - CodecStates.name(codecState) + ", allowed transitions: " + - CodecStates.transitions(codecState)); + switch (codecState) + { + case CodecStates.V0_REFERENCEDNAME_DONE: + return; + default: + throw new IllegalStateException("Not fully encoded, current state: " + + CodecStates.name(codecState) + ", allowed transitions: " + + CodecStates.transitions(codecState)); + } } } diff --git a/sbe-tool/src/test/cpp/BoundsCheckTest.cpp b/sbe-tool/src/test/cpp/BoundsCheckTest.cpp index e3b81677dc..2adf48f06c 100644 --- a/sbe-tool/src/test/cpp/BoundsCheckTest.cpp +++ b/sbe-tool/src/test/cpp/BoundsCheckTest.cpp @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include #include #include @@ -217,43 +216,43 @@ class BoundsCheckTest : public testing::Test performanceFigures.next(); EXPECT_EQ(performanceFigures.octaneRating(), 95); - Car::PerformanceFigures::Acceleration &acceleration = performanceFigures.acceleration(); - EXPECT_EQ(acceleration.count(), 3u); - EXPECT_TRUE(acceleration.hasNext()); - acceleration.next(); - EXPECT_EQ(acceleration.mph(), 30); - EXPECT_EQ(acceleration.seconds(), 4.0f); + Car::PerformanceFigures::Acceleration &acceleration1 = performanceFigures.acceleration(); + EXPECT_EQ(acceleration1.count(), 3u); + EXPECT_TRUE(acceleration1.hasNext()); + acceleration1.next(); + EXPECT_EQ(acceleration1.mph(), 30); + EXPECT_EQ(acceleration1.seconds(), 4.0f); - EXPECT_TRUE(acceleration.hasNext()); - acceleration.next(); - EXPECT_EQ(acceleration.mph(), 60); - EXPECT_EQ(acceleration.seconds(), 7.5f); + EXPECT_TRUE(acceleration1.hasNext()); + acceleration1.next(); + EXPECT_EQ(acceleration1.mph(), 60); + EXPECT_EQ(acceleration1.seconds(), 7.5f); - EXPECT_TRUE(acceleration.hasNext()); - acceleration.next(); - EXPECT_EQ(acceleration.mph(), 100); - EXPECT_EQ(acceleration.seconds(), 12.2f); + EXPECT_TRUE(acceleration1.hasNext()); + acceleration1.next(); + EXPECT_EQ(acceleration1.mph(), 100); + EXPECT_EQ(acceleration1.seconds(), 12.2f); EXPECT_TRUE(performanceFigures.hasNext()); performanceFigures.next(); EXPECT_EQ(performanceFigures.octaneRating(), 99); - acceleration = performanceFigures.acceleration(); - EXPECT_EQ(acceleration.count(), 3u); - EXPECT_TRUE(acceleration.hasNext()); - acceleration.next(); - EXPECT_EQ(acceleration.mph(), 30); - EXPECT_EQ(acceleration.seconds(), 3.8f); - - EXPECT_TRUE(acceleration.hasNext()); - acceleration.next(); - EXPECT_EQ(acceleration.mph(), 60); - EXPECT_EQ(acceleration.seconds(), 7.1f); - - EXPECT_TRUE(acceleration.hasNext()); - acceleration.next(); - EXPECT_EQ(acceleration.mph(), 100); - EXPECT_EQ(acceleration.seconds(), 11.8f); + Car::PerformanceFigures::Acceleration &acceleration2 = performanceFigures.acceleration(); + EXPECT_EQ(acceleration2.count(), 3u); + EXPECT_TRUE(acceleration2.hasNext()); + acceleration2.next(); + EXPECT_EQ(acceleration2.mph(), 30); + EXPECT_EQ(acceleration2.seconds(), 3.8f); + + EXPECT_TRUE(acceleration2.hasNext()); + acceleration2.next(); + EXPECT_EQ(acceleration2.mph(), 60); + EXPECT_EQ(acceleration2.seconds(), 7.1f); + + EXPECT_TRUE(acceleration2.hasNext()); + acceleration2.next(); + EXPECT_EQ(acceleration2.mph(), 100); + EXPECT_EQ(acceleration2.seconds(), 11.8f); return m_carDecoder.encodedLength(); } diff --git a/sbe-tool/src/test/cpp/CMakeLists.txt b/sbe-tool/src/test/cpp/CMakeLists.txt index 03d1ab1cfb..136c3cc0bf 100644 --- a/sbe-tool/src/test/cpp/CMakeLists.txt +++ b/sbe-tool/src/test/cpp/CMakeLists.txt @@ -18,6 +18,7 @@ function(sbe_test name) add_executable("${name}" "${name}.cpp") target_include_directories("${name}" PRIVATE ${GTEST_SOURCE_DIR}/googletest/include + PRIVATE ${GTEST_SOURCE_DIR}/googlemock/include PRIVATE ${CXX_CODEC_TARGET_DIR} ) target_compile_options("${name}" PRIVATE $<$:-Werror>) @@ -40,6 +41,7 @@ set(MESSAGE_BLOCK_LENGTH_TEST ${CODEC_SCHEMA_DIR}/message-block-length-test.xml) set(GROUP_WITH_DATA_SCHEMA ${CODEC_SCHEMA_DIR}/group-with-data-schema.xml) set(ISSUE835_SCHEMA ${CODEC_SCHEMA_DIR}/issue835.xml) set(ISSUE889_SCHEMA ${CODEC_SCHEMA_DIR}/issue889.xml) +set(ACCESS_ORDER_SCHEMA ${CODEC_SCHEMA_DIR}/field-order-check-schema.xml) set(GENERATED_CODECS ${CXX_CODEC_TARGET_DIR} @@ -54,6 +56,7 @@ add_custom_command( ${GROUP_WITH_DATA_SCHEMA} ${ISSUE835_SCHEMA} ${ISSUE889_SCHEMA} + ${ACCESS_ORDER_SCHEMA} sbe-jar ${SBE_JAR} COMMAND ${Java_JAVA_EXECUTABLE} @@ -68,6 +71,7 @@ add_custom_command( ${COMPOSITE_ELEMENTS_SCHEMA} ${ISSUE835_SCHEMA} ${ISSUE889_SCHEMA} + ${ACCESS_ORDER_SCHEMA} ) add_custom_target(codecs DEPENDS ${GENERATED_CODECS}) @@ -83,3 +87,5 @@ sbe_test(Rc3OtfFullIrTest codecs) sbe_test(CompositeElementsTest codecs) sbe_test(Issue835Test codecs) sbe_test(Issue889Test codecs) +sbe_test(FieldAccessOrderCheckTest codecs) +target_compile_definitions(FieldAccessOrderCheckTest PRIVATE ENABLE_ACCESS_ORDER_CHECKS) diff --git a/sbe-tool/src/test/cpp/CodeGenTest.cpp b/sbe-tool/src/test/cpp/CodeGenTest.cpp index 698d7ec849..6675172c8d 100644 --- a/sbe-tool/src/test/cpp/CodeGenTest.cpp +++ b/sbe-tool/src/test/cpp/CodeGenTest.cpp @@ -680,43 +680,43 @@ TEST_F(CodeGenTest, shouldBeAbleToEncodeAndDecodeHeaderPlusCarCorrectly) performanceFigures.next(); EXPECT_EQ(performanceFigures.octaneRating(), perf1Octane); - Car::PerformanceFigures::Acceleration &acceleration = performanceFigures.acceleration(); - EXPECT_EQ(acceleration.count(), ACCELERATION_COUNT); - ASSERT_TRUE(acceleration.hasNext()); - acceleration.next(); - EXPECT_EQ(acceleration.mph(), perf1aMph); - EXPECT_EQ(acceleration.seconds(), perf1aSeconds); - - ASSERT_TRUE(acceleration.hasNext()); - acceleration.next(); - EXPECT_EQ(acceleration.mph(), perf1bMph); - EXPECT_EQ(acceleration.seconds(), perf1bSeconds); - - ASSERT_TRUE(acceleration.hasNext()); - acceleration.next(); - EXPECT_EQ(acceleration.mph(), perf1cMph); - EXPECT_EQ(acceleration.seconds(), perf1cSeconds); + Car::PerformanceFigures::Acceleration &acceleration1 = performanceFigures.acceleration(); + EXPECT_EQ(acceleration1.count(), ACCELERATION_COUNT); + ASSERT_TRUE(acceleration1.hasNext()); + acceleration1.next(); + EXPECT_EQ(acceleration1.mph(), perf1aMph); + EXPECT_EQ(acceleration1.seconds(), perf1aSeconds); + + ASSERT_TRUE(acceleration1.hasNext()); + acceleration1.next(); + EXPECT_EQ(acceleration1.mph(), perf1bMph); + EXPECT_EQ(acceleration1.seconds(), perf1bSeconds); + + ASSERT_TRUE(acceleration1.hasNext()); + acceleration1.next(); + EXPECT_EQ(acceleration1.mph(), perf1cMph); + EXPECT_EQ(acceleration1.seconds(), perf1cSeconds); ASSERT_TRUE(performanceFigures.hasNext()); performanceFigures.next(); EXPECT_EQ(performanceFigures.octaneRating(), perf2Octane); - acceleration = performanceFigures.acceleration(); - EXPECT_EQ(acceleration.count(), ACCELERATION_COUNT); - ASSERT_TRUE(acceleration.hasNext()); - acceleration.next(); - EXPECT_EQ(acceleration.mph(), perf2aMph); - EXPECT_EQ(acceleration.seconds(), perf2aSeconds); + Car::PerformanceFigures::Acceleration &acceleration2 = performanceFigures.acceleration(); + EXPECT_EQ(acceleration2.count(), ACCELERATION_COUNT); + ASSERT_TRUE(acceleration2.hasNext()); + acceleration2.next(); + EXPECT_EQ(acceleration2.mph(), perf2aMph); + EXPECT_EQ(acceleration2.seconds(), perf2aSeconds); - ASSERT_TRUE(acceleration.hasNext()); - acceleration.next(); - EXPECT_EQ(acceleration.mph(), perf2bMph); - EXPECT_EQ(acceleration.seconds(), perf2bSeconds); + ASSERT_TRUE(acceleration2.hasNext()); + acceleration2.next(); + EXPECT_EQ(acceleration2.mph(), perf2bMph); + EXPECT_EQ(acceleration2.seconds(), perf2bSeconds); - ASSERT_TRUE(acceleration.hasNext()); - acceleration.next(); - EXPECT_EQ(acceleration.mph(), perf2cMph); - EXPECT_EQ(acceleration.seconds(), perf2cSeconds); + ASSERT_TRUE(acceleration2.hasNext()); + acceleration2.next(); + EXPECT_EQ(acceleration2.mph(), perf2cMph); + EXPECT_EQ(acceleration2.seconds(), perf2cSeconds); EXPECT_EQ(m_carDecoder.manufacturerLength(), MANUFACTURER_LENGTH); EXPECT_EQ(std::string(m_carDecoder.manufacturer(), MANUFACTURER_LENGTH), MANUFACTURER); @@ -748,7 +748,7 @@ struct CallbacksForEach void operator()(Car::PerformanceFigures &performanceFigures) { - Car::PerformanceFigures::Acceleration acceleration = performanceFigures.acceleration(); + Car::PerformanceFigures::Acceleration &acceleration = performanceFigures.acceleration(); countOfPerformanceFigures++; acceleration.forEach(*this); @@ -806,7 +806,7 @@ TEST_F(CodeGenTest, shouldBeAbleUseOnStackCodecsAndGroupForEach) #if __cplusplus >= 201103L performanceFigures.forEach([&](Car::PerformanceFigures &figures) { - Car::PerformanceFigures::Acceleration acceleration = figures.acceleration(); + Car::PerformanceFigures::Acceleration &acceleration = figures.acceleration(); cbs.countOfPerformanceFigures++; acceleration.forEach( @@ -964,13 +964,13 @@ TEST_F(CodeGenTest, shouldBeAbleToUseStdStringMethodsForDecode) Car::PerformanceFigures &perfFigures = carDecoder.performanceFigures(); perfFigures.next(); - Car::PerformanceFigures::Acceleration &acceleration = perfFigures.acceleration(); + Car::PerformanceFigures::Acceleration &acceleration1 = perfFigures.acceleration(); - acceleration.next().next().next(); + acceleration1.next().next().next(); perfFigures.next(); - acceleration = perfFigures.acceleration(); - acceleration.next().next().next(); + Car::PerformanceFigures::Acceleration &acceleration2 = perfFigures.acceleration(); + acceleration2.next().next().next(); EXPECT_EQ(carDecoder.getManufacturerAsString(), manufacturer); EXPECT_EQ(carDecoder.getModelAsString(), model); @@ -1037,15 +1037,15 @@ TEST_F(CodeGenTest, shouldPrintFullDecodedFlyweightRegardlessOfReadPosition) Car::PerformanceFigures &perfFigures = carDecoder.performanceFigures(); perfFigures.next(); - Car::PerformanceFigures::Acceleration &acceleration = perfFigures.acceleration(); + Car::PerformanceFigures::Acceleration &acceleration1 = perfFigures.acceleration(); - acceleration.next().next().next(); + acceleration1.next().next().next(); expectDisplayString(expectedDisplayString, carDecoder); perfFigures.next(); - acceleration = perfFigures.acceleration(); - acceleration.next().next().next(); + Car::PerformanceFigures::Acceleration &acceleration2 = perfFigures.acceleration(); + acceleration2.next().next().next(); EXPECT_EQ(carDecoder.getManufacturerAsString(), manufacturer); EXPECT_EQ(carDecoder.getModelAsString(), model); diff --git a/sbe-tool/src/test/cpp/CompositeOffsetsCodeGenTest.cpp b/sbe-tool/src/test/cpp/CompositeOffsetsCodeGenTest.cpp index 28bcbb1880..39759c1d50 100644 --- a/sbe-tool/src/test/cpp/CompositeOffsetsCodeGenTest.cpp +++ b/sbe-tool/src/test/cpp/CompositeOffsetsCodeGenTest.cpp @@ -141,7 +141,7 @@ TEST_F(CompositeOffsetsCodeGenTest, shouldBeAbleToDecodeHeaderAndMsgCorrectly) m_msgDecoder.wrapForDecode( buffer, hdrSz, TestMessage1::sbeBlockLength(), TestMessage1::sbeSchemaVersion(), hdrSz + sz); - TestMessage1::Entries entries = m_msgDecoder.entries(); + TestMessage1::Entries &entries = m_msgDecoder.entries(); EXPECT_EQ(entries.count(), 2u); ASSERT_TRUE(entries.hasNext()); diff --git a/sbe-tool/src/test/cpp/FieldAccessOrderCheckTest.cpp b/sbe-tool/src/test/cpp/FieldAccessOrderCheckTest.cpp new file mode 100644 index 0000000000..0da7b10761 --- /dev/null +++ b/sbe-tool/src/test/cpp/FieldAccessOrderCheckTest.cpp @@ -0,0 +1,4837 @@ +/* + * 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. + */ + +#include "gtest/gtest.h" +#include "gmock/gmock.h" +#include "order_check/MultipleVarLength.h" +#include "order_check/GroupAndVarLength.h" +#include "order_check/VarLengthInsideGroup.h" +#include "order_check/NestedGroups.h" +#include "order_check/CompositeInsideGroup.h" +#include "order_check/AddPrimitiveV1.h" +#include "order_check/AddPrimitiveV0.h" +#include "order_check/AddPrimitiveBeforeGroupV1.h" +#include "order_check/AddPrimitiveBeforeGroupV0.h" +#include "order_check/AddPrimitiveBeforeVarDataV1.h" +#include "order_check/AddPrimitiveBeforeVarDataV0.h" +#include "order_check/AddPrimitiveInsideGroupV1.h" +#include "order_check/AddPrimitiveInsideGroupV0.h" +#include "order_check/AddGroupBeforeVarDataV1.h" +#include "order_check/AddGroupBeforeVarDataV0.h" +#include "order_check/AddEnumBeforeGroupV1.h" +#include "order_check/AddEnumBeforeGroupV0.h" +#include "order_check/AddCompositeBeforeGroupV1.h" +#include "order_check/AddCompositeBeforeGroupV0.h" +#include "order_check/AddArrayBeforeGroupV1.h" +#include "order_check/AddArrayBeforeGroupV0.h" +#include "order_check/AddBitSetBeforeGroupV1.h" +#include "order_check/AddBitSetBeforeGroupV0.h" +#include "order_check/EnumInsideGroup.h" +#include "order_check/BitSetInsideGroup.h" +#include "order_check/ArrayInsideGroup.h" +#include "order_check/MultipleGroups.h" +#include "order_check/AddVarDataV1.h" +#include "order_check/AddVarDataV0.h" +#include "order_check/AsciiInsideGroup.h" +#include "order_check/AddAsciiBeforeGroupV1.h" +#include "order_check/AddAsciiBeforeGroupV0.h" +#include "order_check/NoBlock.h" +#include "order_check/GroupWithNoBlock.h" +#include "order_check/NestedGroupWithVarLength.h" + +using namespace order::check; +using ::testing::HasSubstr; + +class FieldAccessOrderCheckTest : public testing::Test +{ +public: + static const std::size_t BUFFER_LEN = 2048u; + static const std::size_t OFFSET = 0; + + char m_buffer[BUFFER_LEN] = {}; + +}; + +TEST_F(FieldAccessOrderCheckTest, allowsEncodingAndDecodingVariableLengthFieldsInSchemaDefinedOrder1) +{ + MultipleVarLength encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + encoder.putB("abc"); + encoder.putC("def"); + encoder.checkEncodingIsComplete(); + + MultipleVarLength decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + MultipleVarLength::sbeBlockLength(), + MultipleVarLength::sbeSchemaVersion(), + BUFFER_LEN + ); + + EXPECT_EQ(decoder.a(), 42); + EXPECT_EQ(decoder.getBAsString(), "abc"); + EXPECT_EQ(decoder.getCAsString(), "def"); +} + +TEST_F(FieldAccessOrderCheckTest, allowsEncodingAndDecodingVariableLengthFieldsInSchemaDefinedOrder2) +{ + MultipleVarLength encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + encoder.putB("abc"); + encoder.putC("def"); + encoder.checkEncodingIsComplete(); + + MultipleVarLength decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + MultipleVarLength::sbeBlockLength(), + MultipleVarLength::sbeSchemaVersion(), + BUFFER_LEN + ); + + EXPECT_EQ(decoder.a(), 42); + auto bLength = decoder.bLength(); + auto *b = decoder.b(); + const std::string bStr(b, bLength); + EXPECT_EQ(bStr, "abc"); + auto cLength = decoder.cLength(); + auto *c = decoder.c(); + const std::string cStr(c, cLength); + EXPECT_EQ(cStr, "def"); +} + +TEST_F(FieldAccessOrderCheckTest, allowsEncodingAndDecodingVariableLengthFieldsInSchemaDefinedOrder3) +{ + MultipleVarLength encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + auto *bIn = "abc"; + encoder.putB(bIn, strlen(bIn)); + auto *cIn = "def"; + encoder.putC(cIn, strlen(cIn)); + encoder.checkEncodingIsComplete(); + + MultipleVarLength decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + MultipleVarLength::sbeBlockLength(), + MultipleVarLength::sbeSchemaVersion(), + BUFFER_LEN + ); + + EXPECT_EQ(decoder.a(), 42); + + const auto bLength = decoder.bLength(); + auto *bOut = new char[bLength]; + decoder.getB(bOut, bLength); + EXPECT_EQ(std::strncmp(bOut, "abc", bLength), 0); + delete[] bOut; + + auto cLength = decoder.cLength(); + auto *cOut = new char[cLength]; + decoder.getC(cOut, cLength); + EXPECT_EQ(std::strncmp(cOut, "def", cLength), 0); + delete[] cOut; +} + +TEST_F(FieldAccessOrderCheckTest, allowsDecodingVariableLengthFieldsAfterRewind) +{ + MultipleVarLength encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + encoder.putB("abc"); + encoder.putC("def"); + encoder.checkEncodingIsComplete(); + + MultipleVarLength decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + MultipleVarLength::sbeBlockLength(), + MultipleVarLength::sbeSchemaVersion(), + BUFFER_LEN + ); + + EXPECT_EQ(decoder.a(), 42); + EXPECT_EQ(decoder.getBAsString(), "abc"); + EXPECT_EQ(decoder.getCAsString(), "def"); + + decoder.sbeRewind(); + + EXPECT_EQ(decoder.a(), 42); + EXPECT_EQ(decoder.getBAsString(), "abc"); + EXPECT_EQ(decoder.getCAsString(), "def"); +} + +TEST_F(FieldAccessOrderCheckTest, allowsDecodingToSkipVariableLengthFields) +{ + MultipleVarLength encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + encoder.putB("abc"); + encoder.putC("def"); + encoder.checkEncodingIsComplete(); + + MultipleVarLength decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + MultipleVarLength::sbeBlockLength(), + MultipleVarLength::sbeSchemaVersion(), + BUFFER_LEN + ); + + EXPECT_EQ(decoder.a(), 42); + EXPECT_EQ(decoder.skipB(), 3ul); + EXPECT_EQ(decoder.getCAsString(), "def"); +} + +TEST_F(FieldAccessOrderCheckTest, allowsReEncodingTopLevelPrimitiveFields) +{ + MultipleVarLength encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + encoder.putB("abc"); + encoder.putC("def"); + encoder.a(43); + encoder.checkEncodingIsComplete(); + + MultipleVarLength decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + MultipleVarLength::sbeBlockLength(), + MultipleVarLength::sbeSchemaVersion(), + BUFFER_LEN + ); + + EXPECT_EQ(decoder.a(), 43); + EXPECT_EQ(decoder.getBAsString(), "abc"); + EXPECT_EQ(decoder.getCAsString(), "def"); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsSkippingEncodingOfVariableLengthField1) +{ + MultipleVarLength encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + EXPECT_THROW( + { + try + { + encoder.putC("def"); + } + catch (const std::exception &e) + { + EXPECT_THAT( + e.what(), + HasSubstr("Cannot access field \"c\" in state: V0_BLOCK") + ); + throw; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsSkippingEncodingOfVariableLengthField2) +{ + MultipleVarLength encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + EXPECT_THROW( + { + try + { + auto *cIn = "cIn"; + encoder.putC(cIn, std::strlen(cIn)); + } + catch (const std::exception &e) + { + EXPECT_THAT( + e.what(), + HasSubstr("Cannot access field \"c\" in state: V0_BLOCK") + ); + throw; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsReEncodingEarlierVariableLengthFields) +{ + MultipleVarLength encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + encoder.putB("abc"); + encoder.putC("def"); + EXPECT_THROW( + { + try + { + encoder.putB("ghi"); + } + catch (const std::exception &e) + { + EXPECT_THAT( + e.what(), + HasSubstr("Cannot access field \"b\" in state: V0_C_DONE") + ); + throw; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsReEncodingLatestVariableLengthField) +{ + MultipleVarLength encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + encoder.putB("abc"); + encoder.putC("def"); + EXPECT_THROW( + { + try + { + encoder.putC("ghi"); + } + catch (const std::exception &e) + { + EXPECT_THAT( + e.what(), + HasSubstr("Cannot access field \"c\" in state: V0_C_DONE") + ); + throw; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsSkippingDecodingOfVariableLengthField1) +{ + MultipleVarLength encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + encoder.putB("abc"); + encoder.putC("def"); + encoder.checkEncodingIsComplete(); + + MultipleVarLength decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + MultipleVarLength::sbeBlockLength(), + MultipleVarLength::sbeSchemaVersion(), + BUFFER_LEN + ); + + EXPECT_EQ(decoder.a(), 42); + + EXPECT_THROW( + { + try + { + decoder.getCAsString(); + } + catch (const std::exception &e) + { + EXPECT_THAT( + e.what(), + HasSubstr("Cannot access field \"c\" in state: V0_BLOCK") + ); + throw; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsSkippingDecodingOfVariableLengthField2) +{ + MultipleVarLength encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + encoder.putB("abc"); + encoder.putC("def"); + encoder.checkEncodingIsComplete(); + + MultipleVarLength decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + MultipleVarLength::sbeBlockLength(), + MultipleVarLength::sbeSchemaVersion(), + BUFFER_LEN + ); + + EXPECT_EQ(decoder.a(), 42); + + EXPECT_THROW( + { + try + { + decoder.cLength(); + } + catch (const std::exception &e) + { + EXPECT_THAT( + e.what(), + HasSubstr("Cannot decode length of var data \"c\" in state: V0_BLOCK") + ); + throw; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsSkippingDecodingOfVariableLengthField3) +{ + MultipleVarLength encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + encoder.putB("abc"); + encoder.putC("def"); + encoder.checkEncodingIsComplete(); + + MultipleVarLength decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + MultipleVarLength::sbeBlockLength(), + MultipleVarLength::sbeSchemaVersion(), + BUFFER_LEN + ); + + EXPECT_EQ(decoder.a(), 42); + + EXPECT_THROW( + { + try + { + char cOut[3]; + decoder.getC(cOut, 3); + } + catch (const std::exception &e) + { + EXPECT_THAT( + e.what(), + HasSubstr("Cannot access field \"c\" in state: V0_BLOCK") + ); + throw; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsReDecodingEarlierVariableLengthField) +{ + MultipleVarLength encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + encoder.putB("abc"); + encoder.putC("def"); + encoder.checkEncodingIsComplete(); + + MultipleVarLength decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + MultipleVarLength::sbeBlockLength(), + MultipleVarLength::sbeSchemaVersion(), + BUFFER_LEN + ); + + EXPECT_EQ(decoder.a(), 42); + EXPECT_EQ(decoder.getBAsString(), "abc"); + EXPECT_EQ(decoder.getCAsString(), "def"); + + EXPECT_THROW( + { + try + { + decoder.getBAsString(); + } + catch (const std::exception &e) + { + EXPECT_THAT( + e.what(), + HasSubstr("Cannot access field \"b\" in state: V0_C_DONE") + ); + throw; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsReDecodingLatestVariableLengthField) +{ + MultipleVarLength encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + encoder.putB("abc"); + encoder.putC("def"); + encoder.checkEncodingIsComplete(); + + MultipleVarLength decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + MultipleVarLength::sbeBlockLength(), + MultipleVarLength::sbeSchemaVersion(), + BUFFER_LEN + ); + + EXPECT_EQ(decoder.a(), 42); + EXPECT_EQ(decoder.getBAsString(), "abc"); + EXPECT_EQ(decoder.getCAsString(), "def"); + + EXPECT_THROW( + { + try + { + decoder.getCAsString(); + } + catch (const std::exception &e) + { + EXPECT_THAT( + e.what(), + HasSubstr("Cannot access field \"c\" in state: V0_C_DONE") + ); + throw; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, allowsEncodingAndDecodingGroupAndVariableLengthFieldsInSchemaDefinedOrder) +{ + GroupAndVarLength encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + GroupAndVarLength::B &b = encoder.bCount(2); + b.next().c(1); + b.next().c(2); + encoder.putD("abc"); + encoder.checkEncodingIsComplete(); + + GroupAndVarLength decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + GroupAndVarLength::sbeBlockLength(), + GroupAndVarLength::sbeSchemaVersion(), + BUFFER_LEN + ); + + EXPECT_EQ(decoder.a(), 42); + GroupAndVarLength::B &bDecoder = decoder.b(); + EXPECT_EQ(bDecoder.count(), 2u); + EXPECT_EQ(bDecoder.next().c(), 1); + EXPECT_EQ(bDecoder.next().c(), 2); + EXPECT_EQ(decoder.getDAsString(), "abc"); +} + +TEST_F(FieldAccessOrderCheckTest, allowsDecodingGroupAndVariableLengthFieldsAfterRewind) +{ + GroupAndVarLength encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + GroupAndVarLength::B &b = encoder.bCount(2); + b.next().c(1); + b.next().c(2); + encoder.putD("abc"); + encoder.checkEncodingIsComplete(); + + GroupAndVarLength decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + GroupAndVarLength::sbeBlockLength(), + GroupAndVarLength::sbeSchemaVersion(), + BUFFER_LEN + ); + + EXPECT_EQ(decoder.a(), 42); + GroupAndVarLength::B &bDecoder = decoder.b(); + EXPECT_EQ(bDecoder.count(), 2u); + EXPECT_EQ(bDecoder.next().c(), 1); + EXPECT_EQ(bDecoder.next().c(), 2); + EXPECT_EQ(decoder.getDAsString(), "abc"); + + decoder.sbeRewind(); + + EXPECT_EQ(decoder.a(), 42); + GroupAndVarLength::B &bDecoder2 = decoder.b(); + EXPECT_EQ(bDecoder2.count(), 2u); + EXPECT_EQ(bDecoder2.next().c(), 1); + EXPECT_EQ(bDecoder2.next().c(), 2); + EXPECT_EQ(decoder.getDAsString(), "abc"); +} + +TEST_F(FieldAccessOrderCheckTest, allowsDecodingToSkipMessage) +{ + GroupAndVarLength encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + GroupAndVarLength::B &b = encoder.bCount(2); + b.next().c(1); + b.next().c(2); + encoder.putD("abc"); + encoder.checkEncodingIsComplete(); + + const std::uint64_t nextEncodeOffset = encoder.sbePosition(); + encoder.wrapForEncode(m_buffer, nextEncodeOffset, BUFFER_LEN); + encoder.a(43); + GroupAndVarLength::B &b2 = encoder.bCount(2); + b2.next().c(3); + b2.next().c(4); + encoder.putD("def"); + encoder.checkEncodingIsComplete(); + + GroupAndVarLength decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + GroupAndVarLength::sbeBlockLength(), + GroupAndVarLength::sbeSchemaVersion(), + BUFFER_LEN + ); + + decoder.skip(); + const std::uint64_t nextDecodeOffset = decoder.sbePosition(); + EXPECT_EQ(nextDecodeOffset, nextEncodeOffset); + + decoder.wrapForDecode( + m_buffer, + nextDecodeOffset, + GroupAndVarLength::sbeBlockLength(), + GroupAndVarLength::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 43); + GroupAndVarLength::B &bDecoder = decoder.b(); + EXPECT_EQ(bDecoder.count(), 2u); + EXPECT_EQ(bDecoder.next().c(), 3); + EXPECT_EQ(bDecoder.next().c(), 4); + EXPECT_EQ(decoder.getDAsString(), "def"); +} + +TEST_F(FieldAccessOrderCheckTest, allowsDecodingToDetermineMessageLengthBeforeReadingFields) +{ + GroupAndVarLength encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(43); + GroupAndVarLength::B &b = encoder.bCount(2); + b.next().c(3); + b.next().c(4); + encoder.putD("def"); + encoder.checkEncodingIsComplete(); + + GroupAndVarLength decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + GroupAndVarLength::sbeBlockLength(), + GroupAndVarLength::sbeSchemaVersion(), + BUFFER_LEN + ); + + EXPECT_EQ(decoder.decodeLength(), 18u); + EXPECT_EQ(decoder.a(), 43); + GroupAndVarLength::B &bDecoder = decoder.b(); + EXPECT_EQ(bDecoder.count(), 2u); + EXPECT_EQ(bDecoder.next().c(), 3); + EXPECT_EQ(bDecoder.next().c(), 4); + EXPECT_EQ(decoder.getDAsString(), "def"); +} + +TEST_F(FieldAccessOrderCheckTest, allowsEncodingAndDecodingEmptyGroupAndVariableLengthFieldsInSchemaDefinedOrder) +{ + GroupAndVarLength encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + encoder.bCount(0); + encoder.putD("abc"); + encoder.checkEncodingIsComplete(); + + GroupAndVarLength decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + GroupAndVarLength::sbeBlockLength(), + GroupAndVarLength::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 42); + GroupAndVarLength::B &bDecoder = decoder.b(); + EXPECT_EQ(bDecoder.count(), 0u); + EXPECT_EQ(decoder.getDAsString(), "abc"); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsEncodingGroupElementBeforeCallingNext) +{ + GroupAndVarLength encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + GroupAndVarLength::B &bEncoder = encoder.bCount(1); + EXPECT_THROW( + { + try + { + bEncoder.c(1); + } + catch (const std::logic_error &e) + { + EXPECT_THAT( + e.what(), + HasSubstr("Cannot access field \"b.c\" in state: V0_B_N") + ); + throw; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsDecodingGroupElementBeforeCallingNext) +{ + GroupAndVarLength encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + GroupAndVarLength::B &bEncoder = encoder.bCount(2); + bEncoder.next().c(1); + bEncoder.next().c(2); + encoder.putD("abc"); + encoder.checkEncodingIsComplete(); + + GroupAndVarLength decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + GroupAndVarLength::sbeBlockLength(), + GroupAndVarLength::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 42); + GroupAndVarLength::B &bDecoder = decoder.b(); + EXPECT_EQ(bDecoder.count(), 2u); + + EXPECT_THROW( + { + try + { + bDecoder.c(); + } + catch (const std::logic_error &e) + { + EXPECT_THAT( + e.what(), + HasSubstr("Cannot access field \"b.c\" in state: V0_B_N") + ); + throw; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsSkippingEncodingOfGroup) +{ + GroupAndVarLength encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + EXPECT_THROW( + { + try + { + encoder.putD("abc"); + } + catch (const std::logic_error &e) + { + EXPECT_THAT( + e.what(), + HasSubstr("Cannot access field \"d\" in state: V0_BLOCK") + ); + throw; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsReEncodingVariableLengthFieldAfterGroup) +{ + GroupAndVarLength encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + GroupAndVarLength::B &bEncoder = encoder.bCount(2); + bEncoder.next().c(1); + bEncoder.next().c(2); + encoder.putD("abc"); + EXPECT_THROW( + { + try + { + encoder.putD("def"); + } + catch (const std::logic_error &e) + { + EXPECT_THAT( + e.what(), + HasSubstr("Cannot access field \"d\" in state: V0_D_DONE") + ); + throw; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsReEncodingGroupCount) +{ + GroupAndVarLength encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + GroupAndVarLength::B &bEncoder = encoder.bCount(2); + bEncoder.next().c(1); + bEncoder.next().c(2); + encoder.putD("abc"); + EXPECT_THROW( + { + try + { + encoder.bCount(1); + } + catch (const std::logic_error &e) + { + EXPECT_THAT( + e.what(), + HasSubstr("Cannot encode count of repeating group \"b\" in state: V0_D_DONE") + ); + throw; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsMissedDecodingOfGroupBeforeVariableLengthField) +{ + GroupAndVarLength encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + GroupAndVarLength::B &bEncoder = encoder.bCount(2); + bEncoder.next().c(1); + bEncoder.next().c(2); + encoder.putD("abc"); + encoder.checkEncodingIsComplete(); + + GroupAndVarLength decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + GroupAndVarLength::sbeBlockLength(), + GroupAndVarLength::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 42); + EXPECT_THROW( + { + try + { + decoder.getDAsString(); + } + catch (const std::logic_error &e) + { + EXPECT_THAT( + e.what(), + HasSubstr("Cannot access field \"d\" in state: V0_BLOCK") + ); + throw; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsReDecodingVariableLengthFieldAfterGroup) +{ + GroupAndVarLength encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + GroupAndVarLength::B &bEncoder = encoder.bCount(2); + bEncoder.next().c(1); + bEncoder.next().c(2); + encoder.putD("abc"); + encoder.checkEncodingIsComplete(); + + GroupAndVarLength decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + GroupAndVarLength::sbeBlockLength(), + GroupAndVarLength::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 42); + GroupAndVarLength::B &bDecoder = decoder.b(); + EXPECT_EQ(bDecoder.count(), 2u); + EXPECT_EQ(bDecoder.next().c(), 1); + EXPECT_EQ(bDecoder.next().c(), 2); + EXPECT_EQ(decoder.getDAsString(), "abc"); + EXPECT_THROW( + { + try + { + decoder.getDAsString(); + } + catch (const std::logic_error &e) + { + EXPECT_THAT( + e.what(), + HasSubstr("Cannot access field \"d\" in state: V0_D_DONE") + ); + throw; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsReDecodingGroupAfterVariableLengthField) +{ + GroupAndVarLength encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + GroupAndVarLength::B &bEncoder = encoder.bCount(2); + bEncoder.next().c(1); + bEncoder.next().c(2); + encoder.putD("abc"); + encoder.checkEncodingIsComplete(); + + GroupAndVarLength decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + GroupAndVarLength::sbeBlockLength(), + GroupAndVarLength::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 42); + GroupAndVarLength::B &bDecoder = decoder.b(); + EXPECT_EQ(bDecoder.count(), 2u); + EXPECT_EQ(bDecoder.next().c(), 1); + EXPECT_EQ(bDecoder.next().c(), 2); + EXPECT_EQ(decoder.getDAsString(), "abc"); + EXPECT_THROW( + { + try + { + decoder.b(); + } + catch (const std::logic_error &e) + { + EXPECT_THAT( + e.what(), + HasSubstr("Cannot decode count of repeating group \"b\" in state: V0_D_DONE") + ); + throw; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, allowsEncodingAndDecodingVariableLengthFieldInsideGroupInSchemaDefinedOrder) +{ + VarLengthInsideGroup encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + VarLengthInsideGroup::B &bEncoder = encoder.bCount(2); + bEncoder.next().c(1).putD("abc"); + bEncoder.next().c(2).putD("def"); + encoder.putE("ghi"); + encoder.checkEncodingIsComplete(); + + VarLengthInsideGroup decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + VarLengthInsideGroup::sbeBlockLength(), + VarLengthInsideGroup::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 42); + VarLengthInsideGroup::B &bDecoder = decoder.b(); + EXPECT_EQ(bDecoder.count(), 2u); + EXPECT_EQ(bDecoder.next().c(), 1); + EXPECT_EQ(bDecoder.getDAsString(), "abc"); + EXPECT_EQ(bDecoder.next().c(), 2); + EXPECT_EQ(bDecoder.getDAsString(), "def"); + EXPECT_EQ(decoder.getEAsString(), "ghi"); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsMissedGroupElementVariableLengthFieldToEncodeAtTopLevel) +{ + VarLengthInsideGroup encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + VarLengthInsideGroup::B &bEncoder = encoder.bCount(1); + bEncoder.next().c(1); + EXPECT_THROW( + { + try + { + encoder.putE("abc"); + } + catch (const std::logic_error &e) + { + EXPECT_THAT( + e.what(), + HasSubstr("Cannot access field \"e\" in state: V0_B_1_BLOCK") + ); + throw; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsMissedGroupElementVariableLengthFieldToEncodeNextElement) +{ + VarLengthInsideGroup encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + VarLengthInsideGroup::B &bEncoder = encoder.bCount(2); + bEncoder.next(); + EXPECT_THROW( + { + try + { + bEncoder.next(); + } + catch (const std::logic_error &e) + { + EXPECT_THAT( + e.what(), + HasSubstr("Cannot access next element in repeating group \"b\" in state: V0_B_N_BLOCK") + ); + throw; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsMissedGroupElementEncoding) +{ + VarLengthInsideGroup encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + VarLengthInsideGroup::B &bEncoder = encoder.bCount(2); + bEncoder.next().c(1).putD("abc"); + EXPECT_THROW( + { + try + { + encoder.putE("abc"); + } + catch (const std::logic_error &e) + { + EXPECT_THAT( + e.what(), + HasSubstr("Cannot access field \"e\" in state: V0_B_N_D_DONE") + ); + throw; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsReEncodingGroupElementVariableLengthField) +{ + VarLengthInsideGroup encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + VarLengthInsideGroup::B &bEncoder = encoder.bCount(1); + bEncoder.next().c(1).putD("abc"); + encoder.putE("def"); + EXPECT_THROW( + { + try + { + bEncoder.putD("ghi"); + } + catch (const std::logic_error &e) + { + EXPECT_THAT( + e.what(), + HasSubstr("Cannot access field \"b.d\" in state: V0_E_DONE") + ); + throw; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsReDecodingGroupElementVariableLengthField) +{ + VarLengthInsideGroup encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + VarLengthInsideGroup::B &bEncoder = encoder.bCount(2); + bEncoder.next().c(1).putD("abc"); + bEncoder.next().c(2).putD("def"); + encoder.putE("ghi"); + encoder.checkEncodingIsComplete(); + + VarLengthInsideGroup decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + VarLengthInsideGroup::sbeBlockLength(), + VarLengthInsideGroup::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 42); + VarLengthInsideGroup::B &bDecoder = decoder.b(); + EXPECT_EQ(bDecoder.count(), 2u); + EXPECT_EQ(bDecoder.next().c(), 1); + EXPECT_EQ(bDecoder.getDAsString(), "abc"); + EXPECT_THROW( + { + try + { + bDecoder.getDAsString(); + } + catch (const std::logic_error &e) + { + EXPECT_THAT( + e.what(), + HasSubstr("Cannot access field \"b.d\" in state: V0_B_N_D_DONE") + ); + throw; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsMissedDecodingOfGroupElementVariableLengthFieldToNextElement) +{ + VarLengthInsideGroup encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + VarLengthInsideGroup::B &bEncoder = encoder.bCount(2); + bEncoder.next().c(1).putD("abc"); + bEncoder.next().c(2).putD("def"); + encoder.putE("ghi"); + encoder.checkEncodingIsComplete(); + + VarLengthInsideGroup decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + VarLengthInsideGroup::sbeBlockLength(), + VarLengthInsideGroup::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 42); + VarLengthInsideGroup::B &bDecoder = decoder.b(); + EXPECT_EQ(bDecoder.count(), 2u); + EXPECT_EQ(bDecoder.next().c(), 1); + EXPECT_THROW( + { + try + { + bDecoder.next(); + } + catch (const std::logic_error &e) + { + EXPECT_THAT( + e.what(), + HasSubstr("Cannot access next element in repeating group \"b\" in state: V0_B_N_BLOCK") + ); + throw; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsMissedDecodingOfGroupElementVariableLengthFieldToTopLevel) +{ + VarLengthInsideGroup encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + VarLengthInsideGroup::B &bEncoder = encoder.bCount(1); + bEncoder.next().c(1).putD("abc"); + encoder.putE("ghi"); + encoder.checkEncodingIsComplete(); + + VarLengthInsideGroup decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + VarLengthInsideGroup::sbeBlockLength(), + VarLengthInsideGroup::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 42); + VarLengthInsideGroup::B &bDecoder = decoder.b(); + EXPECT_EQ(bDecoder.count(), 1u); + EXPECT_EQ(bDecoder.next().c(), 1); + EXPECT_THROW( + { + try + { + decoder.getEAsString(); + } + catch (const std::logic_error &e) + { + EXPECT_THAT( + e.what(), + HasSubstr("Cannot access field \"e\" in state: V0_B_1_BLOCK") + ); + throw; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsMissedDecodingOfGroupElement) +{ + VarLengthInsideGroup encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + VarLengthInsideGroup::B &bEncoder = encoder.bCount(2); + bEncoder.next().c(1).putD("abc"); + bEncoder.next().c(2).putD("def"); + encoder.putE("ghi"); + encoder.checkEncodingIsComplete(); + + VarLengthInsideGroup decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + VarLengthInsideGroup::sbeBlockLength(), + VarLengthInsideGroup::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 42); + VarLengthInsideGroup::B &bDecoder = decoder.b(); + EXPECT_EQ(bDecoder.count(), 2u); + EXPECT_EQ(bDecoder.next().c(), 1); + EXPECT_THROW( + { + try + { + decoder.getEAsString(); + } + catch (const std::logic_error &e) + { + EXPECT_THAT( + e.what(), + HasSubstr("Cannot access field \"e\" in state: V0_B_N_BLOCK") + ); + throw; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, allowsEncodingNestedGroupsInSchemaDefinedOrder) +{ + NestedGroups encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + NestedGroups::B &bEncoder = encoder.bCount(2); + bEncoder.next(); + bEncoder.c(1); + NestedGroups::B::D &b0dEncoder = bEncoder.dCount(2); + b0dEncoder.next().e(2); + b0dEncoder.next().e(3); + NestedGroups::B::F &b0fEncoder = bEncoder.fCount(1); + b0fEncoder.next().g(4); + bEncoder.next(); + bEncoder.c(5); + NestedGroups::B::D &b1dEncoder = bEncoder.dCount(1); + b1dEncoder.next().e(6); + NestedGroups::B::F &b1fEncoder = bEncoder.fCount(1); + b1fEncoder.next().g(7); + NestedGroups::H &hEncoder = encoder.hCount(1); + hEncoder.next().i(8); + encoder.checkEncodingIsComplete(); + + NestedGroups decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + NestedGroups::sbeBlockLength(), + NestedGroups::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 42); + NestedGroups::B &bDecoder = decoder.b(); + EXPECT_EQ(bDecoder.count(), 2u); + NestedGroups::B &b0Decoder = bDecoder.next(); + EXPECT_EQ(b0Decoder.c(), 1); + NestedGroups::B::D &b0dDecoder = b0Decoder.d(); + EXPECT_EQ(b0dDecoder.count(), 2u); + EXPECT_EQ(b0dDecoder.next().e(), 2); + EXPECT_EQ(b0dDecoder.next().e(), 3); + NestedGroups::B::F &b0fDecoder = b0Decoder.f(); + EXPECT_EQ(b0fDecoder.count(), 1u); + EXPECT_EQ(b0fDecoder.next().g(), 4); + NestedGroups::B &b1Decoder = bDecoder.next(); + EXPECT_EQ(b1Decoder.c(), 5); + NestedGroups::B::D &b1dDecoder = b1Decoder.d(); + EXPECT_EQ(b1dDecoder.count(), 1u); + EXPECT_EQ(b1dDecoder.next().e(), 6); + NestedGroups::B::F &b1fDecoder = b1Decoder.f(); + EXPECT_EQ(b1fDecoder.count(), 1u); + EXPECT_EQ(b1fDecoder.next().g(), 7); + NestedGroups::H &hDecoder = decoder.h(); + EXPECT_EQ(hDecoder.count(), 1u); + EXPECT_EQ(hDecoder.next().i(), 8); +} + +TEST_F(FieldAccessOrderCheckTest, allowsEncodingEmptyNestedGroupsInSchemaDefinedOrder) +{ + NestedGroups encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + encoder.bCount(0); + encoder.hCount(0); + encoder.checkEncodingIsComplete(); + + NestedGroups decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + NestedGroups::sbeBlockLength(), + NestedGroups::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 42); + NestedGroups::B &bDecoder = decoder.b(); + EXPECT_EQ(bDecoder.count(), 0u); + NestedGroups::H &hDecoder = decoder.h(); + EXPECT_EQ(hDecoder.count(), 0u); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsMissedEncodingOfNestedGroup) +{ + NestedGroups encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + NestedGroups::B &bEncoder = encoder.bCount(1).next(); + bEncoder.c(1); + EXPECT_THROW( + { + try + { + bEncoder.fCount(1); + } + catch (const std::logic_error &e) + { + EXPECT_THAT( + e.what(), + HasSubstr("Cannot encode count of repeating group \"b.f\" in state: V0_B_1_BLOCK") + ); + throw; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, allowsEncodingAndDecodingCompositeInsideGroupInSchemaDefinedOrder) +{ + CompositeInsideGroup encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + Point &aEncoder = encoder.a(); + aEncoder.x(1); + aEncoder.y(2); + CompositeInsideGroup::B &bEncoder = encoder.bCount(1).next(); + Point &cEncoder = bEncoder.c(); + cEncoder.x(3); + cEncoder.y(4); + encoder.checkEncodingIsComplete(); + + CompositeInsideGroup decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + CompositeInsideGroup::sbeBlockLength(), + CompositeInsideGroup::sbeSchemaVersion(), + BUFFER_LEN + ); + Point &aDecoder = decoder.a(); + EXPECT_EQ(aDecoder.x(), 1); + EXPECT_EQ(aDecoder.y(), 2); + CompositeInsideGroup::B &bDecoder = decoder.b(); + EXPECT_EQ(bDecoder.count(), 1u); + Point &cDecoder = bDecoder.next().c(); + EXPECT_EQ(cDecoder.x(), 3); + EXPECT_EQ(cDecoder.y(), 4); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsEncodingCompositeInsideGroupBeforeCallingNext) +{ + CompositeInsideGroup encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + Point &aEncoder = encoder.a(); + aEncoder.x(1); + aEncoder.y(2); + CompositeInsideGroup::B &bEncoder = encoder.bCount(1); + EXPECT_THROW( + { + try + { + bEncoder.c(); + } + catch (const std::logic_error &e) + { + EXPECT_THAT( + e.what(), + HasSubstr("Cannot access field \"b.c\" in state: V0_B_N") + ); + throw; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsDecodingCompositeInsideGroupBeforeCallingNext) +{ + CompositeInsideGroup encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + Point &aEncoder = encoder.a(); + aEncoder.x(1); + aEncoder.y(2); + CompositeInsideGroup::B &bEncoder = encoder.bCount(1).next(); + Point &cEncoder = bEncoder.c(); + cEncoder.x(3); + cEncoder.y(4); + + CompositeInsideGroup decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + CompositeInsideGroup::sbeBlockLength(), + CompositeInsideGroup::sbeSchemaVersion(), + BUFFER_LEN + ); + Point &aDecoder = decoder.a(); + EXPECT_EQ(aDecoder.x(), 1); + EXPECT_EQ(aDecoder.y(), 2); + CompositeInsideGroup::B &bDecoder = decoder.b(); + EXPECT_EQ(bDecoder.count(), 1u); + EXPECT_THROW( + { + try + { + bDecoder.c(); + } + catch (const std::logic_error &e) + { + EXPECT_THAT( + e.what(), + HasSubstr("Cannot access field \"b.c\" in state: V0_B_N") + ); + throw; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, allowsReEncodingTopLevelCompositeViaReWrap) +{ + CompositeInsideGroup encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + Point &aEncoder = encoder.a(); + aEncoder.x(1); + aEncoder.y(2); + CompositeInsideGroup::B &bEncoder = encoder.bCount(1).next(); + Point &cEncoder = bEncoder.c(); + cEncoder.x(3); + cEncoder.y(4); + encoder.a().x(5); + encoder.a().y(6); + encoder.checkEncodingIsComplete(); + + CompositeInsideGroup decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + CompositeInsideGroup::sbeBlockLength(), + CompositeInsideGroup::sbeSchemaVersion(), + BUFFER_LEN + ); + Point &aDecoder = decoder.a(); + EXPECT_EQ(aDecoder.x(), 5); + EXPECT_EQ(aDecoder.y(), 6); + CompositeInsideGroup::B &bDecoder = decoder.b(); + EXPECT_EQ(bDecoder.count(), 1u); + Point &cDecoder = bDecoder.next().c(); + EXPECT_EQ(cDecoder.x(), 3); + EXPECT_EQ(cDecoder.y(), 4); +} + +TEST_F(FieldAccessOrderCheckTest, allowsReEncodingTopLevelCompositeViaEncoderReference) +{ + CompositeInsideGroup encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + Point &aEncoder = encoder.a(); + aEncoder.x(1); + aEncoder.y(2); + CompositeInsideGroup::B &bEncoder = encoder.bCount(1).next(); + Point &cEncoder = bEncoder.c(); + cEncoder.x(3); + cEncoder.y(4); + aEncoder.x(5); + aEncoder.y(6); + encoder.checkEncodingIsComplete(); + + CompositeInsideGroup decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + CompositeInsideGroup::sbeBlockLength(), + CompositeInsideGroup::sbeSchemaVersion(), + BUFFER_LEN + ); + Point &aDecoder = decoder.a(); + EXPECT_EQ(aDecoder.x(), 5); + EXPECT_EQ(aDecoder.y(), 6); + CompositeInsideGroup::B &bDecoder = decoder.b(); + EXPECT_EQ(bDecoder.count(), 1u); + Point &cDecoder = bDecoder.next().c(); + EXPECT_EQ(cDecoder.x(), 3); + EXPECT_EQ(cDecoder.y(), 4); +} + +TEST_F(FieldAccessOrderCheckTest, allowsReEncodingGroupElementCompositeViaReWrap) +{ + CompositeInsideGroup encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + Point &aEncoder = encoder.a(); + aEncoder.x(1); + aEncoder.y(2); + CompositeInsideGroup::B &bEncoder = encoder.bCount(1).next(); + bEncoder.c().x(3).y(4); + bEncoder.c().x(5).y(6); + encoder.checkEncodingIsComplete(); + + CompositeInsideGroup decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + CompositeInsideGroup::sbeBlockLength(), + CompositeInsideGroup::sbeSchemaVersion(), + BUFFER_LEN + ); + Point &aDecoder = decoder.a(); + EXPECT_EQ(aDecoder.x(), 1); + EXPECT_EQ(aDecoder.y(), 2); + CompositeInsideGroup::B &bDecoder = decoder.b(); + EXPECT_EQ(bDecoder.count(), 1u); + Point &cDecoder = bDecoder.next().c(); + EXPECT_EQ(cDecoder.x(), 5); + EXPECT_EQ(cDecoder.y(), 6); +} + +TEST_F(FieldAccessOrderCheckTest, allowsReEncodingGroupElementCompositeViaEncoderReference) +{ + CompositeInsideGroup encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + Point &aEncoder = encoder.a(); + aEncoder.x(1); + aEncoder.y(2); + CompositeInsideGroup::B &bEncoder = encoder.bCount(1).next(); + Point &cEncoder = bEncoder.c(); + cEncoder.x(3); + cEncoder.y(4); + cEncoder.x(5); + cEncoder.y(6); + encoder.checkEncodingIsComplete(); + + CompositeInsideGroup decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + CompositeInsideGroup::sbeBlockLength(), + CompositeInsideGroup::sbeSchemaVersion(), + BUFFER_LEN + ); + Point &aDecoder = decoder.a(); + EXPECT_EQ(aDecoder.x(), 1); + EXPECT_EQ(aDecoder.y(), 2); + CompositeInsideGroup::B &bDecoder = decoder.b(); + EXPECT_EQ(bDecoder.count(), 1u); + Point &cDecoder = bDecoder.next().c(); + EXPECT_EQ(cDecoder.x(), 5); + EXPECT_EQ(cDecoder.y(), 6); +} + +TEST_F(FieldAccessOrderCheckTest, allowsReDecodingTopLevelCompositeViaReWrap) +{ + CompositeInsideGroup encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + Point &aEncoder = encoder.a(); + aEncoder.x(1); + aEncoder.y(2); + CompositeInsideGroup::B &bEncoder = encoder.bCount(1).next(); + bEncoder.c().x(3).y(4); + encoder.checkEncodingIsComplete(); + + CompositeInsideGroup decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + CompositeInsideGroup::sbeBlockLength(), + CompositeInsideGroup::sbeSchemaVersion(), + BUFFER_LEN + ); + Point &a1Decoder = decoder.a(); + EXPECT_EQ(a1Decoder.x(), 1); + EXPECT_EQ(a1Decoder.y(), 2); + CompositeInsideGroup::B &bDecoder = decoder.b(); + EXPECT_EQ(bDecoder.count(), 1u); + Point &cDecoder = bDecoder.next().c(); + EXPECT_EQ(cDecoder.x(), 3); + EXPECT_EQ(cDecoder.y(), 4); + Point &a2Decoder = decoder.a(); + EXPECT_EQ(a2Decoder.x(), 1); + EXPECT_EQ(a2Decoder.y(), 2); +} + +TEST_F(FieldAccessOrderCheckTest, allowsReDecodingTopLevelCompositeViaEncoderReference) +{ + CompositeInsideGroup encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + Point &aEncoder = encoder.a(); + aEncoder.x(1); + aEncoder.y(2); + CompositeInsideGroup::B &bEncoder = encoder.bCount(1).next(); + bEncoder.c().x(3).y(4); + encoder.checkEncodingIsComplete(); + + CompositeInsideGroup decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + CompositeInsideGroup::sbeBlockLength(), + CompositeInsideGroup::sbeSchemaVersion(), + BUFFER_LEN + ); + Point &aDecoder = decoder.a(); + EXPECT_EQ(aDecoder.x(), 1); + EXPECT_EQ(aDecoder.y(), 2); + CompositeInsideGroup::B &bDecoder = decoder.b(); + EXPECT_EQ(bDecoder.count(), 1u); + Point &cDecoder = bDecoder.next().c(); + EXPECT_EQ(cDecoder.x(), 3); + EXPECT_EQ(cDecoder.y(), 4); + EXPECT_EQ(aDecoder.x(), 1); + EXPECT_EQ(aDecoder.y(), 2); +} + +TEST_F(FieldAccessOrderCheckTest, allowsReDecodingGroupElementCompositeViaReWrap) +{ + CompositeInsideGroup encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + Point &aEncoder = encoder.a(); + aEncoder.x(1); + aEncoder.y(2); + CompositeInsideGroup::B &bEncoder = encoder.bCount(1).next(); + bEncoder.c().x(3).y(4); + encoder.checkEncodingIsComplete(); + + CompositeInsideGroup decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + CompositeInsideGroup::sbeBlockLength(), + CompositeInsideGroup::sbeSchemaVersion(), + BUFFER_LEN + ); + Point &aDecoder = decoder.a(); + EXPECT_EQ(aDecoder.x(), 1); + EXPECT_EQ(aDecoder.y(), 2); + CompositeInsideGroup::B &bDecoder = decoder.b(); + EXPECT_EQ(bDecoder.count(), 1u); + Point &c1Decoder = bDecoder.next().c(); + EXPECT_EQ(c1Decoder.x(), 3); + EXPECT_EQ(c1Decoder.y(), 4); + Point &c2Decoder = bDecoder.c(); + EXPECT_EQ(c2Decoder.x(), 3); + EXPECT_EQ(c2Decoder.y(), 4); +} + +TEST_F(FieldAccessOrderCheckTest, allowsReDecodingGroupElementCompositeViaEncoderReference) +{ + CompositeInsideGroup encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + Point &aEncoder = encoder.a(); + aEncoder.x(1); + aEncoder.y(2); + CompositeInsideGroup::B &bEncoder = encoder.bCount(1).next(); + bEncoder.c().x(3).y(4); + encoder.checkEncodingIsComplete(); + + CompositeInsideGroup decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + CompositeInsideGroup::sbeBlockLength(), + CompositeInsideGroup::sbeSchemaVersion(), + BUFFER_LEN + ); + Point &aDecoder = decoder.a(); + EXPECT_EQ(aDecoder.x(), 1); + EXPECT_EQ(aDecoder.y(), 2); + CompositeInsideGroup::B &bDecoder = decoder.b(); + EXPECT_EQ(bDecoder.count(), 1u); + Point &cDecoder = bDecoder.next().c(); + EXPECT_EQ(cDecoder.x(), 3); + EXPECT_EQ(cDecoder.y(), 4); + EXPECT_EQ(cDecoder.x(), 3); + EXPECT_EQ(cDecoder.y(), 4); +} + +TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeAddedPrimitiveField) +{ + AddPrimitiveV1 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + encoder.b(2); + encoder.checkEncodingIsComplete(); + + AddPrimitiveV1 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddPrimitiveV1::sbeBlockLength(), + AddPrimitiveV1::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + EXPECT_EQ(decoder.b(), 2); +} + +TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeMissingPrimitiveFieldAsNullValue) +{ + AddPrimitiveV0 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + encoder.checkEncodingIsComplete(); + + AddPrimitiveV1 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddPrimitiveV0::sbeBlockLength(), + 0, + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + EXPECT_EQ(decoder.b(), AddPrimitiveV1::bNullValue()); +} + +TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeAddedPrimitiveFieldBeforeGroup) +{ + AddPrimitiveBeforeGroupV1 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + encoder.d(3); + AddPrimitiveBeforeGroupV1::B &bEncoder = encoder.bCount(1).next(); + bEncoder.c(2); + encoder.checkEncodingIsComplete(); + + AddPrimitiveBeforeGroupV1 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddPrimitiveBeforeGroupV1::sbeBlockLength(), + AddPrimitiveBeforeGroupV1::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + EXPECT_EQ(decoder.d(), 3); + AddPrimitiveBeforeGroupV1::B &bDecoder = decoder.b(); + EXPECT_EQ(bDecoder.count(), 1u); + EXPECT_EQ(bDecoder.next().c(), 2); +} + +TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeMissingPrimitiveFieldBeforeGroupAsNullValue) +{ + AddPrimitiveBeforeGroupV0 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + AddPrimitiveBeforeGroupV0::B &bEncoder = encoder.bCount(1).next(); + bEncoder.c(2); + encoder.checkEncodingIsComplete(); + + AddPrimitiveBeforeGroupV1 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddPrimitiveBeforeGroupV0::sbeBlockLength(), + 0, + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + EXPECT_EQ(decoder.d(), AddPrimitiveBeforeGroupV1::dNullValue()); + AddPrimitiveBeforeGroupV1::B &bDecoder = decoder.b(); + EXPECT_EQ(bDecoder.count(), 1u); + EXPECT_EQ(bDecoder.next().c(), 2); +} + +TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToSkipPresentButAddedPrimitiveFieldBeforeGroup) +{ + AddPrimitiveBeforeGroupV1 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + encoder.d(3); + AddPrimitiveBeforeGroupV1::B &bEncoder = encoder.bCount(1).next(); + bEncoder.c(2); + encoder.checkEncodingIsComplete(); + + AddPrimitiveBeforeGroupV1 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddPrimitiveBeforeGroupV1::sbeBlockLength(), + AddPrimitiveBeforeGroupV1::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + AddPrimitiveBeforeGroupV1::B &bDecoder = decoder.b(); + EXPECT_EQ(bDecoder.count(), 1u); + EXPECT_EQ(bDecoder.next().c(), 2); +} + +TEST_F(FieldAccessOrderCheckTest, allowsOldDecoderToSkipAddedPrimitiveFieldBeforeGroup) +{ + AddPrimitiveBeforeGroupV1 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + encoder.d(3); + AddPrimitiveBeforeGroupV1::B &bEncoder = encoder.bCount(1).next(); + bEncoder.c(2); + encoder.checkEncodingIsComplete(); + + AddPrimitiveBeforeGroupV0 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddPrimitiveBeforeGroupV1::sbeBlockLength(), + 1, + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + AddPrimitiveBeforeGroupV0::B &bDecoder = decoder.b(); + EXPECT_EQ(bDecoder.count(), 1u); + EXPECT_EQ(bDecoder.next().c(), 2); +} + +TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeAddedPrimitiveFieldBeforeVarData) +{ + AddPrimitiveBeforeVarDataV1 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + encoder.c(3); + encoder.putB("abc"); + encoder.checkEncodingIsComplete(); + + AddPrimitiveBeforeVarDataV1 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddPrimitiveBeforeVarDataV1::sbeBlockLength(), + AddPrimitiveBeforeVarDataV1::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + EXPECT_EQ(decoder.c(), 3); + EXPECT_EQ(decoder.getBAsString(), "abc"); +} + +TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeMissingPrimitiveFieldBeforeVarDataAsNullValue) +{ + AddPrimitiveBeforeVarDataV0 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + encoder.putB("abc"); + encoder.checkEncodingIsComplete(); + + AddPrimitiveBeforeVarDataV1 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddPrimitiveBeforeVarDataV0::sbeBlockLength(), + 0, + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + EXPECT_EQ(decoder.c(), AddPrimitiveBeforeVarDataV1::cNullValue()); + EXPECT_EQ(decoder.getBAsString(), "abc"); +} + +TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToSkipPresentButAddedPrimitiveFieldBeforeVarData) +{ + AddPrimitiveBeforeVarDataV1 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + encoder.c(3); + encoder.putB("abc"); + encoder.checkEncodingIsComplete(); + + AddPrimitiveBeforeVarDataV1 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddPrimitiveBeforeVarDataV1::sbeBlockLength(), + AddPrimitiveBeforeVarDataV1::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + EXPECT_EQ(decoder.getBAsString(), "abc"); +} + +TEST_F(FieldAccessOrderCheckTest, allowsOldDecoderToSkipAddedPrimitiveFieldBeforeVarData) +{ + AddPrimitiveBeforeVarDataV1 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + encoder.c(3); + encoder.putB("abc"); + encoder.checkEncodingIsComplete(); + + AddPrimitiveBeforeVarDataV0 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddPrimitiveBeforeVarDataV1::sbeBlockLength(), + 1, + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + EXPECT_EQ(decoder.getBAsString(), "abc"); +} + +TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeAddedPrimitiveFieldInsideGroup) +{ + AddPrimitiveInsideGroupV1 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + encoder.bCount(1).next().c(2).d(3); + encoder.checkEncodingIsComplete(); + + AddPrimitiveInsideGroupV1 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddPrimitiveInsideGroupV1::sbeBlockLength(), + AddPrimitiveInsideGroupV1::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + AddPrimitiveInsideGroupV1::B &bDecoder = decoder.b(); + EXPECT_EQ(bDecoder.count(), 1u); + EXPECT_EQ(bDecoder.next().c(), 2); + EXPECT_EQ(bDecoder.d(), 3); +} + +TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeMissingPrimitiveFieldInsideGroupAsNullValue) +{ + AddPrimitiveInsideGroupV0 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + encoder.bCount(1).next().c(2); + encoder.checkEncodingIsComplete(); + + AddPrimitiveInsideGroupV1 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddPrimitiveInsideGroupV0::sbeBlockLength(), + 0, + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + AddPrimitiveInsideGroupV1::B &bDecoder = decoder.b(); + EXPECT_EQ(bDecoder.count(), 1u); + EXPECT_EQ(bDecoder.next().c(), 2); + EXPECT_EQ(bDecoder.d(), AddPrimitiveInsideGroupV1::B::dNullValue()); +} + +TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToSkipPresentButAddedPrimitiveFieldInsideGroup) +{ + AddPrimitiveInsideGroupV1 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + encoder.bCount(2).next().c(2).d(3).next().c(4).d(5); + encoder.checkEncodingIsComplete(); + + AddPrimitiveInsideGroupV1 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddPrimitiveInsideGroupV1::sbeBlockLength(), + AddPrimitiveInsideGroupV1::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + AddPrimitiveInsideGroupV1::B &bDecoder = decoder.b(); + EXPECT_EQ(bDecoder.count(), 2u); + EXPECT_EQ(bDecoder.next().c(), 2); + EXPECT_EQ(bDecoder.next().c(), 4); +} + +TEST_F(FieldAccessOrderCheckTest, allowsOldDecoderToSkipAddedPrimitiveFieldInsideGroup) +{ + AddPrimitiveInsideGroupV1 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + encoder.bCount(2).next().c(2).d(3).next().c(4).d(5); + encoder.checkEncodingIsComplete(); + + AddPrimitiveInsideGroupV0 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddPrimitiveInsideGroupV1::sbeBlockLength(), + 1, + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + AddPrimitiveInsideGroupV0::B &bDecoder = decoder.b(); + EXPECT_EQ(bDecoder.count(), 2u); + EXPECT_EQ(bDecoder.next().c(), 2); + EXPECT_EQ(bDecoder.next().c(), 4); +} + +TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeAddedGroupBeforeVarData) +{ + AddGroupBeforeVarDataV1 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + encoder.cCount(1).next().d(2); + encoder.putB("abc"); + encoder.checkEncodingIsComplete(); + + AddGroupBeforeVarDataV1 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddGroupBeforeVarDataV1::sbeBlockLength(), + AddGroupBeforeVarDataV1::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + AddGroupBeforeVarDataV1::C &cDecoder = decoder.c(); + EXPECT_EQ(cDecoder.count(), 1u); + EXPECT_EQ(cDecoder.next().d(), 2); + EXPECT_EQ(decoder.getBAsString(), "abc"); +} + +TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeMissingGroupBeforeVarDataAsNullValue) +{ + AddGroupBeforeVarDataV0 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + encoder.putB("abc"); + encoder.checkEncodingIsComplete(); + + AddGroupBeforeVarDataV1 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddGroupBeforeVarDataV0::sbeBlockLength(), + 0, + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + AddGroupBeforeVarDataV1::C &cDecoder = decoder.c(); + EXPECT_EQ(cDecoder.count(), 0u); + EXPECT_EQ(decoder.getBAsString(), "abc"); +} + +TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToSkipMissingGroupBeforeVarData) +{ + AddGroupBeforeVarDataV0 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + encoder.putB("abc"); + encoder.checkEncodingIsComplete(); + + AddGroupBeforeVarDataV1 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddGroupBeforeVarDataV0::sbeBlockLength(), + 0, + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + EXPECT_EQ(decoder.getBAsString(), "abc"); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsNewDecoderToSkipPresentButAddedGroupBeforeVarData) +{ + AddGroupBeforeVarDataV1 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + encoder.cCount(1).next().d(2); + encoder.putB("abc"); + encoder.checkEncodingIsComplete(); + + AddGroupBeforeVarDataV1 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddGroupBeforeVarDataV1::sbeBlockLength(), + AddGroupBeforeVarDataV1::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + EXPECT_THROW( + { + try + { + decoder.b(); + } + catch (const std::exception &e) + { + EXPECT_THAT( + e.what(), + HasSubstr("Cannot access field \"b\" in state: V1_BLOCK") + ); + throw; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, allowsOldDecoderToSkipAddedGroupBeforeVarData) +{ + AddGroupBeforeVarDataV1 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + encoder.cCount(1).next().d(2); + encoder.putB("abc"); + encoder.checkEncodingIsComplete(); + + AddGroupBeforeVarDataV0 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddGroupBeforeVarDataV1::sbeBlockLength(), + AddGroupBeforeVarDataV1::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + + // In reality, we would use a numGroups field in the message header to skip over unknown groups + // rather than the hardcoded knowledge of one extra group below: + + GroupSizeEncoding groupSizeEncodingDecoder; + groupSizeEncodingDecoder.wrap( + m_buffer, + decoder.sbePosition(), + GroupSizeEncoding::sbeSchemaVersion(), + BUFFER_LEN + ); + const std::uint64_t bytesToSkip = GroupSizeEncoding::encodedLength() + + groupSizeEncodingDecoder.blockLength() * groupSizeEncodingDecoder.numInGroup(); + decoder.sbePosition(decoder.sbePosition() + bytesToSkip); + + EXPECT_EQ(decoder.getBAsString(), "abc"); +} + +TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeAddedEnumFieldBeforeGroup) +{ + AddEnumBeforeGroupV1 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + encoder.d(Direction::BUY); + encoder.bCount(1).next().c(2); + encoder.checkEncodingIsComplete(); + + AddEnumBeforeGroupV1 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddEnumBeforeGroupV1::sbeBlockLength(), + 1, + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + EXPECT_EQ(decoder.d(), Direction::BUY); + AddEnumBeforeGroupV1::B &b = decoder.b(); + EXPECT_EQ(b.count(), 1u); + EXPECT_EQ(b.next().c(), 2); +} + +TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeMissingEnumFieldBeforeGroupAsNullValue) +{ + AddEnumBeforeGroupV0 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + encoder.bCount(1).next().c(2); + encoder.checkEncodingIsComplete(); + + AddEnumBeforeGroupV1 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddEnumBeforeGroupV0::sbeBlockLength(), + 0, + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + EXPECT_EQ(decoder.d(), Direction::NULL_VALUE); + AddEnumBeforeGroupV1::B &b = decoder.b(); + EXPECT_EQ(b.count(), 1u); + EXPECT_EQ(b.next().c(), 2); +} + +TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToSkipPresentButAddedEnumFieldBeforeGroup) +{ + AddEnumBeforeGroupV1 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + encoder.d(Direction::SELL); + encoder.bCount(1).next().c(2); + encoder.checkEncodingIsComplete(); + + AddEnumBeforeGroupV1 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddEnumBeforeGroupV1::sbeBlockLength(), + 1, + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + AddEnumBeforeGroupV1::B &b = decoder.b(); + EXPECT_EQ(b.count(), 1u); + EXPECT_EQ(b.next().c(), 2); +} + +TEST_F(FieldAccessOrderCheckTest, allowsOldDecoderToSkipAddedEnumFieldBeforeGroup) +{ + AddEnumBeforeGroupV1 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + encoder.d(Direction::BUY); + encoder.bCount(1).next().c(2); + encoder.checkEncodingIsComplete(); + + AddEnumBeforeGroupV0 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddEnumBeforeGroupV1::sbeBlockLength(), + 1, + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + AddEnumBeforeGroupV0::B &b = decoder.b(); + EXPECT_EQ(b.count(), 1u); + EXPECT_EQ(b.next().c(), 2); +} + +TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeAddedCompositeFieldBeforeGroup) +{ + AddCompositeBeforeGroupV1 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + Point &d = encoder.d(); + d.x(-1); + d.y(-2); + encoder.bCount(1).next().c(2); + encoder.checkEncodingIsComplete(); + + AddCompositeBeforeGroupV1 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddCompositeBeforeGroupV1::sbeBlockLength(), + 1, + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + Point &d2 = decoder.d(); + EXPECT_EQ(d2.x(), -1); + EXPECT_EQ(d2.y(), -2); + AddCompositeBeforeGroupV1::B &b = decoder.b(); + EXPECT_EQ(b.count(), 1u); + EXPECT_EQ(b.next().c(), 2); +} + +TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToSkipPresentButAddedCompositeFieldBeforeGroup) +{ + AddCompositeBeforeGroupV1 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + Point &d = encoder.d(); + d.x(-1); + d.y(-2); + encoder.bCount(1).next().c(2); + encoder.checkEncodingIsComplete(); + + AddCompositeBeforeGroupV1 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddCompositeBeforeGroupV1::sbeBlockLength(), + 1, + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + AddCompositeBeforeGroupV1::B &b = decoder.b(); + EXPECT_EQ(b.count(), 1u); + EXPECT_EQ(b.next().c(), 2); +} + +TEST_F(FieldAccessOrderCheckTest, allowsOldDecoderToSkipAddedCompositeFieldBeforeGroup) +{ + AddCompositeBeforeGroupV1 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + Point &d = encoder.d(); + d.x(-1); + d.y(-2); + encoder.bCount(1).next().c(2); + encoder.checkEncodingIsComplete(); + + AddCompositeBeforeGroupV0 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddCompositeBeforeGroupV1::sbeBlockLength(), + 1, + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + AddCompositeBeforeGroupV0::B &b = decoder.b(); + EXPECT_EQ(b.count(), 1u); + EXPECT_EQ(b.next().c(), 2); +} + +TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeAddedArrayFieldBeforeGroup) +{ + AddArrayBeforeGroupV1 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + encoder.putD(1, 2, 3, 4); + encoder.bCount(1).next().c(2); + encoder.checkEncodingIsComplete(); + + AddArrayBeforeGroupV1 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddArrayBeforeGroupV1::sbeBlockLength(), + 1, + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + EXPECT_EQ(decoder.d(0), 1); + EXPECT_EQ(decoder.d(1), 2); + EXPECT_EQ(decoder.d(2), 3); + EXPECT_EQ(decoder.d(3), 4); + AddArrayBeforeGroupV1::B &b = decoder.b(); + EXPECT_EQ(b.count(), 1u); + EXPECT_EQ(b.next().c(), 2); +} + +TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeMissingArrayFieldBeforeGroupAsNullValue1) +{ + AddArrayBeforeGroupV0 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + encoder.bCount(1).next().c(2); + encoder.checkEncodingIsComplete(); + + AddArrayBeforeGroupV1 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddArrayBeforeGroupV0::sbeBlockLength(), + 0, + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + EXPECT_EQ(decoder.d(0), AddArrayBeforeGroupV1::dNullValue()); + EXPECT_EQ(decoder.d(1), AddArrayBeforeGroupV1::dNullValue()); + EXPECT_EQ(decoder.d(2), AddArrayBeforeGroupV1::dNullValue()); + EXPECT_EQ(decoder.d(3), AddArrayBeforeGroupV1::dNullValue()); + AddArrayBeforeGroupV1::B &b = decoder.b(); + EXPECT_EQ(b.count(), 1u); + EXPECT_EQ(b.next().c(), 2); +} + +TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeMissingArrayFieldBeforeGroupAsNullValue2) +{ + AddArrayBeforeGroupV0 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + encoder.bCount(1).next().c(2); + encoder.checkEncodingIsComplete(); + + AddArrayBeforeGroupV1 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddArrayBeforeGroupV0::sbeBlockLength(), + 0, + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + char *d = decoder.d(); + EXPECT_EQ(d, nullptr); + AddArrayBeforeGroupV1::B &b = decoder.b(); + EXPECT_EQ(b.count(), 1u); + EXPECT_EQ(b.next().c(), 2); +} + +TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeMissingArrayFieldBeforeGroupAsNullValue3) +{ + AddArrayBeforeGroupV0 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + encoder.bCount(1).next().c(2); + encoder.checkEncodingIsComplete(); + + AddArrayBeforeGroupV1 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddArrayBeforeGroupV0::sbeBlockLength(), + 0, + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + const AddArrayBeforeGroupV1 &constDecoder = decoder; + const char *d = constDecoder.d(); + EXPECT_EQ(d, nullptr); + AddArrayBeforeGroupV1::B &b = decoder.b(); + EXPECT_EQ(b.count(), 1u); + EXPECT_EQ(b.next().c(), 2); +} + +TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToSkipPresentButAddedArrayFieldBeforeGroup) +{ + AddArrayBeforeGroupV1 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + encoder.putD(1, 2, 3, 4); + encoder.bCount(1).next().c(2); + encoder.checkEncodingIsComplete(); + + AddArrayBeforeGroupV1 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddArrayBeforeGroupV1::sbeBlockLength(), + 1, + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + AddArrayBeforeGroupV1::B &b = decoder.b(); + EXPECT_EQ(b.count(), 1u); + EXPECT_EQ(b.next().c(), 2); +} + +TEST_F(FieldAccessOrderCheckTest, allowsOldDecoderToSkipAddedArrayFieldBeforeGroup) +{ + AddArrayBeforeGroupV1 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + encoder.putD(1, 2, 3, 4); + encoder.bCount(1).next().c(2); + encoder.checkEncodingIsComplete(); + + AddArrayBeforeGroupV0 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddArrayBeforeGroupV1::sbeBlockLength(), + 1, + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + AddArrayBeforeGroupV0::B &b = decoder.b(); + EXPECT_EQ(b.count(), 1u); + EXPECT_EQ(b.next().c(), 2); +} + +TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeAddedBitSetFieldBeforeGroup) +{ + AddBitSetBeforeGroupV1 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1).d().guacamole(true).cheese(true).sourCream(false); + encoder.bCount(1).next().c(2); + encoder.checkEncodingIsComplete(); + + AddBitSetBeforeGroupV1 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddBitSetBeforeGroupV1::sbeBlockLength(), + 0, + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + Flags &d = decoder.d(); + EXPECT_EQ(d.guacamole(), true); + EXPECT_EQ(d.cheese(), true); + EXPECT_EQ(d.sourCream(), false); + AddBitSetBeforeGroupV1::B &b = decoder.b(); + EXPECT_EQ(b.count(), 1u); + EXPECT_EQ(b.next().c(), 2); +} + +TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeMissingBitSetFieldBeforeGroupAsNullValue) +{ + AddBitSetBeforeGroupV0 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1).bCount(1).next().c(2); + encoder.checkEncodingIsComplete(); + + AddBitSetBeforeGroupV1 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddBitSetBeforeGroupV0::sbeBlockLength(), + 0, + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + EXPECT_EQ(decoder.dInActingVersion(), false); + AddBitSetBeforeGroupV1::B &b = decoder.b(); + EXPECT_EQ(b.count(), 1u); + EXPECT_EQ(b.next().c(), 2); +} + +TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToSkipPresentButAddedBitSetFieldBeforeGroup) +{ + AddBitSetBeforeGroupV1 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1).d().guacamole(true).cheese(true).sourCream(false); + encoder.bCount(1).next().c(2); + encoder.checkEncodingIsComplete(); + + AddBitSetBeforeGroupV1 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddBitSetBeforeGroupV1::sbeBlockLength(), + 0, + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + AddBitSetBeforeGroupV1::B &b = decoder.b(); + EXPECT_EQ(b.count(), 1u); + EXPECT_EQ(b.next().c(), 2); +} + +TEST_F(FieldAccessOrderCheckTest, allowsOldDecoderToSkipAddedBitSetFieldBeforeGroup) +{ + AddBitSetBeforeGroupV1 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1).d().guacamole(true).cheese(true).sourCream(false); + encoder.bCount(1).next().c(2); + encoder.checkEncodingIsComplete(); + + AddBitSetBeforeGroupV0 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddBitSetBeforeGroupV1::sbeBlockLength(), + 1, + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + AddBitSetBeforeGroupV0::B &b = decoder.b(); + EXPECT_EQ(b.count(), 1u); + EXPECT_EQ(b.next().c(), 2); +} + +TEST_F(FieldAccessOrderCheckTest, allowsEncodingAndDecodingEnumInsideGroupInSchemaDefinedOrder) +{ + EnumInsideGroup encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(Direction::BUY) + .bCount(1) + .next() + .c(Direction::SELL); + encoder.checkEncodingIsComplete(); + + EnumInsideGroup decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + EnumInsideGroup::sbeBlockLength(), + EnumInsideGroup::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), Direction::BUY); + EnumInsideGroup::B &b = decoder.b(); + EXPECT_EQ(b.count(), 1u); + EXPECT_EQ(b.next().c(), Direction::SELL); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsEncodingEnumInsideGroupBeforeCallingNext) +{ + EnumInsideGroup encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(Direction::BUY); + EnumInsideGroup::B &bEncoder = encoder.bCount(1); + EXPECT_THROW( + { + try + { + bEncoder.c(Direction::SELL); + } + catch (const std::logic_error &e) + { + EXPECT_THAT( + e.what(), + HasSubstr("Cannot access field \"b.c\" in state: V0_B_N") + ); + throw; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsDecodingEnumInsideGroupBeforeCallingNext) +{ + EnumInsideGroup encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(Direction::BUY) + .bCount(1) + .next() + .c(Direction::SELL); + encoder.checkEncodingIsComplete(); + + EnumInsideGroup decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + EnumInsideGroup::sbeBlockLength(), + EnumInsideGroup::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), Direction::BUY); + EnumInsideGroup::B &b = decoder.b(); + EXPECT_EQ(b.count(), 1u); + EXPECT_THROW( + { + try + { + b.c(); + } + catch (const std::logic_error &e) + { + EXPECT_THAT( + e.what(), + HasSubstr("Cannot access field \"b.c\" in state: V0_B_N") + ); + throw; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, allowsReEncodingTopLevelEnum) +{ + EnumInsideGroup encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(Direction::BUY) + .bCount(1) + .next() + .c(Direction::SELL); + + encoder.a(Direction::SELL); + encoder.checkEncodingIsComplete(); + + EnumInsideGroup decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + EnumInsideGroup::sbeBlockLength(), + EnumInsideGroup::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), Direction::SELL); + EnumInsideGroup::B &b = decoder.b(); + EXPECT_EQ(b.count(), 1u); + EXPECT_EQ(b.next().c(), Direction::SELL); +} + +TEST_F(FieldAccessOrderCheckTest, allowsEncodingAndDecodingBitSetInsideGroupInSchemaDefinedOrder) +{ + BitSetInsideGroup encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + Flags &a = encoder.a(); + a.cheese(true).guacamole(true).sourCream(false); + BitSetInsideGroup::B &b = encoder.bCount(1) + .next(); + Flags &c = b.c(); + c.cheese(false).guacamole(false).sourCream(true); + encoder.checkEncodingIsComplete(); + + BitSetInsideGroup decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + BitSetInsideGroup::sbeBlockLength(), + BitSetInsideGroup::sbeSchemaVersion(), + BUFFER_LEN + ); + Flags &aFlags = decoder.a(); + EXPECT_EQ(aFlags.guacamole(), true); + EXPECT_EQ(aFlags.cheese(), true); + EXPECT_EQ(aFlags.sourCream(), false); + BitSetInsideGroup::B &bGroup = decoder.b(); + EXPECT_EQ(bGroup.count(), 1u); + Flags &cFlags = bGroup.next().c(); + EXPECT_EQ(cFlags.guacamole(), false); + EXPECT_EQ(cFlags.cheese(), false); + EXPECT_EQ(cFlags.sourCream(), true); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsEncodingBitSetInsideGroupBeforeCallingNext) +{ + BitSetInsideGroup encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + Flags &a = encoder.a(); + a.cheese(true).guacamole(true).sourCream(false); + BitSetInsideGroup::B &b = encoder.bCount(1); + EXPECT_THROW( + { + try + { + b.c(); + } + catch (const std::logic_error &e) + { + EXPECT_THAT( + e.what(), + HasSubstr("Cannot access field \"b.c\" in state: V0_B_N") + ); + throw; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsDecodingBitSetInsideGroupBeforeCallingNext) +{ + BitSetInsideGroup encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + Flags &a = encoder.a(); + a.cheese(true).guacamole(true).sourCream(false); + BitSetInsideGroup::B &b = encoder.bCount(1) + .next(); + Flags &c = b.c(); + c.cheese(false).guacamole(false).sourCream(true); + encoder.checkEncodingIsComplete(); + + BitSetInsideGroup decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + BitSetInsideGroup::sbeBlockLength(), + BitSetInsideGroup::sbeSchemaVersion(), + BUFFER_LEN + ); + Flags &aFlags = decoder.a(); + EXPECT_EQ(aFlags.guacamole(), true); + EXPECT_EQ(aFlags.cheese(), true); + EXPECT_EQ(aFlags.sourCream(), false); + BitSetInsideGroup::B &bGroup = decoder.b(); + EXPECT_EQ(bGroup.count(), 1u); + EXPECT_THROW( + { + try + { + bGroup.c(); + } + catch (const std::logic_error &e) + { + EXPECT_THAT( + e.what(), + HasSubstr("Cannot access field \"b.c\" in state: V0_B_N") + ); + throw; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, allowsReEncodingTopLevelBitSetViaReWrap) +{ + BitSetInsideGroup encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + Flags &a = encoder.a(); + a.cheese(true).guacamole(true).sourCream(false); + BitSetInsideGroup::B &b = encoder.bCount(1) + .next(); + Flags &c = b.c(); + c.cheese(false).guacamole(false).sourCream(true); + + Flags &aPrime = encoder.a(); + aPrime.sourCream(true); + encoder.checkEncodingIsComplete(); + + BitSetInsideGroup decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + BitSetInsideGroup::sbeBlockLength(), + BitSetInsideGroup::sbeSchemaVersion(), + BUFFER_LEN + ); + Flags &aFlags = decoder.a(); + EXPECT_EQ(aFlags.guacamole(), true); + EXPECT_EQ(aFlags.cheese(), true); + EXPECT_EQ(aFlags.sourCream(), true); + BitSetInsideGroup::B &bGroup = decoder.b(); + EXPECT_EQ(bGroup.count(), 1u); + Flags &cFlags = bGroup.next().c(); + EXPECT_EQ(cFlags.guacamole(), false); + EXPECT_EQ(cFlags.cheese(), false); + EXPECT_EQ(cFlags.sourCream(), true); +} + +TEST_F(FieldAccessOrderCheckTest, allowsEncodingAndDecodingArrayInsideGroupInSchemaDefinedOrder) +{ + ArrayInsideGroup encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.putA(1, 2, 3, 4); + ArrayInsideGroup::B &b = encoder.bCount(1) + .next(); + b.putC(5, 6, 7, 8); + encoder.checkEncodingIsComplete(); + + ArrayInsideGroup decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + ArrayInsideGroup::sbeBlockLength(), + ArrayInsideGroup::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(0), 1); + EXPECT_EQ(decoder.a(1), 2); + EXPECT_EQ(decoder.a(2), 3); + EXPECT_EQ(decoder.a(3), 4); + ArrayInsideGroup::B &bGroup = decoder.b(); + EXPECT_EQ(bGroup.count(), 1u); + bGroup.next(); + EXPECT_EQ(bGroup.c(0), 5); + EXPECT_EQ(bGroup.c(1), 6); + EXPECT_EQ(bGroup.c(2), 7); + EXPECT_EQ(bGroup.c(3), 8); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsEncodingArrayInsideGroupBeforeCallingNext1) +{ + ArrayInsideGroup encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.putA(1, 2, 3, 4); + ArrayInsideGroup::B &bEncoder = encoder.bCount(1); + EXPECT_THROW( + { + try + { + bEncoder.putC(5, 6, 7, 8); + } + catch (const std::logic_error &e) + { + EXPECT_THAT(e.what(), testing::HasSubstr("Cannot access field \"b.c\" in state: V0_B_N")); + throw; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsEncodingArrayInsideGroupBeforeCallingNext2) +{ + ArrayInsideGroup encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.putA(1, 2, 3, 4); + ArrayInsideGroup::B &bEncoder = encoder.bCount(1); + const char c[4] = {5, 6, 7, 8}; + const char *cPtr = c; + EXPECT_THROW( + { + try + { + bEncoder.putC(cPtr); + } + catch (const std::logic_error &e) + { + EXPECT_THAT(e.what(), testing::HasSubstr("Cannot access field \"b.c\" in state: V0_B_N")); + throw; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsDecodingArrayInsideGroupBeforeCallingNext1) +{ + ArrayInsideGroup encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.putA(1, 2, 3, 4); + ArrayInsideGroup::B &bEncoder = encoder.bCount(1); + bEncoder.next(); + bEncoder.putC(5, 6, 7, 8); + encoder.checkEncodingIsComplete(); + + ArrayInsideGroup decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + ArrayInsideGroup::sbeBlockLength(), + ArrayInsideGroup::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(0), 1); + EXPECT_EQ(decoder.a(1), 2); + EXPECT_EQ(decoder.a(2), 3); + EXPECT_EQ(decoder.a(3), 4); + ArrayInsideGroup::B &bGroup = decoder.b(); + EXPECT_EQ(bGroup.count(), 1u); + EXPECT_THROW( + { + try + { + bGroup.c(0); + } + catch (const std::logic_error &e) + { + EXPECT_THAT(e.what(), testing::HasSubstr("Cannot access field \"b.c\" in state: V0_B_N")); + throw; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsDecodingArrayInsideGroupBeforeCallingNext2) +{ + ArrayInsideGroup encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.putA(1, 2, 3, 4); + ArrayInsideGroup::B &bEncoder = encoder.bCount(1); + bEncoder.next(); + bEncoder.putC(5, 6, 7, 8); + encoder.checkEncodingIsComplete(); + + ArrayInsideGroup decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + ArrayInsideGroup::sbeBlockLength(), + ArrayInsideGroup::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(0), 1); + EXPECT_EQ(decoder.a(1), 2); + EXPECT_EQ(decoder.a(2), 3); + EXPECT_EQ(decoder.a(3), 4); + ArrayInsideGroup::B &bGroup = decoder.b(); + EXPECT_EQ(bGroup.count(), 1u); + EXPECT_THROW( + { + try + { + const std::uint64_t charCount = 4; + char c[charCount]; + bGroup.getC(c, charCount); + } + catch (const std::logic_error &e) + { + EXPECT_THAT(e.what(), testing::HasSubstr("Cannot access field \"b.c\" in state: V0_B_N")); + throw; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsDecodingArrayInsideGroupBeforeCallingNext3) +{ + ArrayInsideGroup encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.putA(1, 2, 3, 4); + ArrayInsideGroup::B &bEncoder = encoder.bCount(1); + bEncoder.next(); + bEncoder.putC(5, 6, 7, 8); + encoder.checkEncodingIsComplete(); + + ArrayInsideGroup decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + ArrayInsideGroup::sbeBlockLength(), + ArrayInsideGroup::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(0), 1); + EXPECT_EQ(decoder.a(1), 2); + EXPECT_EQ(decoder.a(2), 3); + EXPECT_EQ(decoder.a(3), 4); + ArrayInsideGroup::B &bGroup = decoder.b(); + EXPECT_EQ(bGroup.count(), 1u); + EXPECT_THROW( + { + try + { + bGroup.c(); + } + catch (const std::logic_error &e) + { + EXPECT_THAT(e.what(), testing::HasSubstr("Cannot access field \"b.c\" in state: V0_B_N")); + throw; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, allowsReEncodingTopLevelArray) +{ + ArrayInsideGroup encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.putA(1, 2, 3, 4); + ArrayInsideGroup::B &bEncoder = encoder.bCount(1); + bEncoder.next(); + bEncoder.putC(5, 6, 7, 8); + + encoder.putA(9, 10, 11, 12); + encoder.checkEncodingIsComplete(); + + ArrayInsideGroup decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + ArrayInsideGroup::sbeBlockLength(), + ArrayInsideGroup::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(0), 9); + EXPECT_EQ(decoder.a(1), 10); + EXPECT_EQ(decoder.a(2), 11); + EXPECT_EQ(decoder.a(3), 12); + ArrayInsideGroup::B &bGroup = decoder.b(); + EXPECT_EQ(bGroup.count(), 1u); + bGroup.next(); + EXPECT_EQ(bGroup.c(0), 5); + EXPECT_EQ(bGroup.c(1), 6); + EXPECT_EQ(bGroup.c(2), 7); + EXPECT_EQ(bGroup.c(3), 8); +} + +TEST_F(FieldAccessOrderCheckTest, allowsEncodingAndDecodingGroupFieldsInSchemaDefinedOrder1) +{ + MultipleGroups encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + encoder.bCount(0); + MultipleGroups::D &dEncoder = encoder.dCount(1); + dEncoder.next(); + dEncoder.e(43); + encoder.checkEncodingIsComplete(); + + MultipleGroups decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + MultipleGroups::sbeBlockLength(), + MultipleGroups::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 42); + MultipleGroups::B &bGroup = decoder.b(); + EXPECT_EQ(bGroup.count(), 0u); + MultipleGroups::D &dGroup = decoder.d(); + EXPECT_EQ(dGroup.count(), 1u); + dGroup.next(); + EXPECT_EQ(dGroup.e(), 43); +} + +TEST_F(FieldAccessOrderCheckTest, allowsEncodingAndDecodingGroupFieldsInSchemaDefinedOrder2) +{ + MultipleGroups encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(41); + MultipleGroups::B &bEncoder = encoder.bCount(1); + bEncoder.next(); + bEncoder.c(42); + MultipleGroups::D &dEncoder = encoder.dCount(1); + dEncoder.next(); + dEncoder.e(43); + encoder.checkEncodingIsComplete(); + + MultipleGroups decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + MultipleGroups::sbeBlockLength(), + MultipleGroups::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 41); + MultipleGroups::B &bGroup = decoder.b(); + EXPECT_EQ(bGroup.count(), 1u); + bGroup.next(); + EXPECT_EQ(bGroup.c(), 42); + MultipleGroups::D &dGroup = decoder.d(); + EXPECT_EQ(dGroup.count(), 1u); + dGroup.next(); + EXPECT_EQ(dGroup.e(), 43); +} + +TEST_F(FieldAccessOrderCheckTest, allowsReEncodingTopLevelPrimitiveFieldsAfterGroups) +{ + MultipleGroups encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(41); + MultipleGroups::B &bEncoder = encoder.bCount(1); + bEncoder.next(); + bEncoder.c(42); + MultipleGroups::D &dEncoder = encoder.dCount(1); + dEncoder.next(); + dEncoder.e(43); + encoder.a(44); + encoder.checkEncodingIsComplete(); + + MultipleGroups decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + MultipleGroups::sbeBlockLength(), + MultipleGroups::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 44); + MultipleGroups::B &bGroup = decoder.b(); + EXPECT_EQ(bGroup.count(), 1u); + bGroup.next(); + EXPECT_EQ(bGroup.c(), 42); + MultipleGroups::D &dGroup = decoder.d(); + EXPECT_EQ(dGroup.count(), 1u); + dGroup.next(); + EXPECT_EQ(dGroup.e(), 43); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsMissedEncodingOfGroupField) +{ + MultipleGroups encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(41); + EXPECT_THROW( + { + try + { + encoder.dCount(0); + } + catch (const std::logic_error &e) + { + EXPECT_THAT( + e.what(), + HasSubstr("Cannot encode count of repeating group \"d\" in state: V0_BLOCK") + ); + throw; + } + }, std::logic_error); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsReEncodingEarlierGroupFields) +{ + MultipleGroups encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(41); + MultipleGroups::B &bEncoder = encoder.bCount(1); + bEncoder.next(); + bEncoder.c(42); + MultipleGroups::D &dEncoder = encoder.dCount(1); + dEncoder.next(); + dEncoder.e(43); + EXPECT_THROW( + { + try + { + encoder.bCount(1); + } + catch (const std::logic_error &e) + { + EXPECT_THAT( + e.what(), + HasSubstr("Cannot encode count of repeating group \"b\" in state: V0_D_1_BLOCK") + ); + throw; + } + }, std::logic_error); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsReEncodingLatestGroupField) +{ + MultipleGroups encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(41); + MultipleGroups::B &bEncoder = encoder.bCount(1); + bEncoder.next(); + bEncoder.c(42); + MultipleGroups::D &dEncoder = encoder.dCount(1); + dEncoder.next(); + dEncoder.e(43); + EXPECT_THROW( + { + try + { + encoder.dCount(1); + } + catch (const std::logic_error &e) + { + EXPECT_THAT( + e.what(), + HasSubstr("Cannot encode count of repeating group \"d\" in state: V0_D_1_BLOCK") + ); + throw; + } + }, std::logic_error); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsMissedDecodingOfGroupField) +{ + MultipleGroups encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(41); + MultipleGroups::B &bEncoder = encoder.bCount(1); + bEncoder.next(); + bEncoder.c(42); + MultipleGroups::D &dEncoder = encoder.dCount(1); + dEncoder.next(); + dEncoder.e(43); + encoder.checkEncodingIsComplete(); + + MultipleGroups decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + MultipleGroups::sbeBlockLength(), + MultipleGroups::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 41); + EXPECT_THROW( + { + try + { + decoder.d(); + } + catch (const std::logic_error &e) + { + EXPECT_THAT( + e.what(), + HasSubstr("Cannot decode count of repeating group \"d\" in state: V0_BLOCK") + ); + throw; + } + }, std::logic_error); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsReDecodingEarlierGroupField) +{ + MultipleGroups encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(41); + MultipleGroups::B &bEncoder = encoder.bCount(1); + bEncoder.next(); + bEncoder.c(42); + MultipleGroups::D &dEncoder = encoder.dCount(1); + dEncoder.next(); + dEncoder.e(43); + encoder.checkEncodingIsComplete(); + + MultipleGroups decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + MultipleGroups::sbeBlockLength(), + MultipleGroups::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 41); + MultipleGroups::B &bGroup = decoder.b(); + EXPECT_EQ(bGroup.count(), 1u); + EXPECT_EQ(bGroup.next().c(), 42); + MultipleGroups::D &dGroup = decoder.d(); + EXPECT_EQ(dGroup.count(), 1u); + EXPECT_EQ(dGroup.next().e(), 43); + EXPECT_THROW( + { + try + { + decoder.b(); + } + catch (const std::logic_error &e) + { + EXPECT_THAT( + e.what(), + HasSubstr("Cannot decode count of repeating group \"b\" in state: V0_D_1_BLOCK") + ); + throw; + } + }, std::logic_error); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsReDecodingLatestGroupField) +{ + MultipleGroups encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(41); + MultipleGroups::B &bEncoder = encoder.bCount(1); + bEncoder.next(); + bEncoder.c(42); + MultipleGroups::D &dEncoder = encoder.dCount(1); + dEncoder.next(); + dEncoder.e(43); + encoder.checkEncodingIsComplete(); + + MultipleGroups decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + MultipleGroups::sbeBlockLength(), + MultipleGroups::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 41); + MultipleGroups::B &bGroup = decoder.b(); + EXPECT_EQ(bGroup.count(), 1u); + EXPECT_EQ(bGroup.next().c(), 42); + MultipleGroups::D &dGroup = decoder.d(); + EXPECT_EQ(dGroup.count(), 1u); + EXPECT_EQ(dGroup.next().e(), 43); + EXPECT_THROW( + { + try + { + decoder.d(); + } + catch (const std::logic_error &e) + { + EXPECT_THAT( + e.what(), + HasSubstr("Cannot decode count of repeating group \"d\" in state: V0_D_1_BLOCK") + ); + throw; + } + }, std::logic_error); +} + +TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeAddedVarData) +{ + AddVarDataV1 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + encoder.putB("abc"); + encoder.checkEncodingIsComplete(); + + AddVarDataV1 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddVarDataV1::sbeBlockLength(), + AddVarDataV1::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 42); + EXPECT_EQ(decoder.getBAsString(), "abc"); +} + +TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeMissingAddedVarDataAsNullValue1) +{ + AddVarDataV0 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + encoder.checkEncodingIsComplete(); + + AddVarDataV1 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddVarDataV1::sbeBlockLength(), + 0, + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 42); + EXPECT_EQ(decoder.b(), nullptr); +} + +TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeMissingAddedVarDataAsNullValue2) +{ + AddVarDataV0 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + encoder.checkEncodingIsComplete(); + + AddVarDataV1 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddVarDataV1::sbeBlockLength(), + 0, + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 42); + EXPECT_EQ(decoder.getBAsString(), ""); +} + +TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeMissingAddedVarDataAsNullValue3) +{ + AddVarDataV0 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + encoder.checkEncodingIsComplete(); + + AddVarDataV1 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddVarDataV1::sbeBlockLength(), + 0, + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 42); + EXPECT_EQ(decoder.getBAsJsonEscapedString(), ""); +} + +TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeMissingAddedVarDataAsNullValue4) +{ + AddVarDataV0 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + encoder.checkEncodingIsComplete(); + + AddVarDataV1 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddVarDataV1::sbeBlockLength(), + 0, + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 42); + char b[16]; + EXPECT_EQ(decoder.getB(b, 16), 0u); +} + +TEST_F(FieldAccessOrderCheckTest, allowsEncodingAndDecodingAsciiInsideGroupInSchemaDefinedOrder1) +{ + AsciiInsideGroup encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.putA("GBPUSD"); + AsciiInsideGroup::B &bGroup = encoder.bCount(1); + bGroup.next().putC("EURUSD"); + encoder.checkEncodingIsComplete(); + + AsciiInsideGroup decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AsciiInsideGroup::sbeBlockLength(), + AsciiInsideGroup::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.getAAsString(), "GBPUSD"); + AsciiInsideGroup::B &b = decoder.b(); + EXPECT_EQ(b.count(), 1u); + EXPECT_EQ(b.next().getCAsString(), "EURUSD"); +} + +TEST_F(FieldAccessOrderCheckTest, allowsEncodingAndDecodingAsciiInsideGroupInSchemaDefinedOrder2) +{ + AsciiInsideGroup encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + std::string a("GBPUSD"); + encoder.putA(a); + AsciiInsideGroup::B &bGroup = encoder.bCount(1); + bGroup.next() + .c(0, 'E') + .c(1, 'U') + .c(2, 'R') + .c(3, 'U') + .c(4, 'S') + .c(5, 'D'); + encoder.checkEncodingIsComplete(); + + AsciiInsideGroup decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AsciiInsideGroup::sbeBlockLength(), + AsciiInsideGroup::sbeSchemaVersion(), + BUFFER_LEN + ); + char aOut[6]; + decoder.getA(aOut, 6); + EXPECT_STREQ(aOut, "GBPUSD"); + AsciiInsideGroup::B &b = decoder.b(); + EXPECT_EQ(b.count(), 1u); + EXPECT_EQ(b.next().c(0), 'E'); + EXPECT_EQ(b.c(1), 'U'); + EXPECT_EQ(b.c(2), 'R'); + EXPECT_EQ(b.c(3), 'U'); + EXPECT_EQ(b.c(4), 'S'); + EXPECT_EQ(b.c(5), 'D'); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsEncodingAsciiInsideGroupBeforeCallingNext1) +{ + AsciiInsideGroup encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.putA("GBPUSD"); + AsciiInsideGroup::B &bGroup = encoder.bCount(1); + EXPECT_THROW( + { + try + { + bGroup.putC("EURUSD"); + } + catch (const std::logic_error &e) + { + EXPECT_THAT( + e.what(), + HasSubstr("Cannot access field \"b.c\" in state: V0_B_N") + ); + throw; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsEncodingAsciiInsideGroupBeforeCallingNext2) +{ + AsciiInsideGroup encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.putA("GBPUSD"); + AsciiInsideGroup::B &bGroup = encoder.bCount(1); + EXPECT_THROW( + { + try + { + const std::string c("EURUSD"); + bGroup.putC(c); + } + catch (const std::logic_error &e) + { + EXPECT_THAT( + e.what(), + HasSubstr("Cannot access field \"b.c\" in state: V0_B_N") + ); + throw; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsEncodingAsciiInsideGroupBeforeCallingNext3) +{ + AsciiInsideGroup encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.putA("GBPUSD"); + AsciiInsideGroup::B &bGroup = encoder.bCount(1); + EXPECT_THROW( + { + try + { + bGroup.c(0, 'E') + .c(1, 'U') + .c(2, 'R') + .c(3, 'U') + .c(4, 'S') + .c(5, 'D'); + } + catch (const std::logic_error &e) + { + EXPECT_THAT( + e.what(), + HasSubstr("Cannot access field \"b.c\" in state: V0_B_N") + ); + throw; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsDecodingAsciiInsideGroupBeforeCallingNext1) +{ + AsciiInsideGroup encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.putA("GBPUSD"); + AsciiInsideGroup::B &bGroup = encoder.bCount(1); + bGroup.next().putC("EURUSD"); + encoder.checkEncodingIsComplete(); + + AsciiInsideGroup decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AsciiInsideGroup::sbeBlockLength(), + AsciiInsideGroup::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.getAAsString(), "GBPUSD"); + AsciiInsideGroup::B &b = decoder.b(); + EXPECT_EQ(b.count(), 1u); + EXPECT_THROW( + { + try + { + b.getCAsString(); + } + catch (const std::logic_error &e) + { + EXPECT_THAT( + e.what(), + HasSubstr("Cannot access field \"b.c\" in state: V0_B_N") + ); + throw; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsDecodingAsciiInsideGroupBeforeCallingNext2) +{ + AsciiInsideGroup encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.putA("GBPUSD"); + AsciiInsideGroup::B &bGroup = encoder.bCount(1); + bGroup.next().putC("EURUSD"); + encoder.checkEncodingIsComplete(); + + AsciiInsideGroup decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AsciiInsideGroup::sbeBlockLength(), + AsciiInsideGroup::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.getAAsString(), "GBPUSD"); + AsciiInsideGroup::B &b = decoder.b(); + EXPECT_EQ(b.count(), 1u); + EXPECT_THROW( + { + try + { + b.c(); + } + catch (const std::logic_error &e) + { + EXPECT_THAT( + e.what(), + HasSubstr("Cannot access field \"b.c\" in state: V0_B_N") + ); + throw; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsDecodingAsciiInsideGroupBeforeCallingNext3) +{ + AsciiInsideGroup encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.putA("GBPUSD"); + AsciiInsideGroup::B &bGroup = encoder.bCount(1); + bGroup.next().putC("EURUSD"); + encoder.checkEncodingIsComplete(); + + AsciiInsideGroup decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AsciiInsideGroup::sbeBlockLength(), + AsciiInsideGroup::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.getAAsString(), "GBPUSD"); + AsciiInsideGroup::B &b = decoder.b(); + EXPECT_EQ(b.count(), 1u); + EXPECT_THROW( + { + try + { + char c[6]; + b.getC(c, 6); + } + catch (const std::logic_error &e) + { + EXPECT_THAT( + e.what(), + HasSubstr("Cannot access field \"b.c\" in state: V0_B_N") + ); + throw; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsDecodingAsciiInsideGroupBeforeCallingNext4) +{ + AsciiInsideGroup encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.putA("GBPUSD"); + AsciiInsideGroup::B &bGroup = encoder.bCount(1); + bGroup.next().putC("EURUSD"); + encoder.checkEncodingIsComplete(); + + AsciiInsideGroup decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AsciiInsideGroup::sbeBlockLength(), + AsciiInsideGroup::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.getAAsString(), "GBPUSD"); + AsciiInsideGroup::B &b = decoder.b(); + EXPECT_EQ(b.count(), 1u); + EXPECT_THROW( + { + try + { + b.c(0); + } + catch (const std::logic_error &e) + { + EXPECT_THAT( + e.what(), + HasSubstr("Cannot access field \"b.c\" in state: V0_B_N") + ); + throw; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsDecodingAsciiInsideGroupBeforeCallingNext5) +{ + AsciiInsideGroup encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.putA("GBPUSD"); + AsciiInsideGroup::B &bGroup = encoder.bCount(1); + bGroup.next().putC("EURUSD"); + encoder.checkEncodingIsComplete(); + + AsciiInsideGroup decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AsciiInsideGroup::sbeBlockLength(), + AsciiInsideGroup::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.getAAsString(), "GBPUSD"); + AsciiInsideGroup::B &b = decoder.b(); + EXPECT_EQ(b.count(), 1u); + EXPECT_THROW( + { + try + { + b.getCAsJsonEscapedString(); + } + catch (const std::logic_error &e) + { + EXPECT_THAT( + e.what(), + HasSubstr("Cannot access field \"b.c\" in state: V0_B_N") + ); + throw; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, allowsReEncodingTopLevelAscii) +{ + AsciiInsideGroup encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.putA("GBPUSD"); + AsciiInsideGroup::B &bGroup = encoder.bCount(1); + bGroup.next().putC("EURUSD"); + + encoder.putA("CADUSD"); + encoder.checkEncodingIsComplete(); + + AsciiInsideGroup decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AsciiInsideGroup::sbeBlockLength(), + AsciiInsideGroup::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.getAAsString(), "CADUSD"); + AsciiInsideGroup::B &b = decoder.b(); + EXPECT_EQ(b.count(), 1u); + b.next(); + EXPECT_EQ(b.getCAsString(), "EURUSD"); +} + +TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeAddedAsciiFieldBeforeGroup1) +{ + AddAsciiBeforeGroupV1 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + encoder.putD("EURUSD"); + AddAsciiBeforeGroupV1::B &bGroup = encoder.bCount(1); + bGroup.next().c(2); + encoder.checkEncodingIsComplete(); + + AddAsciiBeforeGroupV1 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddAsciiBeforeGroupV1::sbeBlockLength(), + AddAsciiBeforeGroupV1::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + EXPECT_EQ(decoder.getDAsString(), "EURUSD"); + AddAsciiBeforeGroupV1::B &b = decoder.b(); + EXPECT_EQ(b.count(), 1u); + EXPECT_EQ(b.next().c(), 2); +} + +TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeAddedAsciiFieldBeforeGroup2) +{ + AddAsciiBeforeGroupV1 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + encoder.putD("EURUSD"); + AddAsciiBeforeGroupV1::B &bGroup = encoder.bCount(1); + bGroup.next().c(2); + encoder.checkEncodingIsComplete(); + + AddAsciiBeforeGroupV1 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddAsciiBeforeGroupV1::sbeBlockLength(), + AddAsciiBeforeGroupV1::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + const char *d = decoder.d(); + const std::string dAsString(d, 6); + ASSERT_EQ(dAsString, "EURUSD"); + AddAsciiBeforeGroupV1::B &b = decoder.b(); + EXPECT_EQ(b.count(), 1u); + EXPECT_EQ(b.next().c(), 2); +} + +TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeAddedAsciiFieldBeforeGroup3) +{ + AddAsciiBeforeGroupV1 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + encoder.putD("EURUSD"); + AddAsciiBeforeGroupV1::B &bGroup = encoder.bCount(1); + bGroup.next().c(2); + encoder.checkEncodingIsComplete(); + + AddAsciiBeforeGroupV1 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddAsciiBeforeGroupV1::sbeBlockLength(), + AddAsciiBeforeGroupV1::sbeSchemaVersion(), + BUFFER_LEN + ); + const AddAsciiBeforeGroupV1 &constDecoder = decoder; + EXPECT_EQ(constDecoder.a(), 1); + const char *d = constDecoder.d(); + const std::string dAsString(d, 6); + ASSERT_EQ(dAsString, "EURUSD"); + AddAsciiBeforeGroupV1::B &b = decoder.b(); + EXPECT_EQ(b.count(), 1u); + EXPECT_EQ(b.next().c(), 2); +} + +TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeAddedAsciiFieldBeforeGroup4) +{ + AddAsciiBeforeGroupV1 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + encoder.putD("EURUSD"); + AddAsciiBeforeGroupV1::B &bGroup = encoder.bCount(1); + bGroup.next().c(2); + encoder.checkEncodingIsComplete(); + + AddAsciiBeforeGroupV1 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddAsciiBeforeGroupV1::sbeBlockLength(), + AddAsciiBeforeGroupV1::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + char d[6]; + decoder.getD(d, 6); + const std::string dAsString(d, 6); + ASSERT_EQ(dAsString, "EURUSD"); + AddAsciiBeforeGroupV1::B &b = decoder.b(); + EXPECT_EQ(b.count(), 1u); + EXPECT_EQ(b.next().c(), 2); +} + +TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeAddedAsciiFieldBeforeGroup5) +{ + AddAsciiBeforeGroupV1 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + encoder.putD("EURUSD"); + AddAsciiBeforeGroupV1::B &bGroup = encoder.bCount(1); + bGroup.next().c(2); + encoder.checkEncodingIsComplete(); + + AddAsciiBeforeGroupV1 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddAsciiBeforeGroupV1::sbeBlockLength(), + AddAsciiBeforeGroupV1::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + ASSERT_EQ(decoder.d(0), 'E'); + ASSERT_EQ(decoder.d(1), 'U'); + ASSERT_EQ(decoder.d(2), 'R'); + ASSERT_EQ(decoder.d(3), 'U'); + ASSERT_EQ(decoder.d(4), 'S'); + ASSERT_EQ(decoder.d(5), 'D'); + AddAsciiBeforeGroupV1::B &b = decoder.b(); + EXPECT_EQ(b.count(), 1u); + EXPECT_EQ(b.next().c(), 2); +} + +TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeAddedAsciiFieldBeforeGroup6) +{ + AddAsciiBeforeGroupV1 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + encoder.putD("EURUSD"); + AddAsciiBeforeGroupV1::B &bGroup = encoder.bCount(1); + bGroup.next().c(2); + encoder.checkEncodingIsComplete(); + + AddAsciiBeforeGroupV1 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddAsciiBeforeGroupV1::sbeBlockLength(), + AddAsciiBeforeGroupV1::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + ASSERT_EQ(decoder.dLength(), 6u); + AddAsciiBeforeGroupV1::B &b = decoder.b(); + EXPECT_EQ(b.count(), 1u); + EXPECT_EQ(b.next().c(), 2); +} + +TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeMissingAsciiFieldBeforeGroupAsNullValue1) +{ + AddAsciiBeforeGroupV0 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + AddAsciiBeforeGroupV0::B &bGroup = encoder.bCount(1); + bGroup.next().c(2); + encoder.checkEncodingIsComplete(); + + AddAsciiBeforeGroupV1 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddAsciiBeforeGroupV0::sbeBlockLength(), + 0, + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + ASSERT_EQ(decoder.d(0), AddAsciiBeforeGroupV1::dNullValue()); + ASSERT_EQ(decoder.d(1), AddAsciiBeforeGroupV1::dNullValue()); + ASSERT_EQ(decoder.d(2), AddAsciiBeforeGroupV1::dNullValue()); + ASSERT_EQ(decoder.d(3), AddAsciiBeforeGroupV1::dNullValue()); + ASSERT_EQ(decoder.d(4), AddAsciiBeforeGroupV1::dNullValue()); + ASSERT_EQ(decoder.d(5), AddAsciiBeforeGroupV1::dNullValue()); + AddAsciiBeforeGroupV1::B &b = decoder.b(); + EXPECT_EQ(b.count(), 1u); + EXPECT_EQ(b.next().c(), 2); +} + +TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeMissingAsciiFieldBeforeGroupAsNullValue2) +{ + AddAsciiBeforeGroupV0 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + AddAsciiBeforeGroupV0::B &bGroup = encoder.bCount(1); + bGroup.next().c(2); + encoder.checkEncodingIsComplete(); + + AddAsciiBeforeGroupV1 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddAsciiBeforeGroupV0::sbeBlockLength(), + 0, + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + char d[6]; + ASSERT_EQ(decoder.getD(d, 6), 0u); + AddAsciiBeforeGroupV1::B &b = decoder.b(); + EXPECT_EQ(b.count(), 1u); + EXPECT_EQ(b.next().c(), 2); +} + +TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeMissingAsciiFieldBeforeGroupAsNullValue3) +{ + AddAsciiBeforeGroupV0 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + AddAsciiBeforeGroupV0::B &bGroup = encoder.bCount(1); + bGroup.next().c(2); + encoder.checkEncodingIsComplete(); + + AddAsciiBeforeGroupV1 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddAsciiBeforeGroupV0::sbeBlockLength(), + 0, + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + ASSERT_EQ(decoder.getDAsString(), ""); + AddAsciiBeforeGroupV1::B &b = decoder.b(); + EXPECT_EQ(b.count(), 1u); + EXPECT_EQ(b.next().c(), 2); +} + +TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToSkipPresentButAddedAsciiFieldBeforeGroup) +{ + AddAsciiBeforeGroupV1 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + encoder.putD("EURUSD"); + AddAsciiBeforeGroupV1::B &bGroup = encoder.bCount(1); + bGroup.next().c(2); + encoder.checkEncodingIsComplete(); + + AddAsciiBeforeGroupV1 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddAsciiBeforeGroupV1::sbeBlockLength(), + AddAsciiBeforeGroupV1::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + AddAsciiBeforeGroupV1::B &b = decoder.b(); + EXPECT_EQ(b.count(), 1u); + EXPECT_EQ(b.next().c(), 2); +} + +TEST_F(FieldAccessOrderCheckTest, allowsOldDecoderToSkipAddedAsciiFieldBeforeGroup) +{ + AddAsciiBeforeGroupV1 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + encoder.putD("EURUSD"); + AddAsciiBeforeGroupV1::B &bGroup = encoder.bCount(1); + bGroup.next().c(2); + encoder.checkEncodingIsComplete(); + + AddAsciiBeforeGroupV0 decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + AddAsciiBeforeGroupV1::sbeBlockLength(), + AddAsciiBeforeGroupV1::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + AddAsciiBeforeGroupV0::B &b = decoder.b(); + EXPECT_EQ(b.count(), 1u); + EXPECT_EQ(b.next().c(), 2); +} + +TEST_F(FieldAccessOrderCheckTest, allowsEncodeAndDecodeOfMessagesWithNoBlock) +{ + NoBlock encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.putA("abc"); + encoder.checkEncodingIsComplete(); + + NoBlock decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + NoBlock::sbeBlockLength(), + NoBlock::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.getAAsString(), "abc"); +} + +TEST_F(FieldAccessOrderCheckTest, allowsEncodeAndDecodeOfGroupsWithNoBlock) +{ + GroupWithNoBlock encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + GroupWithNoBlock::A &aGroup = encoder.aCount(1); + aGroup.next().putB("abc"); + encoder.checkEncodingIsComplete(); + + GroupWithNoBlock decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + GroupWithNoBlock::sbeBlockLength(), + GroupWithNoBlock::sbeSchemaVersion(), + BUFFER_LEN + ); + GroupWithNoBlock::A &a = decoder.a(); + EXPECT_EQ(a.count(), 1u); + EXPECT_EQ(a.next().getBAsString(), "abc"); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsEncodingElementOfEmptyGroup1) +{ + MultipleGroups encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + MultipleGroups::B &bGroup = encoder.bCount(0); + MultipleGroups::D &dGroup = encoder.dCount(1); + dGroup.next().e(43); + + EXPECT_THROW( + { + try + { + bGroup.c(44); + } + catch (const std::logic_error &e) + { + EXPECT_THAT(e.what(), testing::HasSubstr("Cannot access field \"b.c\" in state: V0_D_1_BLOCK")); + throw e; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsEncodingElementOfEmptyGroup2) +{ + NestedGroups encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + NestedGroups::B &bGroup = encoder.bCount(1); + bGroup.next(); + bGroup.c(43); + NestedGroups::B::D &dGroup = bGroup.dCount(0); + bGroup.fCount(0); + encoder.hCount(0); + + EXPECT_THROW( + { + try + { + dGroup.e(44); + } + catch (const std::logic_error &e) + { + EXPECT_THAT(e.what(), testing::HasSubstr("Cannot access field \"b.d.e\" in state: V0_H_0")); + throw e; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsEncodingElementOfEmptyGroup3) +{ + NestedGroups encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + NestedGroups::B &bGroup = encoder.bCount(1); + bGroup.next(); + bGroup.c(43); + NestedGroups::B::D &dGroup = bGroup.dCount(0); + bGroup.fCount(0); + + EXPECT_THROW( + { + try + { + dGroup.e(44); + } + catch (const std::logic_error &e) + { + EXPECT_THAT(e.what(), testing::HasSubstr("Cannot access field \"b.d.e\" in state: V0_B_1_F_0")); + throw e; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsEncodingElementOfEmptyGroup4) +{ + AddPrimitiveInsideGroupV1 encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + AddPrimitiveInsideGroupV1::B &bGroup = encoder.bCount(0); + + EXPECT_THROW( + { + try + { + bGroup.c(43); + } + catch (const std::logic_error &e) + { + EXPECT_THAT(e.what(), testing::HasSubstr("Cannot access field \"b.c\" in state: V1_B_0")); + throw e; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsEncodingElementOfEmptyGroup5) +{ + GroupAndVarLength encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + GroupAndVarLength::B &bGroup = encoder.bCount(0); + encoder.putD("abc"); + + EXPECT_THROW( + { + try + { + bGroup.c(43); + } + catch (const std::logic_error &e) + { + EXPECT_THAT(e.what(), testing::HasSubstr("Cannot access field \"b.c\" in state: V0_D_DONE")); + throw e; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, allowsEncodingAndDecodingNestedGroupWithVarDataInSchemaDefinedOrder) +{ + NestedGroupWithVarLength encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + NestedGroupWithVarLength::B &bGroup = encoder.bCount(3); + bGroup.next().c(2).dCount(0); + bGroup.next().c(3).dCount(1).next().e(4).putF("abc"); + bGroup.next().c(5).dCount(2).next().e(6).putF("def").next().e(7).putF("ghi"); + encoder.checkEncodingIsComplete(); + + NestedGroupWithVarLength decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + NestedGroupWithVarLength::sbeBlockLength(), + NestedGroupWithVarLength::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + NestedGroupWithVarLength::B &bDecoder = decoder.b(); + EXPECT_EQ(bDecoder.count(), 3u); + EXPECT_EQ(bDecoder.next().c(), 2); + NestedGroupWithVarLength::B::D &dDecoder1 = bDecoder.d(); + EXPECT_EQ(dDecoder1.count(), 0u); + EXPECT_EQ(bDecoder.next().c(), 3); + NestedGroupWithVarLength::B::D &dDecoder2 = bDecoder.d(); + EXPECT_EQ(dDecoder2.count(), 1u); + EXPECT_EQ(dDecoder2.next().e(), 4); + EXPECT_EQ(dDecoder2.getFAsString(), "abc"); + EXPECT_EQ(bDecoder.next().c(), 5); + NestedGroupWithVarLength::B::D &dDecoder3 = bDecoder.d(); + EXPECT_EQ(dDecoder3.count(), 2u); + EXPECT_EQ(dDecoder3.next().e(), 6); + EXPECT_EQ(dDecoder3.getFAsString(), "def"); + EXPECT_EQ(dDecoder3.next().e(), 7); + EXPECT_EQ(dDecoder3.getFAsString(), "ghi"); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsMissedEncodingOfVarLengthFieldInNestedGroupToNextInnerElement1) +{ + NestedGroupWithVarLength encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + NestedGroupWithVarLength::B &bGroup = encoder.bCount(1); + NestedGroupWithVarLength::B::D &dGroup = bGroup.next().c(5).dCount(2); + dGroup.next().e(7); + + EXPECT_THROW( + { + try + { + dGroup.next(); + } + catch (const std::logic_error &e) + { + EXPECT_THAT(e.what(), + testing::HasSubstr( + "Cannot access next element in repeating group \"b.d\" in state: V0_B_1_D_N_BLOCK")); + EXPECT_THAT(e.what(), + testing::HasSubstr( + "Expected one of these transitions: [\"b.d.e(?)\", \"b.d.fLength()\", \"b.d.f(?)\"].")); + throw e; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsMissedEncodingOfVarLengthFieldInNestedGroupToNextInnerElement2) +{ + NestedGroupWithVarLength encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + NestedGroupWithVarLength::B &bGroup = encoder.bCount(2); + NestedGroupWithVarLength::B::D &dGroup = bGroup.next().c(5).dCount(2); + dGroup.next().e(7); + + EXPECT_THROW( + { + try + { + dGroup.next(); + } + catch (const std::logic_error &e) + { + EXPECT_THAT(e.what(), + testing::HasSubstr( + "Cannot access next element in repeating group \"b.d\" in state: V0_B_N_D_N_BLOCK")); + EXPECT_THAT(e.what(), + testing::HasSubstr( + "Expected one of these transitions: [\"b.d.e(?)\", \"b.d.fLength()\", \"b.d.f(?)\"].")); + throw e; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsMissedDecodingOfVarLengthFieldInNestedGroupToNextInnerElement1) +{ + NestedGroupWithVarLength encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + NestedGroupWithVarLength::B &bGroup = encoder.bCount(1); + NestedGroupWithVarLength::B::D &dGroup = bGroup.next().c(2).dCount(2); + dGroup.next().e(3).putF("abc"); + dGroup.next().e(4).putF("def"); + encoder.checkEncodingIsComplete(); + + NestedGroupWithVarLength decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + NestedGroupWithVarLength::sbeBlockLength(), + NestedGroupWithVarLength::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + NestedGroupWithVarLength::B &bDecoder = decoder.b(); + EXPECT_EQ(bDecoder.count(), 1u); + EXPECT_EQ(bDecoder.next().c(), 2); + NestedGroupWithVarLength::B::D &dDecoder = bDecoder.d(); + EXPECT_EQ(dDecoder.count(), 2u); + EXPECT_EQ(dDecoder.next().e(), 3); + + EXPECT_THROW( + { + try + { + dDecoder.next(); + } + catch (const std::logic_error &e) + { + EXPECT_THAT(e.what(), + testing::HasSubstr( + "Cannot access next element in repeating group \"b.d\" in state: V0_B_1_D_N_BLOCK.")); + EXPECT_THAT(e.what(), + testing::HasSubstr( + "Expected one of these transitions: [\"b.d.e(?)\", \"b.d.fLength()\", \"b.d.f(?)\"].")); + throw e; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsMissedDecodingOfVarLengthFieldInNestedGroupToNextInnerElement2) +{ + NestedGroupWithVarLength encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + NestedGroupWithVarLength::B &bGroup = encoder.bCount(2); + NestedGroupWithVarLength::B::D &dGroup = bGroup.next().c(2).dCount(2); + dGroup.next().e(3).putF("abc"); + dGroup.next().e(4).putF("def"); + bGroup.next().c(5).dCount(0); + encoder.checkEncodingIsComplete(); + + NestedGroupWithVarLength decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + NestedGroupWithVarLength::sbeBlockLength(), + NestedGroupWithVarLength::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + NestedGroupWithVarLength::B &bDecoder = decoder.b(); + EXPECT_EQ(bDecoder.count(), 2u); + EXPECT_EQ(bDecoder.next().c(), 2); + NestedGroupWithVarLength::B::D &dDecoder = bDecoder.d(); + EXPECT_EQ(dDecoder.count(), 2u); + EXPECT_EQ(dDecoder.next().e(), 3); + + EXPECT_THROW( + { + try + { + dDecoder.next(); + } + catch (const std::logic_error &e) + { + EXPECT_THAT(e.what(), + testing::HasSubstr( + "Cannot access next element in repeating group \"b.d\" in state: V0_B_N_D_N_BLOCK.")); + EXPECT_THAT(e.what(), + testing::HasSubstr( + "Expected one of these transitions: [\"b.d.e(?)\", \"b.d.fLength()\", \"b.d.f(?)\"].")); + throw e; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsMissedDecodingOfVarLengthFieldInNestedGroupToNextOuterElement1) +{ + NestedGroupWithVarLength encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + NestedGroupWithVarLength::B &bGroup = encoder.bCount(2); + NestedGroupWithVarLength::B::D &dGroup = bGroup.next().c(2).dCount(2); + dGroup.next().e(3).putF("abc"); + dGroup.next().e(4).putF("def"); + bGroup.next().c(5).dCount(0); + encoder.checkEncodingIsComplete(); + + NestedGroupWithVarLength decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + NestedGroupWithVarLength::sbeBlockLength(), + NestedGroupWithVarLength::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + NestedGroupWithVarLength::B &bDecoder = decoder.b(); + EXPECT_EQ(bDecoder.count(), 2u); + EXPECT_EQ(bDecoder.next().c(), 2); + NestedGroupWithVarLength::B::D &dDecoder = bDecoder.d(); + EXPECT_EQ(dDecoder.count(), 2u); + EXPECT_EQ(dDecoder.next().e(), 3); + + EXPECT_THROW( + { + try + { + bDecoder.next(); + } + catch (const std::logic_error &e) + { + EXPECT_THAT(e.what(), + testing::HasSubstr( + "Cannot access next element in repeating group \"b\" in state: V0_B_N_D_N_BLOCK.")); + EXPECT_THAT(e.what(), + testing::HasSubstr( + "Expected one of these transitions: [\"b.d.e(?)\", \"b.d.fLength()\", \"b.d.f(?)\"].")); + throw e; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsMissedDecodingOfVarLengthFieldInNestedGroupToNextOuterElement2) +{ + NestedGroupWithVarLength encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + NestedGroupWithVarLength::B &bGroup = encoder.bCount(2); + NestedGroupWithVarLength::B::D &dGroup = bGroup.next().c(2).dCount(1); + dGroup.next().e(3).putF("abc"); + bGroup.next().c(5).dCount(0); + encoder.checkEncodingIsComplete(); + + NestedGroupWithVarLength decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + NestedGroupWithVarLength::sbeBlockLength(), + NestedGroupWithVarLength::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 1); + NestedGroupWithVarLength::B &bDecoder = decoder.b(); + EXPECT_EQ(bDecoder.count(), 2u); + EXPECT_EQ(bDecoder.next().c(), 2); + NestedGroupWithVarLength::B::D &dDecoder = bDecoder.d(); + EXPECT_EQ(dDecoder.count(), 1u); + EXPECT_EQ(dDecoder.next().e(), 3); + + EXPECT_THROW( + { + try + { + bDecoder.next(); + } + catch (const std::logic_error &e) + { + EXPECT_THAT(e.what(), + testing::HasSubstr( + "Cannot access next element in repeating group \"b\" in state: V0_B_N_D_1_BLOCK.")); + EXPECT_THAT(e.what(), + testing::HasSubstr( + "Expected one of these transitions: [\"b.d.e(?)\", \"b.d.fLength()\", \"b.d.f(?)\"].")); + throw e; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsIncompleteMessagesDueToMissingVarLengthField1) +{ + MultipleVarLength encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1).putB("abc"); + + EXPECT_THROW( + { + try + { + encoder.checkEncodingIsComplete(); + } + catch (const std::logic_error &e) + { + EXPECT_THAT(e.what(), + testing::HasSubstr( + std::string("Not fully encoded, current state: V0_B_DONE, ") + + "allowed transitions: \"cLength()\", \"c(?)\"")); + throw e; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsIncompleteMessagesDueToMissingVarLengthField2) +{ + NoBlock encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + + EXPECT_THROW( + { + try + { + encoder.checkEncodingIsComplete(); + } + catch (const std::logic_error &e) + { + EXPECT_THAT(e.what(), + testing::HasSubstr( + std::string("Not fully encoded, current state: V0_BLOCK, ") + + "allowed transitions: \"aLength()\", \"a(?)\"")); + throw e; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsIncompleteMessagesDueToMissingTopLevelGroup1) +{ + MultipleGroups encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1).bCount(0); + + EXPECT_THROW( + { + try + { + encoder.checkEncodingIsComplete(); + } + catch (const std::logic_error &e) + { + EXPECT_THAT(e.what(), testing::HasSubstr( + "Not fully encoded, current state: V0_B_0, allowed transitions: \"dCount(0)\", \"dCount(>0)\"")); + throw e; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsIncompleteMessagesDueToMissingTopLevelGroup2) +{ + MultipleGroups encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1).bCount(1).next().c(2); + + EXPECT_THROW( + { + try + { + encoder.checkEncodingIsComplete(); + } + catch (const std::logic_error &e) + { + EXPECT_THAT(e.what(), testing::HasSubstr( + "Not fully encoded, current state: V0_B_1_BLOCK, allowed transitions: " + "\"b.c(?)\", \"dCount(0)\", \"dCount(>0)\"")); + throw e; + } + }, + std::logic_error + ); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsIncompleteMessagesDueToMissingTopLevelGroup3) +{ + MultipleGroups encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1); + + EXPECT_THROW( + { + try + { + encoder.checkEncodingIsComplete(); + } + catch (const std::logic_error &e) + { + EXPECT_THAT(e.what(), testing::HasSubstr( + "Not fully encoded, current state: V0_BLOCK, allowed transitions: " + "\"a(?)\", \"bCount(0)\", \"bCount(>0)\"")); + throw e; + } + }, + std::logic_error + ); +} + +class DisallowsIncompleteMessagesPart1Test : public FieldAccessOrderCheckTest, + public testing::WithParamInterface> +{ +}; + +TEST_P(DisallowsIncompleteMessagesPart1Test, disallowsIncompleteMessagesDueToMissingNestedGroup1) +{ + const auto bCount = std::get<0>(GetParam()); + const auto expectedState = std::get<1>(GetParam()); + + NestedGroupWithVarLength encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1).bCount(bCount).next().c(2); + + EXPECT_THROW( + { + try + { + encoder.checkEncodingIsComplete(); + } + catch (const std::logic_error &e) + { + EXPECT_THAT(e.what(), testing::HasSubstr( + "Not fully encoded, current state: " + expectedState)); + throw e; + } + }, + std::logic_error + ); +} + +INSTANTIATE_TEST_SUITE_P( + FieldAccessOrderCheckTest, + DisallowsIncompleteMessagesPart1Test, + testing::Values( + std::make_tuple(1, "V0_B_1_BLOCK"), + std::make_tuple(2, "V0_B_N_BLOCK") + ) +); + +class DisallowsIncompleteMessagesPart2Test : public FieldAccessOrderCheckTest, + public testing::WithParamInterface> +{ +}; + +TEST_P(DisallowsIncompleteMessagesPart2Test, disallowsIncompleteMessagesDueToMissingNestedGroup2) +{ + const auto bCount = std::get<0>(GetParam()); + const auto dCount = std::get<1>(GetParam()); + const auto expectedState = std::get<2>(GetParam()); + + NestedGroupWithVarLength encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1).bCount(bCount).next().c(2).dCount(dCount); + + EXPECT_THROW( + { + try + { + encoder.checkEncodingIsComplete(); + } + catch (const std::logic_error &e) + { + EXPECT_THAT(e.what(), testing::HasSubstr( + "Not fully encoded, current state: " + expectedState)); + throw e; + } + }, + std::logic_error + ); +} + +INSTANTIATE_TEST_SUITE_P( + FieldAccessOrderCheckTest, + DisallowsIncompleteMessagesPart2Test, + testing::Values( + std::make_tuple(1, 1, "V0_B_1_D_N"), + std::make_tuple(1, 2, "V0_B_1_D_N"), + std::make_tuple(2, 0, "V0_B_N_D_0"), + std::make_tuple(2, 1, "V0_B_N_D_N"), + std::make_tuple(2, 2, "V0_B_N_D_N") + ) +); + +class DisallowsIncompleteMessagesPart3Test : public FieldAccessOrderCheckTest, + public testing::WithParamInterface> +{ +}; + +TEST_P(DisallowsIncompleteMessagesPart3Test, disallowsIncompleteMessagesDueToMissingVarDataInNestedGroup) +{ + const auto bCount = std::get<0>(GetParam()); + const auto dCount = std::get<1>(GetParam()); + const auto expectedState = std::get<2>(GetParam()); + + NestedGroupWithVarLength encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(1).bCount(bCount).next().c(2).dCount(dCount).next().e(10); + + EXPECT_THROW( + { + try + { + encoder.checkEncodingIsComplete(); + } + catch (const std::logic_error &e) + { + EXPECT_THAT(e.what(), testing::HasSubstr( + "Not fully encoded, current state: " + expectedState)); + throw e; + } + }, + std::logic_error + ); +} + +INSTANTIATE_TEST_SUITE_P( + FieldAccessOrderCheckTest, + DisallowsIncompleteMessagesPart3Test, + testing::Values( + std::make_tuple(1, 1, "V0_B_1_D_1_BLOCK"), + std::make_tuple(1, 2, "V0_B_1_D_N_BLOCK"), + std::make_tuple(2, 1, "V0_B_N_D_1_BLOCK"), + std::make_tuple(2, 2, "V0_B_N_D_N_BLOCK") + ) +); diff --git a/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldAccessOrderCheckTest.java b/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldAccessOrderCheckTest.java index 0b55ca3d6e..553997d2ff 100644 --- a/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldAccessOrderCheckTest.java +++ b/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldAccessOrderCheckTest.java @@ -251,17 +251,21 @@ void disallowsSkippingDecodingOfVariableLengthField5() { final MultipleVarLengthDecoder decoder = decodeUntilVarLengthFields(); final Exception exception = - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::cLength); + assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> decoder.getC(new ExpandableArrayBuffer(), 0, 3)); assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_BLOCK")); } @Test - void disallowsSkippingDecodingOfVariableLengthField6() + void allowsRepeatedDecodingOfVariableLengthDataLength() { final MultipleVarLengthDecoder decoder = decodeUntilVarLengthFields(); - final Exception exception = - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> decoder.getC(new ExpandableArrayBuffer(), 0, 3)); - assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_BLOCK")); + assertThat(decoder.bLength(), equalTo(3)); + assertThat(decoder.bLength(), equalTo(3)); + assertThat(decoder.bLength(), equalTo(3)); + assertThat(decoder.b(), equalTo("abc")); + assertThat(decoder.cLength(), equalTo(3)); + assertThat(decoder.cLength(), equalTo(3)); + assertThat(decoder.cLength(), equalTo(3)); } @Test @@ -2939,7 +2943,7 @@ void allowsOldDecoderToSkipAddedAsciiFieldBeforeGroup() } @Test - void allowsEncodeAndDecodeOfMessagesWithNoABlock() + void allowsEncodeAndDecodeOfMessagesWithNoBlock() { final NoBlockEncoder encoder = new NoBlockEncoder() .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); @@ -2998,23 +3002,6 @@ void disallowsEncodingElementOfEmptyGroup2() @Test void disallowsEncodingElementOfEmptyGroup3() - { - final NestedGroupsEncoder encoder = new NestedGroupsEncoder() - .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); - encoder.a(42); - final NestedGroupsEncoder.BEncoder bEncoder = encoder.bCount(1); - bEncoder.next(); - bEncoder.c(43); - final NestedGroupsEncoder.BEncoder.DEncoder dEncoder = bEncoder.dCount(0); - bEncoder.fCount(0); - encoder.hCount(0); - final IllegalStateException exception = - assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> dEncoder.e(44)); - assertThat(exception.getMessage(), containsString("Cannot access field \"b.d.e\" in state: V0_H_0")); - } - - @Test - void disallowsEncodingElementOfEmptyGroup4() { final NestedGroupsEncoder encoder = new NestedGroupsEncoder() .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); @@ -3030,7 +3017,7 @@ void disallowsEncodingElementOfEmptyGroup4() } @Test - void disallowsEncodingElementOfEmptyGroup5() + void disallowsEncodingElementOfEmptyGroup4() { final AddPrimitiveInsideGroupV1Encoder encoder = new AddPrimitiveInsideGroupV1Encoder() .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); @@ -3042,7 +3029,7 @@ void disallowsEncodingElementOfEmptyGroup5() } @Test - void disallowsEncodingElementOfEmptyGroup6() + void disallowsEncodingElementOfEmptyGroup5() { final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder() .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); @@ -3107,7 +3094,7 @@ void disallowsMissedEncodingOfVarLengthFieldInNestedGroupToNextInnerElement( assertThat(exception.getMessage(), containsString("Cannot access next element in repeating group \"b.d\" in state: " + expectedState)); assertThat(exception.getMessage(), - containsString("Expected one of these transitions: [\"b.d.e(?)\", \"b.d.f(?)\"].")); + containsString("Expected one of these transitions: [\"b.d.e(?)\", \"b.d.fLength()\", \"b.d.f(?)\"].")); } @CsvSource(value = { @@ -3129,7 +3116,7 @@ void disallowsMissedEncodingOfVarLengthFieldInNestedGroupToNextOuterElement( assertThat(exception.getMessage(), containsString("Cannot access next element in repeating group \"b\" in state: " + expectedState)); assertThat(exception.getMessage(), - containsString("Expected one of these transitions: [\"b.d.e(?)\", \"b.d.f(?)\"].")); + containsString("Expected one of these transitions: [\"b.d.e(?)\", \"b.d.fLength()\", \"b.d.f(?)\"].")); } @Test @@ -3155,7 +3142,7 @@ void disallowsMissedDecodingOfVarLengthFieldInNestedGroupToNextInnerElement1() assertThat(exception.getMessage(), containsString("Cannot access next element in repeating group \"b.d\" in state: V0_B_1_D_N_BLOCK.")); assertThat(exception.getMessage(), - containsString("Expected one of these transitions: [\"b.d.e(?)\", \"b.d.f(?)\"].")); + containsString("Expected one of these transitions: [\"b.d.e(?)\", \"b.d.fLength()\", \"b.d.f(?)\"].")); } @Test @@ -3182,7 +3169,7 @@ void disallowsMissedDecodingOfVarLengthFieldInNestedGroupToNextInnerElement2() assertThat(exception.getMessage(), containsString("Cannot access next element in repeating group \"b.d\" in state: V0_B_N_D_N_BLOCK.")); assertThat(exception.getMessage(), - containsString("Expected one of these transitions: [\"b.d.e(?)\", \"b.d.f(?)\"].")); + containsString("Expected one of these transitions: [\"b.d.e(?)\", \"b.d.fLength()\", \"b.d.f(?)\"].")); } @Test @@ -3209,7 +3196,7 @@ void disallowsMissedDecodingOfVarLengthFieldInNestedGroupToNextOuterElement1() assertThat(exception.getMessage(), containsString("Cannot access next element in repeating group \"b\" in state: V0_B_N_D_N_BLOCK.")); assertThat(exception.getMessage(), - containsString("Expected one of these transitions: [\"b.d.e(?)\", \"b.d.f(?)\"].")); + containsString("Expected one of these transitions: [\"b.d.e(?)\", \"b.d.fLength()\", \"b.d.f(?)\"].")); } @Test @@ -3236,7 +3223,7 @@ void disallowsMissedDecodingOfVarLengthFieldInNestedGroupToNextOuterElement2() assertThat(exception.getMessage(), containsString("Cannot access next element in repeating group \"b\" in state: V0_B_N_D_1_BLOCK.")); assertThat(exception.getMessage(), - containsString("Expected one of these transitions: [\"b.d.e(?)\", \"b.d.f(?)\"].")); + containsString("Expected one of these transitions: [\"b.d.e(?)\", \"b.d.fLength()\", \"b.d.f(?)\"].")); } @Test @@ -3246,8 +3233,8 @@ void disallowsIncompleteMessagesDueToMissingVarLengthField1() .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); encoder.a(1).b("abc"); final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, encoder::checkEncodingIsComplete); - assertThat(exception.getMessage(), - containsString("Not fully encoded, current state: V0_B_DONE, allowed transitions: \"c(?)\"")); + assertThat(exception.getMessage(), containsString( + "Not fully encoded, current state: V0_B_DONE, allowed transitions: \"cLength()\", \"c(?)\"")); } @Test @@ -3256,8 +3243,8 @@ void disallowsIncompleteMessagesDueToMissingVarLengthField2() final NoBlockEncoder encoder = new NoBlockEncoder() .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, encoder::checkEncodingIsComplete); - assertThat(exception.getMessage(), - containsString("Not fully encoded, current state: V0_BLOCK, allowed transitions: \"a(?)\"")); + assertThat(exception.getMessage(), containsString( + "Not fully encoded, current state: V0_BLOCK, allowed transitions: \"aLength()\", \"a(?)\"")); } @Test From 2f9b25887a43133fdd088c6b0d7c396ee8ce93d5 Mon Sep 17 00:00:00 2001 From: Zach Bray Date: Tue, 15 Aug 2023 15:27:40 +0100 Subject: [PATCH 38/50] [C#] Improve indentation generation. Previously, some sections were not correctly aligned. --- .../sbe/generation/csharp/CSharpGenerator.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java index 41cf6c8fb0..dacbf341aa 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java @@ -156,8 +156,8 @@ public void generate() throws IOException out.append(generateClassDeclaration(className)); out.append(generateMessageFlyweightCode(className, msgToken, accessOrderModel, BASE_INDENT)); - out.append(generateFieldOrderStates(BASE_INDENT, accessOrderModel)); - out.append(generateFullyEncodedCheck(BASE_INDENT, accessOrderModel)); + out.append(generateFieldOrderStates(BASE_INDENT + INDENT, accessOrderModel)); + out.append(generateFullyEncodedCheck(BASE_INDENT + INDENT, accessOrderModel)); out.append(generateFields(accessOrderModel, fields, BASE_INDENT)); @@ -412,7 +412,7 @@ private CharSequence generateGroupProperty( toUpperFirstChar(groupName), token.id())); - generateAccessOrderListenerMethodForGroupWrap(sb, accessOrderModel, indent + INDENT, token); + generateAccessOrderListenerMethodForGroupWrap(sb, accessOrderModel, indent, token); generateSinceActingDeprecated(sb, indent, toUpperFirstChar(groupName), token); @@ -1927,13 +1927,13 @@ private CharSequence generateFields( final Token encodingToken = tokens.get(i + 1); final String propertyName = signalToken.name(); - generateFieldIdMethod(sb, signalToken, indent); + generateFieldIdMethod(sb, signalToken, indent + INDENT); generateSinceActingDeprecated( - sb, indent, CSharpUtil.formatPropertyName(signalToken.name()), signalToken); + sb, indent + INDENT, CSharpUtil.formatPropertyName(signalToken.name()), signalToken); generateOffsetMethod(sb, signalToken, indent + INDENT); generateFieldMetaAttributeMethod(sb, signalToken, indent + INDENT); - generateAccessOrderListenerMethod(sb, accessOrderModel, indent, signalToken); + generateAccessOrderListenerMethod(sb, accessOrderModel, indent + INDENT, signalToken); switch (encodingToken.signal()) { From a0eb0ea17e5d51788a5c8d14b399a070913d6605 Mon Sep 17 00:00:00 2001 From: Zach Bray Date: Tue, 15 Aug 2023 18:46:27 +0100 Subject: [PATCH 39/50] [Java, C#, C++] Model `resetCountToIndex()` state machine transition. Previously, the access order checks did not work with the `resetCountToIndex()` methods (generated on flyweights for repeating groups). Now, the valid transitions using these methods are modelled in the state machine and this is reflected in the code we generate for Java, C# and C++. Note that the `resetCountToIndex()` method does not change the `limit`/`position` of the message; therefore, it is only valid to use it when the `limit` aligns with a boundary between group elements. --- .../sbe-tests/FieldAccessOrderCheckTests.cs | 93 +++++++++++++-- .../generation/common/AccessOrderModel.java | 69 ++++++++++- .../sbe/generation/cpp/CppGenerator.java | 29 +++++ .../generation/csharp/CSharpGenerator.java | 32 ++++- .../sbe/generation/java/JavaGenerator.java | 31 ++++- .../test/cpp/FieldAccessOrderCheckTest.cpp | 112 +++++++++++++++++- .../sbe/FieldAccessOrderCheckTest.java | 83 ++++++++++++- 7 files changed, 423 insertions(+), 26 deletions(-) diff --git a/csharp/sbe-tests/FieldAccessOrderCheckTests.cs b/csharp/sbe-tests/FieldAccessOrderCheckTests.cs index 6a2912cc08..d0b7e16992 100644 --- a/csharp/sbe-tests/FieldAccessOrderCheckTests.cs +++ b/csharp/sbe-tests/FieldAccessOrderCheckTests.cs @@ -224,6 +224,79 @@ public void AllowsEncodingAndDecodingEmptyGroupAndVariableLengthFieldsInSchemaDe Assert.IsTrue(decoder.ToString().Contains("A=42|B=[]|D='abc'")); } + [TestMethod] + public void AllowsEncoderToResetZeroGroupLengthToZero() + { + var encoder = new GroupAndVarLength(); + encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader); + encoder.A = 42; + encoder.BCount(0).ResetCountToIndex(); + encoder.SetD("abc"); + encoder.CheckEncodingIsComplete(); + + var decoder = new GroupAndVarLength(); + decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader); + Assert.AreEqual(42, decoder.A); + var bDecoder = decoder.B; + Assert.AreEqual(0, bDecoder.Count); + Assert.AreEqual("abc", decoder.GetD()); + Assert.IsTrue(decoder.ToString().Contains("A=42|B=[]|D='abc'")); + } + + [TestMethod] + public void AllowsEncoderToResetNonZeroGroupLengthToZeroBeforeCallingNext() + { + var encoder = new GroupAndVarLength(); + encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader); + encoder.A = 42; + encoder.BCount(2).ResetCountToIndex(); + encoder.SetD("abc"); + encoder.CheckEncodingIsComplete(); + + var decoder = new GroupAndVarLength(); + decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader); + Assert.AreEqual(42, decoder.A); + var bDecoder = decoder.B; + Assert.AreEqual(0, bDecoder.Count); + Assert.AreEqual("abc", decoder.GetD()); + Assert.IsTrue(decoder.ToString().Contains("A=42|B=[]|D='abc'")); + } + + [TestMethod] + public void AllowsEncoderToResetNonZeroGroupLengthToNonZero() + { + var encoder = new GroupAndVarLength(); + encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader); + encoder.A = 42; + var bEncoder = encoder.BCount(2); + bEncoder.Next().C = 43; + bEncoder.ResetCountToIndex(); + encoder.SetD("abc"); + encoder.CheckEncodingIsComplete(); + + var decoder = new GroupAndVarLength(); + decoder.WrapForDecodeAndApplyHeader(_buffer, Offset, _messageHeader); + Assert.AreEqual(42, decoder.A); + var bDecoder = decoder.B; + Assert.AreEqual(1, bDecoder.Count); + Assert.AreEqual(43, bDecoder.Next().C); + Assert.AreEqual("abc", decoder.GetD()); + Assert.IsTrue(decoder.ToString().Contains("A=42|B=[(C=43)]|D='abc'")); + } + + [TestMethod] + public void DisallowsEncoderToResetGroupLengthMidGroupElement() + { + var encoder = new NestedGroups(); + encoder.WrapForEncodeAndApplyHeader(_buffer, Offset, _messageHeader); + encoder.A = 42; + var bEncoder = encoder.BCount(2).Next(); + bEncoder.C = 43; + var exception = Assert.ThrowsException(() => bEncoder.ResetCountToIndex()); + Assert.IsTrue(exception.Message.Contains( + "Cannot reset count of repeating group \"b\" in state: V0_B_N_BLOCK")); + } + [TestMethod] public void DisallowsEncodingGroupElementBeforeCallingNext() { @@ -2311,7 +2384,7 @@ public void DisallowsEncodingAsciiInsideGroupBeforeCallingNext5() { var bEncoder = EncodeUntilGroupWithAsciiInside(); - Exception exception = Assert.ThrowsException(() => + Exception exception = Assert.ThrowsException(() => bEncoder.SetC(Encoding.ASCII.GetBytes("EURUSD"), 0)); Assert.IsTrue(exception.Message.Contains("Cannot access field \"b.c\" in state: V0_B_N")); @@ -2322,7 +2395,7 @@ public void DisallowsEncodingAsciiInsideGroupBeforeCallingNext6() { var bEncoder = EncodeUntilGroupWithAsciiInside(); - Exception exception = Assert.ThrowsException(() => + Exception exception = Assert.ThrowsException(() => bEncoder.SetC(Encoding.ASCII.GetBytes("EURUSD"))); Assert.IsTrue(exception.Message.Contains("Cannot access field \"b.c\" in state: V0_B_N")); @@ -2693,7 +2766,7 @@ public void DisallowsEncodingElementOfEmptyGroup2() encoder.HCount(0); var ex = Assert.ThrowsException(() => dEncoder.E = 44); - Assert.IsTrue(ex.Message.Contains("Cannot access field \"b.d.e\" in state: V0_H_0")); + Assert.IsTrue(ex.Message.Contains("Cannot access field \"b.d.e\" in state: V0_H_DONE")); } [TestMethod] @@ -2710,7 +2783,7 @@ public void DisallowsEncodingElementOfEmptyGroup3() encoder.HCount(0); var ex = Assert.ThrowsException(() => dEncoder.E = 44); - Assert.IsTrue(ex.Message.Contains("Cannot access field \"b.d.e\" in state: V0_H_0")); + Assert.IsTrue(ex.Message.Contains("Cannot access field \"b.d.e\" in state: V0_H_DONE")); } [TestMethod] @@ -2726,7 +2799,7 @@ public void DisallowsEncodingElementOfEmptyGroup4() bEncoder.FCount(0); var ex = Assert.ThrowsException(() => dEncoder.E = 44); - Assert.IsTrue(ex.Message.Contains("Cannot access field \"b.d.e\" in state: V0_B_1_F_0")); + Assert.IsTrue(ex.Message.Contains("Cannot access field \"b.d.e\" in state: V0_B_1_F_DONE")); } @@ -2738,7 +2811,7 @@ public void DisallowsEncodingElementOfEmptyGroup5() encoder.A = 42; var bEncoder = encoder.BCount(0); var exception = Assert.ThrowsException(() => bEncoder.C = 43); - Assert.IsTrue(exception.Message.Contains("Cannot access field \"b.c\" in state: V1_B_0")); + Assert.IsTrue(exception.Message.Contains("Cannot access field \"b.c\" in state: V1_B_DONE")); } [TestMethod] @@ -3003,7 +3076,8 @@ public void DisallowsIncompleteMessagesDueToMissingTopLevelGroup1() encoder.BCount(0); var exception = Assert.ThrowsException(encoder.CheckEncodingIsComplete); StringAssert.Contains(exception.Message, - "Not fully encoded, current state: V0_B_0, allowed transitions: \"dCount(0)\", \"dCount(>0)\""); + "Not fully encoded, current state: V0_B_DONE, allowed transitions: " + + "\"b.resetCountToIndex()\", \"dCount(0)\", \"dCount(>0)\""); } [TestMethod] @@ -3016,7 +3090,8 @@ public void DisallowsIncompleteMessagesDueToMissingTopLevelGroup2() bEncoder.C = 2; var exception = Assert.ThrowsException(encoder.CheckEncodingIsComplete); StringAssert.Contains(exception.Message, - "Not fully encoded, current state: V0_B_1_BLOCK, allowed transitions: \"b.c(?)\", \"dCount(0)\", \"dCount(>0)\""); + "Not fully encoded, current state: V0_B_1_BLOCK, allowed transitions:" + + " \"b.c(?)\", \"b.resetCountToIndex()\", \"dCount(0)\", \"dCount(>0)\""); } [TestMethod] @@ -3047,7 +3122,7 @@ public void DisallowsIncompleteMessagesDueToMissingNestedGroup1(int bCount, stri [DataTestMethod] [DataRow(1, 1, "V0_B_1_D_N")] [DataRow(1, 2, "V0_B_1_D_N")] - [DataRow(2, 0, "V0_B_N_D_0")] + [DataRow(2, 0, "V0_B_N_D_DONE")] [DataRow(2, 1, "V0_B_N_D_N")] [DataRow(2, 2, "V0_B_N_D_N")] public void DisallowsIncompleteMessagesDueToMissingNestedGroup2( diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/AccessOrderModel.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/AccessOrderModel.java index d8465f400d..7f40901a75 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/AccessOrderModel.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/AccessOrderModel.java @@ -499,13 +499,13 @@ public void onEnterRepeatingGroup( final State nRemainingGroup = allocateState(groupPrefix + "N"); final State nRemainingGroupElement = allocateState(groupPrefix + "N_BLOCK"); final State oneRemainingGroupElement = allocateState(groupPrefix + "1_BLOCK"); - final State emptyGroup = allocateState(groupPrefix + "0"); + final State doneGroup = allocateState(groupPrefix + "DONE"); final Set beginGroupStates = new HashSet<>(currentStates); // fooCount(0) final CodecInteraction emptyGroupInteraction = interactionFactory.determineGroupIsEmpty(token); - allocateTransitions(emptyGroupInteraction, beginGroupStates, emptyGroup); + allocateTransitions(emptyGroupInteraction, beginGroupStates, doneGroup); // fooCount(N) where N > 0 final CodecInteraction nonEmptyGroupInteraction = interactionFactory.determineGroupHasElements(token); @@ -548,8 +548,16 @@ public void onEnterRepeatingGroup( ); walkSchemaLevel(oneRemainingCollector, groupFields, groupGroups, groupVarData); + final CodecInteraction resetCountToIndexInteraction = interactionFactory.resetCountToIndex(token); currentStates.clear(); - currentStates.add(emptyGroup); + currentStates.add(doneGroup); + currentStates.add(nRemainingGroup); + currentStates.addAll(nRemainingCollector.exitStates()); + currentStates.addAll(oneRemainingCollector.exitStates()); + allocateTransitions(resetCountToIndexInteraction, currentStates, doneGroup); + + currentStates.clear(); + currentStates.add(doneGroup); currentStates.addAll(oneRemainingCollector.exitStates()); } } @@ -935,6 +943,41 @@ String exampleConditions() } } + /** + * When the number of elements in a repeating group is set to be its current extent. + */ + private static final class ResetCountToIndex extends CodecInteraction + { + private final String groupPath; + private final Token token; + + private ResetCountToIndex(final String groupPath, final Token token) + { + assert groupPath != null; + assert token.signal() == Signal.BEGIN_GROUP; + this.groupPath = groupPath; + this.token = token; + } + + @Override + public String groupQualifiedName() + { + return groupPath + token.name(); + } + + @Override + String exampleCode() + { + return groupPath + token.name() + ".resetCountToIndex()"; + } + + @Override + String exampleConditions() + { + return ""; + } + } + /** * When the length of a variable length field is accessed without adjusting the position. */ @@ -984,6 +1027,7 @@ public static final class HashConsingFactory private final Map determineGroupHasElementsInteractions = new HashMap<>(); private final Map moveToNextElementInteractions = new HashMap<>(); private final Map moveToLastElementInteractions = new HashMap<>(); + private final Map resetCountToIndexInteractions = new HashMap<>(); private final Map accessVarDataLengthInteractions = new HashMap<>(); private final Map groupPathsByField; private final Set topLevelBlockFields; @@ -1100,6 +1144,25 @@ public CodecInteraction moveToLastElement(final Token token) t -> new MoveToLastElement(groupPathsByField.get(t), t)); } + + /** + * Find or create a {@link CodecInteraction} to represent resetting the count + * of a repeating group to the current index. + * + *

For encoders, decoders, and codecs, this will be when the {@code resetCountToIndex} + * method is called, e.g., {@code myGroup.resetCountToIndex()}. + * + *

The supplied token must carry a {@link Signal#BEGIN_GROUP} signal. + * + * @param token the token identifying the group + * @return the {@link CodecInteraction} instance + */ + public CodecInteraction resetCountToIndex(final Token token) + { + return resetCountToIndexInteractions.computeIfAbsent(token, + t -> new ResetCountToIndex(groupPathsByField.get(t), t)); + } + /** * Find or create a {@link CodecInteraction} to represent accessing the length * of a variable-length data field without advancing the codec position. diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java index 35556c6faa..eadb6caf55 100755 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java @@ -521,6 +521,33 @@ private static void generateAccessOrderListenerMethodForNextGroupElement( .append(indent).append("}\n"); } + private static void generateAccessOrderListenerMethodForResetGroupCount( + final StringBuilder sb, + final AccessOrderModel accessOrderModel, + final String indent, + final Token token) + { + if (null == accessOrderModel) + { + return; + } + + sb.append(indent).append("void onResetCountToIndex()\n") + .append(indent).append("{\n"); + + final AccessOrderModel.CodecInteraction resetCountToIndex = + accessOrderModel.interactionFactory().resetCountToIndex(token); + + generateAccessOrderListener( + sb, + indent + " ", + "reset count of repeating group", + accessOrderModel, + resetCountToIndex); + + sb.append(indent).append("}\n"); + } + private void generateGroups( final StringBuilder sb, final List tokens, @@ -733,6 +760,7 @@ private static void generateGroupClassHeader( } generateAccessOrderListenerMethodForNextGroupElement(sb, accessOrderModel, indent + INDENT, groupToken); + generateAccessOrderListenerMethodForResetGroupCount(sb, accessOrderModel, indent, groupToken); final CharSequence onNextAccessOrderCall = null == accessOrderModel ? "" : generateAccessOrderListenerCall(accessOrderModel, indent + TWO_INDENT, "onNextElementAccessed"); @@ -803,6 +831,7 @@ private static void generateGroupClassHeader( sb.append("\n") .append(indent).append(" inline std::uint64_t resetCountToIndex()\n") .append(indent).append(" {\n") + .append(generateAccessOrderListenerCall(accessOrderModel, indent + TWO_INDENT, "onResetCountToIndex")) .append(indent).append(" m_count = m_index;\n") .append(indent).append(" ").append(dimensionsClassName) .append(" dimensions(m_buffer, m_initialPosition, m_bufferLength, m_actingVersion);\n") diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java index dacbf341aa..4c85c97427 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java @@ -344,19 +344,22 @@ private void generateGroupEnumerator( final String indent) { generateAccessOrderListenerMethodForNextGroupElement(sb, accessOrderModel, indent + INDENT, groupToken); + generateAccessOrderListenerMethodForResetGroupCount(sb, accessOrderModel, indent + INDENT, groupToken); sb.append( indent + INDENT + "public int ActingBlockLength { get { return _blockLength; } }\n\n" + indent + INDENT + "public int Count { get { return _count; } }\n\n" + - indent + INDENT + "public bool HasNext { get { return _index < _count; } }\n"); + indent + INDENT + "public bool HasNext { get { return _index < _count; } }\n\n"); sb.append(String.format("\n" + indent + INDENT + "public int ResetCountToIndex()\n" + indent + INDENT + "{\n" + + "%s" + indent + INDENT + INDENT + "_count = _index;\n" + indent + INDENT + INDENT + "_dimensions.NumInGroup = (%s) _count;\n\n" + indent + INDENT + INDENT + "return _count;\n" + indent + INDENT + "}\n", + generateAccessOrderListenerCall(accessOrderModel, indent + TWO_INDENT, "OnResetCountToIndex"), typeForNumInGroup)); sb.append(String.format("\n" + @@ -1833,6 +1836,33 @@ private static void generateAccessOrderListenerMethodForNextGroupElement( .append(indent).append("}\n"); } + private static void generateAccessOrderListenerMethodForResetGroupCount( + final StringBuilder sb, + final AccessOrderModel accessOrderModel, + final String indent, + final Token token) + { + if (null == accessOrderModel) + { + return; + } + + sb.append(indent).append("private void OnResetCountToIndex()\n") + .append(indent).append("{\n"); + + final AccessOrderModel.CodecInteraction resetCountToIndex = + accessOrderModel.interactionFactory().resetCountToIndex(token); + + generateAccessOrderListener( + sb, + indent + " ", + "reset count of repeating group", + accessOrderModel, + resetCountToIndex); + + sb.append(indent).append("}\n"); + } + private static void generateAccessOrderListenerMethodForVarDataLength( final StringBuilder sb, final AccessOrderModel accessOrderModel, diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java index 31260779d2..6fa27c66b2 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java @@ -657,6 +657,33 @@ private static void generateAccessOrderListenerMethodForNextGroupElement( .append(indent).append("}\n"); } + private static void generateAccessOrderListenerMethodForResetGroupCount( + final StringBuilder sb, + final AccessOrderModel accessOrderModel, + final String indent, + final Token token) + { + if (null == accessOrderModel) + { + return; + } + + sb.append(indent).append("private void onResetCountToIndex()\n") + .append(indent).append("{\n"); + + final AccessOrderModel.CodecInteraction resetCountToIndex = + accessOrderModel.interactionFactory().resetCountToIndex(token); + + generateAccessOrderListener( + sb, + indent + " ", + "reset count of repeating group", + accessOrderModel, + resetCountToIndex); + + sb.append(indent).append("}\n"); + } + private static void generateAccessOrderListenerMethodForVarDataLength( final StringBuilder sb, final AccessOrderModel accessOrderModel, @@ -1076,6 +1103,7 @@ private void generateGroupEncoderClassHeader( numInGroupPut); generateAccessOrderListenerMethodForNextGroupElement(sb, accessOrderModel, ind + " ", groupToken); + generateAccessOrderListenerMethodForResetGroupCount(sb, accessOrderModel, ind + " ", groupToken); sb.append("\n") .append(ind).append(" public ").append(encoderName(groupName)).append(" next()\n") @@ -1089,7 +1117,7 @@ private void generateGroupEncoderClassHeader( .append(ind).append(" parentMessage.limit(offset + sbeBlockLength());\n") .append(ind).append(" ++index;\n\n") .append(ind).append(" return this;\n") - .append(ind).append(" }\n"); + .append(ind).append(" }\n\n"); final String countOffset = "initialLimit + " + numInGroupToken.offset(); final String resetCountPut = generatePut( @@ -1098,6 +1126,7 @@ private void generateGroupEncoderClassHeader( sb.append("\n") .append(ind).append(" public int resetCountToIndex()\n") .append(ind).append(" {\n") + .append(generateAccessOrderListenerCall(accessOrderModel, ind + " ", "onResetCountToIndex")) .append(ind).append(" count = index;\n") .append(ind).append(" ").append(resetCountPut).append(";\n\n") .append(ind).append(" return count;\n") diff --git a/sbe-tool/src/test/cpp/FieldAccessOrderCheckTest.cpp b/sbe-tool/src/test/cpp/FieldAccessOrderCheckTest.cpp index 0da7b10761..89aeeca267 100644 --- a/sbe-tool/src/test/cpp/FieldAccessOrderCheckTest.cpp +++ b/sbe-tool/src/test/cpp/FieldAccessOrderCheckTest.cpp @@ -694,6 +694,105 @@ TEST_F(FieldAccessOrderCheckTest, allowsEncodingAndDecodingEmptyGroupAndVariable EXPECT_EQ(decoder.getDAsString(), "abc"); } +TEST_F(FieldAccessOrderCheckTest, allowsEncoderToResetZeroGroupLengthToNonZero) +{ + GroupAndVarLength encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + encoder.bCount(0).resetCountToIndex(); + encoder.putD("abc"); + encoder.checkEncodingIsComplete(); + + GroupAndVarLength decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + GroupAndVarLength::sbeBlockLength(), + GroupAndVarLength::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 42); + GroupAndVarLength::B &bDecoder = decoder.b(); + EXPECT_EQ(bDecoder.count(), 0u); + EXPECT_EQ(decoder.getDAsString(), "abc"); +} + +TEST_F(FieldAccessOrderCheckTest, allowsEncoderToResetNonZeroGroupLengthToNonZeroBeforeCallingNext) +{ + GroupAndVarLength encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + encoder.bCount(2).resetCountToIndex(); + encoder.putD("abc"); + encoder.checkEncodingIsComplete(); + + GroupAndVarLength decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + GroupAndVarLength::sbeBlockLength(), + GroupAndVarLength::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 42); + GroupAndVarLength::B &bDecoder = decoder.b(); + EXPECT_EQ(bDecoder.count(), 0u); + EXPECT_EQ(decoder.getDAsString(), "abc"); +} + +TEST_F(FieldAccessOrderCheckTest, allowsEncoderToResetNonZeroGroupLengthToNonZero) +{ + GroupAndVarLength encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + GroupAndVarLength::B &bEncoder = encoder.bCount(2).next(); + bEncoder.c(43); + bEncoder.resetCountToIndex(); + encoder.putD("abc"); + encoder.checkEncodingIsComplete(); + + GroupAndVarLength decoder; + decoder.wrapForDecode( + m_buffer, + OFFSET, + GroupAndVarLength::sbeBlockLength(), + GroupAndVarLength::sbeSchemaVersion(), + BUFFER_LEN + ); + EXPECT_EQ(decoder.a(), 42); + GroupAndVarLength::B &bDecoder = decoder.b(); + EXPECT_EQ(bDecoder.count(), 1u); + EXPECT_EQ(bDecoder.next().c(), 43); + EXPECT_EQ(decoder.getDAsString(), "abc"); +} + +TEST_F(FieldAccessOrderCheckTest, disallowsEncoderToResetGroupLengthMidGroupElement) +{ + NestedGroups encoder; + encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN); + encoder.a(42); + NestedGroups::B &bEncoder = encoder.bCount(2).next(); + bEncoder.c(43); + EXPECT_THROW( + { + try + { + bEncoder.resetCountToIndex(); + } + catch (const std::logic_error &e) + { + EXPECT_THAT( + e.what(), + HasSubstr("Cannot reset count of repeating group \"b\" in state: V0_B_N_BLOCK") + ); + throw; + } + }, + std::logic_error + ); +} + + TEST_F(FieldAccessOrderCheckTest, disallowsEncodingGroupElementBeforeCallingNext) { GroupAndVarLength encoder; @@ -4214,7 +4313,7 @@ TEST_F(FieldAccessOrderCheckTest, disallowsEncodingElementOfEmptyGroup2) } catch (const std::logic_error &e) { - EXPECT_THAT(e.what(), testing::HasSubstr("Cannot access field \"b.d.e\" in state: V0_H_0")); + EXPECT_THAT(e.what(), testing::HasSubstr("Cannot access field \"b.d.e\" in state: V0_H_DONE")); throw e; } }, @@ -4241,7 +4340,7 @@ TEST_F(FieldAccessOrderCheckTest, disallowsEncodingElementOfEmptyGroup3) } catch (const std::logic_error &e) { - EXPECT_THAT(e.what(), testing::HasSubstr("Cannot access field \"b.d.e\" in state: V0_B_1_F_0")); + EXPECT_THAT(e.what(), testing::HasSubstr("Cannot access field \"b.d.e\" in state: V0_B_1_F_DONE")); throw e; } }, @@ -4264,7 +4363,7 @@ TEST_F(FieldAccessOrderCheckTest, disallowsEncodingElementOfEmptyGroup4) } catch (const std::logic_error &e) { - EXPECT_THAT(e.what(), testing::HasSubstr("Cannot access field \"b.c\" in state: V1_B_0")); + EXPECT_THAT(e.what(), testing::HasSubstr("Cannot access field \"b.c\" in state: V1_B_DONE")); throw e; } }, @@ -4653,7 +4752,8 @@ TEST_F(FieldAccessOrderCheckTest, disallowsIncompleteMessagesDueToMissingTopLeve catch (const std::logic_error &e) { EXPECT_THAT(e.what(), testing::HasSubstr( - "Not fully encoded, current state: V0_B_0, allowed transitions: \"dCount(0)\", \"dCount(>0)\"")); + "Not fully encoded, current state: V0_B_DONE, allowed transitions:" + " \"b.resetCountToIndex()\", \"dCount(0)\", \"dCount(>0)\"")); throw e; } }, @@ -4677,7 +4777,7 @@ TEST_F(FieldAccessOrderCheckTest, disallowsIncompleteMessagesDueToMissingTopLeve { EXPECT_THAT(e.what(), testing::HasSubstr( "Not fully encoded, current state: V0_B_1_BLOCK, allowed transitions: " - "\"b.c(?)\", \"dCount(0)\", \"dCount(>0)\"")); + "\"b.c(?)\", \"b.resetCountToIndex()\", \"dCount(0)\", \"dCount(>0)\"")); throw e; } }, @@ -4787,7 +4887,7 @@ INSTANTIATE_TEST_SUITE_P( testing::Values( std::make_tuple(1, 1, "V0_B_1_D_N"), std::make_tuple(1, 2, "V0_B_1_D_N"), - std::make_tuple(2, 0, "V0_B_N_D_0"), + std::make_tuple(2, 0, "V0_B_N_D_DONE"), std::make_tuple(2, 1, "V0_B_N_D_N"), std::make_tuple(2, 2, "V0_B_N_D_N") ) diff --git a/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldAccessOrderCheckTest.java b/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldAccessOrderCheckTest.java index 553997d2ff..97d51b199c 100644 --- a/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldAccessOrderCheckTest.java +++ b/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldAccessOrderCheckTest.java @@ -330,6 +330,76 @@ void allowsEncodingAndDecodingGroupAndVariableLengthFieldsInSchemaDefinedOrder() assertThat(decoder.toString(), containsString("a=42|b=[(c=1),(c=2)]|d='abc'")); } + @Test + void allowsEncoderToResetZeroGroupLengthToZero() + { + final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + encoder.bCount(0).resetCountToIndex(); + encoder.d("abc"); + encoder.checkEncodingIsComplete(); + + final GroupAndVarLengthDecoder decoder = new GroupAndVarLengthDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(42)); + final GroupAndVarLengthDecoder.BDecoder bs = decoder.b(); + assertThat(bs.count(), equalTo(0)); + assertThat(decoder.d(), equalTo("abc")); + assertThat(decoder.toString(), containsString("a=42|b=[]|d='abc'")); + } + + @Test + void allowsEncoderToResetNonZeroGroupLengthToZeroBeforeCallingNext() + { + final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + encoder.bCount(2).resetCountToIndex(); + encoder.d("abc"); + encoder.checkEncodingIsComplete(); + + final GroupAndVarLengthDecoder decoder = new GroupAndVarLengthDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(42)); + final GroupAndVarLengthDecoder.BDecoder bs = decoder.b(); + assertThat(bs.count(), equalTo(0)); + assertThat(decoder.d(), equalTo("abc")); + assertThat(decoder.toString(), containsString("a=42|b=[]|d='abc'")); + } + + @Test + void allowsEncoderToResetNonZeroGroupLengthToNonZero() + { + final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + encoder.bCount(2).next().c(43).resetCountToIndex(); + encoder.d("abc"); + encoder.checkEncodingIsComplete(); + + final GroupAndVarLengthDecoder decoder = new GroupAndVarLengthDecoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder); + assertThat(decoder.a(), equalTo(42)); + final GroupAndVarLengthDecoder.BDecoder bs = decoder.b(); + assertThat(bs.count(), equalTo(1)); + assertThat(bs.next().c(), equalTo(43)); + assertThat(decoder.d(), equalTo("abc")); + assertThat(decoder.toString(), containsString("a=42|b=[(c=43)]|d='abc'")); + } + + @Test + void disallowsEncoderToResetGroupLengthMidGroupElement() + { + final NestedGroupsEncoder encoder = new NestedGroupsEncoder() + .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder); + encoder.a(42); + final NestedGroupsEncoder.BEncoder bEncoder = encoder.bCount(2).next().c(43); + final IllegalStateException exception = assertThrows(IllegalStateException.class, bEncoder::resetCountToIndex); + assertThat(exception.getMessage(), + containsString("Cannot reset count of repeating group \"b\" in state: V0_B_N_BLOCK")); + } + @Test void allowsDecodingGroupAndVariableLengthFieldsAfterRewind() { @@ -2997,7 +3067,7 @@ void disallowsEncodingElementOfEmptyGroup2() encoder.hCount(0); final IllegalStateException exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> dEncoder.e(44)); - assertThat(exception.getMessage(), containsString("Cannot access field \"b.d.e\" in state: V0_H_0")); + assertThat(exception.getMessage(), containsString("Cannot access field \"b.d.e\" in state: V0_H_DONE")); } @Test @@ -3013,7 +3083,7 @@ void disallowsEncodingElementOfEmptyGroup3() bEncoder.fCount(0); final IllegalStateException exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> dEncoder.e(44)); - assertThat(exception.getMessage(), containsString("Cannot access field \"b.d.e\" in state: V0_B_1_F_0")); + assertThat(exception.getMessage(), containsString("Cannot access field \"b.d.e\" in state: V0_B_1_F_DONE")); } @Test @@ -3025,7 +3095,7 @@ void disallowsEncodingElementOfEmptyGroup4() final AddPrimitiveInsideGroupV1Encoder.BEncoder bEncoder = encoder.bCount(0); final IllegalStateException exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.c(43)); - assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V1_B_0")); + assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V1_B_DONE")); } @Test @@ -3255,7 +3325,8 @@ void disallowsIncompleteMessagesDueToMissingTopLevelGroup1() encoder.a(1).bCount(0); final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, encoder::checkEncodingIsComplete); assertThat(exception.getMessage(), containsString( - "Not fully encoded, current state: V0_B_0, allowed transitions: \"dCount(0)\", \"dCount(>0)\"")); + "Not fully encoded, current state: V0_B_DONE, allowed transitions: " + + "\"b.resetCountToIndex()\", \"dCount(0)\", \"dCount(>0)\"")); } @Test @@ -3267,7 +3338,7 @@ void disallowsIncompleteMessagesDueToMissingTopLevelGroup2() final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, encoder::checkEncodingIsComplete); assertThat(exception.getMessage(), containsString( "Not fully encoded, current state: V0_B_1_BLOCK, allowed transitions: " + - "\"b.c(?)\", \"dCount(0)\", \"dCount(>0)\"")); + "\"b.c(?)\", \"b.resetCountToIndex()\", \"dCount(0)\", \"dCount(>0)\"")); } @Test @@ -3299,7 +3370,7 @@ void disallowsIncompleteMessagesDueToMissingNestedGroup1(final int bCount, final @CsvSource(value = { "1,1,V0_B_1_D_N", "1,2,V0_B_1_D_N", - "2,0,V0_B_N_D_0", + "2,0,V0_B_N_D_DONE", "2,1,V0_B_N_D_N", "2,2,V0_B_N_D_N", }) From 39d28c102a5d906ea6b37a791817b235edc5f316 Mon Sep 17 00:00:00 2001 From: Zach Bray Date: Tue, 22 Aug 2023 13:52:35 +0100 Subject: [PATCH 40/50] [Java, C++, C#] Disable access order checking feature by default. We can deliver this new feature (and the C++ changes around copy constructors and copy assignment operators) without breaking any existing consumers by disabling the new code generation by default. Once released, and we've started to get feedback, we can decide whether we want to change the default and encourage our users to make use of the new feature. --- build.gradle | 12 ++++++---- .../generation/common/AccessOrderModel.java | 2 +- .../sbe/generation/cpp/CppGenerator.java | 2 +- .../generation/csharp/CSharpGenerator.java | 24 ++++++++++++------- sbe-tool/src/test/cpp/CMakeLists.txt | 2 ++ 5 files changed, 27 insertions(+), 15 deletions(-) diff --git a/build.gradle b/build.gradle index 1ccb8e95e7..2d7304a227 100644 --- a/build.gradle +++ b/build.gradle @@ -279,7 +279,8 @@ project(':sbe-tool') { 'sbe.output.dir': generatedDir, 'sbe.target.language': 'Java', 'sbe.validation.stop.on.error': 'true', - 'sbe.validation.xsd': validationXsdPath) + 'sbe.validation.xsd': validationXsdPath, + 'sbe.generate.access.order.checks': 'true') args = ['src/test/resources/json-printer-test-schema.xml', 'src/test/resources/composite-elements-schema.xml', 'src/test/resources/field-order-check-schema.xml'] @@ -732,7 +733,8 @@ tasks.register('generateCSharpCodecsTests', JavaExec) { 'sbe.output.dir': 'csharp/sbe-generated', 'sbe.target.language': 'uk.co.real_logic.sbe.generation.csharp.CSharp', 'sbe.xinclude.aware': 'true', - 'sbe.validation.xsd': validationXsdPath) + 'sbe.validation.xsd': validationXsdPath, + 'sbe.generate.access.order.checks': 'true') args = ['sbe-tool/src/test/resources/FixBinary.xml', 'sbe-tool/src/test/resources/issue435.xml', 'sbe-tool/src/test/resources/issue483.xml', @@ -756,7 +758,8 @@ tasks.register('generateJavaIrCodecs', JavaExec) { systemProperties( 'sbe.output.dir': 'sbe-tool/src/main/java', 'sbe.target.language': 'Java', - 'sbe.validation.xsd': validationXsdPath) + 'sbe.validation.xsd': validationXsdPath, + 'sbe.generate.access.order.checks': 'true') args = ['sbe-tool/src/main/resources/sbe-ir.xml'] } @@ -766,7 +769,8 @@ tasks.register('generateCppIrCodecs', JavaExec) { systemProperties( 'sbe.output.dir': 'sbe-tool/src/main/cpp', 'sbe.target.language': 'cpp', - 'sbe.validation.xsd': validationXsdPath) + 'sbe.validation.xsd': validationXsdPath, + 'sbe.generate.access.order.checks': 'true') args = ['sbe-tool/src/main/resources/sbe-ir.xml'] } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/AccessOrderModel.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/AccessOrderModel.java index 7f40901a75..970e3caaac 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/AccessOrderModel.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/AccessOrderModel.java @@ -41,7 +41,7 @@ public final class AccessOrderModel { private static final boolean GENERATE_ACCESS_ORDER_CHECKS = Boolean.parseBoolean( - System.getProperty("sbe.generate.access.order.checks", "true")); + System.getProperty("sbe.generate.access.order.checks", "false")); private final Map groupPathsByField = new HashMap<>(); private final Set topLevelBlockFields = new HashSet<>(); private final CodecInteraction.HashConsingFactory interactionFactory = diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java index eadb6caf55..11439cae2e 100755 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java @@ -47,7 +47,7 @@ public class CppGenerator implements CodeGenerator { private static final boolean DISABLE_IMPLICIT_COPYING = Boolean.parseBoolean( - System.getProperty("sbe.disable.implicit.copying", "true")); + System.getProperty("sbe.cpp.disable.implicit.copying", "false")); private static final String BASE_INDENT = ""; private static final String INDENT = " "; private static final String TWO_INDENT = INDENT + INDENT; diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java index 4c85c97427..63ee8ee62d 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java @@ -2552,12 +2552,15 @@ private CharSequence generateDisplay( append(sb, TWO_INDENT, " }"); sb.append('\n'); append(sb, TWO_INDENT, " int originalLimit = this.Limit;"); - sb.append("#if ENABLE_ACCESS_ORDER_CHECKS\n"); - append(sb, TWO_INDENT, " CodecState originalState = _codecState;"); - sb.append(THREE_INDENT).append("_codecState = ") - .append(qualifiedStateCase(accessOrderModel.notWrappedState())).append(";\n"); - append(sb, TWO_INDENT, " OnWrapForDecode(_actingVersion);"); - sb.append("#endif\n"); + if (null != accessOrderModel) + { + sb.append("#if ENABLE_ACCESS_ORDER_CHECKS\n"); + append(sb, TWO_INDENT, " CodecState originalState = _codecState;"); + sb.append(THREE_INDENT).append("_codecState = ") + .append(qualifiedStateCase(accessOrderModel.notWrappedState())).append(";\n"); + append(sb, TWO_INDENT, " OnWrapForDecode(_actingVersion);"); + sb.append("#endif\n"); + } append(sb, TWO_INDENT, " this.Limit = _offset + _actingBlockLength;"); append(sb, TWO_INDENT, " builder.Append(\"[" + name + "](sbeTemplateId=\");"); append(sb, TWO_INDENT, " builder.Append(" + name + ".TemplateId);"); @@ -2581,9 +2584,12 @@ private CharSequence generateDisplay( sb.append('\n'); appendDisplay(sb, tokens, groups, varData, THREE_INDENT); sb.append('\n'); - sb.append("#if ENABLE_ACCESS_ORDER_CHECKS\n"); - append(sb, TWO_INDENT, " _codecState = originalState;"); - sb.append("#endif\n"); + if (null != accessOrderModel) + { + sb.append("#if ENABLE_ACCESS_ORDER_CHECKS\n"); + append(sb, TWO_INDENT, " _codecState = originalState;"); + sb.append("#endif\n"); + } append(sb, TWO_INDENT, " this.Limit = originalLimit;"); sb.append('\n'); append(sb, TWO_INDENT, "}"); diff --git a/sbe-tool/src/test/cpp/CMakeLists.txt b/sbe-tool/src/test/cpp/CMakeLists.txt index 136c3cc0bf..cd92ff756c 100644 --- a/sbe-tool/src/test/cpp/CMakeLists.txt +++ b/sbe-tool/src/test/cpp/CMakeLists.txt @@ -63,6 +63,8 @@ add_custom_command( -Dsbe.output.dir=${CXX_CODEC_TARGET_DIR} -Dsbe.generate.ir="true" -Dsbe.target.language="cpp" + -Dsbe.generate.access.order.checks="true" + -Dsbe.cpp.disable.implicit.copying="true" -jar ${SBE_JAR} ${CODE_GENERATION_SCHEMA} ${COMPOSITE_OFFSETS_SCHEMA} From 3d44354d2ecabb829cdb31d8df8fa066e56da18c Mon Sep 17 00:00:00 2001 From: Zach Bray Date: Wed, 23 Aug 2023 12:56:28 +0100 Subject: [PATCH 41/50] [C++] Make access listener methods private. Also, fix some indentation issues. --- .../sbe/generation/cpp/CppGenerator.java | 90 ++++++++++--------- 1 file changed, 50 insertions(+), 40 deletions(-) diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java index 11439cae2e..c4fa59baa0 100755 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java @@ -259,18 +259,20 @@ private static void generateAccessOrderListenerMethod( final String constDeclaration = canChangeState(accessOrderModel, fieldAccess) ? "" : " const"; sb.append("\n") - .append(indent).append("void ").append(accessOrderListenerMethodName(token)).append("()") + .append(indent).append("private:\n") + .append(indent).append(INDENT).append("void ").append(accessOrderListenerMethodName(token)).append("()") .append(constDeclaration).append("\n") - .append(indent).append("{\n"); + .append(indent).append(INDENT).append("{\n"); generateAccessOrderListener( sb, - indent + INDENT, + indent + TWO_INDENT, "access field", accessOrderModel, fieldAccess); - sb.append(indent).append("}\n"); + sb.append(indent).append(INDENT).append("}\n\n") + .append(indent).append("public:"); } private static boolean canChangeState( @@ -349,38 +351,40 @@ private static void generateAccessOrderListenerMethodForGroupWrap( } sb.append("\n") - .append(indent).append("void ").append(accessOrderListenerMethodName(token)) + .append(indent).append("private:\n") + .append(indent).append(INDENT).append("void ").append(accessOrderListenerMethodName(token)) .append("(std::uint64_t remaining, std::string action)\n") - .append(indent).append("{\n") - .append(indent).append(INDENT).append("if (0 == remaining)\n") - .append(indent).append(INDENT).append("{\n"); + .append(indent).append(INDENT).append("{\n") + .append(indent).append(TWO_INDENT).append("if (0 == remaining)\n") + .append(indent).append(TWO_INDENT).append("{\n"); final AccessOrderModel.CodecInteraction selectEmptyGroup = accessOrderModel.interactionFactory().determineGroupIsEmpty(token); generateAccessOrderListener( sb, - indent + TWO_INDENT, + indent + THREE_INDENT, "\" + action + \" count of repeating group", accessOrderModel, selectEmptyGroup); - sb.append(indent).append(INDENT).append("}\n") - .append(indent).append(INDENT).append("else\n") - .append(indent).append(INDENT).append("{\n"); + sb.append(indent).append(TWO_INDENT).append("}\n") + .append(indent).append(TWO_INDENT).append("else\n") + .append(indent).append(TWO_INDENT).append("{\n"); final AccessOrderModel.CodecInteraction selectNonEmptyGroup = accessOrderModel.interactionFactory().determineGroupHasElements(token); generateAccessOrderListener( sb, - indent + TWO_INDENT, + indent + THREE_INDENT, "\" + action + \" count of repeating group", accessOrderModel, selectNonEmptyGroup); - sb.append(indent).append(INDENT).append("}\n") - .append(indent).append("}\n"); + sb.append(indent).append(TWO_INDENT).append("}\n") + .append(indent).append(INDENT).append("}\n\n") + .append(indent).append("public:"); } private static void generateAccessOrderListenerMethodForVarDataLength( @@ -395,21 +399,23 @@ private static void generateAccessOrderListenerMethodForVarDataLength( } sb.append("\n") - .append(indent).append("void ").append(accessOrderListenerMethodName(token, "Length")) + .append(indent).append("private:\n") + .append(indent).append(INDENT).append("void ").append(accessOrderListenerMethodName(token, "Length")) .append("() const\n") - .append(indent).append("{\n"); + .append(indent).append(INDENT).append("{\n"); final AccessOrderModel.CodecInteraction accessLength = accessOrderModel.interactionFactory().accessVarDataLength(token); generateAccessOrderListener( sb, - indent + INDENT, + indent + TWO_INDENT, "decode length of var data", accessOrderModel, accessLength); - sb.append(indent).append("}\n"); + sb.append(indent).append(INDENT).append("}\n\n") + .append(indent).append("public:"); } private static void generateAccessOrderListener( @@ -487,38 +493,38 @@ private static void generateAccessOrderListenerMethodForNextGroupElement( sb.append("\n"); - sb.append(indent).append("void onNextElementAccessed()\n") - .append(indent).append("{\n") - .append(indent).append(INDENT).append("std::uint64_t remaining = m_count - m_index;\n") - .append(indent).append(INDENT).append("if (remaining > 1)\n") - .append(indent).append(INDENT).append("{\n"); + sb.append(indent).append(INDENT).append("void onNextElementAccessed()\n") + .append(indent).append(INDENT).append("{\n") + .append(indent).append(TWO_INDENT).append("std::uint64_t remaining = m_count - m_index;\n") + .append(indent).append(TWO_INDENT).append("if (remaining > 1)\n") + .append(indent).append(TWO_INDENT).append("{\n"); final AccessOrderModel.CodecInteraction selectNextElementInGroup = accessOrderModel.interactionFactory().moveToNextElement(token); generateAccessOrderListener( sb, - indent + TWO_INDENT, + indent + THREE_INDENT, "access next element in repeating group", accessOrderModel, selectNextElementInGroup); - sb.append(indent).append(INDENT).append("}\n") - .append(indent).append(INDENT).append("else if (1 == remaining)\n") - .append(indent).append(INDENT).append("{\n"); + sb.append(indent).append(TWO_INDENT).append("}\n") + .append(indent).append(TWO_INDENT).append("else if (1 == remaining)\n") + .append(indent).append(TWO_INDENT).append("{\n"); final AccessOrderModel.CodecInteraction selectLastElementInGroup = accessOrderModel.interactionFactory().moveToLastElement(token); generateAccessOrderListener( sb, - indent + TWO_INDENT, + indent + THREE_INDENT, "access next element in repeating group", accessOrderModel, selectLastElementInGroup); - sb.append(indent).append(INDENT).append("}\n") - .append(indent).append("}\n"); + sb.append(indent).append(TWO_INDENT).append("}\n") + .append(indent).append(INDENT).append("}\n"); } private static void generateAccessOrderListenerMethodForResetGroupCount( @@ -532,20 +538,21 @@ private static void generateAccessOrderListenerMethodForResetGroupCount( return; } - sb.append(indent).append("void onResetCountToIndex()\n") - .append(indent).append("{\n"); + sb.append("\n") + .append(indent).append(INDENT).append("void onResetCountToIndex()\n") + .append(indent).append(INDENT).append("{\n"); final AccessOrderModel.CodecInteraction resetCountToIndex = accessOrderModel.interactionFactory().resetCountToIndex(token); generateAccessOrderListener( sb, - indent + " ", + indent + TWO_INDENT, "reset count of repeating group", accessOrderModel, resetCountToIndex); - sb.append(indent).append("}\n"); + sb.append(indent).append(INDENT).append("}\n"); } private void generateGroups( @@ -759,8 +766,11 @@ private static void generateGroupClassHeader( codecStateNullAssignment); } - generateAccessOrderListenerMethodForNextGroupElement(sb, accessOrderModel, indent + INDENT, groupToken); + + sb.append("\n").append(indent).append("private:"); + generateAccessOrderListenerMethodForNextGroupElement(sb, accessOrderModel, indent, groupToken); generateAccessOrderListenerMethodForResetGroupCount(sb, accessOrderModel, indent, groupToken); + sb.append("\n").append(indent).append("public:"); final CharSequence onNextAccessOrderCall = null == accessOrderModel ? "" : generateAccessOrderListenerCall(accessOrderModel, indent + TWO_INDENT, "onNextElementAccessed"); @@ -883,7 +893,7 @@ private static void generateGroupProperty( generateAccessOrderListenerMethodForGroupWrap( sb, accessOrderModel, - indent + INDENT, + indent, token ); } @@ -995,7 +1005,7 @@ private void generateVarData( generateVarDataDescriptors( sb, token, propertyName, characterEncoding, lengthOfLengthField, indent); - generateAccessOrderListenerMethodForVarDataLength(sb, accessOrderModel, indent + INDENT, token); + generateAccessOrderListenerMethodForVarDataLength(sb, accessOrderModel, indent, token); final CharSequence lengthAccessListenerCall = generateAccessOrderListenerCall( accessOrderModel, indent + TWO_INDENT, @@ -1016,7 +1026,7 @@ private void generateVarData( lengthCppType, lengthAccessListenerCall); - generateAccessOrderListenerMethod(sb, accessOrderModel, indent + INDENT, token); + generateAccessOrderListenerMethod(sb, accessOrderModel, indent, token); final CharSequence accessOrderListenerCall = generateAccessOrderListenerCall(accessOrderModel, indent + TWO_INDENT, token); @@ -3114,7 +3124,7 @@ private void generateFields( generateFieldMetaAttributeMethod(sb, signalToken, indent); generateFieldCommonMethods(indent, sb, signalToken, encodingToken, propertyName); - generateAccessOrderListenerMethod(sb, accessOrderModel, indent + INDENT, signalToken); + generateAccessOrderListenerMethod(sb, accessOrderModel, indent, signalToken); switch (encodingToken.signal()) { From cef3e1f1e264d2c2fee55a57d0770770e4c7d1c0 Mon Sep 17 00:00:00 2001 From: Zach Bray Date: Wed, 20 Sep 2023 12:46:05 +0100 Subject: [PATCH 42/50] [C++, C#] Tidy up. Removes superfluous space before SBE_NOEXCEPT specifier in C++. Tightens access modifiers in C#. --- .../uk/co/real_logic/sbe/generation/cpp/CppGenerator.java | 2 +- .../real_logic/sbe/generation/csharp/CSharpGenerator.java | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java index c4fa59baa0..74234c515f 100755 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java @@ -2222,7 +2222,7 @@ private void generateArrayProperty( new Formatter(sb).format("\n" + indent + " #if __cplusplus >= 201703L\n" + - indent + " SBE_NODISCARD std::string_view get%1$sAsStringView() const %6$s\n" + + indent + " SBE_NODISCARD std::string_view get%1$sAsStringView() const%6$s\n" + indent + " {\n" + "%4$s" + "%5$s" + diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java index 63ee8ee62d..815582bfd3 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java @@ -256,7 +256,8 @@ private void generateGroupClassHeader( } sb.append("\n") - .append(indent).append(INDENT).append("public void NotPresent()\n") + // The "file" access modifier is more suitable but only available from C# 11 onwards. + .append(indent).append(INDENT).append("internal void NotPresent()\n") .append(indent).append(INDENT).append("{\n") .append(indent).append(TWO_INDENT).append("_count = 0;\n") .append(indent).append(TWO_INDENT).append("_index = 0;\n") @@ -1875,7 +1876,8 @@ private static void generateAccessOrderListenerMethodForVarDataLength( } sb.append("\n") - .append(indent).append("void ").append(accessOrderListenerMethodName(token, "Length")).append("()\n") + .append(indent).append("private void ") + .append(accessOrderListenerMethodName(token, "Length")).append("()\n") .append(indent).append("{\n"); final AccessOrderModel.CodecInteraction accessLength = From dab9ae2168ee2b87dc089945155eaf997218ec08 Mon Sep 17 00:00:00 2001 From: Zach Bray Date: Wed, 20 Sep 2023 13:12:35 +0100 Subject: [PATCH 43/50] [Java, C++] Tidy up. Removes extraneous space in Java output when access order checks are disabled. Removes unnecessary access modifiers in C++ when access order checks are disabled. --- .../real_logic/sbe/generation/cpp/CppGenerator.java | 11 +++++++---- .../sbe/generation/java/JavaGenerator.java | 13 +++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java index 74234c515f..cb576f2e7b 100755 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java @@ -767,10 +767,13 @@ private static void generateGroupClassHeader( } - sb.append("\n").append(indent).append("private:"); - generateAccessOrderListenerMethodForNextGroupElement(sb, accessOrderModel, indent, groupToken); - generateAccessOrderListenerMethodForResetGroupCount(sb, accessOrderModel, indent, groupToken); - sb.append("\n").append(indent).append("public:"); + if (null != accessOrderModel) + { + sb.append("\n").append(indent).append("private:"); + generateAccessOrderListenerMethodForNextGroupElement(sb, accessOrderModel, indent, groupToken); + generateAccessOrderListenerMethodForResetGroupCount(sb, accessOrderModel, indent, groupToken); + sb.append("\n").append(indent).append("public:"); + } final CharSequence onNextAccessOrderCall = null == accessOrderModel ? "" : generateAccessOrderListenerCall(accessOrderModel, indent + TWO_INDENT, "onNextElementAccessed"); diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java index 6fa27c66b2..7ba6b3bcea 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java @@ -654,7 +654,7 @@ private static void generateAccessOrderListenerMethodForNextGroupElement( selectLastElementInGroup); sb.append(indent).append(" }\n") - .append(indent).append("}\n"); + .append(indent).append("}\n\n"); } private static void generateAccessOrderListenerMethodForResetGroupCount( @@ -681,7 +681,7 @@ private static void generateAccessOrderListenerMethodForResetGroupCount( accessOrderModel, resetCountToIndex); - sb.append(indent).append("}\n"); + sb.append(indent).append("}\n\n"); } private static void generateAccessOrderListenerMethodForVarDataLength( @@ -964,8 +964,7 @@ private void generateGroupDecoderClassHeader( generateAccessOrderListenerMethodForNextGroupElement(sb, accessOrderModel, indent + " ", groupToken); - sb.append("\n") - .append(indent).append(" public ").append(className).append(" next()\n") + sb.append(indent).append(" public ").append(className).append(" next()\n") .append(indent).append(" {\n") .append(indent).append(" if (index >= count)\n") .append(indent).append(" {\n") @@ -1105,8 +1104,7 @@ private void generateGroupEncoderClassHeader( generateAccessOrderListenerMethodForNextGroupElement(sb, accessOrderModel, ind + " ", groupToken); generateAccessOrderListenerMethodForResetGroupCount(sb, accessOrderModel, ind + " ", groupToken); - sb.append("\n") - .append(ind).append(" public ").append(encoderName(groupName)).append(" next()\n") + sb.append(ind).append(" public ").append(encoderName(groupName)).append(" next()\n") .append(ind).append(" {\n") .append(generateAccessOrderListenerCall(accessOrderModel, ind + " ", "onNextElementAccessed")) .append(ind).append(" if (index >= count)\n") @@ -1123,8 +1121,7 @@ private void generateGroupEncoderClassHeader( final String resetCountPut = generatePut( numInGroupTypeCast, countOffset, numInGroupValue, byteOrderString(numInGroupToken.encoding())); - sb.append("\n") - .append(ind).append(" public int resetCountToIndex()\n") + sb.append(ind).append(" public int resetCountToIndex()\n") .append(ind).append(" {\n") .append(generateAccessOrderListenerCall(accessOrderModel, ind + " ", "onResetCountToIndex")) .append(ind).append(" count = index;\n") From 69d13b61147840189254879fa8b7989aca868fae Mon Sep 17 00:00:00 2001 From: Zach Bray Date: Fri, 20 Oct 2023 11:06:17 +0100 Subject: [PATCH 44/50] [Java, C#, C++] Rename flags to enable field access order checking. The aim of this change is to avoid naming collisions with other flags, particularly in financial domains where "order" is used frequently. --- build.gradle | 10 +- csharp/sbe-generated/sbe-generated.csproj | 4 +- .../generation/common/AccessOrderModel.java | 2 +- .../sbe/generation/cpp/CppGenerator.java | 8 +- .../generation/csharp/CSharpGenerator.java | 10 +- .../sbe/generation/java/JavaGenerator.java | 12 +- .../sbe/ir/generated/FrameCodecDecoder.java | 50 +++--- .../sbe/ir/generated/FrameCodecEncoder.java | 32 ++-- .../sbe/ir/generated/TokenCodecDecoder.java | 168 +++++++++--------- .../sbe/ir/generated/TokenCodecEncoder.java | 94 +++++----- sbe-tool/src/test/cpp/CMakeLists.txt | 4 +- 11 files changed, 197 insertions(+), 197 deletions(-) diff --git a/build.gradle b/build.gradle index 2d7304a227..5bc37ff579 100644 --- a/build.gradle +++ b/build.gradle @@ -280,7 +280,7 @@ project(':sbe-tool') { 'sbe.target.language': 'Java', 'sbe.validation.stop.on.error': 'true', 'sbe.validation.xsd': validationXsdPath, - 'sbe.generate.access.order.checks': 'true') + 'sbe.generate.sequencing.checks': 'true') args = ['src/test/resources/json-printer-test-schema.xml', 'src/test/resources/composite-elements-schema.xml', 'src/test/resources/field-order-check-schema.xml'] @@ -544,7 +544,7 @@ project(':sbe-benchmarks') { 'sbe.validation.xsd': validationXsdPath, 'sbe.java.encoding.buffer.type': 'org.agrona.concurrent.UnsafeBuffer', 'sbe.java.decoding.buffer.type': 'org.agrona.concurrent.UnsafeBuffer', - 'sbe.generate.access.order.checks': 'false') + 'sbe.generate.sequencing.checks': 'false') args = ['src/main/resources/car.xml', 'src/main/resources/fix-message-samples.xml'] } @@ -734,7 +734,7 @@ tasks.register('generateCSharpCodecsTests', JavaExec) { 'sbe.target.language': 'uk.co.real_logic.sbe.generation.csharp.CSharp', 'sbe.xinclude.aware': 'true', 'sbe.validation.xsd': validationXsdPath, - 'sbe.generate.access.order.checks': 'true') + 'sbe.generate.sequencing.checks': 'true') args = ['sbe-tool/src/test/resources/FixBinary.xml', 'sbe-tool/src/test/resources/issue435.xml', 'sbe-tool/src/test/resources/issue483.xml', @@ -759,7 +759,7 @@ tasks.register('generateJavaIrCodecs', JavaExec) { 'sbe.output.dir': 'sbe-tool/src/main/java', 'sbe.target.language': 'Java', 'sbe.validation.xsd': validationXsdPath, - 'sbe.generate.access.order.checks': 'true') + 'sbe.generate.sequencing.checks': 'true') args = ['sbe-tool/src/main/resources/sbe-ir.xml'] } @@ -770,7 +770,7 @@ tasks.register('generateCppIrCodecs', JavaExec) { 'sbe.output.dir': 'sbe-tool/src/main/cpp', 'sbe.target.language': 'cpp', 'sbe.validation.xsd': validationXsdPath, - 'sbe.generate.access.order.checks': 'true') + 'sbe.generate.sequencing.checks': 'true') args = ['sbe-tool/src/main/resources/sbe-ir.xml'] } diff --git a/csharp/sbe-generated/sbe-generated.csproj b/csharp/sbe-generated/sbe-generated.csproj index 362d85ba05..cedbc4321f 100644 --- a/csharp/sbe-generated/sbe-generated.csproj +++ b/csharp/sbe-generated/sbe-generated.csproj @@ -8,11 +8,11 @@ - TRACE,ENABLE_ACCESS_ORDER_CHECKS + TRACE,SBE_ENABLE_SEQUENCING_CHECKS - TRACE,ENABLE_ACCESS_ORDER_CHECKS + TRACE,SBE_ENABLE_SEQUENCING_CHECKS diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/AccessOrderModel.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/AccessOrderModel.java index 970e3caaac..85cd9df9bf 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/AccessOrderModel.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/AccessOrderModel.java @@ -41,7 +41,7 @@ public final class AccessOrderModel { private static final boolean GENERATE_ACCESS_ORDER_CHECKS = Boolean.parseBoolean( - System.getProperty("sbe.generate.access.order.checks", "false")); + System.getProperty("sbe.generate.sequencing.checks", "false")); private final Map groupPathsByField = new HashMap<>(); private final Set topLevelBlockFields = new HashSet<>(); private final CodecInteraction.HashConsingFactory interactionFactory = diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java index cb576f2e7b..074b479731 100755 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java @@ -207,7 +207,7 @@ private static CharSequence generateFullyEncodedCheck(final AccessOrderModel acc sb.append(indent).append("void checkEncodingIsComplete()\n") .append(indent).append("{\n") - .append("#if defined(ENABLE_ACCESS_ORDER_CHECKS)\n") + .append("#if defined(SBE_ENABLE_SEQUENCING_CHECKS)\n") .append(indent).append(INDENT).append("switch (m_codecState)\n") .append(indent).append(INDENT).append("{\n"); @@ -321,7 +321,7 @@ private static CharSequence generateAccessOrderListenerCall( } final StringBuilder sb = new StringBuilder(); - sb.append("#if defined(ENABLE_ACCESS_ORDER_CHECKS)\n") + sb.append("#if defined(SBE_ENABLE_SEQUENCING_CHECKS)\n") .append(indent).append(methodName).append("("); for (int i = 0; i < arguments.length; i++) @@ -3083,7 +3083,7 @@ private CharSequence generateDecoderWrapListenerCall(final AccessOrderModel acce if (accessOrderModel.versionCount() == 1) { final StringBuilder sb = new StringBuilder(); - sb.append("#if defined(ENABLE_ACCESS_ORDER_CHECKS)\n") + sb.append("#if defined(SBE_ENABLE_SEQUENCING_CHECKS)\n") .append(TWO_INDENT).append("codecState(") .append(qualifiedStateCase(accessOrderModel.latestVersionWrappedState())).append(");\n") .append("#endif\n"); @@ -3101,7 +3101,7 @@ private CharSequence generateEncoderWrapListener(final AccessOrderModel accessOr } final StringBuilder sb = new StringBuilder(); - sb.append("#if defined(ENABLE_ACCESS_ORDER_CHECKS)\n") + sb.append("#if defined(SBE_ENABLE_SEQUENCING_CHECKS)\n") .append(TWO_INDENT).append("codecState(") .append(qualifiedStateCase(accessOrderModel.latestVersionWrappedState())) .append(");\n") diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java index 815582bfd3..6d4f143fff 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java @@ -1583,7 +1583,7 @@ private static CharSequence generateFullyEncodedCheck( sb.append(indent).append("public void CheckEncodingIsComplete()\n") .append(indent).append("{\n") - .append("#if ENABLE_ACCESS_ORDER_CHECKS\n") + .append("#if SBE_ENABLE_SEQUENCING_CHECKS\n") .append(indent).append(INDENT).append("switch (_codecState)\n") .append(indent).append(INDENT).append("{\n"); @@ -1670,7 +1670,7 @@ private static CharSequence generateAccessOrderListenerCall( } final StringBuilder sb = new StringBuilder(); - sb.append("#if ENABLE_ACCESS_ORDER_CHECKS\n") + sb.append("#if SBE_ENABLE_SEQUENCING_CHECKS\n") .append(indent).append(methodName).append("("); for (int i = 0; i < arguments.length; i++) @@ -1936,7 +1936,7 @@ private CharSequence generateEncoderWrapListener( } final StringBuilder sb = new StringBuilder(); - sb.append("#if ENABLE_ACCESS_ORDER_CHECKS\n") + sb.append("#if SBE_ENABLE_SEQUENCING_CHECKS\n") .append(indent).append("codecState(") .append(qualifiedStateCase(accessOrderModel.latestVersionWrappedState())) .append(");\n") @@ -2556,7 +2556,7 @@ private CharSequence generateDisplay( append(sb, TWO_INDENT, " int originalLimit = this.Limit;"); if (null != accessOrderModel) { - sb.append("#if ENABLE_ACCESS_ORDER_CHECKS\n"); + sb.append("#if SBE_ENABLE_SEQUENCING_CHECKS\n"); append(sb, TWO_INDENT, " CodecState originalState = _codecState;"); sb.append(THREE_INDENT).append("_codecState = ") .append(qualifiedStateCase(accessOrderModel.notWrappedState())).append(";\n"); @@ -2588,7 +2588,7 @@ private CharSequence generateDisplay( sb.append('\n'); if (null != accessOrderModel) { - sb.append("#if ENABLE_ACCESS_ORDER_CHECKS\n"); + sb.append("#if SBE_ENABLE_SEQUENCING_CHECKS\n"); append(sb, TWO_INDENT, " _codecState = originalState;"); sb.append("#endif\n"); } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java index 7ba6b3bcea..e969396f8c 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java @@ -307,9 +307,9 @@ private static CharSequence generateFieldOrderStates(final AccessOrderModel acce sb.append(" private static final boolean ENABLE_BOUNDS_CHECKS = ") .append("!Boolean.getBoolean(\"agrona.disable.bounds.checks\");\n\n"); - sb.append(" private static final boolean ENABLE_ACCESS_ORDER_CHECKS = ") + sb.append(" private static final boolean SBE_ENABLE_SEQUENCING_CHECKS = ") .append("Boolean.parseBoolean(System.getProperty(\n") - .append(" \"sbe.enable.access.order.checks\",\n") + .append(" \"sbe.enable.sequencing.checks\",\n") .append(" Boolean.toString(ENABLE_BOUNDS_CHECKS)));\n\n"); sb.append(" /**\n"); @@ -410,7 +410,7 @@ private static void generateFullyEncodedCheck( sb.append(" public void checkEncodingIsComplete()\n") .append(" {\n") - .append(" if (ENABLE_ACCESS_ORDER_CHECKS)\n") + .append(" if (SBE_ENABLE_SEQUENCING_CHECKS)\n") .append(" {\n") .append(" switch (codecState)\n") .append(" {\n"); @@ -493,7 +493,7 @@ private static CharSequence generateAccessOrderListenerCall( } final StringBuilder sb = new StringBuilder(); - sb.append(indent).append("if (ENABLE_ACCESS_ORDER_CHECKS)\n") + sb.append(indent).append("if (SBE_ENABLE_SEQUENCING_CHECKS)\n") .append(indent).append("{\n") .append(indent).append(" ").append(methodName).append("("); @@ -755,7 +755,7 @@ private CharSequence generateEncoderWrapListener( } final StringBuilder sb = new StringBuilder(); - sb.append(indent).append("if (ENABLE_ACCESS_ORDER_CHECKS)") + sb.append(indent).append("if (SBE_ENABLE_SEQUENCING_CHECKS)") .append("\n").append(indent).append("{\n") .append(indent).append(" codecState(") .append(qualifiedStateCase(accessOrderModel.latestVersionWrappedState())) @@ -3424,7 +3424,7 @@ private CharSequence generateDecoderFlyweightCode( if (null != accessOrderModel) { - methods.append(" if (ENABLE_ACCESS_ORDER_CHECKS)\n") + methods.append(" if (SBE_ENABLE_SEQUENCING_CHECKS)\n") .append(" {\n") .append(" codecState(currentCodecState);\n") .append(" }\n\n"); diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java index a65b6520bc..005faad113 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java @@ -13,8 +13,8 @@ public final class FrameCodecDecoder { private static final boolean ENABLE_BOUNDS_CHECKS = !Boolean.getBoolean("agrona.disable.bounds.checks"); - private static final boolean ENABLE_ACCESS_ORDER_CHECKS = Boolean.parseBoolean(System.getProperty( - "sbe.enable.access.order.checks", + private static final boolean SBE_ENABLE_SEQUENCING_CHECKS = Boolean.parseBoolean(System.getProperty( + "sbe.enable.sequencing.checks", Boolean.toString(ENABLE_BOUNDS_CHECKS))); /** @@ -172,7 +172,7 @@ public FrameCodecDecoder wrap( this.actingVersion = actingVersion; limit(offset + actingBlockLength); - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onWrap(actingVersion); } @@ -213,7 +213,7 @@ public int sbeDecodedLength() final int decodedLength = encodedLength(); limit(currentLimit); - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { codecState(currentCodecState); } @@ -299,7 +299,7 @@ public static int irIdMaxValue() public int irId() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onIrIdAccessed(); } @@ -366,7 +366,7 @@ public static int irVersionMaxValue() public int irVersion() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onIrVersionAccessed(); } @@ -433,7 +433,7 @@ public static int schemaVersionMaxValue() public int schemaVersion() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onSchemaVersionAccessed(); } @@ -504,7 +504,7 @@ private void onPackageNameAccessed() public int packageNameLength() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onPackageNameLengthAccessed(); } @@ -515,7 +515,7 @@ public int packageNameLength() public int skipPackageName() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onPackageNameAccessed(); } @@ -531,7 +531,7 @@ public int skipPackageName() public int getPackageName(final MutableDirectBuffer dst, final int dstOffset, final int length) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onPackageNameAccessed(); } @@ -548,7 +548,7 @@ public int getPackageName(final MutableDirectBuffer dst, final int dstOffset, fi public int getPackageName(final byte[] dst, final int dstOffset, final int length) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onPackageNameAccessed(); } @@ -565,7 +565,7 @@ public int getPackageName(final byte[] dst, final int dstOffset, final int lengt public void wrapPackageName(final DirectBuffer wrapBuffer) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onPackageNameAccessed(); } @@ -579,7 +579,7 @@ public void wrapPackageName(final DirectBuffer wrapBuffer) public String packageName() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onPackageNameAccessed(); } @@ -662,7 +662,7 @@ private void onNamespaceNameAccessed() public int namespaceNameLength() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onNamespaceNameLengthAccessed(); } @@ -673,7 +673,7 @@ public int namespaceNameLength() public int skipNamespaceName() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onNamespaceNameAccessed(); } @@ -689,7 +689,7 @@ public int skipNamespaceName() public int getNamespaceName(final MutableDirectBuffer dst, final int dstOffset, final int length) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onNamespaceNameAccessed(); } @@ -706,7 +706,7 @@ public int getNamespaceName(final MutableDirectBuffer dst, final int dstOffset, public int getNamespaceName(final byte[] dst, final int dstOffset, final int length) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onNamespaceNameAccessed(); } @@ -723,7 +723,7 @@ public int getNamespaceName(final byte[] dst, final int dstOffset, final int len public void wrapNamespaceName(final DirectBuffer wrapBuffer) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onNamespaceNameAccessed(); } @@ -737,7 +737,7 @@ public void wrapNamespaceName(final DirectBuffer wrapBuffer) public String namespaceName() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onNamespaceNameAccessed(); } @@ -820,7 +820,7 @@ private void onSemanticVersionAccessed() public int semanticVersionLength() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onSemanticVersionLengthAccessed(); } @@ -831,7 +831,7 @@ public int semanticVersionLength() public int skipSemanticVersion() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onSemanticVersionAccessed(); } @@ -847,7 +847,7 @@ public int skipSemanticVersion() public int getSemanticVersion(final MutableDirectBuffer dst, final int dstOffset, final int length) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onSemanticVersionAccessed(); } @@ -864,7 +864,7 @@ public int getSemanticVersion(final MutableDirectBuffer dst, final int dstOffset public int getSemanticVersion(final byte[] dst, final int dstOffset, final int length) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onSemanticVersionAccessed(); } @@ -881,7 +881,7 @@ public int getSemanticVersion(final byte[] dst, final int dstOffset, final int l public void wrapSemanticVersion(final DirectBuffer wrapBuffer) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onSemanticVersionAccessed(); } @@ -895,7 +895,7 @@ public void wrapSemanticVersion(final DirectBuffer wrapBuffer) public String semanticVersion() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onSemanticVersionAccessed(); } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java index 5d866e6dfc..ce88e4ed8c 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java @@ -13,8 +13,8 @@ public final class FrameCodecEncoder { private static final boolean ENABLE_BOUNDS_CHECKS = !Boolean.getBoolean("agrona.disable.bounds.checks"); - private static final boolean ENABLE_ACCESS_ORDER_CHECKS = Boolean.parseBoolean(System.getProperty( - "sbe.enable.access.order.checks", + private static final boolean SBE_ENABLE_SEQUENCING_CHECKS = Boolean.parseBoolean(System.getProperty( + "sbe.enable.sequencing.checks", Boolean.toString(ENABLE_BOUNDS_CHECKS))); /** @@ -151,7 +151,7 @@ public FrameCodecEncoder wrap(final MutableDirectBuffer buffer, final int offset this.offset = offset; limit(offset + BLOCK_LENGTH); - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { codecState(CodecStates.V0_BLOCK); } @@ -245,7 +245,7 @@ public static int irIdMaxValue() public FrameCodecEncoder irId(final int value) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onIrIdAccessed(); } @@ -313,7 +313,7 @@ public static int irVersionMaxValue() public FrameCodecEncoder irVersion(final int value) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onIrVersionAccessed(); } @@ -381,7 +381,7 @@ public static int schemaVersionMaxValue() public FrameCodecEncoder schemaVersion(final int value) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onSchemaVersionAccessed(); } @@ -438,7 +438,7 @@ public FrameCodecEncoder putPackageName(final DirectBuffer src, final int srcOff throw new IllegalStateException("length > maxValue for type: " + length); } - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onPackageNameAccessed(); } @@ -459,7 +459,7 @@ public FrameCodecEncoder putPackageName(final byte[] src, final int srcOffset, f throw new IllegalStateException("length > maxValue for type: " + length); } - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onPackageNameAccessed(); } @@ -483,7 +483,7 @@ public FrameCodecEncoder packageName(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onPackageNameAccessed(); } @@ -544,7 +544,7 @@ public FrameCodecEncoder putNamespaceName(final DirectBuffer src, final int srcO throw new IllegalStateException("length > maxValue for type: " + length); } - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onNamespaceNameAccessed(); } @@ -565,7 +565,7 @@ public FrameCodecEncoder putNamespaceName(final byte[] src, final int srcOffset, throw new IllegalStateException("length > maxValue for type: " + length); } - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onNamespaceNameAccessed(); } @@ -589,7 +589,7 @@ public FrameCodecEncoder namespaceName(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onNamespaceNameAccessed(); } @@ -650,7 +650,7 @@ public FrameCodecEncoder putSemanticVersion(final DirectBuffer src, final int sr throw new IllegalStateException("length > maxValue for type: " + length); } - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onSemanticVersionAccessed(); } @@ -671,7 +671,7 @@ public FrameCodecEncoder putSemanticVersion(final byte[] src, final int srcOffse throw new IllegalStateException("length > maxValue for type: " + length); } - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onSemanticVersionAccessed(); } @@ -695,7 +695,7 @@ public FrameCodecEncoder semanticVersion(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onSemanticVersionAccessed(); } @@ -734,7 +734,7 @@ public StringBuilder appendTo(final StringBuilder builder) public void checkEncodingIsComplete() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { switch (codecState) { diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java index 51a321ea99..e896601355 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java @@ -13,8 +13,8 @@ public final class TokenCodecDecoder { private static final boolean ENABLE_BOUNDS_CHECKS = !Boolean.getBoolean("agrona.disable.bounds.checks"); - private static final boolean ENABLE_ACCESS_ORDER_CHECKS = Boolean.parseBoolean(System.getProperty( - "sbe.enable.access.order.checks", + private static final boolean SBE_ENABLE_SEQUENCING_CHECKS = Boolean.parseBoolean(System.getProperty( + "sbe.enable.sequencing.checks", Boolean.toString(ENABLE_BOUNDS_CHECKS))); /** @@ -219,7 +219,7 @@ public TokenCodecDecoder wrap( this.actingVersion = actingVersion; limit(offset + actingBlockLength); - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onWrap(actingVersion); } @@ -260,7 +260,7 @@ public int sbeDecodedLength() final int decodedLength = encodedLength(); limit(currentLimit); - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { codecState(currentCodecState); } @@ -346,7 +346,7 @@ public static int tokenOffsetMaxValue() public int tokenOffset() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onTokenOffsetAccessed(); } @@ -413,7 +413,7 @@ public static int tokenSizeMaxValue() public int tokenSize() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onTokenSizeAccessed(); } @@ -480,7 +480,7 @@ public static int fieldIdMaxValue() public int fieldId() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onFieldIdAccessed(); } @@ -547,7 +547,7 @@ public static int tokenVersionMaxValue() public int tokenVersion() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onTokenVersionAccessed(); } @@ -614,7 +614,7 @@ public static int componentTokenCountMaxValue() public int componentTokenCount() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onComponentTokenCountAccessed(); } @@ -666,7 +666,7 @@ private void onSignalAccessed() public short signalRaw() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onSignalAccessed(); } @@ -676,7 +676,7 @@ public short signalRaw() public SignalCodec signal() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onSignalAccessed(); } @@ -728,7 +728,7 @@ private void onPrimitiveTypeAccessed() public short primitiveTypeRaw() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onPrimitiveTypeAccessed(); } @@ -738,7 +738,7 @@ public short primitiveTypeRaw() public PrimitiveTypeCodec primitiveType() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onPrimitiveTypeAccessed(); } @@ -790,7 +790,7 @@ private void onByteOrderAccessed() public short byteOrderRaw() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onByteOrderAccessed(); } @@ -800,7 +800,7 @@ public short byteOrderRaw() public ByteOrderCodec byteOrder() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onByteOrderAccessed(); } @@ -852,7 +852,7 @@ private void onPresenceAccessed() public short presenceRaw() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onPresenceAccessed(); } @@ -862,7 +862,7 @@ public short presenceRaw() public PresenceCodec presence() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onPresenceAccessed(); } @@ -929,7 +929,7 @@ public static int deprecatedMaxValue() public int deprecated() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onDeprecatedAccessed(); } @@ -1000,7 +1000,7 @@ private void onNameAccessed() public int nameLength() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onNameLengthAccessed(); } @@ -1011,7 +1011,7 @@ public int nameLength() public int skipName() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onNameAccessed(); } @@ -1027,7 +1027,7 @@ public int skipName() public int getName(final MutableDirectBuffer dst, final int dstOffset, final int length) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onNameAccessed(); } @@ -1044,7 +1044,7 @@ public int getName(final MutableDirectBuffer dst, final int dstOffset, final int public int getName(final byte[] dst, final int dstOffset, final int length) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onNameAccessed(); } @@ -1061,7 +1061,7 @@ public int getName(final byte[] dst, final int dstOffset, final int length) public void wrapName(final DirectBuffer wrapBuffer) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onNameAccessed(); } @@ -1075,7 +1075,7 @@ public void wrapName(final DirectBuffer wrapBuffer) public String name() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onNameAccessed(); } @@ -1158,7 +1158,7 @@ private void onConstValueAccessed() public int constValueLength() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onConstValueLengthAccessed(); } @@ -1169,7 +1169,7 @@ public int constValueLength() public int skipConstValue() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onConstValueAccessed(); } @@ -1185,7 +1185,7 @@ public int skipConstValue() public int getConstValue(final MutableDirectBuffer dst, final int dstOffset, final int length) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onConstValueAccessed(); } @@ -1202,7 +1202,7 @@ public int getConstValue(final MutableDirectBuffer dst, final int dstOffset, fin public int getConstValue(final byte[] dst, final int dstOffset, final int length) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onConstValueAccessed(); } @@ -1219,7 +1219,7 @@ public int getConstValue(final byte[] dst, final int dstOffset, final int length public void wrapConstValue(final DirectBuffer wrapBuffer) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onConstValueAccessed(); } @@ -1233,7 +1233,7 @@ public void wrapConstValue(final DirectBuffer wrapBuffer) public String constValue() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onConstValueAccessed(); } @@ -1316,7 +1316,7 @@ private void onMinValueAccessed() public int minValueLength() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onMinValueLengthAccessed(); } @@ -1327,7 +1327,7 @@ public int minValueLength() public int skipMinValue() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onMinValueAccessed(); } @@ -1343,7 +1343,7 @@ public int skipMinValue() public int getMinValue(final MutableDirectBuffer dst, final int dstOffset, final int length) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onMinValueAccessed(); } @@ -1360,7 +1360,7 @@ public int getMinValue(final MutableDirectBuffer dst, final int dstOffset, final public int getMinValue(final byte[] dst, final int dstOffset, final int length) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onMinValueAccessed(); } @@ -1377,7 +1377,7 @@ public int getMinValue(final byte[] dst, final int dstOffset, final int length) public void wrapMinValue(final DirectBuffer wrapBuffer) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onMinValueAccessed(); } @@ -1391,7 +1391,7 @@ public void wrapMinValue(final DirectBuffer wrapBuffer) public String minValue() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onMinValueAccessed(); } @@ -1474,7 +1474,7 @@ private void onMaxValueAccessed() public int maxValueLength() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onMaxValueLengthAccessed(); } @@ -1485,7 +1485,7 @@ public int maxValueLength() public int skipMaxValue() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onMaxValueAccessed(); } @@ -1501,7 +1501,7 @@ public int skipMaxValue() public int getMaxValue(final MutableDirectBuffer dst, final int dstOffset, final int length) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onMaxValueAccessed(); } @@ -1518,7 +1518,7 @@ public int getMaxValue(final MutableDirectBuffer dst, final int dstOffset, final public int getMaxValue(final byte[] dst, final int dstOffset, final int length) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onMaxValueAccessed(); } @@ -1535,7 +1535,7 @@ public int getMaxValue(final byte[] dst, final int dstOffset, final int length) public void wrapMaxValue(final DirectBuffer wrapBuffer) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onMaxValueAccessed(); } @@ -1549,7 +1549,7 @@ public void wrapMaxValue(final DirectBuffer wrapBuffer) public String maxValue() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onMaxValueAccessed(); } @@ -1632,7 +1632,7 @@ private void onNullValueAccessed() public int nullValueLength() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onNullValueLengthAccessed(); } @@ -1643,7 +1643,7 @@ public int nullValueLength() public int skipNullValue() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onNullValueAccessed(); } @@ -1659,7 +1659,7 @@ public int skipNullValue() public int getNullValue(final MutableDirectBuffer dst, final int dstOffset, final int length) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onNullValueAccessed(); } @@ -1676,7 +1676,7 @@ public int getNullValue(final MutableDirectBuffer dst, final int dstOffset, fina public int getNullValue(final byte[] dst, final int dstOffset, final int length) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onNullValueAccessed(); } @@ -1693,7 +1693,7 @@ public int getNullValue(final byte[] dst, final int dstOffset, final int length) public void wrapNullValue(final DirectBuffer wrapBuffer) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onNullValueAccessed(); } @@ -1707,7 +1707,7 @@ public void wrapNullValue(final DirectBuffer wrapBuffer) public String nullValue() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onNullValueAccessed(); } @@ -1790,7 +1790,7 @@ private void onCharacterEncodingAccessed() public int characterEncodingLength() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onCharacterEncodingLengthAccessed(); } @@ -1801,7 +1801,7 @@ public int characterEncodingLength() public int skipCharacterEncoding() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onCharacterEncodingAccessed(); } @@ -1817,7 +1817,7 @@ public int skipCharacterEncoding() public int getCharacterEncoding(final MutableDirectBuffer dst, final int dstOffset, final int length) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onCharacterEncodingAccessed(); } @@ -1834,7 +1834,7 @@ public int getCharacterEncoding(final MutableDirectBuffer dst, final int dstOffs public int getCharacterEncoding(final byte[] dst, final int dstOffset, final int length) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onCharacterEncodingAccessed(); } @@ -1851,7 +1851,7 @@ public int getCharacterEncoding(final byte[] dst, final int dstOffset, final int public void wrapCharacterEncoding(final DirectBuffer wrapBuffer) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onCharacterEncodingAccessed(); } @@ -1865,7 +1865,7 @@ public void wrapCharacterEncoding(final DirectBuffer wrapBuffer) public String characterEncoding() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onCharacterEncodingAccessed(); } @@ -1948,7 +1948,7 @@ private void onEpochAccessed() public int epochLength() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onEpochLengthAccessed(); } @@ -1959,7 +1959,7 @@ public int epochLength() public int skipEpoch() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onEpochAccessed(); } @@ -1975,7 +1975,7 @@ public int skipEpoch() public int getEpoch(final MutableDirectBuffer dst, final int dstOffset, final int length) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onEpochAccessed(); } @@ -1992,7 +1992,7 @@ public int getEpoch(final MutableDirectBuffer dst, final int dstOffset, final in public int getEpoch(final byte[] dst, final int dstOffset, final int length) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onEpochAccessed(); } @@ -2009,7 +2009,7 @@ public int getEpoch(final byte[] dst, final int dstOffset, final int length) public void wrapEpoch(final DirectBuffer wrapBuffer) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onEpochAccessed(); } @@ -2023,7 +2023,7 @@ public void wrapEpoch(final DirectBuffer wrapBuffer) public String epoch() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onEpochAccessed(); } @@ -2106,7 +2106,7 @@ private void onTimeUnitAccessed() public int timeUnitLength() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onTimeUnitLengthAccessed(); } @@ -2117,7 +2117,7 @@ public int timeUnitLength() public int skipTimeUnit() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onTimeUnitAccessed(); } @@ -2133,7 +2133,7 @@ public int skipTimeUnit() public int getTimeUnit(final MutableDirectBuffer dst, final int dstOffset, final int length) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onTimeUnitAccessed(); } @@ -2150,7 +2150,7 @@ public int getTimeUnit(final MutableDirectBuffer dst, final int dstOffset, final public int getTimeUnit(final byte[] dst, final int dstOffset, final int length) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onTimeUnitAccessed(); } @@ -2167,7 +2167,7 @@ public int getTimeUnit(final byte[] dst, final int dstOffset, final int length) public void wrapTimeUnit(final DirectBuffer wrapBuffer) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onTimeUnitAccessed(); } @@ -2181,7 +2181,7 @@ public void wrapTimeUnit(final DirectBuffer wrapBuffer) public String timeUnit() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onTimeUnitAccessed(); } @@ -2264,7 +2264,7 @@ private void onSemanticTypeAccessed() public int semanticTypeLength() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onSemanticTypeLengthAccessed(); } @@ -2275,7 +2275,7 @@ public int semanticTypeLength() public int skipSemanticType() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onSemanticTypeAccessed(); } @@ -2291,7 +2291,7 @@ public int skipSemanticType() public int getSemanticType(final MutableDirectBuffer dst, final int dstOffset, final int length) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onSemanticTypeAccessed(); } @@ -2308,7 +2308,7 @@ public int getSemanticType(final MutableDirectBuffer dst, final int dstOffset, f public int getSemanticType(final byte[] dst, final int dstOffset, final int length) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onSemanticTypeAccessed(); } @@ -2325,7 +2325,7 @@ public int getSemanticType(final byte[] dst, final int dstOffset, final int leng public void wrapSemanticType(final DirectBuffer wrapBuffer) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onSemanticTypeAccessed(); } @@ -2339,7 +2339,7 @@ public void wrapSemanticType(final DirectBuffer wrapBuffer) public String semanticType() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onSemanticTypeAccessed(); } @@ -2422,7 +2422,7 @@ private void onDescriptionAccessed() public int descriptionLength() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onDescriptionLengthAccessed(); } @@ -2433,7 +2433,7 @@ public int descriptionLength() public int skipDescription() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onDescriptionAccessed(); } @@ -2449,7 +2449,7 @@ public int skipDescription() public int getDescription(final MutableDirectBuffer dst, final int dstOffset, final int length) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onDescriptionAccessed(); } @@ -2466,7 +2466,7 @@ public int getDescription(final MutableDirectBuffer dst, final int dstOffset, fi public int getDescription(final byte[] dst, final int dstOffset, final int length) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onDescriptionAccessed(); } @@ -2483,7 +2483,7 @@ public int getDescription(final byte[] dst, final int dstOffset, final int lengt public void wrapDescription(final DirectBuffer wrapBuffer) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onDescriptionAccessed(); } @@ -2497,7 +2497,7 @@ public void wrapDescription(final DirectBuffer wrapBuffer) public String description() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onDescriptionAccessed(); } @@ -2580,7 +2580,7 @@ private void onReferencedNameAccessed() public int referencedNameLength() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onReferencedNameLengthAccessed(); } @@ -2591,7 +2591,7 @@ public int referencedNameLength() public int skipReferencedName() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onReferencedNameAccessed(); } @@ -2607,7 +2607,7 @@ public int skipReferencedName() public int getReferencedName(final MutableDirectBuffer dst, final int dstOffset, final int length) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onReferencedNameAccessed(); } @@ -2624,7 +2624,7 @@ public int getReferencedName(final MutableDirectBuffer dst, final int dstOffset, public int getReferencedName(final byte[] dst, final int dstOffset, final int length) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onReferencedNameAccessed(); } @@ -2641,7 +2641,7 @@ public int getReferencedName(final byte[] dst, final int dstOffset, final int le public void wrapReferencedName(final DirectBuffer wrapBuffer) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onReferencedNameAccessed(); } @@ -2655,7 +2655,7 @@ public void wrapReferencedName(final DirectBuffer wrapBuffer) public String referencedName() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onReferencedNameAccessed(); } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java index 4aa64244ca..293377446d 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java @@ -13,8 +13,8 @@ public final class TokenCodecEncoder { private static final boolean ENABLE_BOUNDS_CHECKS = !Boolean.getBoolean("agrona.disable.bounds.checks"); - private static final boolean ENABLE_ACCESS_ORDER_CHECKS = Boolean.parseBoolean(System.getProperty( - "sbe.enable.access.order.checks", + private static final boolean SBE_ENABLE_SEQUENCING_CHECKS = Boolean.parseBoolean(System.getProperty( + "sbe.enable.sequencing.checks", Boolean.toString(ENABLE_BOUNDS_CHECKS))); /** @@ -198,7 +198,7 @@ public TokenCodecEncoder wrap(final MutableDirectBuffer buffer, final int offset this.offset = offset; limit(offset + BLOCK_LENGTH); - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { codecState(CodecStates.V0_BLOCK); } @@ -292,7 +292,7 @@ public static int tokenOffsetMaxValue() public TokenCodecEncoder tokenOffset(final int value) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onTokenOffsetAccessed(); } @@ -360,7 +360,7 @@ public static int tokenSizeMaxValue() public TokenCodecEncoder tokenSize(final int value) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onTokenSizeAccessed(); } @@ -428,7 +428,7 @@ public static int fieldIdMaxValue() public TokenCodecEncoder fieldId(final int value) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onFieldIdAccessed(); } @@ -496,7 +496,7 @@ public static int tokenVersionMaxValue() public TokenCodecEncoder tokenVersion(final int value) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onTokenVersionAccessed(); } @@ -564,7 +564,7 @@ public static int componentTokenCountMaxValue() public TokenCodecEncoder componentTokenCount(final int value) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onComponentTokenCountAccessed(); } @@ -617,7 +617,7 @@ private void onSignalAccessed() public TokenCodecEncoder signal(final SignalCodec value) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onSignalAccessed(); } @@ -669,7 +669,7 @@ private void onPrimitiveTypeAccessed() public TokenCodecEncoder primitiveType(final PrimitiveTypeCodec value) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onPrimitiveTypeAccessed(); } @@ -721,7 +721,7 @@ private void onByteOrderAccessed() public TokenCodecEncoder byteOrder(final ByteOrderCodec value) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onByteOrderAccessed(); } @@ -773,7 +773,7 @@ private void onPresenceAccessed() public TokenCodecEncoder presence(final PresenceCodec value) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onPresenceAccessed(); } @@ -840,7 +840,7 @@ public static int deprecatedMaxValue() public TokenCodecEncoder deprecated(final int value) { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onDeprecatedAccessed(); } @@ -897,7 +897,7 @@ public TokenCodecEncoder putName(final DirectBuffer src, final int srcOffset, fi throw new IllegalStateException("length > maxValue for type: " + length); } - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onNameAccessed(); } @@ -918,7 +918,7 @@ public TokenCodecEncoder putName(final byte[] src, final int srcOffset, final in throw new IllegalStateException("length > maxValue for type: " + length); } - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onNameAccessed(); } @@ -942,7 +942,7 @@ public TokenCodecEncoder name(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onNameAccessed(); } @@ -1003,7 +1003,7 @@ public TokenCodecEncoder putConstValue(final DirectBuffer src, final int srcOffs throw new IllegalStateException("length > maxValue for type: " + length); } - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onConstValueAccessed(); } @@ -1024,7 +1024,7 @@ public TokenCodecEncoder putConstValue(final byte[] src, final int srcOffset, fi throw new IllegalStateException("length > maxValue for type: " + length); } - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onConstValueAccessed(); } @@ -1048,7 +1048,7 @@ public TokenCodecEncoder constValue(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onConstValueAccessed(); } @@ -1109,7 +1109,7 @@ public TokenCodecEncoder putMinValue(final DirectBuffer src, final int srcOffset throw new IllegalStateException("length > maxValue for type: " + length); } - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onMinValueAccessed(); } @@ -1130,7 +1130,7 @@ public TokenCodecEncoder putMinValue(final byte[] src, final int srcOffset, fina throw new IllegalStateException("length > maxValue for type: " + length); } - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onMinValueAccessed(); } @@ -1154,7 +1154,7 @@ public TokenCodecEncoder minValue(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onMinValueAccessed(); } @@ -1215,7 +1215,7 @@ public TokenCodecEncoder putMaxValue(final DirectBuffer src, final int srcOffset throw new IllegalStateException("length > maxValue for type: " + length); } - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onMaxValueAccessed(); } @@ -1236,7 +1236,7 @@ public TokenCodecEncoder putMaxValue(final byte[] src, final int srcOffset, fina throw new IllegalStateException("length > maxValue for type: " + length); } - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onMaxValueAccessed(); } @@ -1260,7 +1260,7 @@ public TokenCodecEncoder maxValue(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onMaxValueAccessed(); } @@ -1321,7 +1321,7 @@ public TokenCodecEncoder putNullValue(final DirectBuffer src, final int srcOffse throw new IllegalStateException("length > maxValue for type: " + length); } - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onNullValueAccessed(); } @@ -1342,7 +1342,7 @@ public TokenCodecEncoder putNullValue(final byte[] src, final int srcOffset, fin throw new IllegalStateException("length > maxValue for type: " + length); } - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onNullValueAccessed(); } @@ -1366,7 +1366,7 @@ public TokenCodecEncoder nullValue(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onNullValueAccessed(); } @@ -1427,7 +1427,7 @@ public TokenCodecEncoder putCharacterEncoding(final DirectBuffer src, final int throw new IllegalStateException("length > maxValue for type: " + length); } - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onCharacterEncodingAccessed(); } @@ -1448,7 +1448,7 @@ public TokenCodecEncoder putCharacterEncoding(final byte[] src, final int srcOff throw new IllegalStateException("length > maxValue for type: " + length); } - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onCharacterEncodingAccessed(); } @@ -1472,7 +1472,7 @@ public TokenCodecEncoder characterEncoding(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onCharacterEncodingAccessed(); } @@ -1533,7 +1533,7 @@ public TokenCodecEncoder putEpoch(final DirectBuffer src, final int srcOffset, f throw new IllegalStateException("length > maxValue for type: " + length); } - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onEpochAccessed(); } @@ -1554,7 +1554,7 @@ public TokenCodecEncoder putEpoch(final byte[] src, final int srcOffset, final i throw new IllegalStateException("length > maxValue for type: " + length); } - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onEpochAccessed(); } @@ -1578,7 +1578,7 @@ public TokenCodecEncoder epoch(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onEpochAccessed(); } @@ -1639,7 +1639,7 @@ public TokenCodecEncoder putTimeUnit(final DirectBuffer src, final int srcOffset throw new IllegalStateException("length > maxValue for type: " + length); } - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onTimeUnitAccessed(); } @@ -1660,7 +1660,7 @@ public TokenCodecEncoder putTimeUnit(final byte[] src, final int srcOffset, fina throw new IllegalStateException("length > maxValue for type: " + length); } - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onTimeUnitAccessed(); } @@ -1684,7 +1684,7 @@ public TokenCodecEncoder timeUnit(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onTimeUnitAccessed(); } @@ -1745,7 +1745,7 @@ public TokenCodecEncoder putSemanticType(final DirectBuffer src, final int srcOf throw new IllegalStateException("length > maxValue for type: " + length); } - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onSemanticTypeAccessed(); } @@ -1766,7 +1766,7 @@ public TokenCodecEncoder putSemanticType(final byte[] src, final int srcOffset, throw new IllegalStateException("length > maxValue for type: " + length); } - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onSemanticTypeAccessed(); } @@ -1790,7 +1790,7 @@ public TokenCodecEncoder semanticType(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onSemanticTypeAccessed(); } @@ -1851,7 +1851,7 @@ public TokenCodecEncoder putDescription(final DirectBuffer src, final int srcOff throw new IllegalStateException("length > maxValue for type: " + length); } - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onDescriptionAccessed(); } @@ -1872,7 +1872,7 @@ public TokenCodecEncoder putDescription(final byte[] src, final int srcOffset, f throw new IllegalStateException("length > maxValue for type: " + length); } - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onDescriptionAccessed(); } @@ -1896,7 +1896,7 @@ public TokenCodecEncoder description(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onDescriptionAccessed(); } @@ -1957,7 +1957,7 @@ public TokenCodecEncoder putReferencedName(final DirectBuffer src, final int src throw new IllegalStateException("length > maxValue for type: " + length); } - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onReferencedNameAccessed(); } @@ -1978,7 +1978,7 @@ public TokenCodecEncoder putReferencedName(final byte[] src, final int srcOffset throw new IllegalStateException("length > maxValue for type: " + length); } - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onReferencedNameAccessed(); } @@ -2002,7 +2002,7 @@ public TokenCodecEncoder referencedName(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { onReferencedNameAccessed(); } @@ -2041,7 +2041,7 @@ public StringBuilder appendTo(final StringBuilder builder) public void checkEncodingIsComplete() { - if (ENABLE_ACCESS_ORDER_CHECKS) + if (SBE_ENABLE_SEQUENCING_CHECKS) { switch (codecState) { diff --git a/sbe-tool/src/test/cpp/CMakeLists.txt b/sbe-tool/src/test/cpp/CMakeLists.txt index cd92ff756c..f90aa8de78 100644 --- a/sbe-tool/src/test/cpp/CMakeLists.txt +++ b/sbe-tool/src/test/cpp/CMakeLists.txt @@ -63,7 +63,7 @@ add_custom_command( -Dsbe.output.dir=${CXX_CODEC_TARGET_DIR} -Dsbe.generate.ir="true" -Dsbe.target.language="cpp" - -Dsbe.generate.access.order.checks="true" + -Dsbe.generate.sequencing.checks="true" -Dsbe.cpp.disable.implicit.copying="true" -jar ${SBE_JAR} ${CODE_GENERATION_SCHEMA} @@ -90,4 +90,4 @@ sbe_test(CompositeElementsTest codecs) sbe_test(Issue835Test codecs) sbe_test(Issue889Test codecs) sbe_test(FieldAccessOrderCheckTest codecs) -target_compile_definitions(FieldAccessOrderCheckTest PRIVATE ENABLE_ACCESS_ORDER_CHECKS) +target_compile_definitions(FieldAccessOrderCheckTest PRIVATE SBE_ENABLE_SEQUENCING_CHECKS) From 08367e030b2619bdd0b8b66ce04b3377a1571fdd Mon Sep 17 00:00:00 2001 From: Zach Bray Date: Mon, 13 Nov 2023 16:33:53 +0000 Subject: [PATCH 45/50] [Java,C#,C++] Allow custom flag/properties to enable precedence checks. Some users would like the ability to enable precedence checks on some schemas but not others. Initially, I considered generating flags or system properties with the schema namespace; however, this forces the more-complex scheme on all users. It also might get unwieldly with long namespaces. It also assumes a one-to-one correspondence between namespaces and schemas. Instead, I have introduced two new system properties that can be applied at code generation time: - `sbe.precedence.checks.flagName`, which controls the symbol/macro used to enable precedence checks at build time in the generated C#/C++ code. It defaults to `"SBE_ENABLE_PRECEDENCE_CHECKS"`. - `sbe.precedence.checks.propName`, which controls the property name used to enable precedence checks at runtime in the generated Java code. It defaults to `"sbe.enable.precedence.checks"`. Now, users can run SbeTool with different values for these system properties to generate code with different enablement flags. Note above that the defaults are: - `SBE_ENABLE_PRECEDENCE_CHECKS` rather than `SBE_ENABLE_SEQUENCING_CHECKS`, and - `sbe.enable.precedence.checks` rather than `sbe.enable.sequencing.checks`. These changes and a change to another system property: - `sbe.generate.sequencing.checks` -> `sbe.generate.precedence.checks` address some feedback from Martin on the associated PR #948. --- build.gradle | 17 +- csharp/sbe-generated/sbe-generated.csproj | 4 +- .../generation/common/AccessOrderModel.java | 20 ++- .../sbe/generation/cpp/CppGenerator.java | 8 +- .../generation/csharp/CSharpGenerator.java | 10 +- .../sbe/generation/java/JavaGenerator.java | 13 +- .../sbe/ir/generated/FrameCodecDecoder.java | 50 +++--- .../sbe/ir/generated/FrameCodecEncoder.java | 32 ++-- .../sbe/ir/generated/TokenCodecDecoder.java | 168 +++++++++--------- .../sbe/ir/generated/TokenCodecEncoder.java | 94 +++++----- sbe-tool/src/test/cpp/CMakeLists.txt | 5 +- 11 files changed, 224 insertions(+), 197 deletions(-) diff --git a/build.gradle b/build.gradle index 5bc37ff579..807939e8d3 100644 --- a/build.gradle +++ b/build.gradle @@ -229,6 +229,9 @@ subprojects { } javaLauncher.set(toolchainLauncher) + + systemProperty 'sbe.enable.ir.precedence.checks', 'true' + systemProperty 'sbe.enable.test.precedence.checks', 'true' } } @@ -280,7 +283,8 @@ project(':sbe-tool') { 'sbe.target.language': 'Java', 'sbe.validation.stop.on.error': 'true', 'sbe.validation.xsd': validationXsdPath, - 'sbe.generate.sequencing.checks': 'true') + 'sbe.generate.precedence.checks': 'true', + 'sbe.precedence.checks.propName': 'sbe.enable.test.precedence.checks') args = ['src/test/resources/json-printer-test-schema.xml', 'src/test/resources/composite-elements-schema.xml', 'src/test/resources/field-order-check-schema.xml'] @@ -544,7 +548,7 @@ project(':sbe-benchmarks') { 'sbe.validation.xsd': validationXsdPath, 'sbe.java.encoding.buffer.type': 'org.agrona.concurrent.UnsafeBuffer', 'sbe.java.decoding.buffer.type': 'org.agrona.concurrent.UnsafeBuffer', - 'sbe.generate.sequencing.checks': 'false') + 'sbe.generate.precedence.checks': 'false') args = ['src/main/resources/car.xml', 'src/main/resources/fix-message-samples.xml'] } @@ -734,7 +738,7 @@ tasks.register('generateCSharpCodecsTests', JavaExec) { 'sbe.target.language': 'uk.co.real_logic.sbe.generation.csharp.CSharp', 'sbe.xinclude.aware': 'true', 'sbe.validation.xsd': validationXsdPath, - 'sbe.generate.sequencing.checks': 'true') + 'sbe.generate.precedence.checks': 'true') args = ['sbe-tool/src/test/resources/FixBinary.xml', 'sbe-tool/src/test/resources/issue435.xml', 'sbe-tool/src/test/resources/issue483.xml', @@ -759,7 +763,9 @@ tasks.register('generateJavaIrCodecs', JavaExec) { 'sbe.output.dir': 'sbe-tool/src/main/java', 'sbe.target.language': 'Java', 'sbe.validation.xsd': validationXsdPath, - 'sbe.generate.sequencing.checks': 'true') + 'sbe.generate.precedence.checks': 'true', + 'sbe.precedence.checks.flagName': 'SBE_ENABLE_IR_PRECEDENCE_CHECKS', + 'sbe.precedence.checks.propName': 'sbe.enable.ir.precedence.checks') args = ['sbe-tool/src/main/resources/sbe-ir.xml'] } @@ -770,7 +776,8 @@ tasks.register('generateCppIrCodecs', JavaExec) { 'sbe.output.dir': 'sbe-tool/src/main/cpp', 'sbe.target.language': 'cpp', 'sbe.validation.xsd': validationXsdPath, - 'sbe.generate.sequencing.checks': 'true') + 'sbe.generate.precedence.checks': 'true', + 'sbe.precedence.checks.flagName': 'SBE_ENABLE_IR_PRECEDENCE_CHECKS') args = ['sbe-tool/src/main/resources/sbe-ir.xml'] } diff --git a/csharp/sbe-generated/sbe-generated.csproj b/csharp/sbe-generated/sbe-generated.csproj index cedbc4321f..806168d7d5 100644 --- a/csharp/sbe-generated/sbe-generated.csproj +++ b/csharp/sbe-generated/sbe-generated.csproj @@ -8,11 +8,11 @@ - TRACE,SBE_ENABLE_SEQUENCING_CHECKS + TRACE,SBE_ENABLE_PRECEDENCE_CHECKS - TRACE,SBE_ENABLE_SEQUENCING_CHECKS + TRACE,SBE_ENABLE_PRECEDENCE_CHECKS diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/AccessOrderModel.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/AccessOrderModel.java index 85cd9df9bf..8836d35008 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/AccessOrderModel.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/AccessOrderModel.java @@ -40,8 +40,26 @@ // Therefore, we allow lambdas with code blocks even when a lambda expression is possible. public final class AccessOrderModel { + /** + * Whether to generate access order checks. + */ private static final boolean GENERATE_ACCESS_ORDER_CHECKS = Boolean.parseBoolean( - System.getProperty("sbe.generate.sequencing.checks", "false")); + System.getProperty("sbe.generate.precedence.checks", "false")); + + /** + * The name of the symbol or macro that enables access order checks when building + * generated C# or C++ code. + */ + public static final String PRECEDENCE_CHECKS_FLAG_NAME = + System.getProperty("sbe.precedence.checks.flagName", "SBE_ENABLE_PRECEDENCE_CHECKS"); + + /** + * The name of the system property that enables access order checks at runtime + * in generated Java code. + */ + public static final String PRECEDENCE_CHECKS_PROP_NAME = + System.getProperty("sbe.precedence.checks.propName", "sbe.enable.precedence.checks"); + private final Map groupPathsByField = new HashMap<>(); private final Set topLevelBlockFields = new HashSet<>(); private final CodecInteraction.HashConsingFactory interactionFactory = diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java index 074b479731..9e392661a8 100755 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java @@ -207,7 +207,7 @@ private static CharSequence generateFullyEncodedCheck(final AccessOrderModel acc sb.append(indent).append("void checkEncodingIsComplete()\n") .append(indent).append("{\n") - .append("#if defined(SBE_ENABLE_SEQUENCING_CHECKS)\n") + .append("#if defined(").append(AccessOrderModel.PRECEDENCE_CHECKS_FLAG_NAME).append(")\n") .append(indent).append(INDENT).append("switch (m_codecState)\n") .append(indent).append(INDENT).append("{\n"); @@ -321,7 +321,7 @@ private static CharSequence generateAccessOrderListenerCall( } final StringBuilder sb = new StringBuilder(); - sb.append("#if defined(SBE_ENABLE_SEQUENCING_CHECKS)\n") + sb.append("#if defined(").append(AccessOrderModel.PRECEDENCE_CHECKS_FLAG_NAME).append(")\n") .append(indent).append(methodName).append("("); for (int i = 0; i < arguments.length; i++) @@ -3083,7 +3083,7 @@ private CharSequence generateDecoderWrapListenerCall(final AccessOrderModel acce if (accessOrderModel.versionCount() == 1) { final StringBuilder sb = new StringBuilder(); - sb.append("#if defined(SBE_ENABLE_SEQUENCING_CHECKS)\n") + sb.append("#if defined(").append(AccessOrderModel.PRECEDENCE_CHECKS_FLAG_NAME).append(")\n") .append(TWO_INDENT).append("codecState(") .append(qualifiedStateCase(accessOrderModel.latestVersionWrappedState())).append(");\n") .append("#endif\n"); @@ -3101,7 +3101,7 @@ private CharSequence generateEncoderWrapListener(final AccessOrderModel accessOr } final StringBuilder sb = new StringBuilder(); - sb.append("#if defined(SBE_ENABLE_SEQUENCING_CHECKS)\n") + sb.append("#if defined(").append(AccessOrderModel.PRECEDENCE_CHECKS_FLAG_NAME).append(")\n") .append(TWO_INDENT).append("codecState(") .append(qualifiedStateCase(accessOrderModel.latestVersionWrappedState())) .append(");\n") diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java index 6d4f143fff..c38e5c5a7b 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java @@ -1583,7 +1583,7 @@ private static CharSequence generateFullyEncodedCheck( sb.append(indent).append("public void CheckEncodingIsComplete()\n") .append(indent).append("{\n") - .append("#if SBE_ENABLE_SEQUENCING_CHECKS\n") + .append("#if ").append(AccessOrderModel.PRECEDENCE_CHECKS_FLAG_NAME).append("\n") .append(indent).append(INDENT).append("switch (_codecState)\n") .append(indent).append(INDENT).append("{\n"); @@ -1670,7 +1670,7 @@ private static CharSequence generateAccessOrderListenerCall( } final StringBuilder sb = new StringBuilder(); - sb.append("#if SBE_ENABLE_SEQUENCING_CHECKS\n") + sb.append("#if ").append(AccessOrderModel.PRECEDENCE_CHECKS_FLAG_NAME).append("\n") .append(indent).append(methodName).append("("); for (int i = 0; i < arguments.length; i++) @@ -1936,7 +1936,7 @@ private CharSequence generateEncoderWrapListener( } final StringBuilder sb = new StringBuilder(); - sb.append("#if SBE_ENABLE_SEQUENCING_CHECKS\n") + sb.append("#if ").append(AccessOrderModel.PRECEDENCE_CHECKS_FLAG_NAME).append("\n") .append(indent).append("codecState(") .append(qualifiedStateCase(accessOrderModel.latestVersionWrappedState())) .append(");\n") @@ -2556,7 +2556,7 @@ private CharSequence generateDisplay( append(sb, TWO_INDENT, " int originalLimit = this.Limit;"); if (null != accessOrderModel) { - sb.append("#if SBE_ENABLE_SEQUENCING_CHECKS\n"); + sb.append("#if ").append(AccessOrderModel.PRECEDENCE_CHECKS_FLAG_NAME).append("\n"); append(sb, TWO_INDENT, " CodecState originalState = _codecState;"); sb.append(THREE_INDENT).append("_codecState = ") .append(qualifiedStateCase(accessOrderModel.notWrappedState())).append(";\n"); @@ -2588,7 +2588,7 @@ private CharSequence generateDisplay( sb.append('\n'); if (null != accessOrderModel) { - sb.append("#if SBE_ENABLE_SEQUENCING_CHECKS\n"); + sb.append("#if ").append(AccessOrderModel.PRECEDENCE_CHECKS_FLAG_NAME).append("\n"); append(sb, TWO_INDENT, " _codecState = originalState;"); sb.append("#endif\n"); } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java index e969396f8c..44db96908c 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java @@ -307,9 +307,10 @@ private static CharSequence generateFieldOrderStates(final AccessOrderModel acce sb.append(" private static final boolean ENABLE_BOUNDS_CHECKS = ") .append("!Boolean.getBoolean(\"agrona.disable.bounds.checks\");\n\n"); - sb.append(" private static final boolean SBE_ENABLE_SEQUENCING_CHECKS = ") + sb.append(" private static final boolean ") + .append(AccessOrderModel.PRECEDENCE_CHECKS_FLAG_NAME).append(" = ") .append("Boolean.parseBoolean(System.getProperty(\n") - .append(" \"sbe.enable.sequencing.checks\",\n") + .append(" \"").append(AccessOrderModel.PRECEDENCE_CHECKS_PROP_NAME).append("\",\n") .append(" Boolean.toString(ENABLE_BOUNDS_CHECKS)));\n\n"); sb.append(" /**\n"); @@ -410,7 +411,7 @@ private static void generateFullyEncodedCheck( sb.append(" public void checkEncodingIsComplete()\n") .append(" {\n") - .append(" if (SBE_ENABLE_SEQUENCING_CHECKS)\n") + .append(" if (").append(AccessOrderModel.PRECEDENCE_CHECKS_FLAG_NAME).append(")\n") .append(" {\n") .append(" switch (codecState)\n") .append(" {\n"); @@ -493,7 +494,7 @@ private static CharSequence generateAccessOrderListenerCall( } final StringBuilder sb = new StringBuilder(); - sb.append(indent).append("if (SBE_ENABLE_SEQUENCING_CHECKS)\n") + sb.append(indent).append("if (").append(AccessOrderModel.PRECEDENCE_CHECKS_FLAG_NAME).append(")\n") .append(indent).append("{\n") .append(indent).append(" ").append(methodName).append("("); @@ -755,7 +756,7 @@ private CharSequence generateEncoderWrapListener( } final StringBuilder sb = new StringBuilder(); - sb.append(indent).append("if (SBE_ENABLE_SEQUENCING_CHECKS)") + sb.append(indent).append("if (").append(AccessOrderModel.PRECEDENCE_CHECKS_FLAG_NAME).append(")") .append("\n").append(indent).append("{\n") .append(indent).append(" codecState(") .append(qualifiedStateCase(accessOrderModel.latestVersionWrappedState())) @@ -3424,7 +3425,7 @@ private CharSequence generateDecoderFlyweightCode( if (null != accessOrderModel) { - methods.append(" if (SBE_ENABLE_SEQUENCING_CHECKS)\n") + methods.append(" if (").append(AccessOrderModel.PRECEDENCE_CHECKS_FLAG_NAME).append(")\n") .append(" {\n") .append(" codecState(currentCodecState);\n") .append(" }\n\n"); diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java index 005faad113..b85cbb091b 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java @@ -13,8 +13,8 @@ public final class FrameCodecDecoder { private static final boolean ENABLE_BOUNDS_CHECKS = !Boolean.getBoolean("agrona.disable.bounds.checks"); - private static final boolean SBE_ENABLE_SEQUENCING_CHECKS = Boolean.parseBoolean(System.getProperty( - "sbe.enable.sequencing.checks", + private static final boolean SBE_ENABLE_IR_PRECEDENCE_CHECKS = Boolean.parseBoolean(System.getProperty( + "sbe.enable.ir.precedence.checks", Boolean.toString(ENABLE_BOUNDS_CHECKS))); /** @@ -172,7 +172,7 @@ public FrameCodecDecoder wrap( this.actingVersion = actingVersion; limit(offset + actingBlockLength); - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onWrap(actingVersion); } @@ -213,7 +213,7 @@ public int sbeDecodedLength() final int decodedLength = encodedLength(); limit(currentLimit); - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { codecState(currentCodecState); } @@ -299,7 +299,7 @@ public static int irIdMaxValue() public int irId() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onIrIdAccessed(); } @@ -366,7 +366,7 @@ public static int irVersionMaxValue() public int irVersion() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onIrVersionAccessed(); } @@ -433,7 +433,7 @@ public static int schemaVersionMaxValue() public int schemaVersion() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onSchemaVersionAccessed(); } @@ -504,7 +504,7 @@ private void onPackageNameAccessed() public int packageNameLength() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onPackageNameLengthAccessed(); } @@ -515,7 +515,7 @@ public int packageNameLength() public int skipPackageName() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onPackageNameAccessed(); } @@ -531,7 +531,7 @@ public int skipPackageName() public int getPackageName(final MutableDirectBuffer dst, final int dstOffset, final int length) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onPackageNameAccessed(); } @@ -548,7 +548,7 @@ public int getPackageName(final MutableDirectBuffer dst, final int dstOffset, fi public int getPackageName(final byte[] dst, final int dstOffset, final int length) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onPackageNameAccessed(); } @@ -565,7 +565,7 @@ public int getPackageName(final byte[] dst, final int dstOffset, final int lengt public void wrapPackageName(final DirectBuffer wrapBuffer) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onPackageNameAccessed(); } @@ -579,7 +579,7 @@ public void wrapPackageName(final DirectBuffer wrapBuffer) public String packageName() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onPackageNameAccessed(); } @@ -662,7 +662,7 @@ private void onNamespaceNameAccessed() public int namespaceNameLength() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onNamespaceNameLengthAccessed(); } @@ -673,7 +673,7 @@ public int namespaceNameLength() public int skipNamespaceName() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onNamespaceNameAccessed(); } @@ -689,7 +689,7 @@ public int skipNamespaceName() public int getNamespaceName(final MutableDirectBuffer dst, final int dstOffset, final int length) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onNamespaceNameAccessed(); } @@ -706,7 +706,7 @@ public int getNamespaceName(final MutableDirectBuffer dst, final int dstOffset, public int getNamespaceName(final byte[] dst, final int dstOffset, final int length) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onNamespaceNameAccessed(); } @@ -723,7 +723,7 @@ public int getNamespaceName(final byte[] dst, final int dstOffset, final int len public void wrapNamespaceName(final DirectBuffer wrapBuffer) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onNamespaceNameAccessed(); } @@ -737,7 +737,7 @@ public void wrapNamespaceName(final DirectBuffer wrapBuffer) public String namespaceName() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onNamespaceNameAccessed(); } @@ -820,7 +820,7 @@ private void onSemanticVersionAccessed() public int semanticVersionLength() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onSemanticVersionLengthAccessed(); } @@ -831,7 +831,7 @@ public int semanticVersionLength() public int skipSemanticVersion() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onSemanticVersionAccessed(); } @@ -847,7 +847,7 @@ public int skipSemanticVersion() public int getSemanticVersion(final MutableDirectBuffer dst, final int dstOffset, final int length) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onSemanticVersionAccessed(); } @@ -864,7 +864,7 @@ public int getSemanticVersion(final MutableDirectBuffer dst, final int dstOffset public int getSemanticVersion(final byte[] dst, final int dstOffset, final int length) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onSemanticVersionAccessed(); } @@ -881,7 +881,7 @@ public int getSemanticVersion(final byte[] dst, final int dstOffset, final int l public void wrapSemanticVersion(final DirectBuffer wrapBuffer) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onSemanticVersionAccessed(); } @@ -895,7 +895,7 @@ public void wrapSemanticVersion(final DirectBuffer wrapBuffer) public String semanticVersion() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onSemanticVersionAccessed(); } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java index ce88e4ed8c..2df3744b61 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java @@ -13,8 +13,8 @@ public final class FrameCodecEncoder { private static final boolean ENABLE_BOUNDS_CHECKS = !Boolean.getBoolean("agrona.disable.bounds.checks"); - private static final boolean SBE_ENABLE_SEQUENCING_CHECKS = Boolean.parseBoolean(System.getProperty( - "sbe.enable.sequencing.checks", + private static final boolean SBE_ENABLE_IR_PRECEDENCE_CHECKS = Boolean.parseBoolean(System.getProperty( + "sbe.enable.ir.precedence.checks", Boolean.toString(ENABLE_BOUNDS_CHECKS))); /** @@ -151,7 +151,7 @@ public FrameCodecEncoder wrap(final MutableDirectBuffer buffer, final int offset this.offset = offset; limit(offset + BLOCK_LENGTH); - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { codecState(CodecStates.V0_BLOCK); } @@ -245,7 +245,7 @@ public static int irIdMaxValue() public FrameCodecEncoder irId(final int value) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onIrIdAccessed(); } @@ -313,7 +313,7 @@ public static int irVersionMaxValue() public FrameCodecEncoder irVersion(final int value) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onIrVersionAccessed(); } @@ -381,7 +381,7 @@ public static int schemaVersionMaxValue() public FrameCodecEncoder schemaVersion(final int value) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onSchemaVersionAccessed(); } @@ -438,7 +438,7 @@ public FrameCodecEncoder putPackageName(final DirectBuffer src, final int srcOff throw new IllegalStateException("length > maxValue for type: " + length); } - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onPackageNameAccessed(); } @@ -459,7 +459,7 @@ public FrameCodecEncoder putPackageName(final byte[] src, final int srcOffset, f throw new IllegalStateException("length > maxValue for type: " + length); } - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onPackageNameAccessed(); } @@ -483,7 +483,7 @@ public FrameCodecEncoder packageName(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onPackageNameAccessed(); } @@ -544,7 +544,7 @@ public FrameCodecEncoder putNamespaceName(final DirectBuffer src, final int srcO throw new IllegalStateException("length > maxValue for type: " + length); } - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onNamespaceNameAccessed(); } @@ -565,7 +565,7 @@ public FrameCodecEncoder putNamespaceName(final byte[] src, final int srcOffset, throw new IllegalStateException("length > maxValue for type: " + length); } - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onNamespaceNameAccessed(); } @@ -589,7 +589,7 @@ public FrameCodecEncoder namespaceName(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onNamespaceNameAccessed(); } @@ -650,7 +650,7 @@ public FrameCodecEncoder putSemanticVersion(final DirectBuffer src, final int sr throw new IllegalStateException("length > maxValue for type: " + length); } - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onSemanticVersionAccessed(); } @@ -671,7 +671,7 @@ public FrameCodecEncoder putSemanticVersion(final byte[] src, final int srcOffse throw new IllegalStateException("length > maxValue for type: " + length); } - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onSemanticVersionAccessed(); } @@ -695,7 +695,7 @@ public FrameCodecEncoder semanticVersion(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onSemanticVersionAccessed(); } @@ -734,7 +734,7 @@ public StringBuilder appendTo(final StringBuilder builder) public void checkEncodingIsComplete() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { switch (codecState) { diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java index e896601355..7a452e23cd 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java @@ -13,8 +13,8 @@ public final class TokenCodecDecoder { private static final boolean ENABLE_BOUNDS_CHECKS = !Boolean.getBoolean("agrona.disable.bounds.checks"); - private static final boolean SBE_ENABLE_SEQUENCING_CHECKS = Boolean.parseBoolean(System.getProperty( - "sbe.enable.sequencing.checks", + private static final boolean SBE_ENABLE_IR_PRECEDENCE_CHECKS = Boolean.parseBoolean(System.getProperty( + "sbe.enable.ir.precedence.checks", Boolean.toString(ENABLE_BOUNDS_CHECKS))); /** @@ -219,7 +219,7 @@ public TokenCodecDecoder wrap( this.actingVersion = actingVersion; limit(offset + actingBlockLength); - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onWrap(actingVersion); } @@ -260,7 +260,7 @@ public int sbeDecodedLength() final int decodedLength = encodedLength(); limit(currentLimit); - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { codecState(currentCodecState); } @@ -346,7 +346,7 @@ public static int tokenOffsetMaxValue() public int tokenOffset() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onTokenOffsetAccessed(); } @@ -413,7 +413,7 @@ public static int tokenSizeMaxValue() public int tokenSize() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onTokenSizeAccessed(); } @@ -480,7 +480,7 @@ public static int fieldIdMaxValue() public int fieldId() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onFieldIdAccessed(); } @@ -547,7 +547,7 @@ public static int tokenVersionMaxValue() public int tokenVersion() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onTokenVersionAccessed(); } @@ -614,7 +614,7 @@ public static int componentTokenCountMaxValue() public int componentTokenCount() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onComponentTokenCountAccessed(); } @@ -666,7 +666,7 @@ private void onSignalAccessed() public short signalRaw() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onSignalAccessed(); } @@ -676,7 +676,7 @@ public short signalRaw() public SignalCodec signal() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onSignalAccessed(); } @@ -728,7 +728,7 @@ private void onPrimitiveTypeAccessed() public short primitiveTypeRaw() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onPrimitiveTypeAccessed(); } @@ -738,7 +738,7 @@ public short primitiveTypeRaw() public PrimitiveTypeCodec primitiveType() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onPrimitiveTypeAccessed(); } @@ -790,7 +790,7 @@ private void onByteOrderAccessed() public short byteOrderRaw() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onByteOrderAccessed(); } @@ -800,7 +800,7 @@ public short byteOrderRaw() public ByteOrderCodec byteOrder() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onByteOrderAccessed(); } @@ -852,7 +852,7 @@ private void onPresenceAccessed() public short presenceRaw() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onPresenceAccessed(); } @@ -862,7 +862,7 @@ public short presenceRaw() public PresenceCodec presence() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onPresenceAccessed(); } @@ -929,7 +929,7 @@ public static int deprecatedMaxValue() public int deprecated() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onDeprecatedAccessed(); } @@ -1000,7 +1000,7 @@ private void onNameAccessed() public int nameLength() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onNameLengthAccessed(); } @@ -1011,7 +1011,7 @@ public int nameLength() public int skipName() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onNameAccessed(); } @@ -1027,7 +1027,7 @@ public int skipName() public int getName(final MutableDirectBuffer dst, final int dstOffset, final int length) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onNameAccessed(); } @@ -1044,7 +1044,7 @@ public int getName(final MutableDirectBuffer dst, final int dstOffset, final int public int getName(final byte[] dst, final int dstOffset, final int length) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onNameAccessed(); } @@ -1061,7 +1061,7 @@ public int getName(final byte[] dst, final int dstOffset, final int length) public void wrapName(final DirectBuffer wrapBuffer) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onNameAccessed(); } @@ -1075,7 +1075,7 @@ public void wrapName(final DirectBuffer wrapBuffer) public String name() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onNameAccessed(); } @@ -1158,7 +1158,7 @@ private void onConstValueAccessed() public int constValueLength() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onConstValueLengthAccessed(); } @@ -1169,7 +1169,7 @@ public int constValueLength() public int skipConstValue() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onConstValueAccessed(); } @@ -1185,7 +1185,7 @@ public int skipConstValue() public int getConstValue(final MutableDirectBuffer dst, final int dstOffset, final int length) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onConstValueAccessed(); } @@ -1202,7 +1202,7 @@ public int getConstValue(final MutableDirectBuffer dst, final int dstOffset, fin public int getConstValue(final byte[] dst, final int dstOffset, final int length) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onConstValueAccessed(); } @@ -1219,7 +1219,7 @@ public int getConstValue(final byte[] dst, final int dstOffset, final int length public void wrapConstValue(final DirectBuffer wrapBuffer) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onConstValueAccessed(); } @@ -1233,7 +1233,7 @@ public void wrapConstValue(final DirectBuffer wrapBuffer) public String constValue() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onConstValueAccessed(); } @@ -1316,7 +1316,7 @@ private void onMinValueAccessed() public int minValueLength() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onMinValueLengthAccessed(); } @@ -1327,7 +1327,7 @@ public int minValueLength() public int skipMinValue() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onMinValueAccessed(); } @@ -1343,7 +1343,7 @@ public int skipMinValue() public int getMinValue(final MutableDirectBuffer dst, final int dstOffset, final int length) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onMinValueAccessed(); } @@ -1360,7 +1360,7 @@ public int getMinValue(final MutableDirectBuffer dst, final int dstOffset, final public int getMinValue(final byte[] dst, final int dstOffset, final int length) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onMinValueAccessed(); } @@ -1377,7 +1377,7 @@ public int getMinValue(final byte[] dst, final int dstOffset, final int length) public void wrapMinValue(final DirectBuffer wrapBuffer) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onMinValueAccessed(); } @@ -1391,7 +1391,7 @@ public void wrapMinValue(final DirectBuffer wrapBuffer) public String minValue() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onMinValueAccessed(); } @@ -1474,7 +1474,7 @@ private void onMaxValueAccessed() public int maxValueLength() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onMaxValueLengthAccessed(); } @@ -1485,7 +1485,7 @@ public int maxValueLength() public int skipMaxValue() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onMaxValueAccessed(); } @@ -1501,7 +1501,7 @@ public int skipMaxValue() public int getMaxValue(final MutableDirectBuffer dst, final int dstOffset, final int length) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onMaxValueAccessed(); } @@ -1518,7 +1518,7 @@ public int getMaxValue(final MutableDirectBuffer dst, final int dstOffset, final public int getMaxValue(final byte[] dst, final int dstOffset, final int length) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onMaxValueAccessed(); } @@ -1535,7 +1535,7 @@ public int getMaxValue(final byte[] dst, final int dstOffset, final int length) public void wrapMaxValue(final DirectBuffer wrapBuffer) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onMaxValueAccessed(); } @@ -1549,7 +1549,7 @@ public void wrapMaxValue(final DirectBuffer wrapBuffer) public String maxValue() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onMaxValueAccessed(); } @@ -1632,7 +1632,7 @@ private void onNullValueAccessed() public int nullValueLength() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onNullValueLengthAccessed(); } @@ -1643,7 +1643,7 @@ public int nullValueLength() public int skipNullValue() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onNullValueAccessed(); } @@ -1659,7 +1659,7 @@ public int skipNullValue() public int getNullValue(final MutableDirectBuffer dst, final int dstOffset, final int length) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onNullValueAccessed(); } @@ -1676,7 +1676,7 @@ public int getNullValue(final MutableDirectBuffer dst, final int dstOffset, fina public int getNullValue(final byte[] dst, final int dstOffset, final int length) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onNullValueAccessed(); } @@ -1693,7 +1693,7 @@ public int getNullValue(final byte[] dst, final int dstOffset, final int length) public void wrapNullValue(final DirectBuffer wrapBuffer) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onNullValueAccessed(); } @@ -1707,7 +1707,7 @@ public void wrapNullValue(final DirectBuffer wrapBuffer) public String nullValue() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onNullValueAccessed(); } @@ -1790,7 +1790,7 @@ private void onCharacterEncodingAccessed() public int characterEncodingLength() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onCharacterEncodingLengthAccessed(); } @@ -1801,7 +1801,7 @@ public int characterEncodingLength() public int skipCharacterEncoding() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onCharacterEncodingAccessed(); } @@ -1817,7 +1817,7 @@ public int skipCharacterEncoding() public int getCharacterEncoding(final MutableDirectBuffer dst, final int dstOffset, final int length) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onCharacterEncodingAccessed(); } @@ -1834,7 +1834,7 @@ public int getCharacterEncoding(final MutableDirectBuffer dst, final int dstOffs public int getCharacterEncoding(final byte[] dst, final int dstOffset, final int length) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onCharacterEncodingAccessed(); } @@ -1851,7 +1851,7 @@ public int getCharacterEncoding(final byte[] dst, final int dstOffset, final int public void wrapCharacterEncoding(final DirectBuffer wrapBuffer) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onCharacterEncodingAccessed(); } @@ -1865,7 +1865,7 @@ public void wrapCharacterEncoding(final DirectBuffer wrapBuffer) public String characterEncoding() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onCharacterEncodingAccessed(); } @@ -1948,7 +1948,7 @@ private void onEpochAccessed() public int epochLength() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onEpochLengthAccessed(); } @@ -1959,7 +1959,7 @@ public int epochLength() public int skipEpoch() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onEpochAccessed(); } @@ -1975,7 +1975,7 @@ public int skipEpoch() public int getEpoch(final MutableDirectBuffer dst, final int dstOffset, final int length) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onEpochAccessed(); } @@ -1992,7 +1992,7 @@ public int getEpoch(final MutableDirectBuffer dst, final int dstOffset, final in public int getEpoch(final byte[] dst, final int dstOffset, final int length) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onEpochAccessed(); } @@ -2009,7 +2009,7 @@ public int getEpoch(final byte[] dst, final int dstOffset, final int length) public void wrapEpoch(final DirectBuffer wrapBuffer) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onEpochAccessed(); } @@ -2023,7 +2023,7 @@ public void wrapEpoch(final DirectBuffer wrapBuffer) public String epoch() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onEpochAccessed(); } @@ -2106,7 +2106,7 @@ private void onTimeUnitAccessed() public int timeUnitLength() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onTimeUnitLengthAccessed(); } @@ -2117,7 +2117,7 @@ public int timeUnitLength() public int skipTimeUnit() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onTimeUnitAccessed(); } @@ -2133,7 +2133,7 @@ public int skipTimeUnit() public int getTimeUnit(final MutableDirectBuffer dst, final int dstOffset, final int length) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onTimeUnitAccessed(); } @@ -2150,7 +2150,7 @@ public int getTimeUnit(final MutableDirectBuffer dst, final int dstOffset, final public int getTimeUnit(final byte[] dst, final int dstOffset, final int length) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onTimeUnitAccessed(); } @@ -2167,7 +2167,7 @@ public int getTimeUnit(final byte[] dst, final int dstOffset, final int length) public void wrapTimeUnit(final DirectBuffer wrapBuffer) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onTimeUnitAccessed(); } @@ -2181,7 +2181,7 @@ public void wrapTimeUnit(final DirectBuffer wrapBuffer) public String timeUnit() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onTimeUnitAccessed(); } @@ -2264,7 +2264,7 @@ private void onSemanticTypeAccessed() public int semanticTypeLength() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onSemanticTypeLengthAccessed(); } @@ -2275,7 +2275,7 @@ public int semanticTypeLength() public int skipSemanticType() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onSemanticTypeAccessed(); } @@ -2291,7 +2291,7 @@ public int skipSemanticType() public int getSemanticType(final MutableDirectBuffer dst, final int dstOffset, final int length) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onSemanticTypeAccessed(); } @@ -2308,7 +2308,7 @@ public int getSemanticType(final MutableDirectBuffer dst, final int dstOffset, f public int getSemanticType(final byte[] dst, final int dstOffset, final int length) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onSemanticTypeAccessed(); } @@ -2325,7 +2325,7 @@ public int getSemanticType(final byte[] dst, final int dstOffset, final int leng public void wrapSemanticType(final DirectBuffer wrapBuffer) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onSemanticTypeAccessed(); } @@ -2339,7 +2339,7 @@ public void wrapSemanticType(final DirectBuffer wrapBuffer) public String semanticType() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onSemanticTypeAccessed(); } @@ -2422,7 +2422,7 @@ private void onDescriptionAccessed() public int descriptionLength() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onDescriptionLengthAccessed(); } @@ -2433,7 +2433,7 @@ public int descriptionLength() public int skipDescription() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onDescriptionAccessed(); } @@ -2449,7 +2449,7 @@ public int skipDescription() public int getDescription(final MutableDirectBuffer dst, final int dstOffset, final int length) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onDescriptionAccessed(); } @@ -2466,7 +2466,7 @@ public int getDescription(final MutableDirectBuffer dst, final int dstOffset, fi public int getDescription(final byte[] dst, final int dstOffset, final int length) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onDescriptionAccessed(); } @@ -2483,7 +2483,7 @@ public int getDescription(final byte[] dst, final int dstOffset, final int lengt public void wrapDescription(final DirectBuffer wrapBuffer) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onDescriptionAccessed(); } @@ -2497,7 +2497,7 @@ public void wrapDescription(final DirectBuffer wrapBuffer) public String description() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onDescriptionAccessed(); } @@ -2580,7 +2580,7 @@ private void onReferencedNameAccessed() public int referencedNameLength() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onReferencedNameLengthAccessed(); } @@ -2591,7 +2591,7 @@ public int referencedNameLength() public int skipReferencedName() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onReferencedNameAccessed(); } @@ -2607,7 +2607,7 @@ public int skipReferencedName() public int getReferencedName(final MutableDirectBuffer dst, final int dstOffset, final int length) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onReferencedNameAccessed(); } @@ -2624,7 +2624,7 @@ public int getReferencedName(final MutableDirectBuffer dst, final int dstOffset, public int getReferencedName(final byte[] dst, final int dstOffset, final int length) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onReferencedNameAccessed(); } @@ -2641,7 +2641,7 @@ public int getReferencedName(final byte[] dst, final int dstOffset, final int le public void wrapReferencedName(final DirectBuffer wrapBuffer) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onReferencedNameAccessed(); } @@ -2655,7 +2655,7 @@ public void wrapReferencedName(final DirectBuffer wrapBuffer) public String referencedName() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onReferencedNameAccessed(); } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java index 293377446d..7cd917b6e7 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java @@ -13,8 +13,8 @@ public final class TokenCodecEncoder { private static final boolean ENABLE_BOUNDS_CHECKS = !Boolean.getBoolean("agrona.disable.bounds.checks"); - private static final boolean SBE_ENABLE_SEQUENCING_CHECKS = Boolean.parseBoolean(System.getProperty( - "sbe.enable.sequencing.checks", + private static final boolean SBE_ENABLE_IR_PRECEDENCE_CHECKS = Boolean.parseBoolean(System.getProperty( + "sbe.enable.ir.precedence.checks", Boolean.toString(ENABLE_BOUNDS_CHECKS))); /** @@ -198,7 +198,7 @@ public TokenCodecEncoder wrap(final MutableDirectBuffer buffer, final int offset this.offset = offset; limit(offset + BLOCK_LENGTH); - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { codecState(CodecStates.V0_BLOCK); } @@ -292,7 +292,7 @@ public static int tokenOffsetMaxValue() public TokenCodecEncoder tokenOffset(final int value) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onTokenOffsetAccessed(); } @@ -360,7 +360,7 @@ public static int tokenSizeMaxValue() public TokenCodecEncoder tokenSize(final int value) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onTokenSizeAccessed(); } @@ -428,7 +428,7 @@ public static int fieldIdMaxValue() public TokenCodecEncoder fieldId(final int value) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onFieldIdAccessed(); } @@ -496,7 +496,7 @@ public static int tokenVersionMaxValue() public TokenCodecEncoder tokenVersion(final int value) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onTokenVersionAccessed(); } @@ -564,7 +564,7 @@ public static int componentTokenCountMaxValue() public TokenCodecEncoder componentTokenCount(final int value) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onComponentTokenCountAccessed(); } @@ -617,7 +617,7 @@ private void onSignalAccessed() public TokenCodecEncoder signal(final SignalCodec value) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onSignalAccessed(); } @@ -669,7 +669,7 @@ private void onPrimitiveTypeAccessed() public TokenCodecEncoder primitiveType(final PrimitiveTypeCodec value) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onPrimitiveTypeAccessed(); } @@ -721,7 +721,7 @@ private void onByteOrderAccessed() public TokenCodecEncoder byteOrder(final ByteOrderCodec value) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onByteOrderAccessed(); } @@ -773,7 +773,7 @@ private void onPresenceAccessed() public TokenCodecEncoder presence(final PresenceCodec value) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onPresenceAccessed(); } @@ -840,7 +840,7 @@ public static int deprecatedMaxValue() public TokenCodecEncoder deprecated(final int value) { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onDeprecatedAccessed(); } @@ -897,7 +897,7 @@ public TokenCodecEncoder putName(final DirectBuffer src, final int srcOffset, fi throw new IllegalStateException("length > maxValue for type: " + length); } - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onNameAccessed(); } @@ -918,7 +918,7 @@ public TokenCodecEncoder putName(final byte[] src, final int srcOffset, final in throw new IllegalStateException("length > maxValue for type: " + length); } - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onNameAccessed(); } @@ -942,7 +942,7 @@ public TokenCodecEncoder name(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onNameAccessed(); } @@ -1003,7 +1003,7 @@ public TokenCodecEncoder putConstValue(final DirectBuffer src, final int srcOffs throw new IllegalStateException("length > maxValue for type: " + length); } - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onConstValueAccessed(); } @@ -1024,7 +1024,7 @@ public TokenCodecEncoder putConstValue(final byte[] src, final int srcOffset, fi throw new IllegalStateException("length > maxValue for type: " + length); } - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onConstValueAccessed(); } @@ -1048,7 +1048,7 @@ public TokenCodecEncoder constValue(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onConstValueAccessed(); } @@ -1109,7 +1109,7 @@ public TokenCodecEncoder putMinValue(final DirectBuffer src, final int srcOffset throw new IllegalStateException("length > maxValue for type: " + length); } - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onMinValueAccessed(); } @@ -1130,7 +1130,7 @@ public TokenCodecEncoder putMinValue(final byte[] src, final int srcOffset, fina throw new IllegalStateException("length > maxValue for type: " + length); } - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onMinValueAccessed(); } @@ -1154,7 +1154,7 @@ public TokenCodecEncoder minValue(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onMinValueAccessed(); } @@ -1215,7 +1215,7 @@ public TokenCodecEncoder putMaxValue(final DirectBuffer src, final int srcOffset throw new IllegalStateException("length > maxValue for type: " + length); } - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onMaxValueAccessed(); } @@ -1236,7 +1236,7 @@ public TokenCodecEncoder putMaxValue(final byte[] src, final int srcOffset, fina throw new IllegalStateException("length > maxValue for type: " + length); } - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onMaxValueAccessed(); } @@ -1260,7 +1260,7 @@ public TokenCodecEncoder maxValue(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onMaxValueAccessed(); } @@ -1321,7 +1321,7 @@ public TokenCodecEncoder putNullValue(final DirectBuffer src, final int srcOffse throw new IllegalStateException("length > maxValue for type: " + length); } - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onNullValueAccessed(); } @@ -1342,7 +1342,7 @@ public TokenCodecEncoder putNullValue(final byte[] src, final int srcOffset, fin throw new IllegalStateException("length > maxValue for type: " + length); } - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onNullValueAccessed(); } @@ -1366,7 +1366,7 @@ public TokenCodecEncoder nullValue(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onNullValueAccessed(); } @@ -1427,7 +1427,7 @@ public TokenCodecEncoder putCharacterEncoding(final DirectBuffer src, final int throw new IllegalStateException("length > maxValue for type: " + length); } - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onCharacterEncodingAccessed(); } @@ -1448,7 +1448,7 @@ public TokenCodecEncoder putCharacterEncoding(final byte[] src, final int srcOff throw new IllegalStateException("length > maxValue for type: " + length); } - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onCharacterEncodingAccessed(); } @@ -1472,7 +1472,7 @@ public TokenCodecEncoder characterEncoding(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onCharacterEncodingAccessed(); } @@ -1533,7 +1533,7 @@ public TokenCodecEncoder putEpoch(final DirectBuffer src, final int srcOffset, f throw new IllegalStateException("length > maxValue for type: " + length); } - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onEpochAccessed(); } @@ -1554,7 +1554,7 @@ public TokenCodecEncoder putEpoch(final byte[] src, final int srcOffset, final i throw new IllegalStateException("length > maxValue for type: " + length); } - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onEpochAccessed(); } @@ -1578,7 +1578,7 @@ public TokenCodecEncoder epoch(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onEpochAccessed(); } @@ -1639,7 +1639,7 @@ public TokenCodecEncoder putTimeUnit(final DirectBuffer src, final int srcOffset throw new IllegalStateException("length > maxValue for type: " + length); } - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onTimeUnitAccessed(); } @@ -1660,7 +1660,7 @@ public TokenCodecEncoder putTimeUnit(final byte[] src, final int srcOffset, fina throw new IllegalStateException("length > maxValue for type: " + length); } - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onTimeUnitAccessed(); } @@ -1684,7 +1684,7 @@ public TokenCodecEncoder timeUnit(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onTimeUnitAccessed(); } @@ -1745,7 +1745,7 @@ public TokenCodecEncoder putSemanticType(final DirectBuffer src, final int srcOf throw new IllegalStateException("length > maxValue for type: " + length); } - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onSemanticTypeAccessed(); } @@ -1766,7 +1766,7 @@ public TokenCodecEncoder putSemanticType(final byte[] src, final int srcOffset, throw new IllegalStateException("length > maxValue for type: " + length); } - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onSemanticTypeAccessed(); } @@ -1790,7 +1790,7 @@ public TokenCodecEncoder semanticType(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onSemanticTypeAccessed(); } @@ -1851,7 +1851,7 @@ public TokenCodecEncoder putDescription(final DirectBuffer src, final int srcOff throw new IllegalStateException("length > maxValue for type: " + length); } - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onDescriptionAccessed(); } @@ -1872,7 +1872,7 @@ public TokenCodecEncoder putDescription(final byte[] src, final int srcOffset, f throw new IllegalStateException("length > maxValue for type: " + length); } - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onDescriptionAccessed(); } @@ -1896,7 +1896,7 @@ public TokenCodecEncoder description(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onDescriptionAccessed(); } @@ -1957,7 +1957,7 @@ public TokenCodecEncoder putReferencedName(final DirectBuffer src, final int src throw new IllegalStateException("length > maxValue for type: " + length); } - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onReferencedNameAccessed(); } @@ -1978,7 +1978,7 @@ public TokenCodecEncoder putReferencedName(final byte[] src, final int srcOffset throw new IllegalStateException("length > maxValue for type: " + length); } - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onReferencedNameAccessed(); } @@ -2002,7 +2002,7 @@ public TokenCodecEncoder referencedName(final String value) throw new IllegalStateException("length > maxValue for type: " + length); } - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { onReferencedNameAccessed(); } @@ -2041,7 +2041,7 @@ public StringBuilder appendTo(final StringBuilder builder) public void checkEncodingIsComplete() { - if (SBE_ENABLE_SEQUENCING_CHECKS) + if (SBE_ENABLE_IR_PRECEDENCE_CHECKS) { switch (codecState) { diff --git a/sbe-tool/src/test/cpp/CMakeLists.txt b/sbe-tool/src/test/cpp/CMakeLists.txt index f90aa8de78..cc5bcf89c3 100644 --- a/sbe-tool/src/test/cpp/CMakeLists.txt +++ b/sbe-tool/src/test/cpp/CMakeLists.txt @@ -63,7 +63,8 @@ add_custom_command( -Dsbe.output.dir=${CXX_CODEC_TARGET_DIR} -Dsbe.generate.ir="true" -Dsbe.target.language="cpp" - -Dsbe.generate.sequencing.checks="true" + -Dsbe.generate.precedence.checks="true" + -Dsbe.precedence.checks.flagName="SBE_ENABLE_PRECEDENCE_CHECKS_IN_TESTS" -Dsbe.cpp.disable.implicit.copying="true" -jar ${SBE_JAR} ${CODE_GENERATION_SCHEMA} @@ -90,4 +91,4 @@ sbe_test(CompositeElementsTest codecs) sbe_test(Issue835Test codecs) sbe_test(Issue889Test codecs) sbe_test(FieldAccessOrderCheckTest codecs) -target_compile_definitions(FieldAccessOrderCheckTest PRIVATE SBE_ENABLE_SEQUENCING_CHECKS) +target_compile_definitions(FieldAccessOrderCheckTest PRIVATE SBE_ENABLE_PRECEDENCE_CHECKS_IN_TESTS) From 938fbe92a88322278a38e65a166cc0f038334fe0 Mon Sep 17 00:00:00 2001 From: Zach Bray Date: Mon, 4 Dec 2023 18:20:09 +0000 Subject: [PATCH 46/50] Address Mike's feedback around depending directly on system properties. Also, addresses Mike's feedback around the names of those system properties. In this commit, I've introduced a factory-like class: `PrecedenceChecks`. It creates models for message field access when the checks are enabled. It also provides a convenient place to group together some properties affecting the generation of field precedence checks. I have also renamed `AccessOrderModel` to `FieldPrecedenceModel` in this commit, as it aligns better with the system property names. --- build.gradle | 8 +- .../java/uk/co/real_logic/sbe/SbeTool.java | 18 + .../generation/TargetCodeGeneratorLoader.java | 3 + ...erModel.java => FieldPrecedenceModel.java} | 48 +-- .../generation/common/PrecedenceChecks.java | 257 ++++++++++++ .../sbe/generation/cpp/CppGenerator.java | 376 +++++++++--------- .../sbe/generation/csharp/CSharp.java | 6 +- .../generation/csharp/CSharpGenerator.java | 325 +++++++-------- .../sbe/generation/java/JavaGenerator.java | 346 ++++++++-------- sbe-tool/src/test/cpp/CMakeLists.txt | 2 +- .../generation/java/JavaGeneratorTest.java | 50 ++- 11 files changed, 898 insertions(+), 541 deletions(-) rename sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/{AccessOrderModel.java => FieldPrecedenceModel.java} (96%) create mode 100644 sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/PrecedenceChecks.java diff --git a/build.gradle b/build.gradle index 807939e8d3..2e9843aa4f 100644 --- a/build.gradle +++ b/build.gradle @@ -284,7 +284,7 @@ project(':sbe-tool') { 'sbe.validation.stop.on.error': 'true', 'sbe.validation.xsd': validationXsdPath, 'sbe.generate.precedence.checks': 'true', - 'sbe.precedence.checks.propName': 'sbe.enable.test.precedence.checks') + 'sbe.java.precedence.checks.property.name': 'sbe.enable.test.precedence.checks') args = ['src/test/resources/json-printer-test-schema.xml', 'src/test/resources/composite-elements-schema.xml', 'src/test/resources/field-order-check-schema.xml'] @@ -764,8 +764,8 @@ tasks.register('generateJavaIrCodecs', JavaExec) { 'sbe.target.language': 'Java', 'sbe.validation.xsd': validationXsdPath, 'sbe.generate.precedence.checks': 'true', - 'sbe.precedence.checks.flagName': 'SBE_ENABLE_IR_PRECEDENCE_CHECKS', - 'sbe.precedence.checks.propName': 'sbe.enable.ir.precedence.checks') + 'sbe.precedence.checks.flag.name': 'SBE_ENABLE_IR_PRECEDENCE_CHECKS', + 'sbe.java.precedence.checks.property.name': 'sbe.enable.ir.precedence.checks') args = ['sbe-tool/src/main/resources/sbe-ir.xml'] } @@ -777,7 +777,7 @@ tasks.register('generateCppIrCodecs', JavaExec) { 'sbe.target.language': 'cpp', 'sbe.validation.xsd': validationXsdPath, 'sbe.generate.precedence.checks': 'true', - 'sbe.precedence.checks.flagName': 'SBE_ENABLE_IR_PRECEDENCE_CHECKS') + 'sbe.precedence.checks.flag.name': 'SBE_ENABLE_IR_PRECEDENCE_CHECKS') args = ['sbe-tool/src/main/resources/sbe-ir.xml'] } 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 1134e90c0e..7c5ec14a9f 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 @@ -204,6 +204,24 @@ public class SbeTool */ public static final String SCHEMA_TRANSFORM_VERSION = "sbe.schema.transform.version"; + /** + * Whether to generate field precedence checks. For example, whether to check that repeating groups are encoded + * in schema order. + */ + public static final String GENERATE_PRECEDENCE_CHECKS = "sbe.generate.precedence.checks"; + + /** + * The name of the symbol or macro that enables access order checks when building + * generated C# or C++ code. + */ + public static final String PRECEDENCE_CHECKS_FLAG_NAME = "sbe.precedence.checks.flag.name"; + + /** + * The name of the system property that enables access order checks at runtime + * in generated Java code. + */ + public static final String JAVA_PRECEDENCE_CHECKS_PROPERTY_NAME = "sbe.java.precedence.checks.property.name"; + /** * 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 5d81eb691b..e99d1b8b75 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 @@ -15,6 +15,7 @@ */ package uk.co.real_logic.sbe.generation; +import uk.co.real_logic.sbe.generation.common.PrecedenceChecks; import uk.co.real_logic.sbe.generation.java.JavaOutputManager; import uk.co.real_logic.sbe.generation.c.CGenerator; import uk.co.real_logic.sbe.generation.c.COutputManager; @@ -53,6 +54,7 @@ public CodeGenerator newInstance(final Ir ir, final String outputDir) "true".equals(System.getProperty(JAVA_GENERATE_INTERFACES)), "true".equals(System.getProperty(DECODE_UNKNOWN_ENUM_VALUES)), "true".equals(System.getProperty(TYPES_PACKAGE_OVERRIDE)), + PrecedenceChecks.newInstance(new PrecedenceChecks.Context()), new JavaOutputManager(outputDir, ir.applicableNamespace())); } }, @@ -84,6 +86,7 @@ public CodeGenerator newInstance(final Ir ir, final String outputDir) return new CppGenerator( ir, "true".equals(System.getProperty(DECODE_UNKNOWN_ENUM_VALUES)), + PrecedenceChecks.newInstance(new PrecedenceChecks.Context()), new NamespaceOutputManager(outputDir, ir.applicableNamespace())); } }, diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/AccessOrderModel.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/FieldPrecedenceModel.java similarity index 96% rename from sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/AccessOrderModel.java rename to sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/FieldPrecedenceModel.java index 8836d35008..2259e3c676 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/AccessOrderModel.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/FieldPrecedenceModel.java @@ -38,32 +38,12 @@ @SuppressWarnings("CodeBlock2Expr") // Lambdas without braces tend to conflict with the indentation Checkstyle expects. // Therefore, we allow lambdas with code blocks even when a lambda expression is possible. -public final class AccessOrderModel +public final class FieldPrecedenceModel { - /** - * Whether to generate access order checks. - */ - private static final boolean GENERATE_ACCESS_ORDER_CHECKS = Boolean.parseBoolean( - System.getProperty("sbe.generate.precedence.checks", "false")); - - /** - * The name of the symbol or macro that enables access order checks when building - * generated C# or C++ code. - */ - public static final String PRECEDENCE_CHECKS_FLAG_NAME = - System.getProperty("sbe.precedence.checks.flagName", "SBE_ENABLE_PRECEDENCE_CHECKS"); - - /** - * The name of the system property that enables access order checks at runtime - * in generated Java code. - */ - public static final String PRECEDENCE_CHECKS_PROP_NAME = - System.getProperty("sbe.precedence.checks.propName", "sbe.enable.precedence.checks"); - private final Map groupPathsByField = new HashMap<>(); private final Set topLevelBlockFields = new HashSet<>(); - private final CodecInteraction.HashConsingFactory interactionFactory = - new CodecInteraction.HashConsingFactory(groupPathsByField, topLevelBlockFields); + private final CodecInteraction.CodecInteractionFactory interactionFactory = + new CodecInteraction.CodecInteractionFactory(groupPathsByField, topLevelBlockFields); private final Map> transitionsByInteraction = new LinkedHashMap<>(); private final Map> transitionsByState = new HashMap<>(); private final Int2ObjectHashMap versionWrappedStates = new Int2ObjectHashMap<>(); @@ -71,15 +51,6 @@ public final class AccessOrderModel private State encoderWrappedState; private Set terminalEncoderStates; - /** - * Whether to generate access order checks. - * @return whether to generate access order checks. - */ - public static boolean generateAccessOrderChecks() - { - return GENERATE_ACCESS_ORDER_CHECKS; - } - /** * Builds a state machine that models whether codec interactions are safe. * @@ -90,14 +61,14 @@ public static boolean generateAccessOrderChecks() * @param versionsSelector a function that selects the versions to model in the state machine * @return the access order model */ - public static AccessOrderModel newInstance( + public static FieldPrecedenceModel newInstance( final Token msgToken, final List fields, final List groups, final List varData, final Function versionsSelector) { - final AccessOrderModel model = new AccessOrderModel(); + final FieldPrecedenceModel model = new FieldPrecedenceModel(); model.findTransitions(msgToken, fields, groups, varData, versionsSelector); return model; } @@ -178,7 +149,7 @@ public int stateCount() * @see CodecInteraction * @return a hash-consing factory for codec interactions. */ - public CodecInteraction.HashConsingFactory interactionFactory() + public CodecInteraction.CodecInteractionFactory interactionFactory() { return interactionFactory; } @@ -620,7 +591,7 @@ private State(final int number, final String name) } /** - * In the scope of an {@code AccessOrderModel} instance, state numbers are contiguous + * In the scope of an {@code FieldPrecedenceModel} instance, state numbers are contiguous * and start at 0. This numbering scheme allows easy generation of lookup tables. * @return the state number */ @@ -1037,7 +1008,7 @@ String exampleConditions() * is used to hash-cons the instances, so that they can be compared by * reference. */ - public static final class HashConsingFactory + public static final class CodecInteractionFactory { private final Int2ObjectHashMap wrapInteractions = new Int2ObjectHashMap<>(); private final Map accessFieldInteractions = new HashMap<>(); @@ -1050,7 +1021,7 @@ public static final class HashConsingFactory private final Map groupPathsByField; private final Set topLevelBlockFields; - HashConsingFactory( + CodecInteractionFactory( final Map groupPathsByField, final Set topLevelBlockFields) { @@ -1162,7 +1133,6 @@ public CodecInteraction moveToLastElement(final Token token) t -> new MoveToLastElement(groupPathsByField.get(t), t)); } - /** * Find or create a {@link CodecInteraction} to represent resetting the count * of a repeating group to the current index. diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/PrecedenceChecks.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/PrecedenceChecks.java new file mode 100644 index 0000000000..3e21ba8408 --- /dev/null +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/PrecedenceChecks.java @@ -0,0 +1,257 @@ +/* + * 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.common; + +import uk.co.real_logic.sbe.SbeTool; +import uk.co.real_logic.sbe.ir.Token; + +import java.util.ArrayList; +import java.util.List; +import java.util.OptionalInt; +import java.util.function.Function; +import java.util.stream.IntStream; + +import static uk.co.real_logic.sbe.ir.GenerationUtil.collectFields; +import static uk.co.real_logic.sbe.ir.GenerationUtil.collectGroups; +import static uk.co.real_logic.sbe.ir.GenerationUtil.collectVarData; + +/** + * A factory for creating a {@link FieldPrecedenceModel} for a message from its IR tokens. + */ +public final class PrecedenceChecks +{ + private static final Function SELECT_LATEST_VERSION_ONLY = versions -> + { + final OptionalInt max = versions.max(); + return max.isPresent() ? IntStream.of(max.getAsInt()) : IntStream.empty(); + }; + + private final Context context; + + private PrecedenceChecks(final Context context) + { + this.context = context; + } + + /** + * Creates a {@link FieldPrecedenceModel} for the given message tokens, + * unless precedence checks are disabled, in which case {@code null} is returned. + *

+ * Only the latest version of the message is considered when creating the model. + *

+ * + * @param msgTokens the tokens of the message. + * @return a {@link FieldPrecedenceModel} for the given message tokens or {@code null} if precedence checks + * are disabled. + */ + public FieldPrecedenceModel createEncoderModel(final List msgTokens) + { + return createModel(msgTokens, SELECT_LATEST_VERSION_ONLY); + } + + /** + * Creates a {@link FieldPrecedenceModel} for the given message tokens, + * unless precedence checks are disabled, in which case {@code null} is returned. + *

+ * All versions of the message are considered when creating the model. + *

+ * + * @param msgTokens the tokens of the message. + * @return a {@link FieldPrecedenceModel} for the given message tokens or {@code null} if precedence checks + * are disabled. + */ + public FieldPrecedenceModel createDecoderModel(final List msgTokens) + { + return createModel(msgTokens, Function.identity()); + } + + /** + * Creates a {@link FieldPrecedenceModel} for the given message tokens, + * unless precedence checks are disabled, in which case {@code null} is returned. + *

+ * All versions of the message are considered when creating the model. + *

+ * + * @param msgTokens the tokens of the message. + * @return a {@link FieldPrecedenceModel} for the given message tokens or {@code null} if precedence checks + * are disabled. + */ + public FieldPrecedenceModel createCodecModel(final List msgTokens) + { + return createModel(msgTokens, Function.identity()); + } + + /** + * Returns the {@link Context} describing how precedence checks should be generated. + * + * @return the {@link Context} describing how precedence checks should be generated. + */ + public Context context() + { + return context; + } + + /** + * Creates a new {@link PrecedenceChecks} instance. + * + * @param context the {@link Context} describing how precedence checks should be generated. + * @return a new {@link PrecedenceChecks} instance. + */ + public static PrecedenceChecks newInstance(final Context context) + { + return new PrecedenceChecks(context); + } + + private FieldPrecedenceModel createModel( + final List tokens, + final Function versionsSelector + ) + { + if (context.shouldGeneratePrecedenceChecks()) + { + final Token msgToken = tokens.get(0); + + 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); + + return FieldPrecedenceModel.newInstance(msgToken, fields, groups, varData, versionsSelector); + } + + return null; + } + + static final class Configuration + { + private static final boolean GENERATE_PRECEDENCE_CHECKS_VALUE = + Boolean.parseBoolean(System.getProperty(SbeTool.GENERATE_PRECEDENCE_CHECKS, "false")); + + private static final String PRECEDENCE_CHECKS_FLAG_NAME_VALUE = + System.getProperty(SbeTool.PRECEDENCE_CHECKS_FLAG_NAME, "SBE_ENABLE_PRECEDENCE_CHECKS"); + + private static final String PRECEDENCE_CHECKS_PROPERTY_NAME_VALUE = + System.getProperty(SbeTool.JAVA_PRECEDENCE_CHECKS_PROPERTY_NAME, "sbe.enable.precedence.checks"); + + private Configuration() + { + } + + static boolean shouldGeneratePrecedenceChecks() + { + return GENERATE_PRECEDENCE_CHECKS_VALUE; + } + + static String precedenceChecksFlagName() + { + return PRECEDENCE_CHECKS_FLAG_NAME_VALUE; + } + + static String precedenceChecksPropName() + { + return PRECEDENCE_CHECKS_PROPERTY_NAME_VALUE; + } + } + + /** + * The context describing how precedence checks should be generated. + */ + public static final class Context + { + private boolean shouldGeneratePrecedenceChecks = Configuration.shouldGeneratePrecedenceChecks(); + private String precedenceChecksFlagName = Configuration.precedenceChecksFlagName(); + private String precedenceChecksPropName = Configuration.precedenceChecksPropName(); + + /** + * Returns {@code true} if precedence checks should be generated; {@code false} otherwise. + * + * @return {@code true} if precedence checks should be generated; {@code false} otherwise. + */ + public boolean shouldGeneratePrecedenceChecks() + { + return shouldGeneratePrecedenceChecks; + } + + /** + * Sets whether field precedence checks should be generated. + * + * @param shouldGeneratePrecedenceChecks {@code true} if precedence checks should be generated; + * {@code false} otherwise. + * @return this {@link Context} instance. + */ + public Context shouldGeneratePrecedenceChecks(final boolean shouldGeneratePrecedenceChecks) + { + this.shouldGeneratePrecedenceChecks = shouldGeneratePrecedenceChecks; + return this; + } + + /** + * Returns the name of the flag that can be used to enable precedence checks at runtime. + * + * @return the name of the flag that can be used to enable precedence checks at runtime. + */ + public String precedenceChecksFlagName() + { + return precedenceChecksFlagName; + } + + /** + * Sets the name of the flag that can be used to enable precedence checks at runtime. + * + * @param precedenceChecksFlagName the name of the flag that can be used to enable + * precedence checks at runtime. + * @return this {@link Context} instance. + */ + public Context precedenceChecksFlagName(final String precedenceChecksFlagName) + { + this.precedenceChecksFlagName = precedenceChecksFlagName; + return this; + } + + /** + * Returns the name of the system property that can be used to enable precedence checks + * at runtime. + * + * @return the name of the system property that can be used to enable precedence checks + * at runtime. + */ + public String precedenceChecksPropName() + { + return precedenceChecksPropName; + } + + /** + * Sets the name of the system property that can be used to enable precedence checks + * at runtime. + * + * @param precedenceChecksPropName the name of the system property that can be used to + * enable precedence checks at runtime. + * @return this {@link Context} instance. + */ + public Context precedenceChecksPropName(final String precedenceChecksPropName) + { + this.precedenceChecksPropName = precedenceChecksPropName; + return this; + } + } +} diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java index 9e392661a8..d8307fa450 100755 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java @@ -18,7 +18,8 @@ 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.generation.common.AccessOrderModel; +import uk.co.real_logic.sbe.generation.common.FieldPrecedenceModel; +import uk.co.real_logic.sbe.generation.common.PrecedenceChecks; import uk.co.real_logic.sbe.ir.Encoding; import uk.co.real_logic.sbe.ir.Ir; import uk.co.real_logic.sbe.ir.Signal; @@ -32,7 +33,6 @@ import java.io.Writer; import java.nio.ByteOrder; import java.util.*; -import java.util.function.Function; import static uk.co.real_logic.sbe.generation.Generators.toLowerFirstChar; import static uk.co.real_logic.sbe.generation.Generators.toUpperFirstChar; @@ -56,6 +56,8 @@ public class CppGenerator implements CodeGenerator private final Ir ir; private final OutputManager outputManager; private final boolean shouldDecodeUnknownEnumValues; + private final PrecedenceChecks precedenceChecks; + private final String precedenceChecksFlagName; /** * Create a new Go language {@link CodeGenerator}. @@ -65,12 +67,36 @@ public class CppGenerator implements CodeGenerator * @param outputManager for generating the codecs to. */ public CppGenerator(final Ir ir, final boolean shouldDecodeUnknownEnumValues, final OutputManager outputManager) + { + this( + ir, + shouldDecodeUnknownEnumValues, + PrecedenceChecks.newInstance(new PrecedenceChecks.Context()), + outputManager + ); + } + + /** + * Create a new Go language {@link CodeGenerator}. + * + * @param ir for the messages and types. + * @param shouldDecodeUnknownEnumValues generate support for unknown enum values when decoding. + * @param precedenceChecks whether and how to generate field precedence checks. + * @param outputManager for generating the codecs to. + */ + public CppGenerator( + final Ir ir, + final boolean shouldDecodeUnknownEnumValues, + final PrecedenceChecks precedenceChecks, + final OutputManager outputManager) { Verify.notNull(ir, "ir"); Verify.notNull(outputManager, "outputManager"); this.ir = ir; this.shouldDecodeUnknownEnumValues = shouldDecodeUnknownEnumValues; + this.precedenceChecks = precedenceChecks; + this.precedenceChecksFlagName = precedenceChecks.context().precedenceChecksFlagName(); this.outputManager = outputManager; } @@ -148,6 +174,7 @@ public void generate() throws IOException { final Token msgToken = tokens.get(0); final String className = formatClassName(msgToken.name()); + final FieldPrecedenceModel fieldPrecedenceModel = precedenceChecks.createCodecModel(tokens); try (Writer out = outputManager.createOutput(className)) { @@ -164,39 +191,28 @@ public void generate() throws IOException final List varData = new ArrayList<>(); collectVarData(messageBody, i, varData); - AccessOrderModel accessOrderModel = null; - if (AccessOrderModel.generateAccessOrderChecks()) - { - accessOrderModel = AccessOrderModel.newInstance( - msgToken, - fields, - groups, - varData, - Function.identity()); - } - out.append(generateFileHeader(ir.namespaces(), className, typesToInclude)); out.append(generateClassDeclaration(className)); - out.append(generateMessageFlyweightCode(className, msgToken, accessOrderModel)); - out.append(generateFullyEncodedCheck(accessOrderModel)); + out.append(generateMessageFlyweightCode(className, msgToken, fieldPrecedenceModel)); + out.append(generateFullyEncodedCheck(fieldPrecedenceModel)); final StringBuilder sb = new StringBuilder(); - generateFields(sb, className, fields, accessOrderModel, BASE_INDENT); - generateGroups(sb, groups, accessOrderModel, BASE_INDENT); - generateVarData(sb, className, varData, accessOrderModel, BASE_INDENT); + generateFields(sb, className, fields, fieldPrecedenceModel, BASE_INDENT); + generateGroups(sb, groups, fieldPrecedenceModel, BASE_INDENT); + generateVarData(sb, className, varData, fieldPrecedenceModel, BASE_INDENT); generateDisplay(sb, msgToken.name(), fields, groups, varData); sb.append(generateMessageLength(groups, varData, BASE_INDENT)); sb.append("};\n"); - generateLookupTableDefinitions(sb, className, accessOrderModel); + generateLookupTableDefinitions(sb, className, fieldPrecedenceModel); sb.append(CppUtil.closingBraces(ir.namespaces().length)).append("#endif\n"); out.append(sb); } } } - private static CharSequence generateFullyEncodedCheck(final AccessOrderModel accessOrderModel) + private CharSequence generateFullyEncodedCheck(final FieldPrecedenceModel fieldPrecedenceModel) { - if (null == accessOrderModel) + if (null == fieldPrecedenceModel) { return ""; } @@ -207,11 +223,11 @@ private static CharSequence generateFullyEncodedCheck(final AccessOrderModel acc sb.append(indent).append("void checkEncodingIsComplete()\n") .append(indent).append("{\n") - .append("#if defined(").append(AccessOrderModel.PRECEDENCE_CHECKS_FLAG_NAME).append(")\n") + .append("#if defined(").append(precedenceChecksFlagName).append(")\n") .append(indent).append(INDENT).append("switch (m_codecState)\n") .append(indent).append(INDENT).append("{\n"); - accessOrderModel.forEachTerminalEncoderState(state -> + fieldPrecedenceModel.forEachTerminalEncoderState(state -> { sb.append(indent).append(TWO_INDENT).append("case ").append(stateCaseForSwitchCase(state)).append(":\n") .append(indent).append(THREE_INDENT).append("return;\n"); @@ -244,19 +260,19 @@ private static String accessOrderListenerMethodName(final Token token, final Str private static void generateAccessOrderListenerMethod( final StringBuilder sb, - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final String indent, final Token token) { - if (null == accessOrderModel) + if (null == fieldPrecedenceModel) { return; } - final AccessOrderModel.CodecInteraction fieldAccess = - accessOrderModel.interactionFactory().accessField(token); + final FieldPrecedenceModel.CodecInteraction fieldAccess = + fieldPrecedenceModel.interactionFactory().accessField(token); - final String constDeclaration = canChangeState(accessOrderModel, fieldAccess) ? "" : " const"; + final String constDeclaration = canChangeState(fieldPrecedenceModel, fieldAccess) ? "" : " const"; sb.append("\n") .append(indent).append("private:\n") @@ -268,7 +284,7 @@ private static void generateAccessOrderListenerMethod( sb, indent + TWO_INDENT, "access field", - accessOrderModel, + fieldPrecedenceModel, fieldAccess); sb.append(indent).append(INDENT).append("}\n\n") @@ -276,8 +292,8 @@ private static void generateAccessOrderListenerMethod( } private static boolean canChangeState( - final AccessOrderModel accessOrderModel, - final AccessOrderModel.CodecInteraction fieldAccess) + final FieldPrecedenceModel fieldPrecedenceModel, + final FieldPrecedenceModel.CodecInteraction fieldAccess) { if (fieldAccess.isTopLevelBlockFieldAccess()) { @@ -285,7 +301,7 @@ private static boolean canChangeState( } final MutableBoolean canChangeState = new MutableBoolean(false); - accessOrderModel.forEachTransition(fieldAccess, transition -> + fieldPrecedenceModel.forEachTransition(fieldAccess, transition -> { if (!transition.alwaysEndsInStartState()) { @@ -296,32 +312,32 @@ private static boolean canChangeState( return canChangeState.get(); } - private static CharSequence generateAccessOrderListenerCall( - final AccessOrderModel accessOrderModel, + private CharSequence generateAccessOrderListenerCall( + final FieldPrecedenceModel fieldPrecedenceModel, final String indent, final Token token, final String... arguments) { return generateAccessOrderListenerCall( - accessOrderModel, + fieldPrecedenceModel, indent, accessOrderListenerMethodName(token), arguments); } - private static CharSequence generateAccessOrderListenerCall( - final AccessOrderModel accessOrderModel, + private CharSequence generateAccessOrderListenerCall( + final FieldPrecedenceModel fieldPrecedenceModel, final String indent, final String methodName, final String... arguments) { - if (null == accessOrderModel) + if (null == fieldPrecedenceModel) { return ""; } final StringBuilder sb = new StringBuilder(); - sb.append("#if defined(").append(AccessOrderModel.PRECEDENCE_CHECKS_FLAG_NAME).append(")\n") + sb.append("#if defined(").append(precedenceChecksFlagName).append(")\n") .append(indent).append(methodName).append("("); for (int i = 0; i < arguments.length; i++) @@ -341,11 +357,11 @@ private static CharSequence generateAccessOrderListenerCall( private static void generateAccessOrderListenerMethodForGroupWrap( final StringBuilder sb, - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final String indent, final Token token) { - if (null == accessOrderModel) + if (null == fieldPrecedenceModel) { return; } @@ -358,28 +374,28 @@ private static void generateAccessOrderListenerMethodForGroupWrap( .append(indent).append(TWO_INDENT).append("if (0 == remaining)\n") .append(indent).append(TWO_INDENT).append("{\n"); - final AccessOrderModel.CodecInteraction selectEmptyGroup = - accessOrderModel.interactionFactory().determineGroupIsEmpty(token); + final FieldPrecedenceModel.CodecInteraction selectEmptyGroup = + fieldPrecedenceModel.interactionFactory().determineGroupIsEmpty(token); generateAccessOrderListener( sb, indent + THREE_INDENT, "\" + action + \" count of repeating group", - accessOrderModel, + fieldPrecedenceModel, selectEmptyGroup); sb.append(indent).append(TWO_INDENT).append("}\n") .append(indent).append(TWO_INDENT).append("else\n") .append(indent).append(TWO_INDENT).append("{\n"); - final AccessOrderModel.CodecInteraction selectNonEmptyGroup = - accessOrderModel.interactionFactory().determineGroupHasElements(token); + final FieldPrecedenceModel.CodecInteraction selectNonEmptyGroup = + fieldPrecedenceModel.interactionFactory().determineGroupHasElements(token); generateAccessOrderListener( sb, indent + THREE_INDENT, "\" + action + \" count of repeating group", - accessOrderModel, + fieldPrecedenceModel, selectNonEmptyGroup); sb.append(indent).append(TWO_INDENT).append("}\n") @@ -389,11 +405,11 @@ private static void generateAccessOrderListenerMethodForGroupWrap( private static void generateAccessOrderListenerMethodForVarDataLength( final StringBuilder sb, - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final String indent, final Token token) { - if (null == accessOrderModel) + if (null == fieldPrecedenceModel) { return; } @@ -404,14 +420,14 @@ private static void generateAccessOrderListenerMethodForVarDataLength( .append("() const\n") .append(indent).append(INDENT).append("{\n"); - final AccessOrderModel.CodecInteraction accessLength = - accessOrderModel.interactionFactory().accessVarDataLength(token); + final FieldPrecedenceModel.CodecInteraction accessLength = + fieldPrecedenceModel.interactionFactory().accessVarDataLength(token); generateAccessOrderListener( sb, indent + TWO_INDENT, "decode length of var data", - accessOrderModel, + fieldPrecedenceModel, accessLength); sb.append(indent).append(INDENT).append("}\n\n") @@ -422,13 +438,13 @@ private static void generateAccessOrderListener( final StringBuilder sb, final String indent, final String action, - final AccessOrderModel accessOrderModel, - final AccessOrderModel.CodecInteraction interaction) + final FieldPrecedenceModel fieldPrecedenceModel, + final FieldPrecedenceModel.CodecInteraction interaction) { if (interaction.isTopLevelBlockFieldAccess()) { sb.append(indent).append("if (codecState() == ") - .append(qualifiedStateCase(accessOrderModel.notWrappedState())) + .append(qualifiedStateCase(fieldPrecedenceModel.notWrappedState())) .append(")\n") .append(indent).append("{\n"); generateAccessOrderException(sb, indent + INDENT, action, interaction); @@ -439,7 +455,7 @@ private static void generateAccessOrderListener( sb.append(indent).append("switch (codecState())\n") .append(indent).append("{\n"); - accessOrderModel.forEachTransition(interaction, transitionGroup -> + fieldPrecedenceModel.forEachTransition(interaction, transitionGroup -> { transitionGroup.forEachStartState(startState -> @@ -467,7 +483,7 @@ private static void generateAccessOrderException( final StringBuilder sb, final String indent, final String action, - final AccessOrderModel.CodecInteraction interaction) + final FieldPrecedenceModel.CodecInteraction interaction) { sb.append(indent).append("throw AccessOrderError(") .append("std::string(\"Illegal field access order. \") +\n") @@ -482,11 +498,11 @@ private static void generateAccessOrderException( private static void generateAccessOrderListenerMethodForNextGroupElement( final StringBuilder sb, - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final String indent, final Token token) { - if (null == accessOrderModel) + if (null == fieldPrecedenceModel) { return; } @@ -499,28 +515,28 @@ private static void generateAccessOrderListenerMethodForNextGroupElement( .append(indent).append(TWO_INDENT).append("if (remaining > 1)\n") .append(indent).append(TWO_INDENT).append("{\n"); - final AccessOrderModel.CodecInteraction selectNextElementInGroup = - accessOrderModel.interactionFactory().moveToNextElement(token); + final FieldPrecedenceModel.CodecInteraction selectNextElementInGroup = + fieldPrecedenceModel.interactionFactory().moveToNextElement(token); generateAccessOrderListener( sb, indent + THREE_INDENT, "access next element in repeating group", - accessOrderModel, + fieldPrecedenceModel, selectNextElementInGroup); sb.append(indent).append(TWO_INDENT).append("}\n") .append(indent).append(TWO_INDENT).append("else if (1 == remaining)\n") .append(indent).append(TWO_INDENT).append("{\n"); - final AccessOrderModel.CodecInteraction selectLastElementInGroup = - accessOrderModel.interactionFactory().moveToLastElement(token); + final FieldPrecedenceModel.CodecInteraction selectLastElementInGroup = + fieldPrecedenceModel.interactionFactory().moveToLastElement(token); generateAccessOrderListener( sb, indent + THREE_INDENT, "access next element in repeating group", - accessOrderModel, + fieldPrecedenceModel, selectLastElementInGroup); sb.append(indent).append(TWO_INDENT).append("}\n") @@ -529,11 +545,11 @@ private static void generateAccessOrderListenerMethodForNextGroupElement( private static void generateAccessOrderListenerMethodForResetGroupCount( final StringBuilder sb, - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final String indent, final Token token) { - if (null == accessOrderModel) + if (null == fieldPrecedenceModel) { return; } @@ -542,14 +558,14 @@ private static void generateAccessOrderListenerMethodForResetGroupCount( .append(indent).append(INDENT).append("void onResetCountToIndex()\n") .append(indent).append(INDENT).append("{\n"); - final AccessOrderModel.CodecInteraction resetCountToIndex = - accessOrderModel.interactionFactory().resetCountToIndex(token); + final FieldPrecedenceModel.CodecInteraction resetCountToIndex = + fieldPrecedenceModel.interactionFactory().resetCountToIndex(token); generateAccessOrderListener( sb, indent + TWO_INDENT, "reset count of repeating group", - accessOrderModel, + fieldPrecedenceModel, resetCountToIndex); sb.append(indent).append(INDENT).append("}\n"); @@ -558,7 +574,7 @@ private static void generateAccessOrderListenerMethodForResetGroupCount( private void generateGroups( final StringBuilder sb, final List tokens, - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final String indent) { for (int i = 0, size = tokens.size(); i < size; i++) @@ -573,7 +589,7 @@ private void generateGroups( final Token numInGroupToken = Generators.findFirst("numInGroup", tokens, i); final String cppTypeForNumInGroup = cppTypeName(numInGroupToken.encoding().primitiveType()); - generateGroupClassHeader(sb, groupName, groupToken, tokens, accessOrderModel, i, indent + INDENT); + generateGroupClassHeader(sb, groupName, groupToken, tokens, fieldPrecedenceModel, i, indent + INDENT); ++i; final int groupHeaderTokenCount = tokens.get(i).componentTokenCount(); @@ -581,30 +597,30 @@ private void generateGroups( final List fields = new ArrayList<>(); i = collectFields(tokens, i, fields); - generateFields(sb, formatClassName(groupName), fields, accessOrderModel, indent + INDENT); + generateFields(sb, formatClassName(groupName), fields, fieldPrecedenceModel, indent + INDENT); final List groups = new ArrayList<>(); i = collectGroups(tokens, i, groups); - generateGroups(sb, groups, accessOrderModel, indent + INDENT); + generateGroups(sb, groups, fieldPrecedenceModel, indent + INDENT); final List varData = new ArrayList<>(); i = collectVarData(tokens, i, varData); - generateVarData(sb, formatClassName(groupName), varData, accessOrderModel, indent + INDENT); + generateVarData(sb, formatClassName(groupName), varData, fieldPrecedenceModel, indent + INDENT); sb.append(generateGroupDisplay(groupName, fields, groups, varData, indent + INDENT + INDENT)); sb.append(generateMessageLength(groups, varData, indent + INDENT + INDENT)); sb.append(indent).append(" };\n"); - generateGroupProperty(sb, groupName, groupToken, cppTypeForNumInGroup, accessOrderModel, indent); + generateGroupProperty(sb, groupName, groupToken, cppTypeForNumInGroup, fieldPrecedenceModel, indent); } } - private static void generateGroupClassHeader( + private void generateGroupClassHeader( final StringBuilder sb, final String groupName, final Token groupToken, final List tokens, - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final int index, final String indent) { @@ -638,7 +654,7 @@ private static void generateGroupClassHeader( indent + " }\n\n", groupClassName); - if (null != accessOrderModel) + if (null != fieldPrecedenceModel) { new Formatter(sb).format( indent + " CodecState *m_codecStatePtr = nullptr;\n\n" + @@ -662,11 +678,11 @@ private static void generateGroupClassHeader( sb.append(generateHiddenCopyConstructor(indent + " ", groupClassName)); - final String codecStateParameter = null == accessOrderModel ? + final String codecStateParameter = null == fieldPrecedenceModel ? ")\n" : ",\n " + indent + " CodecState *codecState)\n"; - final String codecStateAssignment = null == accessOrderModel ? + final String codecStateAssignment = null == fieldPrecedenceModel ? "" : indent + " m_codecStatePtr = codecState;\n"; @@ -746,7 +762,7 @@ private static void generateGroupClassHeader( if (groupToken.version() > 0) { - final String codecStateNullAssignment = null == accessOrderModel ? + final String codecStateNullAssignment = null == fieldPrecedenceModel ? "" : indent + " m_codecStatePtr = nullptr;\n"; @@ -767,16 +783,16 @@ private static void generateGroupClassHeader( } - if (null != accessOrderModel) + if (null != fieldPrecedenceModel) { sb.append("\n").append(indent).append("private:"); - generateAccessOrderListenerMethodForNextGroupElement(sb, accessOrderModel, indent, groupToken); - generateAccessOrderListenerMethodForResetGroupCount(sb, accessOrderModel, indent, groupToken); + generateAccessOrderListenerMethodForNextGroupElement(sb, fieldPrecedenceModel, indent, groupToken); + generateAccessOrderListenerMethodForResetGroupCount(sb, fieldPrecedenceModel, indent, groupToken); sb.append("\n").append(indent).append("public:"); } - final CharSequence onNextAccessOrderCall = null == accessOrderModel ? "" : - generateAccessOrderListenerCall(accessOrderModel, indent + TWO_INDENT, "onNextElementAccessed"); + final CharSequence onNextAccessOrderCall = null == fieldPrecedenceModel ? "" : + generateAccessOrderListenerCall(fieldPrecedenceModel, indent + TWO_INDENT, "onNextElementAccessed"); new Formatter(sb).format("\n" + indent + " static SBE_CONSTEXPR std::uint64_t sbeHeaderSize() SBE_NOEXCEPT\n" + @@ -844,7 +860,7 @@ private static void generateGroupClassHeader( sb.append("\n") .append(indent).append(" inline std::uint64_t resetCountToIndex()\n") .append(indent).append(" {\n") - .append(generateAccessOrderListenerCall(accessOrderModel, indent + TWO_INDENT, "onResetCountToIndex")) + .append(generateAccessOrderListenerCall(fieldPrecedenceModel, indent + TWO_INDENT, "onResetCountToIndex")) .append(indent).append(" m_count = m_index;\n") .append(indent).append(" ").append(dimensionsClassName) .append(" dimensions(m_buffer, m_initialPosition, m_bufferLength, m_actingVersion);\n") @@ -864,12 +880,12 @@ private static void generateGroupClassHeader( .append(indent).append(" }\n\n"); } - private static void generateGroupProperty( + private void generateGroupProperty( final StringBuilder sb, final String groupName, final Token token, final String cppTypeForNumInGroup, - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final String indent) { final String className = formatClassName(groupName); @@ -891,20 +907,20 @@ private static void generateGroupProperty( groupName, token.id()); - if (null != accessOrderModel) + if (null != fieldPrecedenceModel) { generateAccessOrderListenerMethodForGroupWrap( sb, - accessOrderModel, + fieldPrecedenceModel, indent, token ); } - final String codecStateArgument = null == accessOrderModel ? "" : ", codecStatePtr()"; + final String codecStateArgument = null == fieldPrecedenceModel ? "" : ", codecStatePtr()"; - final CharSequence onDecodeAccessOrderCall = null == accessOrderModel ? "" : - generateAccessOrderListenerCall(accessOrderModel, indent + TWO_INDENT, token, + final CharSequence onDecodeAccessOrderCall = null == fieldPrecedenceModel ? "" : + generateAccessOrderListenerCall(fieldPrecedenceModel, indent + TWO_INDENT, token, "m_" + propertyName + ".count()", "\"decode\""); if (token.version() > 0) @@ -945,8 +961,8 @@ private static void generateGroupProperty( onDecodeAccessOrderCall); } - final CharSequence onEncodeAccessOrderCall = null == accessOrderModel ? "" : - generateAccessOrderListenerCall(accessOrderModel, indent + TWO_INDENT, token, "count", "\"encode\""); + final CharSequence onEncodeAccessOrderCall = null == fieldPrecedenceModel ? "" : + generateAccessOrderListenerCall(fieldPrecedenceModel, indent + TWO_INDENT, token, "count", "\"encode\""); new Formatter(sb).format("\n" + indent + " %1$s &%2$sCount(const %3$s count)\n" + @@ -983,7 +999,7 @@ private void generateVarData( final StringBuilder sb, final String className, final List tokens, - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final String indent) { for (int i = 0, size = tokens.size(); i < size;) @@ -1008,10 +1024,10 @@ private void generateVarData( generateVarDataDescriptors( sb, token, propertyName, characterEncoding, lengthOfLengthField, indent); - generateAccessOrderListenerMethodForVarDataLength(sb, accessOrderModel, indent, token); + generateAccessOrderListenerMethodForVarDataLength(sb, fieldPrecedenceModel, indent, token); final CharSequence lengthAccessListenerCall = generateAccessOrderListenerCall( - accessOrderModel, indent + TWO_INDENT, + fieldPrecedenceModel, indent + TWO_INDENT, accessOrderListenerMethodName(token, "Length")); new Formatter(sb).format("\n" + @@ -1029,10 +1045,10 @@ private void generateVarData( lengthCppType, lengthAccessListenerCall); - generateAccessOrderListenerMethod(sb, accessOrderModel, indent, token); + generateAccessOrderListenerMethod(sb, fieldPrecedenceModel, indent, token); final CharSequence accessOrderListenerCall = - generateAccessOrderListenerCall(accessOrderModel, indent + TWO_INDENT, token); + generateAccessOrderListenerCall(fieldPrecedenceModel, indent + TWO_INDENT, token); new Formatter(sb).format("\n" + indent + " std::uint64_t skip%1$s()\n" + @@ -1808,7 +1824,7 @@ private void generatePrimitiveProperty( final String propertyName, final Token propertyToken, final Token encodingToken, - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final String indent) { generatePrimitiveFieldMetaData(sb, propertyName, encodingToken, indent); @@ -1820,7 +1836,7 @@ private void generatePrimitiveProperty( else { generatePrimitivePropertyMethods( - sb, containingClassName, propertyName, propertyToken, encodingToken, accessOrderModel, indent); + sb, containingClassName, propertyName, propertyToken, encodingToken, fieldPrecedenceModel, indent); } } @@ -1830,19 +1846,19 @@ private void generatePrimitivePropertyMethods( final String propertyName, final Token propertyToken, final Token encodingToken, - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final String indent) { final int arrayLength = encodingToken.arrayLength(); if (arrayLength == 1) { generateSingleValueProperty(sb, containingClassName, propertyName, propertyToken, encodingToken, - accessOrderModel, indent); + fieldPrecedenceModel, indent); } else if (arrayLength > 1) { generateArrayProperty(sb, containingClassName, propertyName, propertyToken, encodingToken, - accessOrderModel, indent); + fieldPrecedenceModel, indent); } } @@ -1970,9 +1986,9 @@ private CharSequence generateStoreValue( return sb; } - private static String noexceptDeclaration(final AccessOrderModel accessOrderModel) + private static String noexceptDeclaration(final FieldPrecedenceModel fieldPrecedenceModel) { - return accessOrderModel == null ? " SBE_NOEXCEPT" : ""; + return fieldPrecedenceModel == null ? " SBE_NOEXCEPT" : ""; } private void generateSingleValueProperty( @@ -1981,7 +1997,7 @@ private void generateSingleValueProperty( final String propertyName, final Token propertyToken, final Token encodingToken, - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final String indent) { final PrimitiveType primitiveType = encodingToken.encoding().primitiveType(); @@ -1989,9 +2005,9 @@ private void generateSingleValueProperty( final int offset = encodingToken.offset(); final CharSequence accessOrderListenerCall = - generateAccessOrderListenerCall(accessOrderModel, indent + TWO_INDENT, propertyToken); + generateAccessOrderListenerCall(fieldPrecedenceModel, indent + TWO_INDENT, propertyToken); - final String noexceptDeclaration = noexceptDeclaration(accessOrderModel); + final String noexceptDeclaration = noexceptDeclaration(fieldPrecedenceModel); new Formatter(sb).format("\n" + indent + " SBE_NODISCARD %1$s %2$s() const%6$s\n" + @@ -2031,7 +2047,7 @@ private void generateArrayProperty( final String propertyName, final Token propertyToken, final Token encodingToken, - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final String indent) { final PrimitiveType primitiveType = encodingToken.encoding().primitiveType(); @@ -2039,9 +2055,9 @@ private void generateArrayProperty( final int offset = encodingToken.offset(); final CharSequence accessOrderListenerCall = - generateAccessOrderListenerCall(accessOrderModel, indent + TWO_INDENT, propertyToken); + generateAccessOrderListenerCall(fieldPrecedenceModel, indent + TWO_INDENT, propertyToken); - final String noexceptDeclaration = noexceptDeclaration(accessOrderModel); + final String noexceptDeclaration = noexceptDeclaration(fieldPrecedenceModel); final int arrayLength = encodingToken.arrayLength(); new Formatter(sb).format("\n" + @@ -2570,9 +2586,9 @@ private static String generateHiddenCopyConstructor(final String indent, final S private static CharSequence generateConstructorsAndOperators( final String className, - final AccessOrderModel accessOrderModel) + final FieldPrecedenceModel fieldPrecedenceModel) { - final String constructorWithCodecState = null == accessOrderModel ? "" : String.format( + final String constructorWithCodecState = null == fieldPrecedenceModel ? "" : String.format( " %1$s(\n" + " char *buffer,\n" + " const std::uint64_t offset,\n" + @@ -2631,7 +2647,7 @@ private static CharSequence generateConstructorsAndOperators( private CharSequence generateMessageFlyweightCode( final String className, final Token token, - final AccessOrderModel accessOrderModel) + final FieldPrecedenceModel fieldPrecedenceModel) { final String blockLengthType = cppTypeName(ir.headerStructure().blockLengthType()); final String templateIdType = cppTypeName(ir.headerStructure().templateIdType()); @@ -2642,7 +2658,7 @@ private CharSequence generateMessageFlyweightCode( final String semanticVersion = ir.semanticVersion() == null ? "" : ir.semanticVersion(); - final String codecStateArgument = null == accessOrderModel ? "" : ", m_codecState"; + final String codecStateArgument = null == fieldPrecedenceModel ? "" : ", m_codecState"; return String.format( "private:\n" + @@ -2851,23 +2867,23 @@ private CharSequence generateMessageFlyweightCode( generateLiteral(ir.headerStructure().schemaVersionType(), Integer.toString(ir.version())), semanticType, className, - generateConstructorsAndOperators(className, accessOrderModel), + generateConstructorsAndOperators(className, fieldPrecedenceModel), formatClassName(headerType), semanticVersion, codecStateArgument, - generateFieldOrderStateEnum(accessOrderModel), - generateLookupTableDeclarations(accessOrderModel), - generateFieldOrderStateMember(accessOrderModel), - generateAccessOrderErrorType(accessOrderModel), - generateEncoderWrapListener(accessOrderModel), - generateDecoderWrapListener(accessOrderModel), - generateDecoderWrapListenerCall(accessOrderModel), + generateFieldOrderStateEnum(fieldPrecedenceModel), + generateLookupTableDeclarations(fieldPrecedenceModel), + generateFieldOrderStateMember(fieldPrecedenceModel), + generateAccessOrderErrorType(fieldPrecedenceModel), + generateEncoderWrapListener(fieldPrecedenceModel), + generateDecoderWrapListener(fieldPrecedenceModel), + generateDecoderWrapListenerCall(fieldPrecedenceModel), generateHiddenCopyConstructor(" ", className)); } - private CharSequence generateAccessOrderErrorType(final AccessOrderModel accessOrderModel) + private CharSequence generateAccessOrderErrorType(final FieldPrecedenceModel fieldPrecedenceModel) { - if (null == accessOrderModel) + if (null == fieldPrecedenceModel) { return ""; } @@ -2881,19 +2897,19 @@ private CharSequence generateAccessOrderErrorType(final AccessOrderModel accessO return sb; } - private static CharSequence generateLookupTableDeclarations(final AccessOrderModel accessOrderModel) + private static CharSequence generateLookupTableDeclarations(final FieldPrecedenceModel fieldPrecedenceModel) { - if (null == accessOrderModel) + if (null == fieldPrecedenceModel) { return ""; } final StringBuilder sb = new StringBuilder(); sb.append(INDENT).append("static const std::string STATE_NAME_LOOKUP[") - .append(accessOrderModel.stateCount()) + .append(fieldPrecedenceModel.stateCount()) .append("];\n"); sb.append(INDENT).append("static const std::string STATE_TRANSITIONS_LOOKUP[") - .append(accessOrderModel.stateCount()) + .append(fieldPrecedenceModel.stateCount()) .append("];\n\n"); sb.append(INDENT).append("static std::string codecStateName(CodecState state)\n") @@ -2912,31 +2928,31 @@ private static CharSequence generateLookupTableDeclarations(final AccessOrderMod private static void generateLookupTableDefinitions( final StringBuilder sb, final String className, - final AccessOrderModel accessOrderModel) + final FieldPrecedenceModel fieldPrecedenceModel) { - if (null == accessOrderModel) + if (null == fieldPrecedenceModel) { return; } sb.append("\n").append("const std::string ").append(className).append("::STATE_NAME_LOOKUP[") - .append(accessOrderModel.stateCount()).append("] =\n") + .append(fieldPrecedenceModel.stateCount()).append("] =\n") .append("{\n"); - accessOrderModel.forEachStateOrderedByStateNumber(state -> + fieldPrecedenceModel.forEachStateOrderedByStateNumber(state -> { sb.append(INDENT).append("\"").append(state.name()).append("\",\n"); }); sb.append("};\n\n"); sb.append("const std::string ").append(className).append("::STATE_TRANSITIONS_LOOKUP[") - .append(accessOrderModel.stateCount()).append("] =\n") + .append(fieldPrecedenceModel.stateCount()).append("] =\n") .append("{\n"); - accessOrderModel.forEachStateOrderedByStateNumber(state -> + fieldPrecedenceModel.forEachStateOrderedByStateNumber(state -> { sb.append(INDENT).append("\""); final MutableBoolean isFirst = new MutableBoolean(true); final Set transitionDescriptions = new HashSet<>(); - accessOrderModel.forEachTransitionFrom(state, transitionGroup -> + fieldPrecedenceModel.forEachTransitionFrom(state, transitionGroup -> { if (transitionDescriptions.add(transitionGroup.exampleCode())) { @@ -2957,24 +2973,24 @@ private static void generateLookupTableDefinitions( sb.append("};\n\n"); } - private static CharSequence qualifiedStateCase(final AccessOrderModel.State state) + private static CharSequence qualifiedStateCase(final FieldPrecedenceModel.State state) { return "CodecState::" + state.name(); } - private static CharSequence stateCaseForSwitchCase(final AccessOrderModel.State state) + private static CharSequence stateCaseForSwitchCase(final FieldPrecedenceModel.State state) { return qualifiedStateCase(state); } - private static CharSequence unqualifiedStateCase(final AccessOrderModel.State state) + private static CharSequence unqualifiedStateCase(final FieldPrecedenceModel.State state) { return state.name(); } - private static CharSequence generateFieldOrderStateEnum(final AccessOrderModel accessOrderModel) + private static CharSequence generateFieldOrderStateEnum(final FieldPrecedenceModel fieldPrecedenceModel) { - if (null == accessOrderModel) + if (null == fieldPrecedenceModel) { return ""; } @@ -2989,12 +3005,12 @@ private static CharSequence generateFieldOrderStateEnum(final AccessOrderModel a sb.append(" * accessed safely. Tools such as PlantUML and Graphviz can render it.\n"); sb.append(" *\n"); sb.append(" *
{@code\n");
-        accessOrderModel.generateGraph(sb, "     *   ");
+        fieldPrecedenceModel.generateGraph(sb, "     *   ");
         sb.append("     * }
\n"); sb.append(" */\n"); sb.append(INDENT).append("enum class CodecState\n") .append(INDENT).append("{\n"); - accessOrderModel.forEachStateOrderedByStateNumber(state -> + fieldPrecedenceModel.forEachStateOrderedByStateNumber(state -> { sb.append(INDENT).append(INDENT).append(unqualifiedStateCase(state)) .append(" = ").append(state.number()) @@ -3005,9 +3021,9 @@ private static CharSequence generateFieldOrderStateEnum(final AccessOrderModel a return sb; } - private static CharSequence generateFieldOrderStateMember(final AccessOrderModel accessOrderModel) + private static CharSequence generateFieldOrderStateMember(final FieldPrecedenceModel fieldPrecedenceModel) { - if (null == accessOrderModel) + if (null == fieldPrecedenceModel) { return "\n"; } @@ -3015,7 +3031,7 @@ private static CharSequence generateFieldOrderStateMember(final AccessOrderModel final StringBuilder sb = new StringBuilder(); sb.append(INDENT).append("CodecState m_codecState = ") - .append(qualifiedStateCase(accessOrderModel.notWrappedState())) + .append(qualifiedStateCase(fieldPrecedenceModel.notWrappedState())) .append(";\n\n"); sb.append(INDENT).append("CodecState codecState() const\n") @@ -3036,14 +3052,14 @@ private static CharSequence generateFieldOrderStateMember(final AccessOrderModel return sb; } - private static CharSequence generateDecoderWrapListener(final AccessOrderModel accessOrderModel) + private static CharSequence generateDecoderWrapListener(final FieldPrecedenceModel fieldPrecedenceModel) { - if (null == accessOrderModel) + if (null == fieldPrecedenceModel) { return ""; } - if (accessOrderModel.versionCount() == 1) + if (fieldPrecedenceModel.versionCount() == 1) { return ""; } @@ -3054,7 +3070,7 @@ private static CharSequence generateDecoderWrapListener(final AccessOrderModel a .append(INDENT).append(INDENT).append("switch(actingVersion)\n") .append(INDENT).append(INDENT).append("{\n"); - accessOrderModel.forEachWrappedStateByVersion((version, state) -> + fieldPrecedenceModel.forEachWrappedStateByVersion((version, state) -> { sb.append(INDENT).append(TWO_INDENT).append("case ").append(version).append(":\n") .append(INDENT).append(THREE_INDENT).append("codecState(") @@ -3064,7 +3080,7 @@ private static CharSequence generateDecoderWrapListener(final AccessOrderModel a sb.append(INDENT).append(TWO_INDENT).append("default:\n") .append(INDENT).append(THREE_INDENT).append("codecState(") - .append(qualifiedStateCase(accessOrderModel.latestVersionWrappedState())).append(");\n") + .append(qualifiedStateCase(fieldPrecedenceModel.latestVersionWrappedState())).append(");\n") .append(INDENT).append(THREE_INDENT).append("break;\n") .append(INDENT).append(INDENT).append("}\n") .append(INDENT).append("}\n\n"); @@ -3073,37 +3089,37 @@ private static CharSequence generateDecoderWrapListener(final AccessOrderModel a } - private CharSequence generateDecoderWrapListenerCall(final AccessOrderModel accessOrderModel) + private CharSequence generateDecoderWrapListenerCall(final FieldPrecedenceModel fieldPrecedenceModel) { - if (null == accessOrderModel) + if (null == fieldPrecedenceModel) { return ""; } - if (accessOrderModel.versionCount() == 1) + if (fieldPrecedenceModel.versionCount() == 1) { final StringBuilder sb = new StringBuilder(); - sb.append("#if defined(").append(AccessOrderModel.PRECEDENCE_CHECKS_FLAG_NAME).append(")\n") + sb.append("#if defined(").append(precedenceChecksFlagName).append(")\n") .append(TWO_INDENT).append("codecState(") - .append(qualifiedStateCase(accessOrderModel.latestVersionWrappedState())).append(");\n") + .append(qualifiedStateCase(fieldPrecedenceModel.latestVersionWrappedState())).append(");\n") .append("#endif\n"); return sb; } - return generateAccessOrderListenerCall(accessOrderModel, TWO_INDENT, "onWrapForDecode", "actingVersion"); + return generateAccessOrderListenerCall(fieldPrecedenceModel, TWO_INDENT, "onWrapForDecode", "actingVersion"); } - private CharSequence generateEncoderWrapListener(final AccessOrderModel accessOrderModel) + private CharSequence generateEncoderWrapListener(final FieldPrecedenceModel fieldPrecedenceModel) { - if (null == accessOrderModel) + if (null == fieldPrecedenceModel) { return ""; } final StringBuilder sb = new StringBuilder(); - sb.append("#if defined(").append(AccessOrderModel.PRECEDENCE_CHECKS_FLAG_NAME).append(")\n") + sb.append("#if defined(").append(precedenceChecksFlagName).append(")\n") .append(TWO_INDENT).append("codecState(") - .append(qualifiedStateCase(accessOrderModel.latestVersionWrappedState())) + .append(qualifiedStateCase(fieldPrecedenceModel.latestVersionWrappedState())) .append(");\n") .append("#endif\n"); return sb; @@ -3113,7 +3129,7 @@ private void generateFields( final StringBuilder sb, final String containingClassName, final List tokens, - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final String indent) { for (int i = 0, size = tokens.size(); i < size; i++) @@ -3127,29 +3143,29 @@ private void generateFields( generateFieldMetaAttributeMethod(sb, signalToken, indent); generateFieldCommonMethods(indent, sb, signalToken, encodingToken, propertyName); - generateAccessOrderListenerMethod(sb, accessOrderModel, indent, signalToken); + generateAccessOrderListenerMethod(sb, fieldPrecedenceModel, indent, signalToken); switch (encodingToken.signal()) { case ENCODING: generatePrimitiveProperty( sb, containingClassName, propertyName, signalToken, encodingToken, - accessOrderModel, indent); + fieldPrecedenceModel, indent); break; case BEGIN_ENUM: generateEnumProperty(sb, containingClassName, signalToken, propertyName, encodingToken, - accessOrderModel, indent); + fieldPrecedenceModel, indent); break; case BEGIN_SET: generateBitsetProperty(sb, propertyName, signalToken, encodingToken, - accessOrderModel, indent); + fieldPrecedenceModel, indent); break; case BEGIN_COMPOSITE: generateCompositeProperty(sb, propertyName, signalToken, encodingToken, - accessOrderModel, indent); + fieldPrecedenceModel, indent); break; default: @@ -3264,7 +3280,7 @@ private void generateEnumProperty( final Token fieldToken, final String propertyName, final Token encodingToken, - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final String indent) { final String enumName = formatClassName(encodingToken.applicableTypeName()); @@ -3319,9 +3335,9 @@ private void generateEnumProperty( final String offsetStr = Integer.toString(offset); final CharSequence accessOrderListenerCall = generateAccessOrderListenerCall( - accessOrderModel, indent + TWO_INDENT, fieldToken); + fieldPrecedenceModel, indent + TWO_INDENT, fieldToken); - final String noexceptDeclaration = noexceptDeclaration(accessOrderModel); + final String noexceptDeclaration = noexceptDeclaration(fieldPrecedenceModel); new Formatter(sb).format("\n" + indent + " SBE_NODISCARD %1$s %2$sRaw() const%6$s\n" + @@ -3373,12 +3389,12 @@ private void generateEnumProperty( } } - private static void generateBitsetProperty( + private void generateBitsetProperty( final StringBuilder sb, final String propertyName, final Token fieldToken, final Token encodingToken, - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final String indent) { final String bitsetName = formatClassName(encodingToken.applicableTypeName()); @@ -3393,7 +3409,7 @@ private static void generateBitsetProperty( propertyName); final CharSequence accessOrderListenerCall = generateAccessOrderListenerCall( - accessOrderModel, indent + TWO_INDENT, fieldToken); + fieldPrecedenceModel, indent + TWO_INDENT, fieldToken); new Formatter(sb).format( indent + " SBE_NODISCARD %1$s &%2$s()\n" + @@ -3416,12 +3432,12 @@ private static void generateBitsetProperty( encodingToken.encoding().primitiveType().size()); } - private static void generateCompositeProperty( + private void generateCompositeProperty( final StringBuilder sb, final String propertyName, final Token fieldToken, final Token encodingToken, - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final String indent) { final String compositeName = formatClassName(encodingToken.applicableTypeName()); @@ -3435,7 +3451,7 @@ private static void generateCompositeProperty( propertyName); final CharSequence accessOrderListenerCall = generateAccessOrderListenerCall( - accessOrderModel, indent + TWO_INDENT, fieldToken); + fieldPrecedenceModel, indent + TWO_INDENT, fieldToken); new Formatter(sb).format( indent + " SBE_NODISCARD %1$s &%2$s()\n" + diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharp.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharp.java index 5b4870a321..601fb58675 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharp.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharp.java @@ -18,6 +18,7 @@ import uk.co.real_logic.sbe.generation.CodeGenerator; import uk.co.real_logic.sbe.generation.TargetCodeGenerator; +import uk.co.real_logic.sbe.generation.common.PrecedenceChecks; import uk.co.real_logic.sbe.ir.Ir; /** @@ -30,6 +31,9 @@ public class CSharp implements TargetCodeGenerator */ public CodeGenerator newInstance(final Ir ir, final String outputDir) { - return new CSharpGenerator(ir, new CSharpNamespaceOutputManager(outputDir, ir.applicableNamespace())); + return new CSharpGenerator( + ir, + PrecedenceChecks.newInstance(new PrecedenceChecks.Context()), + new CSharpNamespaceOutputManager(outputDir, ir.applicableNamespace())); } } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java index c38e5c5a7b..243f3f6530 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java @@ -19,12 +19,16 @@ import uk.co.real_logic.sbe.PrimitiveType; import uk.co.real_logic.sbe.PrimitiveValue; import uk.co.real_logic.sbe.generation.CodeGenerator; -import org.agrona.collections.MutableBoolean; -import org.agrona.generation.OutputManager; import uk.co.real_logic.sbe.generation.Generators; -import uk.co.real_logic.sbe.generation.common.AccessOrderModel; -import uk.co.real_logic.sbe.ir.*; +import uk.co.real_logic.sbe.generation.common.FieldPrecedenceModel; +import uk.co.real_logic.sbe.generation.common.PrecedenceChecks; +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 org.agrona.Verify; +import org.agrona.collections.MutableBoolean; +import org.agrona.generation.OutputManager; import java.io.IOException; import java.io.Writer; @@ -33,17 +37,12 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.function.Function; import static java.lang.System.lineSeparator; - import static uk.co.real_logic.sbe.generation.Generators.toLowerFirstChar; import static uk.co.real_logic.sbe.generation.Generators.toUpperFirstChar; import static uk.co.real_logic.sbe.generation.csharp.CSharpUtil.*; -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; -import static uk.co.real_logic.sbe.ir.GenerationUtil.findEndSignal; +import static uk.co.real_logic.sbe.ir.GenerationUtil.*; /** * Codec generator for the CSharp programming language. @@ -59,6 +58,8 @@ public class CSharpGenerator implements CodeGenerator private final Ir ir; private final OutputManager outputManager; + private final PrecedenceChecks precedenceChecks; + private final String precedenceChecksFlagName; /** * Create a new C# language {@link CodeGenerator}. @@ -67,11 +68,32 @@ public class CSharpGenerator implements CodeGenerator * @param outputManager for generating the codecs to. */ public CSharpGenerator(final Ir ir, final OutputManager outputManager) + { + this( + ir, + PrecedenceChecks.newInstance(new PrecedenceChecks.Context()), + outputManager + ); + } + + /** + * Create a new C# language {@link CodeGenerator}. + * + * @param ir for the messages and types. + * @param precedenceChecks whether and how to perform field precedence checks. + * @param outputManager for generating the codecs to. + */ + public CSharpGenerator( + final Ir ir, + final PrecedenceChecks precedenceChecks, + final OutputManager outputManager) { Verify.notNull(ir, "ir"); Verify.notNull(outputManager, "outputManager"); this.ir = ir; + this.precedenceChecks = precedenceChecks; + this.precedenceChecksFlagName = precedenceChecks.context().precedenceChecksFlagName(); this.outputManager = outputManager; } @@ -128,6 +150,7 @@ public void generate() throws IOException { final Token msgToken = tokens.get(0); final String className = formatClassName(msgToken.name()); + final FieldPrecedenceModel fieldPrecedenceModel = precedenceChecks.createCodecModel(tokens); try (Writer out = outputManager.createOutput(className)) { @@ -140,35 +163,24 @@ public void generate() throws IOException final List varData = new ArrayList<>(); collectVarData(messageBody, offset, varData); - AccessOrderModel accessOrderModel = null; - if (AccessOrderModel.generateAccessOrderChecks()) - { - accessOrderModel = AccessOrderModel.newInstance( - msgToken, - fields, - groups, - varData, - Function.identity()); - } - out.append(generateFileHeader(ir.applicableNamespace())); out.append(generateDocumentation(BASE_INDENT, msgToken)); out.append(generateClassDeclaration(className)); - out.append(generateMessageFlyweightCode(className, msgToken, accessOrderModel, BASE_INDENT)); + out.append(generateMessageFlyweightCode(className, msgToken, fieldPrecedenceModel, BASE_INDENT)); - out.append(generateFieldOrderStates(BASE_INDENT + INDENT, accessOrderModel)); - out.append(generateFullyEncodedCheck(BASE_INDENT + INDENT, accessOrderModel)); + out.append(generateFieldOrderStates(BASE_INDENT + INDENT, fieldPrecedenceModel)); + out.append(generateFullyEncodedCheck(BASE_INDENT + INDENT, fieldPrecedenceModel)); - out.append(generateFields(accessOrderModel, fields, BASE_INDENT)); + out.append(generateFields(fieldPrecedenceModel, fields, BASE_INDENT)); final StringBuilder sb = new StringBuilder(); - generateGroups(sb, className, groups, accessOrderModel, BASE_INDENT); + generateGroups(sb, className, groups, fieldPrecedenceModel, BASE_INDENT); out.append(sb); - out.append(generateVarData(accessOrderModel, varData, BASE_INDENT + INDENT)); + out.append(generateVarData(fieldPrecedenceModel, varData, BASE_INDENT + INDENT)); out.append(generateDisplay(toUpperFirstChar(msgToken.name()), - fields, groups, varData, accessOrderModel)); + fields, groups, varData, fieldPrecedenceModel)); out.append(INDENT + "}\n"); out.append("}\n"); @@ -180,7 +192,7 @@ private void generateGroups( final StringBuilder sb, final String parentMessageClassName, final List tokens, - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final String indent) { for (int i = 0, size = tokens.size(); i < size; i++) @@ -191,24 +203,24 @@ private void generateGroups( throw new IllegalStateException("tokens must begin with BEGIN_GROUP: token=" + groupToken); } final String groupName = groupToken.name(); - sb.append(generateGroupProperty(groupName, accessOrderModel, groupToken, indent + INDENT)); + sb.append(generateGroupProperty(groupName, fieldPrecedenceModel, groupToken, indent + INDENT)); generateGroupClassHeader(sb, groupName, parentMessageClassName, tokens, - accessOrderModel, i, indent + INDENT); + fieldPrecedenceModel, i, indent + INDENT); i++; i += tokens.get(i).componentTokenCount(); final List fields = new ArrayList<>(); i = collectFields(tokens, i, fields); - sb.append(generateFields(accessOrderModel, fields, indent + INDENT)); + sb.append(generateFields(fieldPrecedenceModel, fields, indent + INDENT)); final List groups = new ArrayList<>(); i = collectGroups(tokens, i, groups); - generateGroups(sb, parentMessageClassName, groups, accessOrderModel, indent + INDENT); + generateGroups(sb, parentMessageClassName, groups, fieldPrecedenceModel, indent + INDENT); final List varData = new ArrayList<>(); i = collectVarData(tokens, i, varData); - sb.append(generateVarData(accessOrderModel, varData, indent + INDENT + INDENT)); + sb.append(generateVarData(fieldPrecedenceModel, varData, indent + INDENT + INDENT)); appendGroupInstanceDisplay(sb, fields, groups, varData, indent + TWO_INDENT); @@ -221,7 +233,7 @@ private void generateGroupClassHeader( final String groupName, final String parentMessageClassName, final List tokens, - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final int index, final String indent) { @@ -317,7 +329,7 @@ private void generateGroupClassHeader( blockLength, dimensionHeaderLength)); - if (null != accessOrderModel) + if (null != fieldPrecedenceModel) { sb.append("\n") .append(indent).append(" private CodecState codecState()\n") @@ -333,19 +345,19 @@ private void generateGroupClassHeader( } final Token groupToken = tokens.get(index); - generateGroupEnumerator(sb, accessOrderModel, groupToken, groupName, typeForNumInGroup, indent); + generateGroupEnumerator(sb, fieldPrecedenceModel, groupToken, groupName, typeForNumInGroup, indent); } private void generateGroupEnumerator( final StringBuilder sb, - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final Token groupToken, final String groupName, final String typeForNumInGroup, final String indent) { - generateAccessOrderListenerMethodForNextGroupElement(sb, accessOrderModel, indent + INDENT, groupToken); - generateAccessOrderListenerMethodForResetGroupCount(sb, accessOrderModel, indent + INDENT, groupToken); + generateAccessOrderListenerMethodForNextGroupElement(sb, fieldPrecedenceModel, indent + INDENT, groupToken); + generateAccessOrderListenerMethodForResetGroupCount(sb, fieldPrecedenceModel, indent + INDENT, groupToken); sb.append( indent + INDENT + "public int ActingBlockLength { get { return _blockLength; } }\n\n" + @@ -360,7 +372,7 @@ private void generateGroupEnumerator( indent + INDENT + INDENT + "_dimensions.NumInGroup = (%s) _count;\n\n" + indent + INDENT + INDENT + "return _count;\n" + indent + INDENT + "}\n", - generateAccessOrderListenerCall(accessOrderModel, indent + TWO_INDENT, "OnResetCountToIndex"), + generateAccessOrderListenerCall(fieldPrecedenceModel, indent + TWO_INDENT, "OnResetCountToIndex"), typeForNumInGroup)); sb.append(String.format("\n" + @@ -370,7 +382,7 @@ private void generateGroupEnumerator( indent + INDENT + INDENT + "{\n" + indent + INDENT + INDENT + INDENT + "ThrowHelper.ThrowInvalidOperationException();\n" + indent + INDENT + INDENT + "}\n\n" + - generateAccessOrderListenerCall(accessOrderModel, indent + TWO_INDENT, "OnNextElementAccessed") + + generateAccessOrderListenerCall(fieldPrecedenceModel, indent + TWO_INDENT, "OnNextElementAccessed") + indent + INDENT + INDENT + "_offset = _parentMessage.Limit;\n" + indent + INDENT + INDENT + "_parentMessage.Limit = _offset + _blockLength;\n" + indent + INDENT + INDENT + "++_index;\n\n" + @@ -397,7 +409,7 @@ private boolean isRepresentableByInt32(final Encoding encoding) private CharSequence generateGroupProperty( final String groupName, - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final Token token, final String indent) { @@ -416,14 +428,14 @@ private CharSequence generateGroupProperty( toUpperFirstChar(groupName), token.id())); - generateAccessOrderListenerMethodForGroupWrap(sb, accessOrderModel, indent, token); + generateAccessOrderListenerMethodForGroupWrap(sb, fieldPrecedenceModel, indent, token); generateSinceActingDeprecated(sb, indent, toUpperFirstChar(groupName), token); final String groupField = "_" + toLowerFirstChar(groupName); final CharSequence accessOrderListenerCallOnDecode = generateAccessOrderListenerCall( - accessOrderModel, + fieldPrecedenceModel, indent + TWO_INDENT, token, groupField + ".Count", @@ -451,7 +463,7 @@ private CharSequence generateGroupProperty( accessOrderListenerCallOnDecode)); final CharSequence accessOrderListenerCallOnEncode = generateAccessOrderListenerCall( - accessOrderModel, + fieldPrecedenceModel, indent + INDENT, token, "count", @@ -472,7 +484,7 @@ private CharSequence generateGroupProperty( } private CharSequence generateVarData( - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final List tokens, final String indent) { @@ -501,8 +513,8 @@ private CharSequence generateVarData( final ByteOrder byteOrder = lengthEncoding.byteOrder(); final String byteOrderStr = generateByteOrder(byteOrder, lengthEncoding.primitiveType().size()); - generateAccessOrderListenerMethodForVarDataLength(sb, accessOrderModel, indent, token); - generateAccessOrderListenerMethod(sb, accessOrderModel, indent, token); + generateAccessOrderListenerMethodForVarDataLength(sb, fieldPrecedenceModel, indent, token); + generateAccessOrderListenerMethod(sb, fieldPrecedenceModel, indent, token); sb.append(String.format("\n" + indent + "public const int %sHeaderSize = %d;\n", @@ -510,7 +522,7 @@ private CharSequence generateVarData( sizeOfLengthField)); final CharSequence lengthAccessOrderListenerCall = generateAccessOrderListenerCall( - accessOrderModel, indent + INDENT, accessOrderListenerMethodName(token, "Length")); + fieldPrecedenceModel, indent + INDENT, accessOrderListenerMethodName(token, "Length")); sb.append(String.format(indent + "\n" + indent + "public int %1$sLength()\n" + @@ -528,7 +540,7 @@ private CharSequence generateVarData( lengthAccessOrderListenerCall)); final CharSequence accessOrderListenerCall = - generateAccessOrderListenerCall(accessOrderModel, indent + INDENT, token); + generateAccessOrderListenerCall(fieldPrecedenceModel, indent + INDENT, token); sb.append(String.format("\n" + indent + "public int Get%1$s(byte[] dst, int dstOffset, int length) =>\n" + @@ -707,24 +719,24 @@ private CharSequence generateCompositePropertyElements(final List tokens, { final Token token = tokens.get(i); final String propertyName = formatPropertyName(token.name()); - final AccessOrderModel accessOrderModel = null; + final FieldPrecedenceModel fieldPrecedenceModel = null; switch (token.signal()) { case ENCODING: - sb.append(generatePrimitiveProperty(propertyName, token, token, accessOrderModel, indent)); + sb.append(generatePrimitiveProperty(propertyName, token, token, fieldPrecedenceModel, indent)); break; case BEGIN_ENUM: - sb.append(generateEnumProperty(propertyName, token, token, accessOrderModel, indent)); + sb.append(generateEnumProperty(propertyName, token, token, fieldPrecedenceModel, indent)); break; case BEGIN_SET: - sb.append(generateBitSetProperty(propertyName, token, token, accessOrderModel, indent)); + sb.append(generateBitSetProperty(propertyName, token, token, fieldPrecedenceModel, indent)); break; case BEGIN_COMPOSITE: - sb.append(generateCompositeProperty(propertyName, token, token, accessOrderModel, indent)); + sb.append(generateCompositeProperty(propertyName, token, token, fieldPrecedenceModel, indent)); break; default: @@ -870,7 +882,7 @@ private CharSequence generatePrimitiveProperty( final String propertyName, final Token fieldToken, final Token typeToken, - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final String indent) { final StringBuilder sb = new StringBuilder(); @@ -884,7 +896,7 @@ private CharSequence generatePrimitiveProperty( else { sb.append(generatePrimitivePropertyMethods(propertyName, fieldToken, typeToken, - accessOrderModel, indent)); + fieldPrecedenceModel, indent)); } return sb; @@ -894,7 +906,7 @@ private CharSequence generatePrimitivePropertyMethods( final String propertyName, final Token fieldToken, final Token typeToken, - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final String indent) { final int arrayLength = typeToken.arrayLength(); @@ -902,12 +914,12 @@ private CharSequence generatePrimitivePropertyMethods( if (arrayLength == 1) { return generateSingleValueProperty(propertyName, fieldToken, typeToken, - accessOrderModel, indent + INDENT); + fieldPrecedenceModel, indent + INDENT); } else if (arrayLength > 1) { return generateArrayProperty(propertyName, fieldToken, typeToken, - accessOrderModel, indent + INDENT); + fieldPrecedenceModel, indent + INDENT); } return ""; @@ -937,7 +949,7 @@ private CharSequence generateSingleValueProperty( final String propertyName, final Token fieldToken, final Token typeToken, - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final String indent) { final String typeName = cSharpTypeName(typeToken.encoding().primitiveType()); @@ -947,7 +959,7 @@ private CharSequence generateSingleValueProperty( final String byteOrderStr = generateByteOrder(byteOrder, typeToken.encoding().primitiveType().size()); final CharSequence accessOrderListenerCall = - generateAccessOrderListenerCall(accessOrderModel, indent + TWO_INDENT, fieldToken); + generateAccessOrderListenerCall(fieldPrecedenceModel, indent + TWO_INDENT, fieldToken); return String.format("\n" + "%1$s" + @@ -1065,7 +1077,7 @@ private CharSequence generateArrayProperty( final String propertyName, final Token fieldToken, final Token typeToken, - final AccessOrderModel accessOrderModel, final String indent) + final FieldPrecedenceModel fieldPrecedenceModel, final String indent) { final String typeName = cSharpTypeName(typeToken.encoding().primitiveType()); final String typePrefix = toUpperFirstChar(typeToken.encoding().primitiveType().primitiveName()); @@ -1077,9 +1089,9 @@ private CharSequence generateArrayProperty( final String propName = toUpperFirstChar(propertyName); final CharSequence accessOrderListenerCall = - generateAccessOrderListenerCall(accessOrderModel, indent + INDENT, fieldToken); + generateAccessOrderListenerCall(fieldPrecedenceModel, indent + INDENT, fieldToken); final CharSequence accessOrderListenerCallDoubleIndent = - generateAccessOrderListenerCall(accessOrderModel, indent + TWO_INDENT, fieldToken); + generateAccessOrderListenerCall(fieldPrecedenceModel, indent + TWO_INDENT, fieldToken); final StringBuilder sb = new StringBuilder(); @@ -1353,7 +1365,7 @@ private CharSequence generateFixedFlyweightCode(final int size) private CharSequence generateMessageFlyweightCode( final String className, final Token token, - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final String indent) { final String blockLengthType = cSharpTypeName(ir.headerStructure().blockLengthType()); @@ -1453,29 +1465,32 @@ private CharSequence generateMessageFlyweightCode( semanticType, className, semanticVersion, - generateEncoderWrapListener(accessOrderModel, indent + TWO_INDENT), - generateDecoderWrapListener(accessOrderModel, indent + INDENT), - generateAccessOrderListenerCall(accessOrderModel, indent + TWO_INDENT, "OnWrapForDecode", "actingVersion")); + generateEncoderWrapListener(fieldPrecedenceModel, indent + TWO_INDENT), + generateDecoderWrapListener(fieldPrecedenceModel, indent + INDENT), + generateAccessOrderListenerCall(fieldPrecedenceModel, indent + TWO_INDENT, + "OnWrapForDecode", "actingVersion")); } - private static CharSequence qualifiedStateCase(final AccessOrderModel.State state) + private static CharSequence qualifiedStateCase(final FieldPrecedenceModel.State state) { return "CodecState." + state.name(); } - private static CharSequence stateCaseForSwitchCase(final AccessOrderModel.State state) + private static CharSequence stateCaseForSwitchCase(final FieldPrecedenceModel.State state) { return qualifiedStateCase(state); } - private static CharSequence unqualifiedStateCase(final AccessOrderModel.State state) + private static CharSequence unqualifiedStateCase(final FieldPrecedenceModel.State state) { return state.name(); } - private static CharSequence generateFieldOrderStates(final String indent, final AccessOrderModel accessOrderModel) + private static CharSequence generateFieldOrderStates( + final String indent, + final FieldPrecedenceModel fieldPrecedenceModel) { - if (null == accessOrderModel) + if (null == fieldPrecedenceModel) { return ""; } @@ -1494,12 +1509,12 @@ private static CharSequence generateFieldOrderStates(final String indent, final sb.append(indent).append("/// accessed safely. Tools such as PlantUML and Graphviz can render it.\n"); sb.append(indent).append("/// \n"); sb.append(indent).append("/// \n"); - accessOrderModel.generateGraph(sb, indent + "/// "); + fieldPrecedenceModel.generateGraph(sb, indent + "/// "); sb.append(indent).append("/// \n"); sb.append(indent).append("/// \n"); sb.append(indent).append("private enum CodecState\n") .append(indent).append("{\n"); - accessOrderModel.forEachStateOrderedByStateNumber(state -> + fieldPrecedenceModel.forEachStateOrderedByStateNumber(state -> { sb.append(indent).append(INDENT).append(unqualifiedStateCase(state)) .append(" = ").append(state.number()) @@ -1509,7 +1524,7 @@ private static CharSequence generateFieldOrderStates(final String indent, final sb.append("\n").append(indent).append("private static readonly string[] StateNameLookup = new []\n") .append(indent).append("{\n"); - accessOrderModel.forEachStateOrderedByStateNumber(state -> + fieldPrecedenceModel.forEachStateOrderedByStateNumber(state -> { sb.append(indent).append(INDENT).append("\"").append(state.name()).append("\",\n"); }); @@ -1517,12 +1532,12 @@ private static CharSequence generateFieldOrderStates(final String indent, final sb.append(indent).append("private static readonly string[] StateTransitionsLookup = new []\n") .append(indent).append("{\n"); - accessOrderModel.forEachStateOrderedByStateNumber(state -> + fieldPrecedenceModel.forEachStateOrderedByStateNumber(state -> { sb.append(indent).append(INDENT).append("\""); final MutableBoolean isFirst = new MutableBoolean(true); final Set transitionDescriptions = new HashSet<>(); - accessOrderModel.forEachTransitionFrom(state, transitionGroup -> + fieldPrecedenceModel.forEachTransitionFrom(state, transitionGroup -> { if (transitionDescriptions.add(transitionGroup.exampleCode())) { @@ -1553,7 +1568,7 @@ private static CharSequence generateFieldOrderStates(final String indent, final .append(indent).append("}\n\n"); sb.append(indent).append("private CodecState _codecState = ") - .append(qualifiedStateCase(accessOrderModel.notWrappedState())) + .append(qualifiedStateCase(fieldPrecedenceModel.notWrappedState())) .append(";\n\n"); sb.append(indent).append("private CodecState codecState()\n") @@ -1569,11 +1584,11 @@ private static CharSequence generateFieldOrderStates(final String indent, final return sb; } - private static CharSequence generateFullyEncodedCheck( + private CharSequence generateFullyEncodedCheck( final String indent, - final AccessOrderModel accessOrderModel) + final FieldPrecedenceModel fieldPrecedenceModel) { - if (null == accessOrderModel) + if (null == fieldPrecedenceModel) { return ""; } @@ -1583,11 +1598,11 @@ private static CharSequence generateFullyEncodedCheck( sb.append(indent).append("public void CheckEncodingIsComplete()\n") .append(indent).append("{\n") - .append("#if ").append(AccessOrderModel.PRECEDENCE_CHECKS_FLAG_NAME).append("\n") + .append("#if ").append(precedenceChecksFlagName).append("\n") .append(indent).append(INDENT).append("switch (_codecState)\n") .append(indent).append(INDENT).append("{\n"); - accessOrderModel.forEachTerminalEncoderState(state -> + fieldPrecedenceModel.forEachTerminalEncoderState(state -> { sb.append(indent).append(TWO_INDENT).append("case ").append(stateCaseForSwitchCase(state)).append(":\n") .append(indent).append(THREE_INDENT).append("return;\n"); @@ -1619,11 +1634,11 @@ private static String accessOrderListenerMethodName(final Token token, final Str private static void generateAccessOrderListenerMethod( final StringBuilder sb, - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final String indent, final Token token) { - if (null == accessOrderModel) + if (null == fieldPrecedenceModel) { return; } @@ -1632,45 +1647,45 @@ private static void generateAccessOrderListenerMethod( .append(indent).append("private void ").append(accessOrderListenerMethodName(token)).append("()\n") .append(indent).append("{\n"); - final AccessOrderModel.CodecInteraction fieldAccess = - accessOrderModel.interactionFactory().accessField(token); + final FieldPrecedenceModel.CodecInteraction fieldAccess = + fieldPrecedenceModel.interactionFactory().accessField(token); generateAccessOrderListener( sb, indent + INDENT, "access field", - accessOrderModel, + fieldPrecedenceModel, fieldAccess); sb.append(indent).append("}\n"); } - private static CharSequence generateAccessOrderListenerCall( - final AccessOrderModel accessOrderModel, + private CharSequence generateAccessOrderListenerCall( + final FieldPrecedenceModel fieldPrecedenceModel, final String indent, final Token token, final String... arguments) { return generateAccessOrderListenerCall( - accessOrderModel, + fieldPrecedenceModel, indent, accessOrderListenerMethodName(token), arguments); } - private static CharSequence generateAccessOrderListenerCall( - final AccessOrderModel accessOrderModel, + private CharSequence generateAccessOrderListenerCall( + final FieldPrecedenceModel fieldPrecedenceModel, final String indent, final String methodName, final String... arguments) { - if (null == accessOrderModel) + if (null == fieldPrecedenceModel) { return ""; } final StringBuilder sb = new StringBuilder(); - sb.append("#if ").append(AccessOrderModel.PRECEDENCE_CHECKS_FLAG_NAME).append("\n") + sb.append("#if ").append(precedenceChecksFlagName).append("\n") .append(indent).append(methodName).append("("); for (int i = 0; i < arguments.length; i++) @@ -1690,11 +1705,11 @@ private static CharSequence generateAccessOrderListenerCall( private static void generateAccessOrderListenerMethodForGroupWrap( final StringBuilder sb, - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final String indent, final Token token) { - if (null == accessOrderModel) + if (null == fieldPrecedenceModel) { return; } @@ -1706,28 +1721,28 @@ private static void generateAccessOrderListenerMethodForGroupWrap( .append(indent).append(INDENT).append("if (remaining == 0)\n") .append(indent).append(INDENT).append("{\n"); - final AccessOrderModel.CodecInteraction selectEmptyGroup = - accessOrderModel.interactionFactory().determineGroupIsEmpty(token); + final FieldPrecedenceModel.CodecInteraction selectEmptyGroup = + fieldPrecedenceModel.interactionFactory().determineGroupIsEmpty(token); generateAccessOrderListener( sb, indent + TWO_INDENT, "\" + action + \" count of repeating group", - accessOrderModel, + fieldPrecedenceModel, selectEmptyGroup); sb.append(indent).append(INDENT).append("}\n") .append(indent).append(INDENT).append("else\n") .append(indent).append(INDENT).append("{\n"); - final AccessOrderModel.CodecInteraction selectNonEmptyGroup = - accessOrderModel.interactionFactory().determineGroupHasElements(token); + final FieldPrecedenceModel.CodecInteraction selectNonEmptyGroup = + fieldPrecedenceModel.interactionFactory().determineGroupHasElements(token); generateAccessOrderListener( sb, indent + TWO_INDENT, "\" + action + \" count of repeating group", - accessOrderModel, + fieldPrecedenceModel, selectNonEmptyGroup); sb.append(indent).append(INDENT).append("}\n") @@ -1738,13 +1753,13 @@ private static void generateAccessOrderListener( final StringBuilder sb, final String indent, final String action, - final AccessOrderModel accessOrderModel, - final AccessOrderModel.CodecInteraction interaction) + final FieldPrecedenceModel fieldPrecedenceModel, + final FieldPrecedenceModel.CodecInteraction interaction) { if (interaction.isTopLevelBlockFieldAccess()) { sb.append(indent).append("if (codecState() == ") - .append(qualifiedStateCase(accessOrderModel.notWrappedState())) + .append(qualifiedStateCase(fieldPrecedenceModel.notWrappedState())) .append(")\n") .append(indent).append("{\n"); generateAccessOrderException(sb, indent + INDENT, action, interaction); @@ -1755,7 +1770,7 @@ private static void generateAccessOrderListener( sb.append(indent).append("switch (codecState())\n") .append(indent).append("{\n"); - accessOrderModel.forEachTransition(interaction, transitionGroup -> + fieldPrecedenceModel.forEachTransition(interaction, transitionGroup -> { transitionGroup.forEachStartState(startState -> { @@ -1777,7 +1792,7 @@ private static void generateAccessOrderException( final StringBuilder sb, final String indent, final String action, - final AccessOrderModel.CodecInteraction interaction) + final FieldPrecedenceModel.CodecInteraction interaction) { sb.append(indent).append("throw new InvalidOperationException(") .append("\"Illegal field access order. \" +\n") @@ -1792,11 +1807,11 @@ private static void generateAccessOrderException( private static void generateAccessOrderListenerMethodForNextGroupElement( final StringBuilder sb, - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final String indent, final Token token) { - if (null == accessOrderModel) + if (null == fieldPrecedenceModel) { return; } @@ -1809,28 +1824,28 @@ private static void generateAccessOrderListenerMethodForNextGroupElement( .append(indent).append(INDENT).append("if (remaining > 1)\n") .append(indent).append(INDENT).append("{\n"); - final AccessOrderModel.CodecInteraction selectNextElementInGroup = - accessOrderModel.interactionFactory().moveToNextElement(token); + final FieldPrecedenceModel.CodecInteraction selectNextElementInGroup = + fieldPrecedenceModel.interactionFactory().moveToNextElement(token); generateAccessOrderListener( sb, indent + TWO_INDENT, "access next element in repeating group", - accessOrderModel, + fieldPrecedenceModel, selectNextElementInGroup); sb.append(indent).append(INDENT).append("}\n") .append(indent).append(INDENT).append("else if (remaining == 1)\n") .append(indent).append(INDENT).append("{\n"); - final AccessOrderModel.CodecInteraction selectLastElementInGroup = - accessOrderModel.interactionFactory().moveToLastElement(token); + final FieldPrecedenceModel.CodecInteraction selectLastElementInGroup = + fieldPrecedenceModel.interactionFactory().moveToLastElement(token); generateAccessOrderListener( sb, indent + TWO_INDENT, "access next element in repeating group", - accessOrderModel, + fieldPrecedenceModel, selectLastElementInGroup); sb.append(indent).append(INDENT).append("}\n") @@ -1839,11 +1854,11 @@ private static void generateAccessOrderListenerMethodForNextGroupElement( private static void generateAccessOrderListenerMethodForResetGroupCount( final StringBuilder sb, - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final String indent, final Token token) { - if (null == accessOrderModel) + if (null == fieldPrecedenceModel) { return; } @@ -1851,14 +1866,14 @@ private static void generateAccessOrderListenerMethodForResetGroupCount( sb.append(indent).append("private void OnResetCountToIndex()\n") .append(indent).append("{\n"); - final AccessOrderModel.CodecInteraction resetCountToIndex = - accessOrderModel.interactionFactory().resetCountToIndex(token); + final FieldPrecedenceModel.CodecInteraction resetCountToIndex = + fieldPrecedenceModel.interactionFactory().resetCountToIndex(token); generateAccessOrderListener( sb, indent + " ", "reset count of repeating group", - accessOrderModel, + fieldPrecedenceModel, resetCountToIndex); sb.append(indent).append("}\n"); @@ -1866,11 +1881,11 @@ private static void generateAccessOrderListenerMethodForResetGroupCount( private static void generateAccessOrderListenerMethodForVarDataLength( final StringBuilder sb, - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final String indent, final Token token) { - if (null == accessOrderModel) + if (null == fieldPrecedenceModel) { return; } @@ -1880,24 +1895,24 @@ private static void generateAccessOrderListenerMethodForVarDataLength( .append(accessOrderListenerMethodName(token, "Length")).append("()\n") .append(indent).append("{\n"); - final AccessOrderModel.CodecInteraction accessLength = - accessOrderModel.interactionFactory().accessVarDataLength(token); + final FieldPrecedenceModel.CodecInteraction accessLength = + fieldPrecedenceModel.interactionFactory().accessVarDataLength(token); generateAccessOrderListener( sb, indent + INDENT, "decode length of var data", - accessOrderModel, + fieldPrecedenceModel, accessLength); sb.append(indent).append("}\n"); } private static CharSequence generateDecoderWrapListener( - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final String indent) { - if (null == accessOrderModel) + if (null == fieldPrecedenceModel) { return ""; } @@ -1908,7 +1923,7 @@ private static CharSequence generateDecoderWrapListener( .append(indent).append(INDENT).append("switch(actingVersion)\n") .append(indent).append(INDENT).append("{\n"); - accessOrderModel.forEachWrappedStateByVersion((version, state) -> + fieldPrecedenceModel.forEachWrappedStateByVersion((version, state) -> { sb.append(indent).append(TWO_INDENT).append("case ").append(version).append(":\n") .append(indent).append(THREE_INDENT).append("codecState(") @@ -1918,7 +1933,7 @@ private static CharSequence generateDecoderWrapListener( sb.append(indent).append(TWO_INDENT).append("default:\n") .append(indent).append(THREE_INDENT).append("codecState(") - .append(qualifiedStateCase(accessOrderModel.latestVersionWrappedState())).append(");\n") + .append(qualifiedStateCase(fieldPrecedenceModel.latestVersionWrappedState())).append(");\n") .append(indent).append(THREE_INDENT).append("break;\n") .append(indent).append(INDENT).append("}\n") .append(indent).append("}\n\n"); @@ -1927,25 +1942,25 @@ private static CharSequence generateDecoderWrapListener( } private CharSequence generateEncoderWrapListener( - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final String indent) { - if (null == accessOrderModel) + if (null == fieldPrecedenceModel) { return ""; } final StringBuilder sb = new StringBuilder(); - sb.append("#if ").append(AccessOrderModel.PRECEDENCE_CHECKS_FLAG_NAME).append("\n") + sb.append("#if ").append(precedenceChecksFlagName).append("\n") .append(indent).append("codecState(") - .append(qualifiedStateCase(accessOrderModel.latestVersionWrappedState())) + .append(qualifiedStateCase(fieldPrecedenceModel.latestVersionWrappedState())) .append(");\n") .append("#endif\n"); return sb; } private CharSequence generateFields( - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final List tokens, final String indent) { @@ -1965,28 +1980,28 @@ private CharSequence generateFields( generateOffsetMethod(sb, signalToken, indent + INDENT); generateFieldMetaAttributeMethod(sb, signalToken, indent + INDENT); - generateAccessOrderListenerMethod(sb, accessOrderModel, indent + INDENT, signalToken); + generateAccessOrderListenerMethod(sb, fieldPrecedenceModel, indent + INDENT, signalToken); switch (encodingToken.signal()) { case ENCODING: sb.append(generatePrimitiveProperty(propertyName, signalToken, encodingToken, - accessOrderModel, indent)); + fieldPrecedenceModel, indent)); break; case BEGIN_ENUM: sb.append(generateEnumProperty(propertyName, signalToken, encodingToken, - accessOrderModel, indent)); + fieldPrecedenceModel, indent)); break; case BEGIN_SET: sb.append(generateBitSetProperty(propertyName, signalToken, encodingToken, - accessOrderModel, indent)); + fieldPrecedenceModel, indent)); break; case BEGIN_COMPOSITE: sb.append(generateCompositeProperty(propertyName, signalToken, encodingToken, - accessOrderModel, indent)); + fieldPrecedenceModel, indent)); break; default: @@ -2061,7 +2076,7 @@ private CharSequence generateEnumProperty( final String propertyName, final Token fieldToken, final Token typeToken, - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final String indent) { final String enumName = formatClassName(typeToken.applicableTypeName()); @@ -2092,7 +2107,7 @@ private CharSequence generateEnumProperty( else { final CharSequence accessOrderListenerCall = generateAccessOrderListenerCall( - accessOrderModel, indent + TWO_INDENT, fieldToken); + fieldPrecedenceModel, indent + TWO_INDENT, fieldToken); return String.format("\n" + "%1$s" + @@ -2127,7 +2142,7 @@ private String generateBitSetProperty( final String propertyName, final Token fieldToken, final Token typeToken, - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final String indent) { final String bitSetName = formatClassName(typeToken.applicableTypeName()); @@ -2137,7 +2152,7 @@ private String generateBitSetProperty( final String byteOrderStr = generateByteOrder(byteOrder, typeToken.encoding().primitiveType().size()); final String typeName = cSharpTypeName(typeToken.encoding().primitiveType()); final CharSequence accessOrderListenerCall = generateAccessOrderListenerCall( - accessOrderModel, indent + TWO_INDENT, fieldToken); + fieldPrecedenceModel, indent + TWO_INDENT, fieldToken); return String.format("\n" + "%1$s" + @@ -2171,13 +2186,13 @@ private Object generateCompositeProperty( final String propertyName, final Token fieldToken, final Token typeToken, - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final String indent) { final String compositeName = CSharpUtil.formatClassName(typeToken.applicableTypeName()); final int offset = typeToken.offset(); final CharSequence accessOrderListenerCall = generateAccessOrderListenerCall( - accessOrderModel, indent + THREE_INDENT, fieldToken); + fieldPrecedenceModel, indent + THREE_INDENT, fieldToken); final StringBuilder sb = new StringBuilder(); sb.append(String.format("\n" + @@ -2540,7 +2555,7 @@ private CharSequence generateDisplay( final List tokens, final List groups, final List varData, - final AccessOrderModel accessOrderModel) + final FieldPrecedenceModel fieldPrecedenceModel) { final StringBuilder sb = new StringBuilder(100); @@ -2554,12 +2569,12 @@ private CharSequence generateDisplay( append(sb, TWO_INDENT, " }"); sb.append('\n'); append(sb, TWO_INDENT, " int originalLimit = this.Limit;"); - if (null != accessOrderModel) + if (null != fieldPrecedenceModel) { - sb.append("#if ").append(AccessOrderModel.PRECEDENCE_CHECKS_FLAG_NAME).append("\n"); + sb.append("#if ").append(precedenceChecksFlagName).append("\n"); append(sb, TWO_INDENT, " CodecState originalState = _codecState;"); sb.append(THREE_INDENT).append("_codecState = ") - .append(qualifiedStateCase(accessOrderModel.notWrappedState())).append(";\n"); + .append(qualifiedStateCase(fieldPrecedenceModel.notWrappedState())).append(";\n"); append(sb, TWO_INDENT, " OnWrapForDecode(_actingVersion);"); sb.append("#endif\n"); } @@ -2586,9 +2601,9 @@ private CharSequence generateDisplay( sb.append('\n'); appendDisplay(sb, tokens, groups, varData, THREE_INDENT); sb.append('\n'); - if (null != accessOrderModel) + if (null != fieldPrecedenceModel) { - sb.append("#if ").append(AccessOrderModel.PRECEDENCE_CHECKS_FLAG_NAME).append("\n"); + sb.append("#if ").append(precedenceChecksFlagName).append("\n"); append(sb, TWO_INDENT, " _codecState = originalState;"); sb.append("#endif\n"); } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java index 44db96908c..df150fb88a 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java @@ -18,7 +18,8 @@ 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.generation.common.AccessOrderModel; +import uk.co.real_logic.sbe.generation.common.FieldPrecedenceModel; +import uk.co.real_logic.sbe.generation.common.PrecedenceChecks; import uk.co.real_logic.sbe.ir.*; import org.agrona.DirectBuffer; import org.agrona.MutableDirectBuffer; @@ -32,7 +33,6 @@ import java.io.Writer; import java.util.*; import java.util.function.Function; -import java.util.stream.IntStream; import static uk.co.real_logic.sbe.SbeTool.JAVA_INTERFACE_PACKAGE; import static uk.co.real_logic.sbe.generation.java.JavaGenerator.CodecType.DECODER; @@ -71,6 +71,9 @@ enum CodecType private final boolean shouldGenerateInterfaces; private final boolean shouldDecodeUnknownEnumValues; private final boolean shouldSupportTypesPackageNames; + private final PrecedenceChecks precedenceChecks; + private final String precedenceChecksFlagName; + private final String precedenceChecksPropName; private final Set packageNameByTypes = new HashSet<>(); /** @@ -118,6 +121,43 @@ public JavaGenerator( final boolean shouldDecodeUnknownEnumValues, final boolean shouldSupportTypesPackageNames, final DynamicPackageOutputManager outputManager) + { + this( + ir, + mutableBuffer, + readOnlyBuffer, + shouldGenerateGroupOrderAnnotation, + shouldGenerateInterfaces, + shouldDecodeUnknownEnumValues, + shouldSupportTypesPackageNames, + PrecedenceChecks.newInstance(new PrecedenceChecks.Context()), + outputManager + ); + } + + /** + * Create a new Java language {@link CodeGenerator}. + * + * @param ir for the messages and types. + * @param mutableBuffer implementation used for mutating underlying buffers. + * @param readOnlyBuffer implementation used for reading underlying buffers. + * @param shouldGenerateGroupOrderAnnotation in the codecs. + * @param shouldGenerateInterfaces for common methods. + * @param shouldDecodeUnknownEnumValues generate support for unknown enum values when decoding. + * @param shouldSupportTypesPackageNames generator support for types in their own package. + * @param precedenceChecks whether and how to generate field precedence checks. + * @param outputManager for generating the codecs to. + */ + public JavaGenerator( + final Ir ir, + final String mutableBuffer, + final String readOnlyBuffer, + final boolean shouldGenerateGroupOrderAnnotation, + final boolean shouldGenerateInterfaces, + final boolean shouldDecodeUnknownEnumValues, + final boolean shouldSupportTypesPackageNames, + final PrecedenceChecks precedenceChecks, + final DynamicPackageOutputManager outputManager) { Verify.notNull(ir, "ir"); Verify.notNull(outputManager, "outputManager"); @@ -135,6 +175,10 @@ public JavaGenerator( this.shouldGenerateGroupOrderAnnotation = shouldGenerateGroupOrderAnnotation; this.shouldGenerateInterfaces = shouldGenerateInterfaces; this.shouldDecodeUnknownEnumValues = shouldDecodeUnknownEnumValues; + + this.precedenceChecks = precedenceChecks; + this.precedenceChecksFlagName = precedenceChecks.context().precedenceChecksFlagName(); + this.precedenceChecksPropName = precedenceChecks.context().precedenceChecksPropName(); } /** @@ -228,8 +272,11 @@ public void generate() throws IOException final List varData = new ArrayList<>(); collectVarData(messageBody, i, varData); - generateDecoder(msgToken, fields, groups, varData, hasVarData); - generateEncoder(msgToken, fields, groups, varData, hasVarData); + final FieldPrecedenceModel decoderPrecedenceModel = precedenceChecks.createDecoderModel(tokens); + generateDecoder(msgToken, fields, groups, varData, hasVarData, decoderPrecedenceModel); + + final FieldPrecedenceModel encoderPrecedenceModel = precedenceChecks.createEncoderModel(tokens); + generateEncoder(msgToken, fields, groups, varData, hasVarData, encoderPrecedenceModel); } } @@ -238,7 +285,8 @@ private void generateEncoder( final List fields, final List groups, final List varData, - final boolean hasVarData) + final boolean hasVarData, + final FieldPrecedenceModel fieldPrecedenceModel) throws IOException { final String className = formatClassName(encoderName(msgToken.name())); @@ -254,51 +302,40 @@ private void generateEncoder( } out.append(generateDeclaration(className, implementsString, msgToken)); - AccessOrderModel accessOrderModel = null; - if (AccessOrderModel.generateAccessOrderChecks()) - { - final Function selectLatestVersion = versions -> - { - final OptionalInt max = versions.max(); - return max.isPresent() ? IntStream.of(max.getAsInt()) : IntStream.empty(); - }; - accessOrderModel = AccessOrderModel.newInstance(msgToken, fields, groups, varData, selectLatestVersion); - } - - out.append(generateFieldOrderStates(accessOrderModel)); - out.append(generateEncoderFlyweightCode(className, accessOrderModel, msgToken)); + out.append(generateFieldOrderStates(fieldPrecedenceModel)); + out.append(generateEncoderFlyweightCode(className, fieldPrecedenceModel, msgToken)); final StringBuilder sb = new StringBuilder(); - generateEncoderFields(sb, className, accessOrderModel, fields, BASE_INDENT); - generateEncoderGroups(sb, className, accessOrderModel, groups, BASE_INDENT, false); - generateEncoderVarData(sb, className, accessOrderModel, varData, BASE_INDENT); + generateEncoderFields(sb, className, fieldPrecedenceModel, fields, BASE_INDENT); + generateEncoderGroups(sb, className, fieldPrecedenceModel, groups, BASE_INDENT, false); + generateEncoderVarData(sb, className, fieldPrecedenceModel, varData, BASE_INDENT); generateEncoderDisplay(sb, decoderName(msgToken.name())); - generateFullyEncodedCheck(sb, accessOrderModel); + generateFullyEncodedCheck(sb, fieldPrecedenceModel); out.append(sb); out.append("}\n"); } } - private static CharSequence qualifiedStateCase(final AccessOrderModel.State state) + private static CharSequence qualifiedStateCase(final FieldPrecedenceModel.State state) { return "CodecStates." + state.name(); } - private static CharSequence stateCaseForSwitchCase(final AccessOrderModel.State state) + private static CharSequence stateCaseForSwitchCase(final FieldPrecedenceModel.State state) { return qualifiedStateCase(state); } - private static CharSequence unqualifiedStateCase(final AccessOrderModel.State state) + private static CharSequence unqualifiedStateCase(final FieldPrecedenceModel.State state) { return state.name(); } - private static CharSequence generateFieldOrderStates(final AccessOrderModel accessOrderModel) + private CharSequence generateFieldOrderStates(final FieldPrecedenceModel fieldPrecedenceModel) { - if (null == accessOrderModel) + if (null == fieldPrecedenceModel) { return ""; } @@ -308,9 +345,9 @@ private static CharSequence generateFieldOrderStates(final AccessOrderModel acce sb.append(" private static final boolean ENABLE_BOUNDS_CHECKS = ") .append("!Boolean.getBoolean(\"agrona.disable.bounds.checks\");\n\n"); sb.append(" private static final boolean ") - .append(AccessOrderModel.PRECEDENCE_CHECKS_FLAG_NAME).append(" = ") + .append(precedenceChecksFlagName).append(" = ") .append("Boolean.parseBoolean(System.getProperty(\n") - .append(" \"").append(AccessOrderModel.PRECEDENCE_CHECKS_PROP_NAME).append("\",\n") + .append(" \"").append(precedenceChecksPropName).append("\",\n") .append(" Boolean.toString(ENABLE_BOUNDS_CHECKS)));\n\n"); sb.append(" /**\n"); @@ -321,12 +358,12 @@ private static CharSequence generateFieldOrderStates(final AccessOrderModel acce sb.append(" * accessed safely. Tools such as PlantUML and Graphviz can render it.\n"); sb.append(" *\n"); sb.append(" *
{@code\n");
-        accessOrderModel.generateGraph(sb, "     *   ");
+        fieldPrecedenceModel.generateGraph(sb, "     *   ");
         sb.append("     * }
\n"); sb.append(" */\n"); sb.append(" private static class CodecStates\n") .append(" {\n"); - accessOrderModel.forEachStateOrderedByStateNumber(state -> + fieldPrecedenceModel.forEachStateOrderedByStateNumber(state -> { sb.append(" private static final int ") .append(unqualifiedStateCase(state)) @@ -336,7 +373,7 @@ private static CharSequence generateFieldOrderStates(final AccessOrderModel acce sb.append("\n").append(" private static final String[] STATE_NAME_LOOKUP =\n") .append(" {\n"); - accessOrderModel.forEachStateOrderedByStateNumber(state -> + fieldPrecedenceModel.forEachStateOrderedByStateNumber(state -> { sb.append(" \"").append(state.name()).append("\",\n"); }); @@ -344,12 +381,12 @@ private static CharSequence generateFieldOrderStates(final AccessOrderModel acce sb.append(" private static final String[] STATE_TRANSITIONS_LOOKUP =\n") .append(" {\n"); - accessOrderModel.forEachStateOrderedByStateNumber(state -> + fieldPrecedenceModel.forEachStateOrderedByStateNumber(state -> { sb.append(" \""); final MutableBoolean isFirst = new MutableBoolean(true); final Set transitionDescriptions = new HashSet<>(); - accessOrderModel.forEachTransitionFrom(state, transitionGroup -> + fieldPrecedenceModel.forEachTransitionFrom(state, transitionGroup -> { if (transitionDescriptions.add(transitionGroup.exampleCode())) { @@ -382,7 +419,7 @@ private static CharSequence generateFieldOrderStates(final AccessOrderModel acce sb.append(" }\n\n"); sb.append(" private int codecState = ") - .append(qualifiedStateCase(accessOrderModel.notWrappedState())) + .append(qualifiedStateCase(fieldPrecedenceModel.notWrappedState())) .append(";\n\n"); sb.append(" private int codecState()\n") @@ -398,11 +435,11 @@ private static CharSequence generateFieldOrderStates(final AccessOrderModel acce return sb; } - private static void generateFullyEncodedCheck( + private void generateFullyEncodedCheck( final StringBuilder sb, - final AccessOrderModel accessOrderModel) + final FieldPrecedenceModel fieldPrecedenceModel) { - if (null == accessOrderModel) + if (null == fieldPrecedenceModel) { return; } @@ -411,12 +448,12 @@ private static void generateFullyEncodedCheck( sb.append(" public void checkEncodingIsComplete()\n") .append(" {\n") - .append(" if (").append(AccessOrderModel.PRECEDENCE_CHECKS_FLAG_NAME).append(")\n") + .append(" if (").append(precedenceChecksFlagName).append(")\n") .append(" {\n") .append(" switch (codecState)\n") .append(" {\n"); - accessOrderModel.forEachTerminalEncoderState(state -> + fieldPrecedenceModel.forEachTerminalEncoderState(state -> { sb.append(" case ").append(stateCaseForSwitchCase(state)).append(":\n") .append(" return;\n"); @@ -443,11 +480,11 @@ private static String accessOrderListenerMethodName(final Token token, final Str private static void generateAccessOrderListenerMethod( final StringBuilder sb, - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final String indent, final Token token) { - if (null == accessOrderModel) + if (null == fieldPrecedenceModel) { return; } @@ -456,45 +493,45 @@ private static void generateAccessOrderListenerMethod( .append(indent).append("private void ").append(accessOrderListenerMethodName(token)).append("()\n") .append(indent).append("{\n"); - final AccessOrderModel.CodecInteraction fieldAccess = - accessOrderModel.interactionFactory().accessField(token); + final FieldPrecedenceModel.CodecInteraction fieldAccess = + fieldPrecedenceModel.interactionFactory().accessField(token); generateAccessOrderListener( sb, indent + " ", "access field", - accessOrderModel, + fieldPrecedenceModel, fieldAccess); sb.append(indent).append("}\n"); } - private static CharSequence generateAccessOrderListenerCall( - final AccessOrderModel accessOrderModel, + private CharSequence generateAccessOrderListenerCall( + final FieldPrecedenceModel fieldPrecedenceModel, final String indent, final Token token, final String... arguments) { return generateAccessOrderListenerCall( - accessOrderModel, + fieldPrecedenceModel, indent, accessOrderListenerMethodName(token), arguments); } - private static CharSequence generateAccessOrderListenerCall( - final AccessOrderModel accessOrderModel, + private CharSequence generateAccessOrderListenerCall( + final FieldPrecedenceModel fieldPrecedenceModel, final String indent, final String methodName, final String... arguments) { - if (null == accessOrderModel) + if (null == fieldPrecedenceModel) { return ""; } final StringBuilder sb = new StringBuilder(); - sb.append(indent).append("if (").append(AccessOrderModel.PRECEDENCE_CHECKS_FLAG_NAME).append(")\n") + sb.append(indent).append("if (").append(precedenceChecksFlagName).append(")\n") .append(indent).append("{\n") .append(indent).append(" ").append(methodName).append("("); @@ -516,44 +553,44 @@ private static CharSequence generateAccessOrderListenerCall( private static void generateAccessOrderListenerMethodForGroupWrap( final StringBuilder sb, final String action, - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final String indent, final Token token) { - if (null == accessOrderModel) + if (null == fieldPrecedenceModel) { return; } sb.append("\n") .append(indent).append("private void ").append(accessOrderListenerMethodName(token)) - .append("(final int remaining)\n") + .append("(final int count)\n") .append(indent).append("{\n") - .append(indent).append(" if (remaining == 0)\n") + .append(indent).append(" if (count == 0)\n") .append(indent).append(" {\n"); - final AccessOrderModel.CodecInteraction selectEmptyGroup = - accessOrderModel.interactionFactory().determineGroupIsEmpty(token); + final FieldPrecedenceModel.CodecInteraction selectEmptyGroup = + fieldPrecedenceModel.interactionFactory().determineGroupIsEmpty(token); generateAccessOrderListener( sb, indent + " ", action + " count of repeating group", - accessOrderModel, + fieldPrecedenceModel, selectEmptyGroup); sb.append(indent).append(" }\n") .append(indent).append(" else\n") .append(indent).append(" {\n"); - final AccessOrderModel.CodecInteraction selectNonEmptyGroup = - accessOrderModel.interactionFactory().determineGroupHasElements(token); + final FieldPrecedenceModel.CodecInteraction selectNonEmptyGroup = + fieldPrecedenceModel.interactionFactory().determineGroupHasElements(token); generateAccessOrderListener( sb, indent + " ", action + " count of repeating group", - accessOrderModel, + fieldPrecedenceModel, selectNonEmptyGroup); sb.append(indent).append(" }\n") @@ -564,13 +601,13 @@ private static void generateAccessOrderListener( final StringBuilder sb, final String indent, final String action, - final AccessOrderModel accessOrderModel, - final AccessOrderModel.CodecInteraction interaction) + final FieldPrecedenceModel fieldPrecedenceModel, + final FieldPrecedenceModel.CodecInteraction interaction) { if (interaction.isTopLevelBlockFieldAccess()) { sb.append(indent).append("if (codecState() == ") - .append(qualifiedStateCase(accessOrderModel.notWrappedState())) + .append(qualifiedStateCase(fieldPrecedenceModel.notWrappedState())) .append(")\n") .append(indent).append("{\n"); generateAccessOrderException(sb, indent + " ", action, interaction); @@ -581,7 +618,7 @@ private static void generateAccessOrderListener( sb.append(indent).append("switch (codecState())\n") .append(indent).append("{\n"); - accessOrderModel.forEachTransition(interaction, transitionGroup -> + fieldPrecedenceModel.forEachTransition(interaction, transitionGroup -> { transitionGroup.forEachStartState(startState -> sb.append(indent).append(" case ").append(stateCaseForSwitchCase(startState)).append(":\n")); @@ -600,7 +637,7 @@ private static void generateAccessOrderException( final StringBuilder sb, final String indent, final String action, - final AccessOrderModel.CodecInteraction interaction) + final FieldPrecedenceModel.CodecInteraction interaction) { sb.append(indent).append("throw new IllegalStateException(") .append("\"Illegal field access order. \" +\n") @@ -615,11 +652,11 @@ private static void generateAccessOrderException( private static void generateAccessOrderListenerMethodForNextGroupElement( final StringBuilder sb, - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final String indent, final Token token) { - if (null == accessOrderModel) + if (null == fieldPrecedenceModel) { return; } @@ -630,28 +667,28 @@ private static void generateAccessOrderListenerMethodForNextGroupElement( .append(indent).append(" if (remaining > 1)\n") .append(indent).append(" {\n"); - final AccessOrderModel.CodecInteraction selectNextElementInGroup = - accessOrderModel.interactionFactory().moveToNextElement(token); + final FieldPrecedenceModel.CodecInteraction selectNextElementInGroup = + fieldPrecedenceModel.interactionFactory().moveToNextElement(token); generateAccessOrderListener( sb, indent + " ", "access next element in repeating group", - accessOrderModel, + fieldPrecedenceModel, selectNextElementInGroup); sb.append(indent).append(" }\n") .append(indent).append(" else if (remaining == 1)\n") .append(indent).append(" {\n"); - final AccessOrderModel.CodecInteraction selectLastElementInGroup = - accessOrderModel.interactionFactory().moveToLastElement(token); + final FieldPrecedenceModel.CodecInteraction selectLastElementInGroup = + fieldPrecedenceModel.interactionFactory().moveToLastElement(token); generateAccessOrderListener( sb, indent + " ", "access next element in repeating group", - accessOrderModel, + fieldPrecedenceModel, selectLastElementInGroup); sb.append(indent).append(" }\n") @@ -660,11 +697,11 @@ private static void generateAccessOrderListenerMethodForNextGroupElement( private static void generateAccessOrderListenerMethodForResetGroupCount( final StringBuilder sb, - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final String indent, final Token token) { - if (null == accessOrderModel) + if (null == fieldPrecedenceModel) { return; } @@ -672,14 +709,14 @@ private static void generateAccessOrderListenerMethodForResetGroupCount( sb.append(indent).append("private void onResetCountToIndex()\n") .append(indent).append("{\n"); - final AccessOrderModel.CodecInteraction resetCountToIndex = - accessOrderModel.interactionFactory().resetCountToIndex(token); + final FieldPrecedenceModel.CodecInteraction resetCountToIndex = + fieldPrecedenceModel.interactionFactory().resetCountToIndex(token); generateAccessOrderListener( sb, indent + " ", "reset count of repeating group", - accessOrderModel, + fieldPrecedenceModel, resetCountToIndex); sb.append(indent).append("}\n\n"); @@ -687,11 +724,11 @@ private static void generateAccessOrderListenerMethodForResetGroupCount( private static void generateAccessOrderListenerMethodForVarDataLength( final StringBuilder sb, - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final String indent, final Token token) { - if (null == accessOrderModel) + if (null == fieldPrecedenceModel) { return; } @@ -700,24 +737,24 @@ private static void generateAccessOrderListenerMethodForVarDataLength( .append(indent).append("void ").append(accessOrderListenerMethodName(token, "Length")).append("()\n") .append(indent).append("{\n"); - final AccessOrderModel.CodecInteraction accessLength = - accessOrderModel.interactionFactory().accessVarDataLength(token); + final FieldPrecedenceModel.CodecInteraction accessLength = + fieldPrecedenceModel.interactionFactory().accessVarDataLength(token); generateAccessOrderListener( sb, indent + INDENT, "decode length of var data", - accessOrderModel, + fieldPrecedenceModel, accessLength); sb.append(indent).append("}\n"); } private static CharSequence generateDecoderWrapListener( - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final String indent) { - if (null == accessOrderModel) + if (null == fieldPrecedenceModel) { return ""; } @@ -728,7 +765,7 @@ private static CharSequence generateDecoderWrapListener( .append(indent).append(" switch(actingVersion)\n") .append(indent).append(" {\n"); - accessOrderModel.forEachWrappedStateByVersion((version, state) -> + fieldPrecedenceModel.forEachWrappedStateByVersion((version, state) -> { sb.append(indent).append(" case ").append(version).append(":\n") .append(indent).append(" codecState(") @@ -738,7 +775,7 @@ private static CharSequence generateDecoderWrapListener( sb.append(indent).append(" default:\n") .append(indent).append(" codecState(") - .append(qualifiedStateCase(accessOrderModel.latestVersionWrappedState())).append(");\n") + .append(qualifiedStateCase(fieldPrecedenceModel.latestVersionWrappedState())).append(");\n") .append(indent).append(" break;\n") .append(indent).append(" }\n") .append(indent).append("}\n\n"); @@ -747,19 +784,19 @@ private static CharSequence generateDecoderWrapListener( } private CharSequence generateEncoderWrapListener( - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final String indent) { - if (null == accessOrderModel) + if (null == fieldPrecedenceModel) { return ""; } final StringBuilder sb = new StringBuilder(); - sb.append(indent).append("if (").append(AccessOrderModel.PRECEDENCE_CHECKS_FLAG_NAME).append(")") + sb.append(indent).append("if (").append(precedenceChecksFlagName).append(")") .append("\n").append(indent).append("{\n") .append(indent).append(" codecState(") - .append(qualifiedStateCase(accessOrderModel.latestVersionWrappedState())) + .append(qualifiedStateCase(fieldPrecedenceModel.latestVersionWrappedState())) .append(");\n") .append(indent).append("}\n\n"); return sb; @@ -770,7 +807,8 @@ private void generateDecoder( final List fields, final List groups, final List varData, - final boolean hasVarData) + final boolean hasVarData, + final FieldPrecedenceModel fieldPrecedenceModel) throws IOException { final String className = formatClassName(decoderName(msgToken.name())); @@ -784,20 +822,15 @@ private void generateDecoder( { generateAnnotations(BASE_INDENT, className, groups, out, this::decoderName); } - out.append(generateDeclaration(className, implementsString, msgToken)); - AccessOrderModel accessOrderModel = null; - if (AccessOrderModel.generateAccessOrderChecks()) - { - accessOrderModel = AccessOrderModel.newInstance(msgToken, fields, groups, varData, Function.identity()); - } - out.append(generateFieldOrderStates(accessOrderModel)); - out.append(generateDecoderFlyweightCode(accessOrderModel, className, msgToken)); + out.append(generateDeclaration(className, implementsString, msgToken)); + out.append(generateFieldOrderStates(fieldPrecedenceModel)); + out.append(generateDecoderFlyweightCode(fieldPrecedenceModel, className, msgToken)); final StringBuilder sb = new StringBuilder(); - generateDecoderFields(sb, accessOrderModel, fields, BASE_INDENT); - generateDecoderGroups(sb, accessOrderModel, className, groups, BASE_INDENT, false); - generateDecoderVarData(sb, accessOrderModel, varData, BASE_INDENT); + generateDecoderFields(sb, fieldPrecedenceModel, fields, BASE_INDENT); + generateDecoderGroups(sb, fieldPrecedenceModel, className, groups, BASE_INDENT, false); + generateDecoderVarData(sb, fieldPrecedenceModel, varData, BASE_INDENT); generateDecoderDisplay(sb, msgToken.name(), fields, groups, varData); generateMessageLength(sb, className, true, groups, varData, BASE_INDENT); @@ -809,7 +842,7 @@ private void generateDecoder( private void generateDecoderGroups( final StringBuilder sb, - final AccessOrderModel accessOrderModel, final String outerClassName, + final FieldPrecedenceModel fieldPrecedenceModel, final String outerClassName, final List tokens, final String indent, final boolean isSubGroup) throws IOException @@ -838,19 +871,19 @@ private void generateDecoderGroups( final List varData = new ArrayList<>(); i = collectVarData(tokens, i, varData); - generateGroupDecoderProperty(sb, groupName, accessOrderModel, groupToken, indent, isSubGroup); + generateGroupDecoderProperty(sb, groupName, fieldPrecedenceModel, groupToken, indent, isSubGroup); generateTypeJavadoc(sb, indent + INDENT, groupToken); if (shouldGenerateGroupOrderAnnotation) { generateAnnotations(indent + INDENT, groupName, groups, sb, this::decoderName); } - generateGroupDecoderClassHeader(sb, groupName, outerClassName, accessOrderModel, groupToken, + generateGroupDecoderClassHeader(sb, groupName, outerClassName, fieldPrecedenceModel, groupToken, tokens, groups, index, indent + INDENT); - generateDecoderFields(sb, accessOrderModel, fields, indent + INDENT); - generateDecoderGroups(sb, accessOrderModel, outerClassName, groups, indent + INDENT, true); - generateDecoderVarData(sb, accessOrderModel, varData, indent + INDENT); + generateDecoderFields(sb, fieldPrecedenceModel, fields, indent + INDENT); + generateDecoderGroups(sb, fieldPrecedenceModel, outerClassName, groups, indent + INDENT, true); + generateDecoderVarData(sb, fieldPrecedenceModel, varData, indent + INDENT); appendGroupInstanceDecoderDisplay(sb, fields, groups, varData, indent + INDENT); generateMessageLength(sb, groupName, false, groups, varData, indent + INDENT); @@ -862,7 +895,7 @@ private void generateDecoderGroups( private void generateEncoderGroups( final StringBuilder sb, final String outerClassName, - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final List tokens, final String indent, final boolean isSubGroup) throws IOException @@ -892,7 +925,7 @@ private void generateEncoderGroups( final List varData = new ArrayList<>(); i = collectVarData(tokens, i, varData); - generateGroupEncoderProperty(sb, groupName, accessOrderModel, groupToken, indent, isSubGroup); + generateGroupEncoderProperty(sb, groupName, fieldPrecedenceModel, groupToken, indent, isSubGroup); generateTypeJavadoc(sb, indent + INDENT, groupToken); if (shouldGenerateGroupOrderAnnotation) @@ -900,11 +933,12 @@ private void generateEncoderGroups( generateAnnotations(indent + INDENT, groupClassName, groups, sb, this::encoderName); } generateGroupEncoderClassHeader( - sb, groupName, outerClassName, accessOrderModel, groupToken, tokens, groups, index, indent + INDENT); + sb, groupName, outerClassName, fieldPrecedenceModel, groupToken, + tokens, groups, index, indent + INDENT); - generateEncoderFields(sb, groupClassName, accessOrderModel, fields, indent + INDENT); - generateEncoderGroups(sb, outerClassName, accessOrderModel, groups, indent + INDENT, true); - generateEncoderVarData(sb, groupClassName, accessOrderModel, varData, indent + INDENT); + generateEncoderFields(sb, groupClassName, fieldPrecedenceModel, fields, indent + INDENT); + generateEncoderGroups(sb, outerClassName, fieldPrecedenceModel, groups, indent + INDENT, true); + generateEncoderVarData(sb, groupClassName, fieldPrecedenceModel, varData, indent + INDENT); sb.append(indent).append(" }\n"); } @@ -914,7 +948,7 @@ private void generateGroupDecoderClassHeader( final StringBuilder sb, final String groupName, final String parentMessageClassName, - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final Token groupToken, final List tokens, final List subGroupTokens, @@ -963,7 +997,7 @@ private void generateGroupDecoderClassHeader( .append(indent).append(" }\n\n"); - generateAccessOrderListenerMethodForNextGroupElement(sb, accessOrderModel, indent + " ", groupToken); + generateAccessOrderListenerMethodForNextGroupElement(sb, fieldPrecedenceModel, indent + " ", groupToken); sb.append(indent).append(" public ").append(className).append(" next()\n") .append(indent).append(" {\n") @@ -971,7 +1005,7 @@ private void generateGroupDecoderClassHeader( .append(indent).append(" {\n") .append(indent).append(" throw new java.util.NoSuchElementException();\n") .append(indent).append(" }\n\n") - .append(generateAccessOrderListenerCall(accessOrderModel, indent + " ", "onNextElementAccessed")) + .append(generateAccessOrderListenerCall(fieldPrecedenceModel, indent + " ", "onNextElementAccessed")) .append(indent).append(" offset = parentMessage.limit();\n") .append(indent).append(" parentMessage.limit(offset + blockLength);\n") .append(indent).append(" ++index;\n\n") @@ -1020,7 +1054,7 @@ private void generateGroupDecoderClassHeader( .append(indent).append(" return index < count;\n") .append(indent).append(" }\n"); - if (null != accessOrderModel) + if (null != fieldPrecedenceModel) { sb.append("\n") .append(indent).append(" private int codecState()\n") @@ -1040,7 +1074,7 @@ private void generateGroupEncoderClassHeader( final StringBuilder sb, final String groupName, final String parentMessageClassName, - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final Token groupToken, final List tokens, final List subGroupTokens, @@ -1102,12 +1136,12 @@ private void generateGroupEncoderClassHeader( blockLengthPut, numInGroupPut); - generateAccessOrderListenerMethodForNextGroupElement(sb, accessOrderModel, ind + " ", groupToken); - generateAccessOrderListenerMethodForResetGroupCount(sb, accessOrderModel, ind + " ", groupToken); + generateAccessOrderListenerMethodForNextGroupElement(sb, fieldPrecedenceModel, ind + " ", groupToken); + generateAccessOrderListenerMethodForResetGroupCount(sb, fieldPrecedenceModel, ind + " ", groupToken); sb.append(ind).append(" public ").append(encoderName(groupName)).append(" next()\n") .append(ind).append(" {\n") - .append(generateAccessOrderListenerCall(accessOrderModel, ind + " ", "onNextElementAccessed")) + .append(generateAccessOrderListenerCall(fieldPrecedenceModel, ind + " ", "onNextElementAccessed")) .append(ind).append(" if (index >= count)\n") .append(ind).append(" {\n") .append(ind).append(" throw new java.util.NoSuchElementException();\n") @@ -1124,7 +1158,7 @@ private void generateGroupEncoderClassHeader( sb.append(ind).append(" public int resetCountToIndex()\n") .append(ind).append(" {\n") - .append(generateAccessOrderListenerCall(accessOrderModel, ind + " ", "onResetCountToIndex")) + .append(generateAccessOrderListenerCall(fieldPrecedenceModel, ind + " ", "onResetCountToIndex")) .append(ind).append(" count = index;\n") .append(ind).append(" ").append(resetCountPut).append(";\n\n") .append(ind).append(" return count;\n") @@ -1150,7 +1184,7 @@ private void generateGroupEncoderClassHeader( .append(ind).append(" return ").append(blockLength).append(";\n") .append(ind).append(" }\n"); - if (null != accessOrderModel) + if (null != fieldPrecedenceModel) { sb.append("\n") .append(ind).append(" private int codecState()\n") @@ -1274,10 +1308,10 @@ private void generateGroupEncoderClassDeclaration( sb.append(indent).append(" }\n"); } - private static void generateGroupDecoderProperty( + private void generateGroupDecoderProperty( final StringBuilder sb, final String groupName, - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final Token token, final String indent, final boolean isSubGroup) @@ -1319,7 +1353,7 @@ private static void generateGroupDecoderProperty( indent + " return " + propertyName + ";\n" + indent + " }\n\n"; - generateAccessOrderListenerMethodForGroupWrap(sb, "decode", accessOrderModel, indent + " ", token); + generateAccessOrderListenerMethodForGroupWrap(sb, "decode", fieldPrecedenceModel, indent + " ", token); generateFlyweightPropertyJavadoc(sb, indent + INDENT, token, className); new Formatter(sb).format("\n" + @@ -1333,13 +1367,13 @@ private static void generateGroupDecoderProperty( className, propertyName, actingVersionGuard, - generateAccessOrderListenerCall(accessOrderModel, indent + " ", token, propertyName + ".count")); + generateAccessOrderListenerCall(fieldPrecedenceModel, indent + " ", token, propertyName + ".count")); } private void generateGroupEncoderProperty( final StringBuilder sb, final String groupName, - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final Token token, final String indent, final boolean isSubGroup) @@ -1364,7 +1398,7 @@ private void generateGroupEncoderProperty( formatPropertyName(groupName), token.id()); - generateAccessOrderListenerMethodForGroupWrap(sb, "encode", accessOrderModel, indent + " ", token); + generateAccessOrderListenerMethodForGroupWrap(sb, "encode", fieldPrecedenceModel, indent + " ", token); generateGroupEncodePropertyJavadoc(sb, indent + INDENT, token, className); new Formatter(sb).format("\n" + @@ -1376,12 +1410,12 @@ private void generateGroupEncoderProperty( indent + " }\n", className, propertyName, - generateAccessOrderListenerCall(accessOrderModel, indent + " ", token, "count")); + generateAccessOrderListenerCall(fieldPrecedenceModel, indent + " ", token, "count")); } private void generateDecoderVarData( final StringBuilder sb, - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final List tokens, final String indent) { @@ -1414,12 +1448,12 @@ private void generateDecoderVarData( .append(indent).append(" return ").append(sizeOfLengthField).append(";\n") .append(indent).append(" }\n"); - generateAccessOrderListenerMethodForVarDataLength(sb, accessOrderModel, indent + " ", token); + generateAccessOrderListenerMethodForVarDataLength(sb, fieldPrecedenceModel, indent + " ", token); final CharSequence lengthAccessOrderListenerCall = generateAccessOrderListenerCall( - accessOrderModel, indent + " ", accessOrderListenerMethodName(token, "Length")); + fieldPrecedenceModel, indent + " ", accessOrderListenerMethodName(token, "Length")); - generateAccessOrderListenerMethod(sb, accessOrderModel, indent + " ", token); + generateAccessOrderListenerMethod(sb, fieldPrecedenceModel, indent + " ", token); sb.append("\n") .append(indent).append(" public int ").append(methodPropName).append("Length()\n") @@ -1432,7 +1466,7 @@ private void generateDecoderVarData( .append(indent).append(" }\n"); final CharSequence accessOrderListenerCall = - generateAccessOrderListenerCall(accessOrderModel, indent + " ", token); + generateAccessOrderListenerCall(fieldPrecedenceModel, indent + " ", token); generateDataDecodeMethods(sb, token, propertyName, sizeOfLengthField, lengthType, byteOrderStr, characterEncoding, accessOrderListenerCall, indent); @@ -1444,7 +1478,7 @@ private void generateDecoderVarData( private void generateEncoderVarData( final StringBuilder sb, final String className, - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final List tokens, final String indent) { @@ -1477,9 +1511,9 @@ private void generateEncoderVarData( .append(sizeOfLengthField).append(";\n") .append(indent).append(" }\n"); - generateAccessOrderListenerMethod(sb, accessOrderModel, indent + " ", token); + generateAccessOrderListenerMethod(sb, fieldPrecedenceModel, indent + " ", token); final CharSequence accessOrderListenerCall = - generateAccessOrderListenerCall(accessOrderModel, indent + " ", token); + generateAccessOrderListenerCall(fieldPrecedenceModel, indent + " ", token); generateDataEncodeMethods( sb, @@ -3357,7 +3391,7 @@ private CharSequence generateFixedFlyweightCode( } private CharSequence generateDecoderFlyweightCode( - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final String className, final Token token) { @@ -3365,7 +3399,7 @@ private CharSequence generateDecoderFlyweightCode( final StringBuilder methods = new StringBuilder(); - methods.append(generateDecoderWrapListener(accessOrderModel, " ")); + methods.append(generateDecoderWrapListener(fieldPrecedenceModel, " ")); methods.append(" public ").append(className).append(" wrap(\n") .append(" final ").append(readOnlyBuffer).append(" buffer,\n") @@ -3382,7 +3416,7 @@ private CharSequence generateDecoderFlyweightCode( .append(" this.actingBlockLength = actingBlockLength;\n") .append(" this.actingVersion = actingVersion;\n") .append(" limit(offset + actingBlockLength);\n\n") - .append(generateAccessOrderListenerCall(accessOrderModel, " ", "onWrap", "actingVersion")) + .append(generateAccessOrderListenerCall(fieldPrecedenceModel, " ", "onWrap", "actingVersion")) .append(" return this;\n") .append(" }\n\n"); @@ -3413,7 +3447,7 @@ private CharSequence generateDecoderFlyweightCode( .append(" {\n") .append(" final int currentLimit = limit();\n"); - if (null != accessOrderModel) + if (null != fieldPrecedenceModel) { methods.append(" final int currentCodecState = codecState();\n"); } @@ -3423,9 +3457,9 @@ private CharSequence generateDecoderFlyweightCode( .append(" final int decodedLength = encodedLength();\n") .append(" limit(currentLimit);\n\n"); - if (null != accessOrderModel) + if (null != fieldPrecedenceModel) { - methods.append(" if (").append(AccessOrderModel.PRECEDENCE_CHECKS_FLAG_NAME).append(")\n") + methods.append(" if (").append(precedenceChecksFlagName).append(")\n") .append(" {\n") .append(" codecState(currentCodecState);\n") .append(" }\n\n"); @@ -3547,7 +3581,7 @@ private CharSequence generateFlyweightCode( private CharSequence generateEncoderFlyweightCode( final String className, - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final Token token) { final String wrapMethod = @@ -3560,7 +3594,7 @@ private CharSequence generateEncoderFlyweightCode( " this.initialOffset = offset;\n" + " this.offset = offset;\n" + " limit(offset + BLOCK_LENGTH);\n\n" + - generateEncoderWrapListener(accessOrderModel, " ") + + generateEncoderWrapListener(fieldPrecedenceModel, " ") + " return this;\n" + " }\n\n"; @@ -3611,7 +3645,7 @@ private CharSequence generateEncoderFlyweightCode( private void generateEncoderFields( final StringBuilder sb, final String containingClassName, - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final List tokens, final String indent) { @@ -3627,9 +3661,9 @@ private void generateEncoderFields( generateEncodingOffsetMethod(sb, propertyName, fieldToken.offset(), indent); generateEncodingLengthMethod(sb, propertyName, typeToken.encodedLength(), indent); generateFieldMetaAttributeMethod(sb, fieldToken, indent); - generateAccessOrderListenerMethod(sb, accessOrderModel, indent + " ", fieldToken); + generateAccessOrderListenerMethod(sb, fieldPrecedenceModel, indent + " ", fieldToken); final CharSequence accessOrderListenerCall = generateAccessOrderListenerCall( - accessOrderModel, indent + " ", fieldToken); + fieldPrecedenceModel, indent + " ", fieldToken); switch (typeToken.signal()) { @@ -3663,7 +3697,7 @@ private void generateEncoderFields( private void generateDecoderFields( final StringBuilder sb, - final AccessOrderModel accessOrderModel, + final FieldPrecedenceModel fieldPrecedenceModel, final List tokens, final String indent) { @@ -3680,9 +3714,9 @@ private void generateDecoderFields( generateEncodingLengthMethod(sb, propertyName, typeToken.encodedLength(), indent); generateFieldMetaAttributeMethod(sb, fieldToken, indent); - generateAccessOrderListenerMethod(sb, accessOrderModel, indent + " ", fieldToken); + generateAccessOrderListenerMethod(sb, fieldPrecedenceModel, indent + " ", fieldToken); final CharSequence accessOrderListenerCall = generateAccessOrderListenerCall( - accessOrderModel, indent + " ", fieldToken); + fieldPrecedenceModel, indent + " ", fieldToken); switch (typeToken.signal()) { diff --git a/sbe-tool/src/test/cpp/CMakeLists.txt b/sbe-tool/src/test/cpp/CMakeLists.txt index cc5bcf89c3..8c698c83c3 100644 --- a/sbe-tool/src/test/cpp/CMakeLists.txt +++ b/sbe-tool/src/test/cpp/CMakeLists.txt @@ -64,7 +64,7 @@ add_custom_command( -Dsbe.generate.ir="true" -Dsbe.target.language="cpp" -Dsbe.generate.precedence.checks="true" - -Dsbe.precedence.checks.flagName="SBE_ENABLE_PRECEDENCE_CHECKS_IN_TESTS" + -Dsbe.precedence.checks.flag.name="SBE_ENABLE_PRECEDENCE_CHECKS_IN_TESTS" -Dsbe.cpp.disable.implicit.copying="true" -jar ${SBE_JAR} ${CODE_GENERATION_SCHEMA} diff --git a/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/JavaGeneratorTest.java b/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/JavaGeneratorTest.java index e6ddf52142..3f253ed778 100644 --- a/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/JavaGeneratorTest.java +++ b/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/JavaGeneratorTest.java @@ -15,6 +15,12 @@ */ package uk.co.real_logic.sbe.generation.java; +import uk.co.real_logic.sbe.Tests; +import uk.co.real_logic.sbe.generation.common.PrecedenceChecks; +import uk.co.real_logic.sbe.ir.Ir; +import uk.co.real_logic.sbe.xml.IrGenerator; +import uk.co.real_logic.sbe.xml.MessageSchema; +import uk.co.real_logic.sbe.xml.ParserOptions; import org.agrona.DirectBuffer; import org.agrona.MutableDirectBuffer; import org.agrona.concurrent.UnsafeBuffer; @@ -24,16 +30,13 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import uk.co.real_logic.sbe.Tests; -import uk.co.real_logic.sbe.ir.Ir; -import uk.co.real_logic.sbe.xml.IrGenerator; -import uk.co.real_logic.sbe.xml.MessageSchema; -import uk.co.real_logic.sbe.xml.ParserOptions; import java.io.IOException; import java.io.InputStream; +import java.lang.reflect.Field; import java.lang.reflect.Method; import java.nio.ByteOrder; +import java.util.Arrays; import java.util.Map; import static org.hamcrest.MatcherAssert.assertThat; @@ -254,6 +257,37 @@ void shouldGenerateBasicMessage() throws Exception assertThat(msgFlyweight.toString(), startsWith("[Car]")); } + @Test + void shouldGenerateWithoutPrecedenceChecksByDefault() throws Exception + { + final PrecedenceChecks.Context context = new PrecedenceChecks.Context(); + final PrecedenceChecks precedenceChecks = PrecedenceChecks.newInstance(context); + generator(precedenceChecks).generate(); + + final Field field = Arrays.stream(compileCarEncoder().getDeclaredFields()) + .filter(f -> f.getName().equals(context.precedenceChecksFlagName())) + .findFirst() + .orElse(null); + + assertNull(field); + } + + @Test + void shouldGeneratePrecedenceChecksWhenEnabled() throws Exception + { + final PrecedenceChecks.Context context = new PrecedenceChecks.Context() + .shouldGeneratePrecedenceChecks(true); + final PrecedenceChecks precedenceChecks = PrecedenceChecks.newInstance(context); + generator(precedenceChecks).generate(); + + final Field field = Arrays.stream(compileCarEncoder().getDeclaredFields()) + .filter(f -> f.getName().equals(context.precedenceChecksFlagName())) + .findFirst() + .orElse(null); + + assertNotNull(field); + } + @Test void shouldGenerateRepeatingGroupDecoder() throws Exception { @@ -673,6 +707,12 @@ private JavaGenerator generator() return new JavaGenerator(ir, BUFFER_NAME, READ_ONLY_BUFFER_NAME, false, false, false, outputManager); } + private JavaGenerator generator(final PrecedenceChecks precedenceChecks) + { + return new JavaGenerator(ir, BUFFER_NAME, READ_ONLY_BUFFER_NAME, false, false, false, false, + precedenceChecks, outputManager); + } + private void generateTypeStubs() throws IOException { final JavaGenerator javaGenerator = generator(); From 1efe4f257dd6aea9ff4f64d831864d105c1afa65 Mon Sep 17 00:00:00 2001 From: Zach Bray Date: Mon, 4 Dec 2023 18:48:05 +0000 Subject: [PATCH 47/50] Address Mike's feedback: provide more specific diagram location. Previously, we only gave the (quite ambiguous) name of the inner class in the error message. Now, we give a qualified name that includes the codec/encoder/decoder class name (for C++, C#, and Java). --- .../common/FieldPrecedenceModel.java | 19 +++++- .../generation/common/PrecedenceChecks.java | 33 +++++++--- .../sbe/generation/cpp/CppGenerator.java | 11 ++-- .../generation/csharp/CSharpGenerator.java | 11 ++-- .../sbe/generation/java/JavaGenerator.java | 28 +++++--- .../sbe/ir/generated/FrameCodecDecoder.java | 18 +++--- .../sbe/ir/generated/FrameCodecEncoder.java | 12 ++-- .../sbe/ir/generated/TokenCodecDecoder.java | 64 +++++++++---------- .../sbe/ir/generated/TokenCodecEncoder.java | 42 ++++++------ 9 files changed, 143 insertions(+), 95 deletions(-) diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/FieldPrecedenceModel.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/FieldPrecedenceModel.java index 2259e3c676..0e79f88568 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/FieldPrecedenceModel.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/FieldPrecedenceModel.java @@ -48,12 +48,19 @@ public final class FieldPrecedenceModel private final Map> transitionsByState = new HashMap<>(); private final Int2ObjectHashMap versionWrappedStates = new Int2ObjectHashMap<>(); private final State notWrappedState = allocateState("NOT_WRAPPED"); + private final String generatedRepresentationClassName; private State encoderWrappedState; private Set terminalEncoderStates; + private FieldPrecedenceModel(final String generatedRepresentationClassName) + { + this.generatedRepresentationClassName = generatedRepresentationClassName; + } + /** * Builds a state machine that models whether codec interactions are safe. * + * @param stateClassName the qualified name of the class that models the state machine in generated code * @param msgToken the message token * @param fields the fields in the message * @param groups the groups in the message @@ -62,13 +69,14 @@ public final class FieldPrecedenceModel * @return the access order model */ public static FieldPrecedenceModel newInstance( + final String stateClassName, final Token msgToken, final List fields, final List groups, final List varData, final Function versionsSelector) { - final FieldPrecedenceModel model = new FieldPrecedenceModel(); + final FieldPrecedenceModel model = new FieldPrecedenceModel(stateClassName); model.findTransitions(msgToken, fields, groups, varData, versionsSelector); return model; } @@ -154,6 +162,15 @@ public CodecInteraction.CodecInteractionFactory interactionFactory() return interactionFactory; } + /** + * Returns the name of the class that models the state machine in generated code. + * @return the name of the class that models the state machine in generated code. + */ + public String generatedRepresentationClassName() + { + return generatedRepresentationClassName; + } + /** * Iterates over the possible state machine transitions due to the supplied codec interaction. * @param interaction a codec interaction. diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/PrecedenceChecks.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/PrecedenceChecks.java index 3e21ba8408..c2eac29c23 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/PrecedenceChecks.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/PrecedenceChecks.java @@ -53,13 +53,16 @@ private PrecedenceChecks(final Context context) * Only the latest version of the message is considered when creating the model. *

* + * @param stateClassName the name of the generated class that models the state of the encoder. * @param msgTokens the tokens of the message. * @return a {@link FieldPrecedenceModel} for the given message tokens or {@code null} if precedence checks * are disabled. */ - public FieldPrecedenceModel createEncoderModel(final List msgTokens) + public FieldPrecedenceModel createEncoderModel( + final String stateClassName, + final List msgTokens) { - return createModel(msgTokens, SELECT_LATEST_VERSION_ONLY); + return createModel(stateClassName, msgTokens, SELECT_LATEST_VERSION_ONLY); } /** @@ -69,13 +72,16 @@ public FieldPrecedenceModel createEncoderModel(final List msgTokens) * All versions of the message are considered when creating the model. *

* - * @param msgTokens the tokens of the message. + * @param stateClassName the name of the generated class that models the state of the decoder. + * @param msgTokens the tokens of the message. * @return a {@link FieldPrecedenceModel} for the given message tokens or {@code null} if precedence checks * are disabled. */ - public FieldPrecedenceModel createDecoderModel(final List msgTokens) + public FieldPrecedenceModel createDecoderModel( + final String stateClassName, + final List msgTokens) { - return createModel(msgTokens, Function.identity()); + return createModel(stateClassName, msgTokens, Function.identity()); } /** @@ -85,13 +91,17 @@ public FieldPrecedenceModel createDecoderModel(final List msgTokens) * All versions of the message are considered when creating the model. *

* + * @param stateClassName the name of the generated class that models the state of the codec. * @param msgTokens the tokens of the message. * @return a {@link FieldPrecedenceModel} for the given message tokens or {@code null} if precedence checks * are disabled. */ - public FieldPrecedenceModel createCodecModel(final List msgTokens) + public FieldPrecedenceModel createCodecModel( + final String stateClassName, + final List msgTokens + ) { - return createModel(msgTokens, Function.identity()); + return createModel(stateClassName, msgTokens, Function.identity()); } /** @@ -116,6 +126,7 @@ public static PrecedenceChecks newInstance(final Context context) } private FieldPrecedenceModel createModel( + final String stateClassName, final List tokens, final Function versionsSelector ) @@ -136,7 +147,13 @@ private FieldPrecedenceModel createModel( final List varData = new ArrayList<>(); collectVarData(messageBody, i, varData); - return FieldPrecedenceModel.newInstance(msgToken, fields, groups, varData, versionsSelector); + return FieldPrecedenceModel.newInstance( + stateClassName, + msgToken, + fields, + groups, + varData, + versionsSelector); } return null; diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java index d8307fa450..c801c58057 100755 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java @@ -174,7 +174,8 @@ public void generate() throws IOException { final Token msgToken = tokens.get(0); final String className = formatClassName(msgToken.name()); - final FieldPrecedenceModel fieldPrecedenceModel = precedenceChecks.createCodecModel(tokens); + final String stateClassName = className + "::CodecState"; + final FieldPrecedenceModel fieldPrecedenceModel = precedenceChecks.createCodecModel(stateClassName, tokens); try (Writer out = outputManager.createOutput(className)) { @@ -447,7 +448,7 @@ private static void generateAccessOrderListener( .append(qualifiedStateCase(fieldPrecedenceModel.notWrappedState())) .append(")\n") .append(indent).append("{\n"); - generateAccessOrderException(sb, indent + INDENT, action, interaction); + generateAccessOrderException(sb, indent + INDENT, action, fieldPrecedenceModel, interaction); sb.append(indent).append("}\n"); } else @@ -474,7 +475,7 @@ private static void generateAccessOrderListener( }); sb.append(indent).append(INDENT).append("default:\n"); - generateAccessOrderException(sb, indent + TWO_INDENT, action, interaction); + generateAccessOrderException(sb, indent + TWO_INDENT, action, fieldPrecedenceModel, interaction); sb.append(indent).append("}\n"); } } @@ -483,6 +484,7 @@ private static void generateAccessOrderException( final StringBuilder sb, final String indent, final String action, + final FieldPrecedenceModel fieldPrecedenceModel, final FieldPrecedenceModel.CodecInteraction interaction) { sb.append(indent).append("throw AccessOrderError(") @@ -493,7 +495,8 @@ private static void generateAccessOrderException( .append(indent).append(INDENT) .append("\". Expected one of these transitions: [\" + codecStateTransitions(codecState()) +\n") .append(indent).append(INDENT) - .append("\"]. Please see the diagram in the docs of the inner enum #CodecState.\");\n"); + .append("\"]. Please see the diagram in the docs of the enum ") + .append(fieldPrecedenceModel.generatedRepresentationClassName()).append(".\");\n"); } private static void generateAccessOrderListenerMethodForNextGroupElement( diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java index 243f3f6530..8fe17df71a 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java @@ -150,7 +150,8 @@ public void generate() throws IOException { final Token msgToken = tokens.get(0); final String className = formatClassName(msgToken.name()); - final FieldPrecedenceModel fieldPrecedenceModel = precedenceChecks.createCodecModel(tokens); + final String stateClassName = className + ".CodecState"; + final FieldPrecedenceModel fieldPrecedenceModel = precedenceChecks.createCodecModel(stateClassName, tokens); try (Writer out = outputManager.createOutput(className)) { @@ -1762,7 +1763,7 @@ private static void generateAccessOrderListener( .append(qualifiedStateCase(fieldPrecedenceModel.notWrappedState())) .append(")\n") .append(indent).append("{\n"); - generateAccessOrderException(sb, indent + INDENT, action, interaction); + generateAccessOrderException(sb, indent + INDENT, action, fieldPrecedenceModel, interaction); sb.append(indent).append("}\n"); } else @@ -1783,7 +1784,7 @@ private static void generateAccessOrderListener( }); sb.append(indent).append(INDENT).append("default:\n"); - generateAccessOrderException(sb, indent + TWO_INDENT, action, interaction); + generateAccessOrderException(sb, indent + TWO_INDENT, action, fieldPrecedenceModel, interaction); sb.append(indent).append("}\n"); } } @@ -1792,6 +1793,7 @@ private static void generateAccessOrderException( final StringBuilder sb, final String indent, final String action, + final FieldPrecedenceModel fieldPrecedenceModel, final FieldPrecedenceModel.CodecInteraction interaction) { sb.append(indent).append("throw new InvalidOperationException(") @@ -1802,7 +1804,8 @@ private static void generateAccessOrderException( .append(indent).append(INDENT) .append("\". Expected one of these transitions: [\" + codecStateTransitions(codecState()) +\n") .append(indent).append(INDENT) - .append("\"]. Please see the diagram in the docs of the inner enum #CodecState.\");\n"); + .append("\"]. Please see the diagram in the docs of the enum ") + .append(fieldPrecedenceModel.generatedRepresentationClassName()).append(".\");\n"); } private static void generateAccessOrderListenerMethodForNextGroupElement( diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java index df150fb88a..7edf2e68c3 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java @@ -272,15 +272,22 @@ public void generate() throws IOException final List varData = new ArrayList<>(); collectVarData(messageBody, i, varData); - final FieldPrecedenceModel decoderPrecedenceModel = precedenceChecks.createDecoderModel(tokens); - generateDecoder(msgToken, fields, groups, varData, hasVarData, decoderPrecedenceModel); - - final FieldPrecedenceModel encoderPrecedenceModel = precedenceChecks.createEncoderModel(tokens); - generateEncoder(msgToken, fields, groups, varData, hasVarData, encoderPrecedenceModel); + final String decoderClassName = formatClassName(decoderName(msgToken.name())); + final String decoderStateClassName = decoderClassName + "#CodecStates"; + final FieldPrecedenceModel decoderPrecedenceModel = + precedenceChecks.createDecoderModel(decoderStateClassName, tokens); + generateDecoder(decoderClassName, msgToken, fields, groups, varData, hasVarData, decoderPrecedenceModel); + + final String encoderClassName = formatClassName(encoderName(msgToken.name())); + final String encoderStateClassName = encoderClassName + "#CodecStates"; + final FieldPrecedenceModel encoderPrecedenceModel = + precedenceChecks.createEncoderModel(encoderStateClassName, tokens); + generateEncoder(encoderClassName, msgToken, fields, groups, varData, hasVarData, encoderPrecedenceModel); } } private void generateEncoder( + final String className, final Token msgToken, final List fields, final List groups, @@ -289,7 +296,6 @@ private void generateEncoder( final FieldPrecedenceModel fieldPrecedenceModel) throws IOException { - final String className = formatClassName(encoderName(msgToken.name())); final String implementsString = implementsInterface(MessageEncoderFlyweight.class.getSimpleName()); try (Writer out = outputManager.createOutput(className)) @@ -610,7 +616,7 @@ private static void generateAccessOrderListener( .append(qualifiedStateCase(fieldPrecedenceModel.notWrappedState())) .append(")\n") .append(indent).append("{\n"); - generateAccessOrderException(sb, indent + " ", action, interaction); + generateAccessOrderException(sb, indent + " ", action, fieldPrecedenceModel, interaction); sb.append(indent).append("}\n"); } else @@ -628,7 +634,7 @@ private static void generateAccessOrderListener( }); sb.append(indent).append(" default:\n"); - generateAccessOrderException(sb, indent + " ", action, interaction); + generateAccessOrderException(sb, indent + " ", action, fieldPrecedenceModel, interaction); sb.append(indent).append("}\n"); } } @@ -637,6 +643,7 @@ private static void generateAccessOrderException( final StringBuilder sb, final String indent, final String action, + final FieldPrecedenceModel fieldPrecedenceModel, final FieldPrecedenceModel.CodecInteraction interaction) { sb.append(indent).append("throw new IllegalStateException(") @@ -647,7 +654,8 @@ private static void generateAccessOrderException( .append(indent) .append(" \". Expected one of these transitions: [\" + CodecStates.transitions(codecState()) +\n") .append(indent) - .append(" \"]. Please see the diagram in the Javadoc of the inner class #CodecStates.\");\n"); + .append(" \"]. Please see the diagram in the Javadoc of the class ") + .append(fieldPrecedenceModel.generatedRepresentationClassName()).append(".\");\n"); } private static void generateAccessOrderListenerMethodForNextGroupElement( @@ -803,6 +811,7 @@ private CharSequence generateEncoderWrapListener( } private void generateDecoder( + final String className, final Token msgToken, final List fields, final List groups, @@ -811,7 +820,6 @@ private void generateDecoder( final FieldPrecedenceModel fieldPrecedenceModel) throws IOException { - final String className = formatClassName(decoderName(msgToken.name())); final String implementsString = implementsInterface(MessageDecoderFlyweight.class.getSimpleName()); try (Writer out = outputManager.createOutput(className)) diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java index b85cbb091b..89962bd61f 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java @@ -278,7 +278,7 @@ private void onIrIdAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"irId\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class FrameCodecDecoder#CodecStates."); } } @@ -345,7 +345,7 @@ private void onIrVersionAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"irVersion\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class FrameCodecDecoder#CodecStates."); } } @@ -412,7 +412,7 @@ private void onSchemaVersionAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"schemaVersion\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class FrameCodecDecoder#CodecStates."); } } @@ -483,7 +483,7 @@ void onPackageNameLengthAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot decode length of var data \"packageName\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class FrameCodecDecoder#CodecStates."); } } @@ -498,7 +498,7 @@ private void onPackageNameAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"packageName\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class FrameCodecDecoder#CodecStates."); } } @@ -641,7 +641,7 @@ void onNamespaceNameLengthAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot decode length of var data \"namespaceName\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class FrameCodecDecoder#CodecStates."); } } @@ -656,7 +656,7 @@ private void onNamespaceNameAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"namespaceName\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class FrameCodecDecoder#CodecStates."); } } @@ -799,7 +799,7 @@ void onSemanticVersionLengthAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot decode length of var data \"semanticVersion\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class FrameCodecDecoder#CodecStates."); } } @@ -814,7 +814,7 @@ private void onSemanticVersionAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"semanticVersion\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class FrameCodecDecoder#CodecStates."); } } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java index 2df3744b61..a5524033fc 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java @@ -224,7 +224,7 @@ private void onIrIdAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"irId\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class FrameCodecEncoder#CodecStates."); } } @@ -292,7 +292,7 @@ private void onIrVersionAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"irVersion\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class FrameCodecEncoder#CodecStates."); } } @@ -360,7 +360,7 @@ private void onSchemaVersionAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"schemaVersion\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class FrameCodecEncoder#CodecStates."); } } @@ -427,7 +427,7 @@ private void onPackageNameAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"packageName\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class FrameCodecEncoder#CodecStates."); } } @@ -533,7 +533,7 @@ private void onNamespaceNameAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"namespaceName\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class FrameCodecEncoder#CodecStates."); } } @@ -639,7 +639,7 @@ private void onSemanticVersionAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"semanticVersion\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class FrameCodecEncoder#CodecStates."); } } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java index 7a452e23cd..6a48488936 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java @@ -325,7 +325,7 @@ private void onTokenOffsetAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"tokenOffset\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates."); } } @@ -392,7 +392,7 @@ private void onTokenSizeAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"tokenSize\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates."); } } @@ -459,7 +459,7 @@ private void onFieldIdAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"fieldId\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates."); } } @@ -526,7 +526,7 @@ private void onTokenVersionAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"tokenVersion\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates."); } } @@ -593,7 +593,7 @@ private void onComponentTokenCountAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"componentTokenCount\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates."); } } @@ -660,7 +660,7 @@ private void onSignalAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"signal\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates."); } } @@ -722,7 +722,7 @@ private void onPrimitiveTypeAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"primitiveType\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates."); } } @@ -784,7 +784,7 @@ private void onByteOrderAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"byteOrder\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates."); } } @@ -846,7 +846,7 @@ private void onPresenceAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"presence\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates."); } } @@ -908,7 +908,7 @@ private void onDeprecatedAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"deprecated\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates."); } } @@ -979,7 +979,7 @@ void onNameLengthAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot decode length of var data \"name\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates."); } } @@ -994,7 +994,7 @@ private void onNameAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"name\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates."); } } @@ -1137,7 +1137,7 @@ void onConstValueLengthAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot decode length of var data \"constValue\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates."); } } @@ -1152,7 +1152,7 @@ private void onConstValueAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"constValue\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates."); } } @@ -1295,7 +1295,7 @@ void onMinValueLengthAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot decode length of var data \"minValue\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates."); } } @@ -1310,7 +1310,7 @@ private void onMinValueAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"minValue\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates."); } } @@ -1453,7 +1453,7 @@ void onMaxValueLengthAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot decode length of var data \"maxValue\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates."); } } @@ -1468,7 +1468,7 @@ private void onMaxValueAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"maxValue\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates."); } } @@ -1611,7 +1611,7 @@ void onNullValueLengthAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot decode length of var data \"nullValue\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates."); } } @@ -1626,7 +1626,7 @@ private void onNullValueAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"nullValue\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates."); } } @@ -1769,7 +1769,7 @@ void onCharacterEncodingLengthAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot decode length of var data \"characterEncoding\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates."); } } @@ -1784,7 +1784,7 @@ private void onCharacterEncodingAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"characterEncoding\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates."); } } @@ -1927,7 +1927,7 @@ void onEpochLengthAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot decode length of var data \"epoch\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates."); } } @@ -1942,7 +1942,7 @@ private void onEpochAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"epoch\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates."); } } @@ -2085,7 +2085,7 @@ void onTimeUnitLengthAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot decode length of var data \"timeUnit\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates."); } } @@ -2100,7 +2100,7 @@ private void onTimeUnitAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"timeUnit\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates."); } } @@ -2243,7 +2243,7 @@ void onSemanticTypeLengthAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot decode length of var data \"semanticType\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates."); } } @@ -2258,7 +2258,7 @@ private void onSemanticTypeAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"semanticType\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates."); } } @@ -2401,7 +2401,7 @@ void onDescriptionLengthAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot decode length of var data \"description\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates."); } } @@ -2416,7 +2416,7 @@ private void onDescriptionAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"description\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates."); } } @@ -2559,7 +2559,7 @@ void onReferencedNameLengthAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot decode length of var data \"referencedName\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates."); } } @@ -2574,7 +2574,7 @@ private void onReferencedNameAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"referencedName\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates."); } } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java index 7cd917b6e7..a0ece1099d 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java @@ -271,7 +271,7 @@ private void onTokenOffsetAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"tokenOffset\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecEncoder#CodecStates."); } } @@ -339,7 +339,7 @@ private void onTokenSizeAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"tokenSize\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecEncoder#CodecStates."); } } @@ -407,7 +407,7 @@ private void onFieldIdAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"fieldId\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecEncoder#CodecStates."); } } @@ -475,7 +475,7 @@ private void onTokenVersionAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"tokenVersion\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecEncoder#CodecStates."); } } @@ -543,7 +543,7 @@ private void onComponentTokenCountAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"componentTokenCount\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecEncoder#CodecStates."); } } @@ -611,7 +611,7 @@ private void onSignalAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"signal\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecEncoder#CodecStates."); } } @@ -663,7 +663,7 @@ private void onPrimitiveTypeAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"primitiveType\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecEncoder#CodecStates."); } } @@ -715,7 +715,7 @@ private void onByteOrderAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"byteOrder\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecEncoder#CodecStates."); } } @@ -767,7 +767,7 @@ private void onPresenceAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"presence\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecEncoder#CodecStates."); } } @@ -819,7 +819,7 @@ private void onDeprecatedAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"deprecated\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecEncoder#CodecStates."); } } @@ -886,7 +886,7 @@ private void onNameAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"name\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecEncoder#CodecStates."); } } @@ -992,7 +992,7 @@ private void onConstValueAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"constValue\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecEncoder#CodecStates."); } } @@ -1098,7 +1098,7 @@ private void onMinValueAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"minValue\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecEncoder#CodecStates."); } } @@ -1204,7 +1204,7 @@ private void onMaxValueAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"maxValue\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecEncoder#CodecStates."); } } @@ -1310,7 +1310,7 @@ private void onNullValueAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"nullValue\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecEncoder#CodecStates."); } } @@ -1416,7 +1416,7 @@ private void onCharacterEncodingAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"characterEncoding\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecEncoder#CodecStates."); } } @@ -1522,7 +1522,7 @@ private void onEpochAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"epoch\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecEncoder#CodecStates."); } } @@ -1628,7 +1628,7 @@ private void onTimeUnitAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"timeUnit\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecEncoder#CodecStates."); } } @@ -1734,7 +1734,7 @@ private void onSemanticTypeAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"semanticType\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecEncoder#CodecStates."); } } @@ -1840,7 +1840,7 @@ private void onDescriptionAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"description\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecEncoder#CodecStates."); } } @@ -1946,7 +1946,7 @@ private void onReferencedNameAccessed() throw new IllegalStateException("Illegal field access order. " + "Cannot access field \"referencedName\" in state: " + CodecStates.name(codecState()) + ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) + - "]. Please see the diagram in the Javadoc of the inner class #CodecStates."); + "]. Please see the diagram in the Javadoc of the class TokenCodecEncoder#CodecStates."); } } From 0eb1266b1ae5655fdbb939943a8d379e1ccd6f43 Mon Sep 17 00:00:00 2001 From: Zach Bray Date: Tue, 5 Dec 2023 09:44:23 +0000 Subject: [PATCH 48/50] Validate PrecedenceChecks#Context in conclude. --- .../sbe/generation/common/PrecedenceChecks.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/PrecedenceChecks.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/PrecedenceChecks.java index c2eac29c23..1c121b86da 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/PrecedenceChecks.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/PrecedenceChecks.java @@ -24,6 +24,7 @@ import java.util.function.Function; import java.util.stream.IntStream; +import static java.util.Objects.requireNonNull; import static uk.co.real_logic.sbe.ir.GenerationUtil.collectFields; import static uk.co.real_logic.sbe.ir.GenerationUtil.collectGroups; import static uk.co.real_logic.sbe.ir.GenerationUtil.collectVarData; @@ -43,6 +44,7 @@ public final class PrecedenceChecks private PrecedenceChecks(final Context context) { + context.conclude(); this.context = context; } @@ -270,5 +272,14 @@ public Context precedenceChecksPropName(final String precedenceChecksPropName) this.precedenceChecksPropName = precedenceChecksPropName; return this; } + + /** + * Validates this {@link Context} instance. + */ + public void conclude() + { + requireNonNull(precedenceChecksFlagName, "precedenceChecksFlagName"); + requireNonNull(precedenceChecksPropName, "precedenceChecksPropName"); + } } } From 26f79f2cd64530c5f26ee24a67d034af380c1118 Mon Sep 17 00:00:00 2001 From: Zach Bray Date: Tue, 5 Dec 2023 09:45:41 +0000 Subject: [PATCH 49/50] Address Mike's feedback: move sys prop eval higher. In this commit, I've removed the `PrecedenceChecks#Configuration` class in favour of a static method within `TargetCodeGeneratorLoader`. Unfortunately, C# behaves differently to all the other code generators. To avoid duplicating the default configuration for precedence checks in the `CSharp` class, I've made the method publicly visible, which is a bit ugly. It has also introduced a cyclic dependency between the code generators, e.g., `JavaGenerator`, and `TargetCodeGeneratorLoader`, as in order to preserve the existing public signatures I have kept the old constructors around and supplied a default `PrecedenceChecks`. --- .../generation/TargetCodeGeneratorLoader.java | 28 +++++++++++++- .../generation/common/PrecedenceChecks.java | 38 ++----------------- .../sbe/generation/cpp/CppGenerator.java | 3 +- .../sbe/generation/csharp/CSharp.java | 4 +- .../generation/csharp/CSharpGenerator.java | 3 +- .../sbe/generation/java/JavaGenerator.java | 3 +- .../generation/java/JavaGeneratorTest.java | 9 ++++- 7 files changed, 44 insertions(+), 44 deletions(-) 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 e99d1b8b75..f9da87723d 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 @@ -54,7 +54,7 @@ public CodeGenerator newInstance(final Ir ir, final String outputDir) "true".equals(System.getProperty(JAVA_GENERATE_INTERFACES)), "true".equals(System.getProperty(DECODE_UNKNOWN_ENUM_VALUES)), "true".equals(System.getProperty(TYPES_PACKAGE_OVERRIDE)), - PrecedenceChecks.newInstance(new PrecedenceChecks.Context()), + precedenceChecks(), new JavaOutputManager(outputDir, ir.applicableNamespace())); } }, @@ -86,7 +86,7 @@ public CodeGenerator newInstance(final Ir ir, final String outputDir) return new CppGenerator( ir, "true".equals(System.getProperty(DECODE_UNKNOWN_ENUM_VALUES)), - PrecedenceChecks.newInstance(new PrecedenceChecks.Context()), + precedenceChecks(), new NamespaceOutputManager(outputDir, ir.applicableNamespace())); } }, @@ -121,6 +121,30 @@ public CodeGenerator newInstance(final Ir ir, final String outputDir) } }; + /** + * Returns the configured precedence checks. + * + * @return the configured precedence checks. + */ + public static PrecedenceChecks precedenceChecks() + { + final boolean shouldGeneratePrecedenceChecks = + Boolean.parseBoolean(System.getProperty(GENERATE_PRECEDENCE_CHECKS, "false")); + + final String precedenceChecksFlagName = + System.getProperty(PRECEDENCE_CHECKS_FLAG_NAME, "SBE_ENABLE_PRECEDENCE_CHECKS"); + + final String precedenceChecksPropName = + System.getProperty(JAVA_PRECEDENCE_CHECKS_PROPERTY_NAME, "sbe.enable.precedence.checks"); + + final PrecedenceChecks.Context context = new PrecedenceChecks.Context() + .shouldGeneratePrecedenceChecks(shouldGeneratePrecedenceChecks) + .precedenceChecksFlagName(precedenceChecksFlagName) + .precedenceChecksPropName(precedenceChecksPropName); + + return PrecedenceChecks.newInstance(context); + } + /** * Do a case-insensitive lookup of a target language for code generation. * diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/PrecedenceChecks.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/PrecedenceChecks.java index 1c121b86da..d719c2a882 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/PrecedenceChecks.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/PrecedenceChecks.java @@ -15,7 +15,6 @@ */ package uk.co.real_logic.sbe.generation.common; -import uk.co.real_logic.sbe.SbeTool; import uk.co.real_logic.sbe.ir.Token; import java.util.ArrayList; @@ -161,45 +160,14 @@ private FieldPrecedenceModel createModel( return null; } - static final class Configuration - { - private static final boolean GENERATE_PRECEDENCE_CHECKS_VALUE = - Boolean.parseBoolean(System.getProperty(SbeTool.GENERATE_PRECEDENCE_CHECKS, "false")); - - private static final String PRECEDENCE_CHECKS_FLAG_NAME_VALUE = - System.getProperty(SbeTool.PRECEDENCE_CHECKS_FLAG_NAME, "SBE_ENABLE_PRECEDENCE_CHECKS"); - - private static final String PRECEDENCE_CHECKS_PROPERTY_NAME_VALUE = - System.getProperty(SbeTool.JAVA_PRECEDENCE_CHECKS_PROPERTY_NAME, "sbe.enable.precedence.checks"); - - private Configuration() - { - } - - static boolean shouldGeneratePrecedenceChecks() - { - return GENERATE_PRECEDENCE_CHECKS_VALUE; - } - - static String precedenceChecksFlagName() - { - return PRECEDENCE_CHECKS_FLAG_NAME_VALUE; - } - - static String precedenceChecksPropName() - { - return PRECEDENCE_CHECKS_PROPERTY_NAME_VALUE; - } - } - /** * The context describing how precedence checks should be generated. */ public static final class Context { - private boolean shouldGeneratePrecedenceChecks = Configuration.shouldGeneratePrecedenceChecks(); - private String precedenceChecksFlagName = Configuration.precedenceChecksFlagName(); - private String precedenceChecksPropName = Configuration.precedenceChecksPropName(); + private boolean shouldGeneratePrecedenceChecks; + private String precedenceChecksFlagName; + private String precedenceChecksPropName; /** * Returns {@code true} if precedence checks should be generated; {@code false} otherwise. diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java index c801c58057..5c24117cb8 100755 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java @@ -18,6 +18,7 @@ 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.generation.TargetCodeGeneratorLoader; import uk.co.real_logic.sbe.generation.common.FieldPrecedenceModel; import uk.co.real_logic.sbe.generation.common.PrecedenceChecks; import uk.co.real_logic.sbe.ir.Encoding; @@ -71,7 +72,7 @@ public CppGenerator(final Ir ir, final boolean shouldDecodeUnknownEnumValues, fi this( ir, shouldDecodeUnknownEnumValues, - PrecedenceChecks.newInstance(new PrecedenceChecks.Context()), + TargetCodeGeneratorLoader.precedenceChecks(), outputManager ); } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharp.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharp.java index 601fb58675..ee55d214d5 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharp.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharp.java @@ -18,7 +18,7 @@ import uk.co.real_logic.sbe.generation.CodeGenerator; import uk.co.real_logic.sbe.generation.TargetCodeGenerator; -import uk.co.real_logic.sbe.generation.common.PrecedenceChecks; +import uk.co.real_logic.sbe.generation.TargetCodeGeneratorLoader; import uk.co.real_logic.sbe.ir.Ir; /** @@ -33,7 +33,7 @@ public CodeGenerator newInstance(final Ir ir, final String outputDir) { return new CSharpGenerator( ir, - PrecedenceChecks.newInstance(new PrecedenceChecks.Context()), + TargetCodeGeneratorLoader.precedenceChecks(), new CSharpNamespaceOutputManager(outputDir, ir.applicableNamespace())); } } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java index 8fe17df71a..7ea6a168af 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java @@ -20,6 +20,7 @@ import uk.co.real_logic.sbe.PrimitiveValue; import uk.co.real_logic.sbe.generation.CodeGenerator; import uk.co.real_logic.sbe.generation.Generators; +import uk.co.real_logic.sbe.generation.TargetCodeGeneratorLoader; import uk.co.real_logic.sbe.generation.common.FieldPrecedenceModel; import uk.co.real_logic.sbe.generation.common.PrecedenceChecks; import uk.co.real_logic.sbe.ir.Encoding; @@ -71,7 +72,7 @@ public CSharpGenerator(final Ir ir, final OutputManager outputManager) { this( ir, - PrecedenceChecks.newInstance(new PrecedenceChecks.Context()), + TargetCodeGeneratorLoader.precedenceChecks(), outputManager ); } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java index 7edf2e68c3..3860cca8df 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java @@ -18,6 +18,7 @@ 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.generation.TargetCodeGeneratorLoader; import uk.co.real_logic.sbe.generation.common.FieldPrecedenceModel; import uk.co.real_logic.sbe.generation.common.PrecedenceChecks; import uk.co.real_logic.sbe.ir.*; @@ -130,7 +131,7 @@ public JavaGenerator( shouldGenerateInterfaces, shouldDecodeUnknownEnumValues, shouldSupportTypesPackageNames, - PrecedenceChecks.newInstance(new PrecedenceChecks.Context()), + TargetCodeGeneratorLoader.precedenceChecks(), outputManager ); } diff --git a/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/JavaGeneratorTest.java b/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/JavaGeneratorTest.java index 3f253ed778..9e3612d5fd 100644 --- a/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/JavaGeneratorTest.java +++ b/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/JavaGeneratorTest.java @@ -260,7 +260,10 @@ void shouldGenerateBasicMessage() throws Exception @Test void shouldGenerateWithoutPrecedenceChecksByDefault() throws Exception { - final PrecedenceChecks.Context context = new PrecedenceChecks.Context(); + final PrecedenceChecks.Context context = new PrecedenceChecks.Context() + .shouldGeneratePrecedenceChecks(false) + .precedenceChecksPropName("sbe.enable.precedence.checks") + .precedenceChecksFlagName("SBE_ENABLE_PRECEDENCE_CHECKS"); final PrecedenceChecks precedenceChecks = PrecedenceChecks.newInstance(context); generator(precedenceChecks).generate(); @@ -276,7 +279,9 @@ void shouldGenerateWithoutPrecedenceChecksByDefault() throws Exception void shouldGeneratePrecedenceChecksWhenEnabled() throws Exception { final PrecedenceChecks.Context context = new PrecedenceChecks.Context() - .shouldGeneratePrecedenceChecks(true); + .shouldGeneratePrecedenceChecks(true) + .precedenceChecksPropName("sbe.enable.precedence.checks") + .precedenceChecksFlagName("SBE_ENABLE_PRECEDENCE_CHECKS"); final PrecedenceChecks precedenceChecks = PrecedenceChecks.newInstance(context); generator(precedenceChecks).generate(); From 8796e9e2fbcd6c6055e79b5426abd25be156f7de Mon Sep 17 00:00:00 2001 From: Zach Bray Date: Tue, 5 Dec 2023 18:53:31 +0000 Subject: [PATCH 50/50] Avoid cyclic dependency between classes. Move defaults out of property evaluation and into context. --- .../generation/TargetCodeGeneratorLoader.java | 34 +++++++++++-------- .../generation/common/PrecedenceChecks.java | 4 +-- .../sbe/generation/cpp/CppGenerator.java | 3 +- .../generation/csharp/CSharpGenerator.java | 3 +- .../sbe/generation/java/JavaGenerator.java | 3 +- .../generation/java/JavaGeneratorTest.java | 9 ++--- 6 files changed, 27 insertions(+), 29 deletions(-) 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 f9da87723d..6552a72c00 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 @@ -15,15 +15,15 @@ */ package uk.co.real_logic.sbe.generation; -import uk.co.real_logic.sbe.generation.common.PrecedenceChecks; -import uk.co.real_logic.sbe.generation.java.JavaOutputManager; import uk.co.real_logic.sbe.generation.c.CGenerator; import uk.co.real_logic.sbe.generation.c.COutputManager; +import uk.co.real_logic.sbe.generation.common.PrecedenceChecks; 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.java.JavaGenerator; +import uk.co.real_logic.sbe.generation.java.JavaOutputManager; import uk.co.real_logic.sbe.generation.rust.RustGenerator; import uk.co.real_logic.sbe.generation.rust.RustOutputManager; import uk.co.real_logic.sbe.ir.Ir; @@ -122,25 +122,31 @@ public CodeGenerator newInstance(final Ir ir, final String outputDir) }; /** - * Returns the configured precedence checks. + * Returns the precedence checks to run, configured from system properties. * - * @return the configured precedence checks. + * @return the precedence checks to run, configured from system properties. */ public static PrecedenceChecks precedenceChecks() { - final boolean shouldGeneratePrecedenceChecks = - Boolean.parseBoolean(System.getProperty(GENERATE_PRECEDENCE_CHECKS, "false")); + final PrecedenceChecks.Context context = new PrecedenceChecks.Context(); - final String precedenceChecksFlagName = - System.getProperty(PRECEDENCE_CHECKS_FLAG_NAME, "SBE_ENABLE_PRECEDENCE_CHECKS"); + final String shouldGeneratePrecedenceChecks = System.getProperty(GENERATE_PRECEDENCE_CHECKS); + if (shouldGeneratePrecedenceChecks != null) + { + context.shouldGeneratePrecedenceChecks(Boolean.parseBoolean(shouldGeneratePrecedenceChecks)); + } - final String precedenceChecksPropName = - System.getProperty(JAVA_PRECEDENCE_CHECKS_PROPERTY_NAME, "sbe.enable.precedence.checks"); + final String precedenceChecksFlagName = System.getProperty(PRECEDENCE_CHECKS_FLAG_NAME); + if (precedenceChecksFlagName != null) + { + context.precedenceChecksFlagName(precedenceChecksFlagName); + } - final PrecedenceChecks.Context context = new PrecedenceChecks.Context() - .shouldGeneratePrecedenceChecks(shouldGeneratePrecedenceChecks) - .precedenceChecksFlagName(precedenceChecksFlagName) - .precedenceChecksPropName(precedenceChecksPropName); + final String precedenceChecksPropName = System.getProperty(JAVA_PRECEDENCE_CHECKS_PROPERTY_NAME); + if (precedenceChecksPropName != null) + { + context.precedenceChecksPropName(precedenceChecksPropName); + } return PrecedenceChecks.newInstance(context); } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/PrecedenceChecks.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/PrecedenceChecks.java index d719c2a882..9825a3e6b7 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/PrecedenceChecks.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/PrecedenceChecks.java @@ -166,8 +166,8 @@ private FieldPrecedenceModel createModel( public static final class Context { private boolean shouldGeneratePrecedenceChecks; - private String precedenceChecksFlagName; - private String precedenceChecksPropName; + private String precedenceChecksFlagName = "SBE_ENABLE_PRECEDENCE_CHECKS"; + private String precedenceChecksPropName = "sbe.enable.precedence.checks"; /** * Returns {@code true} if precedence checks should be generated; {@code false} otherwise. diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java index 5c24117cb8..c801c58057 100755 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java @@ -18,7 +18,6 @@ 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.generation.TargetCodeGeneratorLoader; import uk.co.real_logic.sbe.generation.common.FieldPrecedenceModel; import uk.co.real_logic.sbe.generation.common.PrecedenceChecks; import uk.co.real_logic.sbe.ir.Encoding; @@ -72,7 +71,7 @@ public CppGenerator(final Ir ir, final boolean shouldDecodeUnknownEnumValues, fi this( ir, shouldDecodeUnknownEnumValues, - TargetCodeGeneratorLoader.precedenceChecks(), + PrecedenceChecks.newInstance(new PrecedenceChecks.Context()), outputManager ); } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java index 7ea6a168af..8fe17df71a 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java @@ -20,7 +20,6 @@ import uk.co.real_logic.sbe.PrimitiveValue; import uk.co.real_logic.sbe.generation.CodeGenerator; import uk.co.real_logic.sbe.generation.Generators; -import uk.co.real_logic.sbe.generation.TargetCodeGeneratorLoader; import uk.co.real_logic.sbe.generation.common.FieldPrecedenceModel; import uk.co.real_logic.sbe.generation.common.PrecedenceChecks; import uk.co.real_logic.sbe.ir.Encoding; @@ -72,7 +71,7 @@ public CSharpGenerator(final Ir ir, final OutputManager outputManager) { this( ir, - TargetCodeGeneratorLoader.precedenceChecks(), + PrecedenceChecks.newInstance(new PrecedenceChecks.Context()), outputManager ); } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java index 3860cca8df..7edf2e68c3 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java @@ -18,7 +18,6 @@ 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.generation.TargetCodeGeneratorLoader; import uk.co.real_logic.sbe.generation.common.FieldPrecedenceModel; import uk.co.real_logic.sbe.generation.common.PrecedenceChecks; import uk.co.real_logic.sbe.ir.*; @@ -131,7 +130,7 @@ public JavaGenerator( shouldGenerateInterfaces, shouldDecodeUnknownEnumValues, shouldSupportTypesPackageNames, - TargetCodeGeneratorLoader.precedenceChecks(), + PrecedenceChecks.newInstance(new PrecedenceChecks.Context()), outputManager ); } diff --git a/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/JavaGeneratorTest.java b/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/JavaGeneratorTest.java index 9e3612d5fd..3f253ed778 100644 --- a/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/JavaGeneratorTest.java +++ b/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/JavaGeneratorTest.java @@ -260,10 +260,7 @@ void shouldGenerateBasicMessage() throws Exception @Test void shouldGenerateWithoutPrecedenceChecksByDefault() throws Exception { - final PrecedenceChecks.Context context = new PrecedenceChecks.Context() - .shouldGeneratePrecedenceChecks(false) - .precedenceChecksPropName("sbe.enable.precedence.checks") - .precedenceChecksFlagName("SBE_ENABLE_PRECEDENCE_CHECKS"); + final PrecedenceChecks.Context context = new PrecedenceChecks.Context(); final PrecedenceChecks precedenceChecks = PrecedenceChecks.newInstance(context); generator(precedenceChecks).generate(); @@ -279,9 +276,7 @@ void shouldGenerateWithoutPrecedenceChecksByDefault() throws Exception void shouldGeneratePrecedenceChecksWhenEnabled() throws Exception { final PrecedenceChecks.Context context = new PrecedenceChecks.Context() - .shouldGeneratePrecedenceChecks(true) - .precedenceChecksPropName("sbe.enable.precedence.checks") - .precedenceChecksFlagName("SBE_ENABLE_PRECEDENCE_CHECKS"); + .shouldGeneratePrecedenceChecks(true); final PrecedenceChecks precedenceChecks = PrecedenceChecks.newInstance(context); generator(precedenceChecks).generate();