Skip to content

Commit

Permalink
Handle IR changes to const enums for Rust; re-enable regression tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ZackPierce committed Jun 25, 2018
1 parent 337ef8b commit 454a117
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 23 deletions.
6 changes: 6 additions & 0 deletions rust/car_example/src/main.rs
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
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
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
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
@@ -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>

0 comments on commit 454a117

Please sign in to comment.