Skip to content

Commit

Permalink
Add support for compact enum with fields (#3873)
Browse files Browse the repository at this point in the history
  • Loading branch information
bernardnormier authored Dec 15, 2023
1 parent d98ae40 commit c5e5c31
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 16 deletions.
53 changes: 53 additions & 0 deletions tests/ZeroC.Slice.Tests/EnumWithFieldsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ public void Decode_enum_ignores_unknown_tagged_fields()
// Assert
Assert.That(decoded, Is.InstanceOf(typeof(Color.Blue)));
Assert.That(decoder.Consumed, Is.EqualTo(encoder.EncodedByteCount));

// 1 for the discriminant, 1 for the tag, 1 for the tagged value size, 2 for code, 1 for the tag end marker
Assert.That(decoder.Consumed, Is.EqualTo(1 + 1 + 1 + 2 + 1));
}

[TestCase("canary", (ushort)7)]
Expand Down Expand Up @@ -112,6 +115,56 @@ public void Decode_enum_with_optional_field(string? name, uint major, uint minor
Assert.That(decoder.Consumed, Is.EqualTo(encoder.EncodedByteCount));
}

[Test]
public void Decode_compact_enum()
{
// Arrange
var buffer = new MemoryBufferWriter(new byte[256]);
var encoder = new SliceEncoder(buffer, SliceEncoding.Slice2);
var shape = new CompactShape.Rectangle(10, 20);
encoder.EncodeCompactShape(shape);

var decoder = new SliceDecoder(buffer.WrittenMemory, SliceEncoding.Slice2);

// Act
var decoded = decoder.DecodeCompactShape();

// Assert
Assert.That(decoded, Is.EqualTo(shape));
Assert.That(decoder.Consumed, Is.EqualTo(encoder.EncodedByteCount));
}

[TestCase("foo", 8u, 4u)]
[TestCase(null, 7u, 3u)]
public void Decode_compact_enum_with_optional_field(string? name, uint major, uint minor)
{
// Arrange
var buffer = new MemoryBufferWriter(new byte[256]);
var encoder = new SliceEncoder(buffer, SliceEncoding.Slice2);
var shape = new CompactShape.Oval(name, major, minor);
encoder.EncodeCompactShape(shape);

var decoder = new SliceDecoder(buffer.WrittenMemory, SliceEncoding.Slice2);

// Act
var decoded = decoder.DecodeCompactShape();

// Assert
Assert.That(decoded, Is.EqualTo(shape));
Assert.That(decoder.Consumed, Is.EqualTo(encoder.EncodedByteCount));
if (name is null)
{
// 1 for the discriminant, 1 for the bit sequence, 4 for major, 4 for minor
Assert.That(decoder.Consumed, Is.EqualTo(1 + 1 + 4 + 4));
}
else
{
// 1 for the discriminant, 1 for the bit sequence, 1 for the name length (assuming small length),
// name.Length bytes (assuming ASCII), 4 for major, 4 for minor
Assert.That(decoder.Consumed, Is.EqualTo(1 + 1 + 1 + name.Length + 4 + 4));
}
}

[Test]
public void Enumerator_with_fields_gets_attribute() =>
Assert.That(typeof(ShapeWithAttribute.Rectangle).GetSliceTypeId(), Is.EqualTo("MyRectangle"));
Expand Down
7 changes: 7 additions & 0 deletions tests/ZeroC.Slice.Tests/EnumWithFieldsTests.slice
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ unchecked enum RevisedShape {
Oval(name: string?, major: uint32, minor: uint32)
}

compact enum CompactShape {
Circle(radius: uint32)
Rectangle(width: uint32, height: uint32)
Triangle(side1: uint32, side2: uint32, side3: uint32)
Oval(name: string?, major: uint32, minor: uint32)
}

enum ShapeWithAttribute {
[cs::attribute("SliceTypeId(\"MyCircle\")")]
Circle(radius: uint32)
Expand Down
5 changes: 2 additions & 3 deletions tools/slicec-cs/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions tools/slicec-cs/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "slicec-cs"
version = "0.2.0"
version = "0.3.0-preview1"
authors = ["ZeroC Inc."]
description = """
The slicec-cs compiler, for compiling Slice files into C# code.
Expand All @@ -14,7 +14,7 @@ rust-version = "1.70"
clap = { version = "4.3.15", features = ["derive"] }
convert_case = "0.6.0"
in_definite = "1.0.0"
slicec = "0.2.1"
slicec = { git = "https://github.com/icerpc/slicec", rev = "afba8cf1cde99fdbf475aa2839c08866076d8e77" }

[dev-dependencies]
test-case = "3.3.1"
Expand Down
22 changes: 11 additions & 11 deletions tools/slicec-cs/src/generators/enum_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ fn enumerators_as_nested_records(enum_def: &Enum) -> CodeBlock {
.add_comments(enumerator.formatted_doc_comment_seealso())
.add_obsolete_attribute(enumerator)
.add_base(enum_def.escape_identifier())
.add_fields(&enumerator.associated_fields().unwrap_or_default());
.add_fields(&enumerator.fields());

// Add cs::attribute
for attribute in enumerator.cs_attributes() {
Expand All @@ -159,9 +159,7 @@ fn enumerators_as_nested_records(enum_def: &Enum) -> CodeBlock {
code.writeln("encoder.EncodeVarInt32(Discriminant);");

if enum_def.is_unchecked {
// TODO: oddly enough, an enumerator declared as E() or E does not result in the same
// associated fields.
if enumerator.associated_fields().unwrap_or_default().is_empty() {
if enumerator.fields().is_empty() {
// For the tag end marker. This assumes an unchecked enum with associated fields is never
// compact.
code.writeln("encoder.EncodeSize(1);");
Expand All @@ -172,16 +170,17 @@ fn enumerators_as_nested_records(enum_def: &Enum) -> CodeBlock {
}

code.writeln(&encode_fields(
&enumerator.associated_fields().unwrap_or_default(),
&enumerator.fields(),
&namespace,
FieldType::NonMangled,
Encoding::Slice2,
));

// TODO: only for non-compact enum
code.writeln("encoder.EncodeVarInt32(Slice2Definitions.TagEndMarker);");
if !enum_def.is_compact {
code.writeln("encoder.EncodeVarInt32(Slice2Definitions.TagEndMarker);");
}

if enum_def.is_unchecked && !enumerator.associated_fields().unwrap_or_default().is_empty() {
if enum_def.is_unchecked && !enumerator.fields().is_empty() {
code.writeln("SliceEncoder.EncodeVarUInt62((ulong)(encoder.EncodedByteCount - startPos), sizePlaceholder);");
}

Expand Down Expand Up @@ -520,15 +519,16 @@ fn enum_decoder_extensions(enum_def: &Enum) -> CodeBlock {
}

code.writeln(&decode_enum_fields(
&enumerator.associated_fields().unwrap_or_default(),
&enumerator.fields(),
&decoded_type,
&namespace,
FieldType::NonMangled,
Encoding::Slice2,
));

// TODO: only for non-compact enum
code.writeln("decoder.SkipTagged();");
if !enum_def.is_compact {
code.writeln("decoder.SkipTagged();");
}

code.writeln("return result;");
code
Expand Down

0 comments on commit c5e5c31

Please sign in to comment.