lunixbochs / struc

Better binary packing for Go
MIT License
576 stars 45 forks source link

Support varint unpacking #52

Open abourget opened 6 years ago

abourget commented 6 years ago

I was trying to integrate a varint32 type (like https://github.com/Smerity/govarint/blob/master/govarint.go#L188:6 ..) by adding a type to types.go but I see a Custom declaration needs a Size() which returns a constant..

Do you think it would be easy to modify the lib to support a varint32 type? The size of the read is never defined beforehand.. it could be one byte, two, three, etc..

thanks!

lunixbochs commented 6 years ago

The custom Size() method is on an instance of the type, so you'd just need to read the instance value to see what size it is and return that.

Unpack Size() only seems to matter for slices, Custom unpack happens here: https://github.com/lunixbochs/struc/blob/master/fields.go#L150

e.g. https://github.com/lunixbochs/struc/blob/master/custom_float16.go#L73 could read:

func (f *Float16) Size(opt *Options) int {
    if *f < 128 {
        return 1
    } else {
        return 2
    }
}
abourget commented 6 years ago

but when Size is called, you don't have the data.. so you can't return a variable length.. like a varint would do. Does that make sense ?

lunixbochs commented 6 years ago

Size is not called on Unpack. You have the data on Pack. I know exactly how varint works.

lunixbochs commented 6 years ago

Seems to work fine for me (note, you shouldn't use Pack to get the size, I just didn't bother looking up the thresholds to hardcode it):

package struc

import (
    "encoding/binary"
    "io"
    "strconv"
)

type ByteWrapper struct {
    io.Reader
}

func (w *ByteWrapper) ReadByte() (byte, error) {
    var b [1]byte
    _, err := w.Read(b[:])
    return b[0], err
}

type Varint uint64

func (v *Varint) Pack(p []byte, opt *Options) (int, error) {
    return binary.PutUvarint(p, uint64(*v)), nil
}
func (v *Varint) Unpack(r io.Reader, length int, opt *Options) error {
    n, err := binary.ReadUvarint(&ByteWrapper{r})
    *v = Varint(n)
    return err
}
func (v *Varint) Size(opt *Options) int {
    var buf [8]byte
    return binary.PutUvarint(buf[:], uint64(*v))
}
func (v *Varint) String() string {
    return strconv.FormatUint(uint64(*v), 10)
}
abourget commented 6 years ago

Ohh I see.. I'll see if I can take a stab at it. Thanks a lot !

geoah commented 6 years ago

@abourget did you by any chance figure out how to integrate varints?

lunixbochs commented 6 years ago

Approximately the code I posted should already work in your own codebase without modifying struc.