Прямо сейчас ответы предлагают unsafe
, но они не обсуждают, почему вы не должны использовать unsafe
и что вы должны делать вместо этого.Итак, позвольте мне попробовать.
В коде C ++, который вы разместили в OP, вы, кажется, пишете своего рода двоичный формат, который читает данные посредством простого преобразования.Простой, но эффективный.Единственная очевидная проблема, которую я вижу, состоит в том, что она не позволяет взаимодействовать между Little Endian и Big Endian, но это другой вопрос.
Способ, которым вы подходите к двоичному кодированию в Go, заключается в использовании удобного пакета encoding/binary
, который способен декодировать двоичные данные непосредственно в структуры фиксированного размера (т. Е. Без строк или секций, которые имеют переменную длину и поэтому длина должна быть закодирована произвольно).
Вот какЯ бы реализовал ваш пример в Go:
package main
import (
"bytes"
"encoding/binary"
"fmt"
)
const textMessage = 11
func main() {
// Set up our data - this is an example of the data I
// imagine you want to decode.
r := bytes.NewReader([]byte{
// Header
byte(textMessageLen + headerLen), 0, 0, 0,
11,
// Body
137, 0, 0, 0,
117, 0, 0, 0,
// Message content
'H', 'e', 'l', 'l', 'o', '!', 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
})
// We first read the header to decide what to do next.
// Notice that we explicitly pass an argument that indicates to
// parse integers using little endian, making this code portable.
var h Header
err := binary.Read(r, binary.LittleEndian, &h)
if err != nil {
fmt.Println(err)
return
}
switch h.Type {
case textMessage:
// It's a text message - make sure the length is right.
if textMessageLen != (int(h.Length) - headerLen) {
fmt.Println("Invalid payload length")
return
}
// Decode the data
var t TextMessage
err = binary.Read(r, binary.LittleEndian, &t)
if err != nil {
fmt.Println(err)
return
}
// Print it out
fmt.Printf("Sender: %d; Receiver: %d\nMessage: %s\n",
t.Sender, t.Receiver, bytes.TrimRight(t.Text[:], "\x00"))
default:
fmt.Println("unknown payload type")
}
}
// If you need to find out what the encoded size of a struct is, don't use unsafe.Sizeof;
// use binary.Size instead.
var headerLen = binary.Size(Header{})
type Header struct {
Length uint32
Type uint8
}
var textMessageLen = binary.Size(TextMessage{})
type TextMessage struct {
Sender, Receiver uint32
Text [64]byte
}
Детская площадка
Итак, вот несколько вещей, на которые стоит обратить внимание:
- В Go двоичные форматы, как правило, НИКОГДА не реализуются при чтении непосредственно из памяти.Это потому, что 1. это зависит от платформы (Little / Big endian), 2. проблемы со строками, слайсами и заполнением структур и 3. это, ну, в общем, небезопасно.Если вы не вмешиваетесь непосредственно в память, Go может в значительной степени гарантировать, что ваша программа будет работать на любой платформе без каких-либо изменений.В тот момент, когда вы начинаете это делать, вы теряете эту гарантию.
- Нам не нужно «продвигать указатель» данных, которые мы читаем, - мы передаем
binary.Read
io.Reader
Это означает, что когда мы читаем что-то из него, считанные данные отбрасываются, поэтому указатель автоматически продвигается. - При работе с памятью с самим собой возможны последствия, когда ГК может думать, что точкав данных больше нет ссылок и их можно свободно использовать - тогда как на самом деле вы все еще используете их, просто не четко ссылаетесь с помощью собственного указателя Go.