Skip to content

Commit dd7f084

Browse files
authored
[Java] Generate bulk put/get/wrap methods for fixed-length data (uint8 array) (#819)
* [Java] Generate bulk put/get/wrap methods for fixed-length data (uint8 array) * [Java] Separated tests for encoding/decoding fixed-length data and use different schema
1 parent 14a12c2 commit dd7f084

File tree

6 files changed

+494
-8
lines changed

6 files changed

+494
-8
lines changed

sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java

Lines changed: 107 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -981,7 +981,7 @@ private void generateVarDataWrapDecoder(
981981
indent + " }\n",
982982
propertyName,
983983
readOnlyBuffer,
984-
generateVarWrapFieldNotPresentCondition(token.version(), indent),
984+
generateWrapFieldNotPresentCondition(token.version(), indent),
985985
sizeOfLengthField,
986986
PrimitiveType.UINT32 == lengthType ? "(int)" : "",
987987
generateGet(lengthType, "limit", byteOrderStr));
@@ -1860,7 +1860,7 @@ private CharSequence generatePrimitivePropertyEncode(
18601860
generatePut(encoding.primitiveType(), "offset + " + offset, "value", byteOrderStr));
18611861
}
18621862

1863-
private CharSequence generateVarWrapFieldNotPresentCondition(final int sinceVersion, final String indent)
1863+
private CharSequence generateWrapFieldNotPresentCondition(final int sinceVersion, final String indent)
18641864
{
18651865
if (0 == sinceVersion)
18661866
{
@@ -1871,6 +1871,7 @@ private CharSequence generateVarWrapFieldNotPresentCondition(final int sinceVers
18711871
indent + " if (parentMessage.actingVersion < " + sinceVersion + ")\n" +
18721872
indent + " {\n" +
18731873
indent + " wrapBuffer.wrap(buffer, offset, 0);\n" +
1874+
indent + " return;\n" +
18741875
indent + " }\n\n";
18751876
}
18761877

@@ -2060,6 +2061,51 @@ private CharSequence generatePrimitiveArrayPropertyDecode(
20602061
offset);
20612062
}
20622063
}
2064+
else if (encoding.primitiveType() == PrimitiveType.UINT8)
2065+
{
2066+
new Formatter(sb).format("\n" +
2067+
indent + " public int get%s(final byte[] dst, final int dstOffset, final int length)\n" +
2068+
indent + " {\n" +
2069+
"%s" +
2070+
indent + " final int bytesCopied = Math.min(length, %d);\n" +
2071+
indent + " buffer.getBytes(offset + %d, dst, dstOffset, bytesCopied);\n" +
2072+
indent + " return bytesCopied;\n" +
2073+
indent + " }\n",
2074+
Generators.toUpperFirstChar(propertyName),
2075+
generateArrayFieldNotPresentCondition(propertyToken.version(), indent),
2076+
fieldLength,
2077+
offset
2078+
);
2079+
2080+
new Formatter(sb).format("\n" +
2081+
indent + " public int get%s(final %s dst, final int dstOffset, final int length)\n" +
2082+
indent + " {\n" +
2083+
"%s" +
2084+
indent + " final int bytesCopied = Math.min(length, %d);\n" +
2085+
indent + " buffer.getBytes(offset + %d, dst, dstOffset, bytesCopied);\n" +
2086+
indent + " return bytesCopied;\n" +
2087+
indent + " }\n",
2088+
Generators.toUpperFirstChar(propertyName),
2089+
fqMutableBuffer,
2090+
generateArrayFieldNotPresentCondition(propertyToken.version(), indent),
2091+
fieldLength,
2092+
offset
2093+
);
2094+
2095+
new Formatter(sb).format("\n" +
2096+
indent + " public void wrap%s(final %s wrapBuffer)\n" +
2097+
indent + " {\n" +
2098+
indent + " final int length = %d;\n" +
2099+
"%s" +
2100+
indent + " wrapBuffer.wrap(buffer, offset + %d, length);\n" +
2101+
indent + " }\n",
2102+
Generators.toUpperFirstChar(propertyName),
2103+
readOnlyBuffer,
2104+
fieldLength,
2105+
generateWrapFieldNotPresentCondition(propertyToken.version(), indent),
2106+
offset
2107+
);
2108+
}
20632109

