From c5e5c31d872ccd63e977f4628f426bc3377b28f7 Mon Sep 17 00:00:00 2001 From: Bernard Normier Date: Fri, 15 Dec 2023 10:50:39 -0500 Subject: [PATCH] Add support for compact enum with fields (#3873) --- .../ZeroC.Slice.Tests/EnumWithFieldsTests.cs | 53 +++++++++++++++++++ .../EnumWithFieldsTests.slice | 7 +++ tools/slicec-cs/Cargo.lock | 5 +- tools/slicec-cs/Cargo.toml | 4 +- .../src/generators/enum_generator.rs | 22 ++++---- 5 files changed, 75 insertions(+), 16 deletions(-) diff --git a/tests/ZeroC.Slice.Tests/EnumWithFieldsTests.cs b/tests/ZeroC.Slice.Tests/EnumWithFieldsTests.cs index 46a7333d8..71dde7b52 100644 --- a/tests/ZeroC.Slice.Tests/EnumWithFieldsTests.cs +++ b/tests/ZeroC.Slice.Tests/EnumWithFieldsTests.cs @@ -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)] @@ -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")); diff --git a/tests/ZeroC.Slice.Tests/EnumWithFieldsTests.slice b/tests/ZeroC.Slice.Tests/EnumWithFieldsTests.slice index 5f1e515a3..b6a37a6f6 100644 --- a/tests/ZeroC.Slice.Tests/EnumWithFieldsTests.slice +++ b/tests/ZeroC.Slice.Tests/EnumWithFieldsTests.slice @@ -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) diff --git a/tools/slicec-cs/Cargo.lock b/tools/slicec-cs/Cargo.lock index 963631d7e..cbd114dcd 100644 --- a/tools/slicec-cs/Cargo.lock +++ b/tools/slicec-cs/Cargo.lock @@ -605,8 +605,7 @@ checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" [[package]] name = "slicec" version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0256bc27900a5e0862920484ac2f2e673a0346dafeab5259ba1d264469272cf6" +source = "git+https://github.com/icerpc/slicec?rev=afba8cf1cde99fdbf475aa2839c08866076d8e77#afba8cf1cde99fdbf475aa2839c08866076d8e77" dependencies = [ "clap", "console", @@ -620,7 +619,7 @@ dependencies = [ [[package]] name = "slicec-cs" -version = "0.2.0" +version = "0.3.0-preview1" dependencies = [ "clap", "convert_case", diff --git a/tools/slicec-cs/Cargo.toml b/tools/slicec-cs/Cargo.toml index a19293466..3f14fa4c8 100644 --- a/tools/slicec-cs/Cargo.toml +++ b/tools/slicec-cs/Cargo.toml @@ -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. @@ -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" diff --git a/tools/slicec-cs/src/generators/enum_generator.rs b/tools/slicec-cs/src/generators/enum_generator.rs index d2363974e..f67c029fb 100644 --- a/tools/slicec-cs/src/generators/enum_generator.rs +++ b/tools/slicec-cs/src/generators/enum_generator.rs @@ -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() { @@ -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);"); @@ -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);"); } @@ -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