go-restruct / restruct

Rich binary (de)serialization library for Golang
https://restruct.io/
ISC License
360 stars 17 forks source link

How might I implement sign magnitude ints? #34

Open umeat opened 5 years ago

umeat commented 5 years ago

I'd like to parse binary sign magnitude integers. Any ideas for how I might implement this?

https://www.ntu.edu.sg/home/ehchua/programming/java/images/DataRep_SignedIntegers.png

I tried making a custom struct type like so:

type sint8 struct {
    sign bool `struct:"uint8:1,variantbool"`
    magnitude uint8 `struct:"uint8:7"`
}

type Foo struct {
    A sint8
}

This is okay, but I want the length of the sint to be variable. Would need to create a separate sint type for every length I need. I'd rather construct a sint from n bits, where sign is first bit and magnitude is n-1 bits long. Something like:

type sint struct {
    sign bool `struct:"uint8:1,variantbool"`
    magnitude uint `struct:"uint:n"`
}

type Foo struct {
    A sint `struct:"n=6"`
    B sint `struct:"n=13"`
}

Need a custom type which is capable of representing positive and negative 0. If you convert to int then Pack(Unpack(data)) would not equal data if there is a -0 sint.

jchv commented 5 years ago

You may need to implement the Packer/Unpacker/BitSizer interfaces for this one, at least until I get back to implementing the expression language features. This would basically amount to manually unpacking. I am at work but I can take a crack at it later today. There’s a bit of documentation in the godoc if you want to try (though it could use improvements.)

https://godoc.org/github.com/go-restruct/restruct

jchv commented 5 years ago

After rereading this, it occurred to me that the struct tag would not be accessible. Interesting. I think right now I don't have a good solution for this problem.

jchv commented 5 years ago

With the existence of expressions, I think we’re headed towards a viable solution. What we really need is structs that can be parameterized somehow, and expressions allow that. There’s a couple ways we could go about this:

umeat commented 5 years ago

Parameterization would be quite useful for me. I have another example from the RTCM parser where it would be useful:

type MsmHeader struct { 
    MessageNumber          uint16 `struct:"uint16:12"`
    ReferenceStationId     uint16 `struct:"uint16:12"`
    Epoch                  uint32 `struct:"uint32:30"`
    MultipleMessageBit     bool `struct:"uint8:1,variantbool"`
    Iods                   uint8 `struct:"uint8:3"`
    Reserved               uint8 `struct:"uint8:7"`
    ClockSteeringIndicator uint8 `struct:"uint8:2"`
    ExternalClockIndicator uint8 `struct:"uint8:2"`
    SmoothingIndicator     bool `struct:"uint8:1,variantbool"`
    SmoothingInterval      uint8 `struct:"uint8:3"`
    SatelliteMask          uint64 `struct:"uint64"`
    SignalMask             uint32 `struct:"uint32"`
    CellMask               uint64 `struct:"bits=bits.OnesCount64(SatelliteMask)*bits.OnesCount32(SignalMask)"`
} 

type SatelliteData57 struct { 
    RangeMilliseconds []uint8 `struct-size:"n"`
    Extended          []uint8 `struct-size:"n"`
    Ranges            []uint16 `struct-size:"n"`
    PhaseRangeRates   []int16 `struct-size:"n"`
} 

type SignalData57 struct { 
    Pseudoranges    []int32 `struct-size:"n"`
    PhaseRanges     []int32 `struct-size:"n"`
    PhaseRangeLocks []uint16 `struct-size:"n"`
    HalfCycles      []bool `struct-size:"n"`
    Cnrs            []uint16 `struct-size:"n"`
    PhaseRangeRates []int16 `struct-size:"n"`
} 

type MessageMsm7 struct {                       
    MsmHeader                       
    SatelliteData SatelliteData57 `struct:"param:n=bits.OnesCount64(MsmHeader.SatelliteMask)"`
    SignalData SignalData57 `struct:"param:n=bits.OnesCount64(MsmHeader.CellMask)"`
}

Currently I have to do this:

type MsmHeader struct { 
    MessageNumber          uint16 `struct:"uint16:12"`
    ReferenceStationId     uint16 `struct:"uint16:12"`
    Epoch                  uint32 `struct:"uint32:30"`
    MultipleMessageBit     bool `struct:"uint8:1,variantbool"`
    Iods                   uint8 `struct:"uint8:3"`
    Reserved               uint8 `struct:"uint8:7"`
    ClockSteeringIndicator uint8 `struct:"uint8:2"`
    ExternalClockIndicator uint8 `struct:"uint8:2"`
    SmoothingIndicator     bool `struct:"uint8:1,variantbool"`
    SmoothingInterval      uint8 `struct:"uint8:3"`
    SatelliteMask          uint64 `struct:"uint64"`
    SignalMask             uint32 `struct:"uint32"`
    CellMask               uint64 `struct:"bits=bits.OnesCount64(SatelliteMask)*bits.OnesCount32(SignalMask)"`
} 

type MessageMsm7 struct { 
    MsmHeader

    RangeMilliseconds []uint8 `struct-size:"bits.OnesCount64(MsmHeader.SatelliteMask)"`
    Extended          []uint8 `struct-size:"bits.OnesCount64(MsmHeader.SatelliteMask)"`
    Ranges            []uint16 `struct-size:"bits.OnesCount64(MsmHeader.SatelliteMask)"`
    PhaseRangeRates   []int16 `struct-size:"bits.OnesCount64(MsmHeader.SatelliteMask)"`

    Pseudoranges    []int32 `struct-size:"bits.OnesCount64(MsmHeader.CellMask)"`
    PhaseRanges     []int32 `struct-size:"bits.OnesCount64(MsmHeader.CellMask)"`
    PhaseRangeLocks []uint16 `struct-size:"bits.OnesCount64(MsmHeader.CellMask)"`
    HalfCycles      []bool `struct-size:"bits.OnesCount64(MsmHeader.CellMask)"`
    Cnrs            []uint16 `struct-size:"bits.OnesCount64(MsmHeader.CellMask)"`
    XPhaseRangeRates []int16 `struct-size:"bits.OnesCount64(MsmHeader.CellMask)"`
}

Which is unideal because I can't reuse the Satellite and SignalData structs. Also I have to re-run the same struct-size expression for every field.