Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Crc32 built-in extension, serialization exception "Reverse binding not allowed on FieldValue attributes" when used on Subtype field combined with SerializeWhen attribute #223

Open
abrasat opened this issue Jul 14, 2023 · 6 comments

Comments

@abrasat
Copy link

abrasat commented Jul 14, 2023

Is it possible to use the Crc32 built-in extension to calculate the checksum over a subtype field? For example in this class from PcapNgNet, how can a new field be added to calculate the Crc32 for the "BlockBody Body" field?

public class Block
{
    [FieldOrder(0)]
    public BlockType Type { get; set; }

    [FieldOrder(1)]
    public int Length { get; set; }

    [FieldOrder(2)]
    [FieldAlignment(4)]
    [FieldLength("Length", ConverterType = typeof(SummingValueConverter), ConverterParameter = -12)]
    [FieldLength("Length2", ConverterType = typeof(SummingValueConverter), ConverterParameter = -12,
        BindingMode = BindingMode.OneWayToSource)]
    [Subtype("Type", BlockType.SectionHeader, typeof(SectionHeaderBlockBody))]
    [Subtype("Type", BlockType.InterfaceDescrption, typeof(InterfaceDescriptionBlockBody))]
    [Subtype("Type", BlockType.EnhancedPacket, typeof(EnhancedPacketBlockBody))]
    [Subtype("Type", BlockType.SimplePacket, typeof(SimplePacketBlockBody))]
    [Subtype("Type", BlockType.NameResolution, typeof(NameResolutionBlockBody))]
    [Subtype("Type", BlockType.InterfaceStatistics, typeof(InterfaceStatisticsBlockBody))]
    [SubtypeDefault(typeof(UnknownBlockBody))]
    public BlockBody Body { get; set; }

    [FieldOrder(3)]
    public int Length2 { get; set; }
}
@jefffhaynes
Copy link
Owner

It should work. Is it not?

@abrasat
Copy link
Author

abrasat commented Jul 14, 2023

It works only if the Crc property is placed after the Subtype field. If I put it before (as I need in my application), it throws this exception:

Error serializing member 'Crc'. See inner exception for detail.

InvalidOperationException: Reverse binding not allowed on FieldValue attributes. Consider swapping source and target.

@jefffhaynes
Copy link
Owner

Yes, crc fields cannot appear before the data they are checking, as this is not strictly serialization.

@abrasat
Copy link
Author

abrasat commented Jul 14, 2023

Any other way to solve my problem? Maybe using a ValueConverter?

@abrasat
Copy link
Author

abrasat commented Aug 24, 2023

