Go conversion between struct and byte array

huangapple go评论81阅读模式
英文:

Go conversion between struct and byte array

问题

我正在使用Go编写一个客户端-服务器应用程序。我想在Go中执行类似C语言的类型转换。

例如,在Go中:

type packet struct {
    opcode uint16
    data   [1024]byte
}

var pkt1 packet
...
n, raddr, err := conn.ReadFromUDP(pkt1)  // 这里有错误

此外,我还想执行类似C语言的memcpy()操作,这将允许我将接收到的网络字节流直接映射到一个结构体。

例如,使用上述接收到的pkt1:

type file_info struct {
    file_size uint32       // 4字节
    file_name [1020]byte
}

var file file_info
if pkt1.opcode == WRITE {
    memcpy(&file, pkt1.data, 1024)
}
英文:

I am writing a client - server application in Go. I want to perform C-like type casting in Go.

E.g. in Go

type packet struct {
    opcode uint16
    data [1024]byte
}

var pkt1 packet
...
n, raddr, err := conn.ReadFromUDP(pkt1)  // error here

Also I want to perform C-like memcpy(), which will allow me to directly map the network byte stream received to a struct.

e.g. with above received pkt1

type file_info struct {
    file_size uint32       // 4 bytes
    file_name [1020]byte
}

var file file_info
if (pkt1.opcode == WRITE) {
    memcpy(&file, pkt1.data, 1024)
}

答案1

得分: 20

unsafe.Pointer 是不安全的,实际上在这里你并不需要它。可以使用 encoding/binary 包代替:

// 创建一个结构体并将其写入。
t := T{A: 0xEEFFEEFF, B: 3.14}
buf := &bytes.Buffer{}
err := binary.Write(buf, binary.BigEndian, t)
if err != nil {
    panic(err)
}
fmt.Println(buf.Bytes())

// 读取到一个空结构体中。
t = T{}
err = binary.Read(buf, binary.BigEndian, &t)
if err != nil {
    panic(err)
}
fmt.Printf("%x %f", t.A, t.B)

Playground

正如你所见,它可以很好地处理大小和字节序。

英文:

unsafe.Pointer is, well, unsafe, and you don't actually need it here. Use encoding/binary package instead:

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

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

Playground

As you can see, it handles sizes and endianness quite neatly.

答案2

得分: 5

我遇到了同样的问题,并通过使用"encoding/binary"包解决了它。这里有一个示例:

package main

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

func main() {
	p := fmt.Println
	b := []byte{43, 1, 0}

	myStruct := MyStruct{}
	err := binary.Read(bytes.NewBuffer(b[:]), binary.BigEndian, &myStruct)

	if err != nil {
		panic(err)
	}

	p(myStruct)
}

type MyStruct struct {
	Num  uint8
	Num2 uint16
}

这是一个运行的示例:https://play.golang.org/p/Q3LjaAWDMh

英文:

I've had the same problem and I solved it by using the "encoding/binary" package. Here's an example:

package main

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

func main() {
  p := fmt.Println
  b := []byte{43, 1, 0}

  myStruct := MyStruct{}
  err := binary.Read(bytes.NewBuffer(b[:]), binary.BigEndian, &myStruct)

  if err != nil {
	panic(err)
  }

  p(myStruct)
}

type MyStruct struct {
  Num uint8
  Num2 uint16
}

Here's the running example: https://play.golang.org/p/Q3LjaAWDMh

答案3

得分: 4

谢谢你的回答,我相信它们完全有效。但在我的情况下,我更感兴趣的是解析作为网络数据包接收的[]byte缓冲区。我使用以下方法解析缓冲区。

var data []byte // 存储接收到的网络数据包
opcode := binary.BigEndian.Uint16(data) // 这将获取前两个字节并解释为uint16数值
raw_data := data[2:len(data)] // 这将将剩余的原始数据复制到raw_data字节流中

在从结构体构建[]byte流时,可以使用以下方法

type packet struct {
    opcode uint16
    blk_no uint16
    data   string
}
pkt := packet{opcode: 2, blk_no: 1, data: "testing"}
var buf []byte = make([]byte, 50) // 确保数据字符串少于46个字节
offset := 0
binary.BigEndian.PutUint16(buf[offset:], pkt.opcode)
offset = offset + 2
binary.BigEndian.PutUint16(buf[offset:], pkt.blk_no)
offset = offset + 2
bytes_copied := copy(buf[offset:], pkt.data)

希望这能给你一个关于如何将[]byte流转换为结构体以及将结构体转换回[]byte流的一般想法。

英文:

Thank you for answers and I am sure they work perfectly. But in my case I was more interested in parsing the []byte buffer received as network packet. I used following method to parse the buffer.

var data []byte // holds the network packet received
opcode := binary.BigEndian.Uint16(data) // this will get first 2 bytes to be interpreted as uint16 number
raw_data := data[2:len(data)] // this will copy rest of the raw data in to raw_data byte stream

While constructing a []byte stream from a struct, you can use following method

type packet struct {
    opcode uint16
    blk_no uint16
    data   string
}
pkt := packet{opcode: 2, blk_no: 1, data: "testing"}
var buf []byte = make([]byte, 50) // make sure the data string is less than 46 bytes
offset := 0
binary.BigEndian.PutUint16(buf[offset:], pkt.opcode)
offset = offset + 2
binary.BigEndian.PutUint16(buf[offset:], pkt.blk_no)
offset = offset + 2
bytes_copied := copy(buf[offset:], pkt.data)

I hope this gives general idea about how to convert []byte stream to struct and struct back to []byte stream.

答案4

得分: 1

你需要使用unsafe关键字,并且在64位系统上,uint类型占据8个字节,如果你想要4个字节,你需要使用uint32类型。

这段代码看起来很丑陋,而且需要自己处理字节序。

type packet struct {
    opcode uint16
    data   [1022]byte
}

type file_info struct {
    file_size uint32     // 4个字节
    file_name [1018]byte // 这个结构体必须适应packet.data的大小
}

func makeData() []byte {
    fi := file_info{file_size: 1 << 20}
    copy(fi.file_name[:], []byte("test.x64"))
    p := packet{
        opcode: 1,
        data:   *(*[1022]byte)(unsafe.Pointer(&fi)),
    }
    mem := *(*[1022]byte)(unsafe.Pointer(&p))
    return mem[:]
}

func main() {
    data := makeData()
    fmt.Println(data)
    p := (*packet)(unsafe.Pointer(&data[0]))
    if p.opcode == 1 {
        fi := (*file_info)(unsafe.Pointer(&p.data[0]))
        fmt.Println(fi.file_size, string(fi.file_name[:8]))
    }
}

play

英文:

You'd have to use unsafe, also uint is 8 bytes on 64bit systems, you have to use uint32 if you want 4 bytes.

It's ugly, unsafe and you have to handle endianess yourself.

type packet struct {
	opcode uint16
	data   [1022]byte
}

type file_info struct {
	file_size uint32     // 4 bytes
	file_name [1018]byte //this struct has to fit in packet.data
}

func makeData() []byte {
	fi := file_info{file_size: 1 &lt;&lt; 20}
	copy(fi.file_name[:], []byte(&quot;test.x64&quot;))
	p := packet{
		opcode: 1,
		data:   *(*[1022]byte)(unsafe.Pointer(&amp;fi)),
	}
	mem := *(*[1022]byte)(unsafe.Pointer(&amp;p))
	return mem[:]
}

func main() {
	data := makeData()
	fmt.Println(data)
	p := (*packet)(unsafe.Pointer(&amp;data[0]))
	if p.opcode == 1 {
		fi := (*file_info)(unsafe.Pointer(&amp;p.data[0]))
		fmt.Println(fi.file_size, string(fi.file_name[:8]))
	}
}

<kbd>play</kbd>

huangapple
  • 本文由 发表于 2014年10月15日 07:43:21
  • 转载请务必保留本文链接:https://go.coder-hub.com/26372227.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定