20642110
return sb;
20652111
}
@@ -2150,6 +2196,11 @@ private CharSequence generatePrimitiveArrayPropertyEncode(
21502196
generateCharArrayEncodeMethods(
21512197
containingClassName, propertyName, indent, encoding, offset, arrayLength, sb);
21522198
}
2199+
else if (primitiveType == PrimitiveType.UINT8)
2200+
{
2201+
generateByteArrayEncodeMethods(
2202+
containingClassName, propertyName, indent, offset, arrayLength, sb);
2203+
}
21532204

21542205
return sb;
21552206
}
@@ -2261,6 +2312,60 @@ private void generateCharArrayEncodeMethods(
22612312
}
22622313
}
22632314

2315+
private void generateByteArrayEncodeMethods(
2316+
final String containingClassName,
2317+
final String propertyName,
2318+
final String indent,
2319+
final int offset,
2320+
final int fieldLength,
2321+
final StringBuilder sb)
2322+
{
2323+
new Formatter(sb).format("\n" +
2324+
indent + " public %s put%s(final byte[] src, final int srcOffset, final int length)\n" +
2325+
indent + " {\n" +
2326+
indent + " if (length > %d)\n" +
2327+
indent + " {\n" +
2328+
indent + " throw new IllegalStateException(" +
2329+
"\"length > maxValue for type: \" + length);" +
2330+
indent + " }\n\n" +
2331+
indent + " buffer.putBytes(offset + %d, src, srcOffset, length);\n" +
2332+
indent + " for (int i = length; i < %d; i++)\n" +
2333+
indent + " {\n" +
2334+
indent + " buffer.putByte(offset + %d + i, (byte)0);\n" +
2335+
indent + " }\n" +
2336+
indent + " return this;\n" +
2337+
indent + " }\n",
2338+
formatClassName(containingClassName),
2339+
Generators.toUpperFirstChar(propertyName),
2340+
fieldLength,
2341+
offset,
2342+
fieldLength,
2343+
offset);
2344+
2345+
new Formatter(sb).format("\n" +
2346+
indent + " public %s put%s(final %s src, final int srcOffset, final int length)\n" +
2347+
indent + " {\n" +
2348+
indent + " if (length > %d)\n" +
2349+
indent + " {\n" +
2350+
indent + " throw new IllegalStateException(" +
2351+
"\"length > maxValue for type: \" + length);" +
2352+
indent + " }\n\n" +
2353+
indent + " buffer.putBytes(offset + %d, src, srcOffset, length);\n" +
2354+
indent + " for (int i = length; i < %d; i++)\n" +
2355+
indent + " {\n" +
2356+
indent + " buffer.putByte(offset + %d + i, (byte)0);\n" +
2357+
indent + " }\n" +
2358+
indent + " return this;\n" +
2359+
indent + " }\n",
2360+
formatClassName(containingClassName),
2361+
Generators.toUpperFirstChar(propertyName),
2362+
fqReadOnlyBuffer,
2363+
fieldLength,
2364+
offset,
2365+
fieldLength,
2366+
offset);
2367+
}
2368+
22642369
private static int sizeOfPrimitive(final Encoding encoding)
22652370
{
22662371
return encoding.primitiveType().size();
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
/*
2+
* Copyright 2013-2020 Real Logic Limited.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package uk.co.real_logic.sbe.generation.java;
17+
18+
import org.agrona.DirectBuffer;
19+
import org.agrona.MutableDirectBuffer;
20+
import org.agrona.concurrent.UnsafeBuffer;
21+
import org.agrona.generation.CompilerUtil;
22+
import org.agrona.generation.StringWriterOutputManager;
23+
import org.junit.jupiter.api.BeforeEach;
24+
import org.junit.jupiter.api.Test;
25+
import uk.co.real_logic.sbe.Tests;
26+
import uk.co.real_logic.sbe.ir.Ir;
27+
import uk.co.real_logic.sbe.xml.IrGenerator;
28+
import uk.co.real_logic.sbe.xml.MessageSchema;
29+
import uk.co.real_logic.sbe.xml.ParserOptions;
30+
31+
import java.nio.charset.StandardCharsets;
32+
import java.util.Map;
33+
34+
import static org.hamcrest.MatcherAssert.assertThat;
35+
import static org.hamcrest.Matchers.*;
36+
import static uk.co.real_logic.sbe.generation.java.ReflectionUtil.*;
37+
import static uk.co.real_logic.sbe.xml.XmlSchemaParser.parse;
38+
39+
public class FixedSizeDataGeneratorTest
40+
{
41+
private static final Class<?> BUFFER_CLASS = MutableDirectBuffer.class;
42+
private static final String BUFFER_NAME = BUFFER_CLASS.getName();
43+
private static final Class<DirectBuffer> READ_ONLY_BUFFER_CLASS = DirectBuffer.class;
44+
private static final String READ_ONLY_BUFFER_NAME = READ_ONLY_BUFFER_CLASS.getName();
45+
46+
private final StringWriterOutputManager outputManager = new StringWriterOutputManager();
47+
48+
private Ir ir;
49+
50+
@BeforeEach
51+
public void setUp() throws Exception
52+
{
53+
final ParserOptions options = ParserOptions.builder().stopOnError(true).build();
54+
final MessageSchema schema = parse(Tests.getLocalResource("extension-schema.xml"), options);
55+
final IrGenerator irg = new IrGenerator();
56+
ir = irg.generate(schema);
57+
58+
outputManager.clear();
59+
outputManager.setPackageName(ir.applicableNamespace());
60+
61+
generator().generate();
62+
}
63+
64+
@Test
65+
public void shouldGeneratePutAndGetByteArrayForFixedLengthBlob() throws Exception
66+
{
67+
final UnsafeBuffer buffer = new UnsafeBuffer(new byte[4096]);
68+
69+
final Object encoder = getTestMessage2Encoder(buffer);
70+
final Object decoder = getTestMessage2Decoder(buffer, encoder);
71+
72+
final byte[] encodedData = " **DATA** ".getBytes(StandardCharsets.US_ASCII);
73+
byte[] decodedData;
74+
int decodedDataLength;
75+
76+
// Every byte written, every byte read
77+
putByteArray(encoder, "putTag6", encodedData, 2, 8);
78+
decodedData = " ".getBytes(StandardCharsets.US_ASCII);
79+
decodedDataLength = getByteArray(decoder, "getTag6", decodedData, 1, 8);
80+
assertThat(decodedDataLength, is(8));
81+
assertThat(new String(decodedData, StandardCharsets.US_ASCII), is(" **DATA** "));
82+
83+
// Every byte written, less bytes read
84+
putByteArray(encoder, "putTag6", encodedData, 2, 8);
85+
decodedData = " ".getBytes(StandardCharsets.US_ASCII);
86+
decodedDataLength = getByteArray(decoder, "getTag6", decodedData, 1, 6);
87+
assertThat(decodedDataLength, is(6));
88+
assertThat(new String(decodedData, StandardCharsets.US_ASCII), is(" **DATA "));
89+
90+
// Less bytes written (padding), every byte read
91+
putByteArray(encoder, "putTag6", encodedData, 2, 6);
92+
decodedData = " ".getBytes(StandardCharsets.US_ASCII);
93+
decodedDataLength = getByteArray(decoder, "getTag6", decodedData, 1, 8);
94+
assertThat(decodedDataLength, is(8));
95+
assertThat(new String(decodedData, StandardCharsets.US_ASCII), is(" **DATA\u0000\u0000 "));
96+
}
97+
98+
@Test
99+
public void shouldGeneratePutAndGetDirectBufferForFixedLengthBlob() throws Exception
100+
{
101+
final UnsafeBuffer buffer = new UnsafeBuffer(new byte[4096]);
102+
103+
final Object encoder = getTestMessage2Encoder(buffer);
104+
final Object decoder = getTestMessage2Decoder(buffer, encoder);
105+
106+
final UnsafeBuffer encodedData = new UnsafeBuffer(" **DATA** ".getBytes(StandardCharsets.US_ASCII));
107+
UnsafeBuffer decodedData;
108+
int decodedDataLength;
109+
110+
// Every byte written, every byte read
111+
putDirectBuffer(encoder, "putTag6", encodedData, 2, 8);
112+
decodedData = new UnsafeBuffer(" ".getBytes(StandardCharsets.US_ASCII));
113+
decodedDataLength = getDirectBuffer(decoder, "getTag6", decodedData, 1, 8);
114+
assertThat(decodedDataLength, is(8));
115+
assertThat(decodedData.getStringWithoutLengthAscii(0, 12), is(" **DATA** "));
116+
117+
// Every byte written, less bytes read
118+
putDirectBuffer(encoder, "putTag6", encodedData, 2, 8);
119+
decodedData = new UnsafeBuffer(" ".getBytes(StandardCharsets.US_ASCII));
120+
decodedDataLength = getDirectBuffer(decoder, "getTag6", decodedData, 1, 6);
121+
assertThat(decodedDataLength, is(6));
122+
assertThat(decodedData.getStringWithoutLengthAscii(0, 12), is(" **DATA "));
123+
124+
// Less bytes written (padding), every byte read
125+
putDirectBuffer(encoder, "putTag6", encodedData, 2, 6);
126+
decodedData = new UnsafeBuffer(" ".getBytes(StandardCharsets.US_ASCII));
127+
decodedDataLength = getDirectBuffer(decoder, "getTag6", decodedData, 1, 8);
128+
assertThat(decodedDataLength, is(8));
129+
assertThat(decodedData.getStringWithoutLengthAscii(0, 12), is(" **DATA\u0000\u0000 "));
130+
}
131+
132+
@Test
133+
public void shouldGenerateWrapForFixedLengthBlob() throws Exception
134+
{
135+
final UnsafeBuffer buffer = new UnsafeBuffer(new byte[4096]);
136+
137+
final Object encoder = getTestMessage2Encoder(buffer);
138+
final Object decoder = getTestMessage2Decoder(buffer, encoder);
139+
140+
final UnsafeBuffer encodedData = new UnsafeBuffer(" **DATA** ".getBytes(StandardCharsets.US_ASCII));
141+
putDirectBuffer(encoder, "putTag6", encodedData, 2, 8);
142+
143+
final UnsafeBuffer decodedData = new UnsafeBuffer();
144+
wrapDirectBuffer(decoder, "wrapTag6", decodedData);
145+
assertThat(decodedData.getStringWithoutLengthAscii(0, decodedData.capacity()), is("**DATA**"));
146+
}
147+
148+
private JavaGenerator generator()
149+
{
150+
return new JavaGenerator(ir, BUFFER_NAME, READ_ONLY_BUFFER_NAME, false, false, false, outputManager);
151+
}
152+
153+
private Class<?> compile(final String className) throws Exception
154+
{
155+
final String fqClassName = ir.applicableNamespace() + "." + className;
156+
final Map<String, CharSequence> sources = outputManager.getSources();
157+
final Class<?> aClass = CompilerUtil.compileInMemory(fqClassName, sources);
158+
if (aClass == null)
159+
{
160+
System.out.println(sources);
161+
}
162+
163+
return aClass;
164+
}
165+
166+
private Object getTestMessage2Encoder(final UnsafeBuffer buffer) throws Exception
167+
{
168+
final Object encoder = compile("TestMessage2Encoder").getConstructor().newInstance();
169+
return wrap(0, encoder, buffer, BUFFER_CLASS);
170+
}
171+
172+
private Object getTestMessage2Decoder(final UnsafeBuffer buffer, final Object encoder) throws Exception
173+
{
174+
final Object decoder = compile("TestMessage2Decoder").getConstructor().newInstance();
175+
return wrap(buffer, decoder, getSbeBlockLength(encoder), getSbeSchemaVersion(encoder));
176+
}
177+
178+
private static Object wrap(
179+
final UnsafeBuffer buffer, final Object decoder, final int blockLength, final int version) throws Exception
180+
{
181+
decoder
182+
.getClass()
183+
.getMethod("wrap", READ_ONLY_BUFFER_CLASS, int.class, int.class, int.class)
184+
.invoke(decoder, buffer, 0, blockLength, version);
185+
186+
return decoder;
187+
}
188+
189+
private static Object wrap(
190+
final int bufferOffset, final Object flyweight, final MutableDirectBuffer buffer, final Class<?> bufferClass)
191+
throws Exception
192+
{
193+
flyweight
194+
.getClass()
195+
.getDeclaredMethod("wrap", bufferClass, int.class)
196+
.invoke(flyweight, buffer, bufferOffset);
197+
return flyweight;
198+
}
199+
200+
}

sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/JavaGeneratorTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,7 @@ public void shouldGeneratePutCharSequence() throws Exception
442442
assertThat(get(decoder, "vehicleCode"), is("R11R12"));
443443
}
444444

445+
445446
private Class<?> getModelClass(final Object encoder) throws ClassNotFoundException
446447
{
447448
final String className = "Model";

0 commit comments

Comments
 (0)