I tried to use the Crc32 built-in with my example using SerializeWhen and Subtypes (see #225 ). This is the code modification that I did:

    public class ValueDataInfo
    {
        [FieldOrder(0)]
        public ValueBlockType BlockType { get; set; } = ValueBlockType.PlainValue;

        [FieldOrder(1)]
        public UInt32 ParameterId { get; set; }

        [FieldOrder(2)]
        [FieldLength(8)]
        public string Name { get; set; }

        [FieldOrder(3)]
        public UInt32 NrValues { get; set; }

        [FieldOrder(4)]
        public ValueDataType DataTypeId { get; set; } = ValueDataType.Datatype_Invalid;

        [FieldOrder(5)]
        [SerializeWhen(nameof(BlockType), ValueBlockType.PlainValue)]
        [Subtype(nameof(DataTypeId), ValueDataType.Datatype_Double, typeof(DoublePlainValuesDataBody))]
        [Subtype(nameof(DataTypeId), ValueDataType.Datatype_Float, typeof(FloatPlainValuesDataBody))]
        [Subtype(nameof(DataTypeId), ValueDataType.Datatype_Int16, typeof(Int16PlainValuesDataBody))]
        [Subtype(nameof(DataTypeId), ValueDataType.Datatype_Int32, typeof(Int32PlainValuesDataBody))]
        [SubtypeDefault(typeof(EmptyPlainValueDataBlock))]
        [FieldCrc32(nameof(Crc))]
        public PlainValueDataBlock Block { get; set; }

        [FieldOrder(6)]
        [SerializeWhen(nameof(BlockType), ValueBlockType.ValueWithDescriptor)]
        [Subtype(nameof(DataTypeId), ValueDataType.Datatype_Double, typeof(DoubleValuesWithDescriptorDataBody))]
        [Subtype(nameof(DataTypeId), ValueDataType.Datatype_Float, typeof(FloatValuesWithDescriptorDataBody))]
        [Subtype(nameof(DataTypeId), ValueDataType.Datatype_Int16, typeof(Int16ValuesWithDescriptorDataBody))]
        [Subtype(nameof(DataTypeId), ValueDataType.Datatype_Int32, typeof(Int32ValuesWithDescriptorDataBody))]
        [SubtypeDefault(typeof(EmptyDescriptorDataBlock))]
        [FieldCrc32(nameof(Crc))]
        public ValueWithDescriptorDataBlock DescriptorBlock { get; set; }

        [FieldOrder(7)]
        public ulong Crc { get; set; }
    }

The following exception is thrown:

Error serializing member 'Crc'. See inner exception for detail.
Inner exception: "Reverse binding not allowed on FieldValue attributes. Consider swapping source and target."

If I put the Crc32 attribute only on the first SerializeWhen option it seems to work, no excepion is generated:

...
        [FieldOrder(5)]
        [SerializeWhen(nameof(BlockType), ValueBlockType.PlainValue)]
        [Subtype(nameof(DataTypeId), ValueDataType.Datatype_Double, typeof(DoublePlainValuesDataBody))]
        [Subtype(nameof(DataTypeId), ValueDataType.Datatype_Float, typeof(FloatPlainValuesDataBody))]
        [Subtype(nameof(DataTypeId), ValueDataType.Datatype_Int16, typeof(Int16PlainValuesDataBody))]
        [Subtype(nameof(DataTypeId), ValueDataType.Datatype_Int32, typeof(Int32PlainValuesDataBody))]
        [SubtypeDefault(typeof(EmptyPlainValueDataBlock))]
        [FieldCrc32(nameof(Crc))]
        public PlainValueDataBlock Block { get; set; }

        [FieldOrder(6)]
        [SerializeWhen(nameof(BlockType), ValueBlockType.ValueWithDescriptor)]
        [Subtype(nameof(DataTypeId), ValueDataType.Datatype_Double, typeof(DoubleValuesWithDescriptorDataBody))]
        [Subtype(nameof(DataTypeId), ValueDataType.Datatype_Float, typeof(FloatValuesWithDescriptorDataBody))]
        [Subtype(nameof(DataTypeId), ValueDataType.Datatype_Int16, typeof(Int16ValuesWithDescriptorDataBody))]
        [Subtype(nameof(DataTypeId), ValueDataType.Datatype_Int32, typeof(Int32ValuesWithDescriptorDataBody))]
        [SubtypeDefault(typeof(EmptyDescriptorDataBlock))]
        //[FieldCrc32(nameof(Crc))]
        public ValueWithDescriptorDataBlock DescriptorBlock { get; set; }

        [FieldOrder(7)]
        public ulong Crc { get; set; }
...

@jefffhaynes could you please take a look, if related to #225 fix
And also some questions: is there any method available to get the calculated Crc32 value after the serialization (for the cases it does not throw an exception)? Can the bound Crc property be marked with the [Ignore] attribute and still get calculated? And is it possible to get the byte-offset of a class property in the serialized byte-array?

@abrasat abrasat changed the title Crc32 built-in extension, calculate checksum over subtype field Crc32 built-in extension, serialization exception "Reverse binding not allowed on FieldValue attributes" when used on Subtype field combined with SerializeWhen attribute Aug 24, 2023
@jefffhaynes
Copy link
Owner

I believe the issue is that the crc can only be used on a single field. If you need to calculate it over multiple fields, simply create a container class for those fields.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants