我想将gob
用于encode
和decode
对象,我这样做:
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
答案 0 :(得分:4)
如果您可以将transProp
字段的公开范围更改为公开字段,例如
type transProp struct {
A []int
B []float64
}
那么您不需要实现自定义二进制编组器/ unmarshaller。您可以使用默认的gob
解码器/编码器。
但是,如果你不能,有很多选择。
最简单的一个,定义导出相关字段的另一个wrapper struct
,换行transProp
,然后使用默认的gob
编码器/解码器,例如
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
}
具有自定义数据布局的自定义marshaller / 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
}