Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions rust/car_example/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ impl std::convert::From<CodecErr> for IoError {
fn decode_car_and_assert_expected_content(buffer: &[u8]) -> CodecResult<()> {
let (h, dec_fields) = start_decoding_car(&buffer).header()?;
assert_eq!(49u16, h.block_length);
assert_eq!(h.block_length as usize, ::std::mem::size_of::<CarFields>());
assert_eq!(1u16, h.template_id);
assert_eq!(1u16, h.schema_id);
assert_eq!(0u16, h.version);
Expand All @@ -67,6 +68,11 @@ fn decode_car_and_assert_expected_content(buffer: &[u8]) -> CodecResult<()> {
assert!(fields.extras.get_cruise_control());
assert!(fields.extras.get_sports_pack());
assert!(!fields.extras.get_sun_roof());
assert_eq!(2000, fields.engine.capacity);
assert_eq!(4, fields.engine.num_cylinders);
assert_eq!(BoostType::NITROUS, fields.engine.booster.boost_type);
assert_eq!(200, fields.engine.booster.horse_power);
println!("Static-length fields all match the expected values");

let dec_perf_figures_header = match dec_fuel_figures_header.fuel_figures_individually()? {
Either::Left(mut dec_ff_members) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,16 @@ public Token typeToken()
return typeToken;
}

public static List<NamedToken> gatherNamedFieldTokens(final List<Token> fields)
public static List<NamedToken> gatherNamedNonConstantFieldTokens(final List<Token> fields)
{
final List<NamedToken> namedTokens = new ArrayList<>();
forEachField(fields, (f, t) -> namedTokens.add(new NamedToken(f.name(), t)));
forEachField(fields, (f, t) ->
{
if (!f.isConstantEncoding())
{
namedTokens.add(new NamedToken(f.name(), t));
}
});

return namedTokens;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,11 +133,7 @@ private static Optional<FieldsRepresentationSummary> generateFieldsRepresentatio
final MessageComponents components,
final OutputManager outputManager) throws IOException
{
final List<NamedToken> namedFieldTokens = NamedToken.gatherNamedFieldTokens(components.fields);
if (namedFieldTokens.isEmpty())
{
return Optional.empty();
}
final List<NamedToken> namedFieldTokens = NamedToken.gatherNamedNonConstantFieldTokens(components.fields);

final String representationStruct = messageTypeName + "Fields";
try (Writer writer = outputManager.createOutput(messageTypeName + " Fixed-size Fields"))
Expand All @@ -149,12 +145,36 @@ private static Optional<FieldsRepresentationSummary> generateFieldsRepresentatio
generateConstantAccessorImpl(writer, representationStruct, components.fields);
}

final int numBytes = components.fields.stream()
.filter((t) -> !t.isConstantEncoding())
.filter((t) -> t.signal() == ENCODING || t.signal() == BEGIN_ENUM || t.signal() == BEGIN_SET)
.mapToInt(Token::encodedLength)
.sum();

// Compute the total static size in bytes of the fields representation
int numBytes = 0;
for (int i = 0, size = components.fields.size(); i < size;)
{
final Token fieldToken = components.fields.get(i);
if (fieldToken.signal() == Signal.BEGIN_FIELD)
{
final int fieldEnd = i + fieldToken.componentTokenCount();
if (!fieldToken.isConstantEncoding())
{
for (int j = i; j < fieldEnd; j++)
{
final Token t = components.fields.get(j);
if (t.isConstantEncoding())
{
continue;
}
if (t.signal() == ENCODING || t.signal() == BEGIN_ENUM || t.signal() == BEGIN_SET)
{
numBytes += t.encodedLength();
}
}
}
i += fieldToken.componentTokenCount();
}
else
{
throw new IllegalStateException("field tokens must include bounding BEGIN_FIELD and END_FIELD tokens");
}
}
return Optional.of(new FieldsRepresentationSummary(representationStruct, numBytes));
}

Expand Down Expand Up @@ -847,7 +867,7 @@ static class GroupTreeNode
this.blockLengthType = blockLengthType;
this.blockLength = blockLength;
this.rawFields = fields;
this.simpleNamedFields = NamedToken.gatherNamedFieldTokens(fields);
this.simpleNamedFields = NamedToken.gatherNamedNonConstantFieldTokens(fields);
this.varData = varData;

parent.ifPresent((p) -> p.addChild(this));
Expand Down Expand Up @@ -927,7 +947,7 @@ String generateVarDataEncoder(
indent(writer, 3).append("return Err(CodecErr::SliceIsLongerThanAllowedBySchema)\n");
indent(writer, 2).append("}\n");
indent(writer, 2).append("// Write data length\n");
indent(writer, 2, "%s.write_type::<%s>(&(l as %s), %s); // group length\n",
indent(writer, 2, "%s.write_type::<%s>(&(l as %s), %s)?; // group length\n",
toScratchChain(groupDepth), rustTypeName(this.lengthType), rustTypeName(this.lengthType),
this.lengthType.size());
indent(writer, 2).append(format("%s.write_slice_without_count::<%s>(s, %s)?;\n",
Expand Down Expand Up @@ -1548,22 +1568,27 @@ private static void generateConstantAccessorImpl(

case BEGIN_ENUM:
final String enumType = formatTypeName(signalToken.applicableTypeName());
String enumValue = null;
final String rawConstValueName = fieldToken.encoding().constValue().toString();
final int indexOfDot = rawConstValueName.indexOf('.');
final String constValueName = -1 == indexOfDot ?
rawConstValueName : rawConstValueName.substring(indexOfDot + 1);
boolean foundMatchingValueName = false;
for (int j = i; j < unfilteredFields.size(); j++)
{
final Token searchAhead = unfilteredFields.get(j);
if (searchAhead.signal() == VALID_VALUE)
if (searchAhead.signal() == VALID_VALUE && searchAhead.name().equals(constValueName))
{
enumValue = searchAhead.name();
foundMatchingValueName = true;
break;
}
}
if (enumValue == null)
if (!foundMatchingValueName)
{
throw new IllegalStateException("Found a constant enum field with incomplete token content");
throw new IllegalStateException(format("Found a constant enum field that requested value %s, " +
"which is not an available enum option.", rawConstValueName));
}
constantRustTypeName = enumType;
constantRustExpression = enumType + "::" + enumValue;
constantRustExpression = enumType + "::" + constValueName;
break;

case BEGIN_SET:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,11 @@ public void fullGenerateBroadUseCase() throws IOException, InterruptedException
{
final String generatedRust = fullGenerateForResource(outputManager, "example-schema");
assertContainsSharedImports(generatedRust);
assertContains(generatedRust,
"pub fn car_fields(mut self) -> CodecResult<(&'d CarFields, CarFuelFiguresHeaderDecoder<'d>)> {\n" +
" let v = self.scratch.read_type::<CarFields>(49)?;\n" +
" Ok((v, CarFuelFiguresHeaderDecoder::wrap(self.scratch)))\n" +
" }");
final String expectedBooleanTypeDeclaration =
"#[derive(Clone,Copy,Debug,PartialEq,Eq,PartialOrd,Ord,Hash)]\n" +
"#[repr(u8)]\n" +
Expand Down Expand Up @@ -236,7 +241,6 @@ private void assertSchemaInterpretableAsRust(final String localResourceSchema)
assertRustBuildable(rust, Optional.of(localResourceSchema));
}

@Ignore
@Test
public void checkValidRustFromAllExampleSchema() throws IOException, InterruptedException
{
Expand Down Expand Up @@ -267,7 +271,36 @@ public void checkValidRustFromAllExampleSchema() throws IOException, Interrupted
}
}

@Ignore
@Test
public void constantEnumFields() throws IOException, InterruptedException
{
final String rust = fullGenerateForResource(outputManager, "constant-enum-fields");
assertContainsSharedImports(rust);
final String expectedCharTypeDeclaration =
"#[derive(Clone,Copy,Debug,PartialEq,Eq,PartialOrd,Ord,Hash)]\n" +
"#[repr(i8)]\n" +
"pub enum Model {\n" +
" A = 65i8,\n" +
" B = 66i8,\n" +
" C = 67i8,\n" +
"}\n";
assertContains(rust, expectedCharTypeDeclaration);
assertContains(rust, "pub struct ConstantEnumsFields {\n}");
assertContains(rust, "impl ConstantEnumsFields {");
assertContains(rust, " pub fn c() -> Model {\n" +
" Model::C\n }");
assertContains(rust, "impl ConstantEnumsFMember {");
assertContains(rust, " pub fn k() -> Model {\n" +
" Model::C\n }");
assertContains(rust,
"pub fn constant_enums_fields(mut self) -> " +
"CodecResult<(&'d ConstantEnumsFields, ConstantEnumsFHeaderDecoder<'d>)> {\n" +
" let v = self.scratch.read_type::<ConstantEnumsFields>(0)?;\n" +
" Ok((v, ConstantEnumsFHeaderDecoder::wrap(self.scratch)))\n" +
" }");
assertRustBuildable(rust, Optional.of("constant-enum-fields"));
}

@Test
public void constantFieldsCase() throws IOException, InterruptedException
{
Expand Down
34 changes: 34 additions & 0 deletions sbe-tool/src/test/resources/constant-enum-fields.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<sbe:messageSchema xmlns:sbe="http://fixprotocol.io/2016/sbe"
package="baseline"
id="1"
version="0"
semanticVersion="5.2"
description="Enum Constants as top-level and group field"
byteOrder="littleEndian">
<types>
<composite name="messageHeader" description="Message identifiers and length of message root">
<type name="blockLength" primitiveType="uint16"/>
<type name="templateId" primitiveType="uint16"/>
<type name="schemaId" primitiveType="uint16"/>
<type name="version" primitiveType="uint16"/>
</composite>
<composite name="groupSizeEncoding" description="Repeating group dimensions">
<type name="blockLength" primitiveType="uint16"/>
<type name="numInGroup" primitiveType="uint16"/>
</composite>
</types>
<types>
<enum name="Model" encodingType="char">
<validValue name="A">A</validValue>
<validValue name="B">B</validValue>
<validValue name="C">C</validValue>
</enum>
</types>
<sbe:message name="ConstantEnums" id="1" description="">
<field name="c" id="4" type="Model" presence="constant" valueRef="Model.C"/>
<group name="f" id="7" dimensionType="groupSizeEncoding">
<field name="k" id="12" type="Model" presence="constant" valueRef="Model.C"/>
</group>
</sbe:message>
</sbe:messageSchema>