jefffhaynes / BinarySerializer

A declarative serialization framework for controlling formatting of data at the byte and bit level using field bindings, converters, and code.
MIT License
290 stars 62 forks source link

Create the FieldPosition attribute #192

Closed sn4k3 closed 2 years ago

sn4k3 commented 2 years ago

This pull request implements #178 which allows to jump to addresses and make use of stream.Seek() when required. Since i have files pointing to tables this will help having a master class rather that multiple classes and jump in between.

Why not improve the FieldOffset instead?

I have tought about that, but that attribute name leads to confusion (at least for me), when I read FieldOffset i always think in offset the stream by a set amount from current position and keep going, instead it is setting the absolute position and reset it position to where it was before.

The FieldPosition follows and do exactly what stream.Seek do by default and so developer don't need to overthink.

To replicate FieldOffset declare as [FieldPosition(offset, true)]

Arguments

Example:

public class FileSpec
{
    [FieldOrder(0)]
    [FieldLength(12)]
    [SerializeAs(SerializedType.TerminatedString)]
    public string FileVersion { get; set; } = "FileMarking";

    [FieldOrder(1)]
    public uint FileVersion { get; set; }

    [FieldOrder(2)]
    public uint HeaderAddress { get; set; }

    [FieldOrder(3)]
    public uint SettingsAddress { get; set; }

    [FieldOrder(4)]
    public uint PreviewAddress { get; set; }

    [FieldOrder(5)]
    public uint LayersAddress { get; set; }

    [FieldOrder(6)]
    [FieldPosition(nameof(HeaderAddress))]
    public uint HeaderField1 { get; set; }

    [FieldOrder(7)]
    public uint HeaderField2 { get; set; }

    [FieldOrder(8)]
    public uint HeaderField3 { get; set; }

    [FieldOrder(9)]
    [FieldPosition(nameof(SettingsAddress))]
    public uint SettingsField1 { get; set; }

    [FieldOrder(10)]
    public uint SettingsField2 { get; set; }

    [FieldOrder(11)]
    [FieldPosition(nameof(PreviewAddress))]
    public uint PreviewSize { get; set; }

    [FieldOrder(12)]
    [FieldCount(nameof(PreviewSize))]
    public byte[] PreviewData {get; set; }

    [FieldOrder(13)]
    [FieldPosition(nameof(LayersAddress))]
    public uint LayerCount { get; set; }

    [FieldOrder(14)]
    [FieldCount(nameof(LayerCount))]
    public Layer[] Layers { get; set; }

    [FieldOrder(15)]
    [FieldPosition(4, SeekOrigin.Current)] // Skip unused 4 bytes first, same as declaring uint Padding
    public uint FileChecksum { get; set; }
}

Hope that makes sense :)

jefffhaynes commented 2 years ago

I see what you're saying about FieldOffset, but why not start by simply making it configurable, rather than introduce an entirely new attribute that does the same thing?

sn4k3 commented 2 years ago

I see what you're saying about FieldOffset, but why not start by simply making it configurable, rather than introduce an entirely new attribute that does the same thing?

Definitely FieldOffset can be improved to have the same fields and produce the same result, however it default behavior must be untouch to not break current uses, which means by default Rewind must be true breaking the common logic of stream.seek and file read/write on pure streams usages, that can lead user to error if they not read the documentation of the FieldOffset.

Either way i'm happy with one, just tell if you want me to repatch to improve FieldOffset instead and i will rewrite it :)

jefffhaynes commented 2 years ago

I guess my feeling is that introducing a new attribute is a lot just to change the default behavior. Maybe start by adding configuration and then at some point we can simply "alias" a new attribute if it makes sense.

sn4k3 commented 2 years ago

Sure, i will close this PR and send a new one in about some mins with the improvements on FieldOffset