如何将元组元安装到二进制中,并在Golang的Array上进行Unmarshal Binary



我想将 gobencodedecode对象使用,我这样做:

type transProp struct {
    a []int
    b []float64
}
func (p transProp) MarshalBinary() ([]byte, error) {
    // A simple encoding: plain text.
    var b bytes.Buffer
    fmt.Fprintln(&b, p.a, p.b)
    return b.Bytes(), nil
}
// UnmarshalBinary modifies the receiver so it must take a pointer receiver.
func (p *transProp) UnmarshalBinary(data []byte) error {
    // A simple encoding: plain text.
    b := bytes.NewBuffer(data)
    _, err := fmt.Fscanln(b, &p.a, &p.b)
    return err
}
func TestGobEncode(t *testing.T) {
    p := transProp{
        a: []int{3, 4, 5},
        b: []float64{1.0, 2.0},
    }
    var network bytes.Buffer
    enc := gob.NewEncoder(&network)
    err := enc.Encode(p)
    if err != nil {
        log.Fatal("encode:", err)
    }
    dec := gob.NewDecoder(&network)
    var p1 transProp
    err = dec.Decode(&p1)
    if err != nil {
        log.Fatal("decode:", err)
    }
    fmt.Println(p1)
}

但是,此报告错误是这样:

decode:can't scan type: *[]int

如果您可以将transProp字段的可见性更改为公共,例如

type transProp struct {
    A []int
    B []float64
}

那么,您无需实施自定义的二进制邮编/Unmarshaller。您可以使用默认的gob解码器/编码器。

但是,如果不能,则有很多选择。

  1. 最简单的一个,定义另一个导出相关字段的 wrapper struct,wrap transProp,然后使用默认的 gob encoder/decoder,例如

    type wrapTransProp struct {
        A []int
        B []float64
    }
    func (p transProp) MarshalBinary() ([]byte, error) {
        //Wrap struct
        w := wrapTransProp{p.a, p.b}
        //use default gob encoder
        var buf bytes.Buffer
        enc := gob.NewEncoder(&buf)
        if err := enc.Encode(w); err != nil {
            return nil, err
        }
        return buf.Bytes(), nil
    }
    func (p *transProp) UnmarshalBinary(data []byte) error {
        w := wrapTransProp{}
        //Use default gob decoder
        reader := bytes.NewReader(data)
        dec := gob.NewDecoder(reader)
        if err := dec.Decode(&w); err != nil {
            return err
        }
        p.a = w.A
        p.b = w.B
        return nil
    }
    
  2. 具有自定义数据布局的自定义邮政编码/Unmarshaller。实施可能性很多。几个考虑因素:

    • 字节订单,小/大端?
    • 数据包/流布局?

    带有流格式的big-endian实现的示例:

    // Big-Endian
    // Size  : 4,         4,      1,        n,     4,       n
    // Types : uint32,    uint32, uint8,    []int, uint32,  []float64
    // Data  : #numbytes, #nInt,  #intSize, p.a,   #nFloat, p.b
    

    挑战在于如何表示int,因为它的体系结构取决于它。样本实现将是:

    func (p transProp) MarshalBinary() ([]byte, error) {
        //Get sizeof int (in bits) from strconv package
        szInt := strconv.IntSize / 8
        nInt := len(p.a)
        nFloat := len(p.b)
        nStream := 4 + 4 + 1 + nInt*szInt + 4 + nFloat*8
        stream := make([]byte, nStream)
        pos := 0
        //total number of bytes
        binary.BigEndian.PutUint32(stream, uint32(nStream))
        pos += 4
        //num of items in p.a
        binary.BigEndian.PutUint32(stream[pos:], uint32(nInt))
        pos += 4
        //int size
        stream[pos] = uint8(szInt)
        pos++
        //items in a
        switch szInt {
        case 1:
            for _, v := range p.a {
                stream[pos] = uint8(v)
                pos++
            }
        case 2: //16-bit
            for _, v := range p.a {
                binary.BigEndian.PutUint16(stream[pos:], uint16(v))
                pos += 2
            }
        case 4: //32-bit
            for _, v := range p.a {
                binary.BigEndian.PutUint32(stream[pos:], uint32(v))
                pos += 4
            }
        case 8: //64-bit
            for _, v := range p.a {
                binary.BigEndian.PutUint64(stream[pos:], uint64(v))
                pos += 8
            }
        }
        //number of items in p.b
        binary.BigEndian.PutUint32(stream[pos:], uint32(nFloat))
        pos += 4
        //items in b
        s := stream[pos:pos] //slice len=0, capacity=nFloat
        buf := bytes.NewBuffer(s)
        if err := binary.Write(buf, binary.BigEndian, p.b); err != nil {
            return nil, err
        }
        return stream, nil
    }
    func (p *transProp) UnmarshalBinary(data []byte) error {
        buf := bytes.NewBuffer(data)
        var intSize uint8
        var k, nBytes, nInt, nFloat uint32
        //num bytes
        if err := binary.Read(buf, binary.BigEndian, &nBytes); err != nil {
            return err
        }
        if len(data) < int(nBytes) {
            return errors.New("len(data) < #Bytes")
        }
        //num of int items
        if err := binary.Read(buf, binary.BigEndian, &nInt); err != nil {
            return err
        }
        //int size
        if err := binary.Read(buf, binary.BigEndian, &intSize); err != nil {
            return err
        }
        //read int into p.a
        pos := 0
        stream := buf.Bytes()
        p.a = make([]int, nInt)
        switch intSize {
        case 1:
            for pos = 0; pos < int(nInt); pos++ {
                p.a[pos] = int(stream[pos])
            }
        case 2:
            for k = 0; k < nInt; k++ {
                p.a[k] = int(binary.BigEndian.Uint16(stream[pos:]))
                pos += 2
            }
        case 4:
            for k = 0; k < nInt; k++ {
                p.a[k] = int(binary.BigEndian.Uint32(stream[pos:]))
                pos += 4
            }
        case 8:
            for k = 0; k < nInt; k++ {
                p.a[k] = int(binary.BigEndian.Uint64(stream[pos:]))
                pos += 8
            }
        }
        //advance buffer
        buf.Next(pos)
        //num of float64 items
        if err := binary.Read(buf, binary.BigEndian, &nFloat); err != nil {
            return err
        }
        //items in b
        p.b = make([]float64, nFloat)
        if err := binary.Read(buf, binary.BigEndian, p.b); err != nil {
            return err
        }
        return nil
    }
    

最新更新