6

I am looking for clean way to cast byte array to struct for client-server application. I know most ppl turn to gob package for this solution however I do not control the encoding for the application. that being said, I only programmed the server application not the client, there is a mutual contract for the protocol that is being exchanged.

The best I could come out is the following.

type T struct {
    A int16
    B int8
    C []byte
}

func main() {
    // Create a struct and write it.
    t := T{A: 99, B: 10}
    buf := &bytes.Buffer{}

    buf1 := []byte{5, 100, 100}
    fmt.Println(buf1)

    buf.Write(buf1)

    //err := binary.Write(buf, binary.BigEndian, t)

    //if err != nil {
    //  panic(err)
    //}
    fmt.Println(buf)

    // Read into an empty struct.
    t = T{}
    err := binary.Read(buf, binary.BigEndian, &t)
    if err != nil {
        panic(err)
    }
    fmt.Printf("%d %d", t.A, t.B)
}

However, as soon as number bytes does not coincide with the size of the struct, then go will send a panic. How can I modify this to work without the panic if undersize or oversize

Go playground

2
  • you just control the server side, means you have already have the serial protocol. What protocol you have, is the key point of your question. golang have encoding package support for widely used protocols, like JSON, BSON or PROTOBUF. so find out the serial protocol and choose the encoding package. or if you have private protocol, implement a encoding yourself. Commented Jul 21, 2015 at 2:07
  • It is private protocol. Any examples/article/goplayground? Commented Jul 21, 2015 at 2:23

2 Answers 2

6

According to http://golang.org/pkg/encoding/binary/#Read :

Data must be a pointer to a fixed-size value or a slice of fixed-size values.

So you can't use slice []byte in your structure. But you can use fixed size array for it.
Like this:

package main

import (
    "bytes"
    "encoding/binary"
    "fmt"
)

type T struct {
    A int16
    B int8
    C [256]byte
}

func main() {
    // Create a struct and write it.
    t := T{A: 99, B: 10}
    buf := &bytes.Buffer{}
        
    err := binary.Write(buf, binary.BigEndian, t)
    
    if err != nil {
        panic(err)
    }
    fmt.Println(buf)

    // Read into an empty struct.
    t = T{}
    err = binary.Read(buf, binary.BigEndian, &t)
    if err != nil {
        panic(err)
    }
    fmt.Printf("%d %d", t.A, t.B)
}

Go playground

Sign up to request clarification or add additional context in comments.

2 Comments

Any idea how to cast to variable size
Depends on your data structure. You can split your source array onto 2 parts. First part will contains only fixed-size data (A and B in your case 3 bytes). Second part will contains array data. In that way you need to create new structure, without array part, like type T struct { A int16 B int8 }. And decode both parts separately
2

I think binpacker will be greate to handle this case:

package main

import (
    "bytes"
    "fmt"

    "github.com/zhuangsirui/binpacker"
)

type T struct {
    A uint16
    B string
    C []byte
}

func main() {
    field1 := uint16(1)
    field2 := "Hello World"
    field3 := []byte("Hello World")
    buffer := new(bytes.Buffer)
    binpacker.NewPacker(buffer).
        PushUint16(field1).
        PushUint16(uint16(len(field2))).PushString(field2).
        PushUint16(uint16(len(field3))).PushBytes(field3)

    t := new(T)
    unpacker := binpacker.NewUnpacker(buffer)
    unpacker.FetchUint16(&t.A).StringWithUint16Perfix(&t.B).BytesWithUint16Perfix(&t.C)
    fmt.Println(t)
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.