Go conversion between struct and byte array - struct

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)
}

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.

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

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.

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 << 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

Related

strings.Builder memory usage

Using this file (data file):
package main
import (
"io/ioutil"
"time"
)
func main() {
ioutil.ReadFile("100mb.file")
time.Sleep(time.Duration(time.Minute))
}
Showed memory usage for me of 107 MB. With this similar file:
package main
import (
"bytes"
"os"
"time"
)
func read(path_s string) (bytes.Buffer, error) {
buf_o := bytes.Buffer{}
open_o, e := os.Open(path_s)
if e != nil {
return buf_o, e
}
buf_o.ReadFrom(open_o)
open_o.Close()
return buf_o, nil
}
func main() {
read("100mb.file")
time.Sleep(time.Duration(time.Minute))
}
Memory usage went to 273 MB. Finally this similar file:
package main
import (
"io"
"os"
"strings"
"time"
)
func read(path_s string) (strings.Builder, error) {
str_o := strings.Builder{}
open_o, e := os.Open(path_s)
if e != nil {
return str_o, e
}
io.Copy(&str_o, open_o)
open_o.Close()
return str_o, nil
}
func main() {
read("100mb.file")
time.Sleep(time.Duration(time.Minute))
}
Memory usage went to 432 MB. I tried to be careful and close files where
possible. Why is the memory usage so high for the second example, and especially
the final example? Can I change something so that they are closer to the first
example?
ioutil.ReadFile("100mb.file") gets the size of the file, allocates a []byte that size and slurps the bytes up into that slice.
buf_o.ReadFrom(open_o) allocates an initial []byte of some size and reads into that slice. If there's more data in the reader than space in the slice, then the function allocates a larger slice, copies existing data to that slice and reads more. This repeats until EOF.
The function ioutil.ReadFile uses bytes Buffer.ReadFrom internally. Take a look at the ioutil.ReadFile implementation to see how to improve direct use of the bytes.Buffer. A synopsis of the logic is this:
var buf bytes.Buffer
// Open file
f, err := os.Open(path)
if err != nil {
return &buf, err
}
defer f.Close()
// Get size.
fi, err := f.Stat()
if err != nil {
return &buf, err
}
// Grow to size of file plus extra slop to ensure no realloc.
buf.Grow(int(fi.Size()) + bytes.MinRead)
_, err := buf.ReadFrom(f)
return &buf, err
The strings.Builder example reallocates the internal buffer several times as in the bytes.Buffer example. In addition, io.Copy allocates a buffer. You can improve the strings.Builder example by growing the builder to the size of the file before reading.
Here's the code for strings.Builder:
var buf strings.Builder
// Open file
f, err := os.Open(path)
if err != nil {
return &buf, err
}
defer f.Close()
// Get size.
fi, err := f.Stat()
if err != nil {
return &buf, err
}
buf.Grow(int(fi.Size()))
_, err = io.Copy(&buf, f)
return &buf, err
io.Copy or some other code using an extra buffer is required because strings.Builder does not have a ReadFrom method. The strings.Builder type does not have a ReadFrom method because that method can leak a reference to the backing array of the internal slice of bytes.
Using suggestion from Muffin Top, I took my second example and added this
directly before the call to ReadFrom:
stat_o, e := open_o.Stat()
if e != nil {
return buf_o, e
}
buf_o.Grow(bytes.MinRead + int(stat_o.Size()))
and the memory went down to 107 MB, basically the same as the first example.

Calling mremap in Go doesn't work, but gives no error

I'm trying to mremap a file from Go, but the size of the file doesn't seem to be changing, despite the returned errno of 0. This results in a segfault when I try to access the mapped memory.
I've included the code below. The implementation is similar to the mmap implementation in the sys package, so I'm not sure what's going wrong here:
package main
import (
"fmt"
"io/ioutil"
"log"
"os"
"reflect"
"unsafe"
"golang.org/x/sys/unix"
)
// taken from <https://github.com/torvalds/linux/blob/f8394f232b1eab649ce2df5c5f15b0e528c92091/include/uapi/linux/mman.h#L8>
const (
MREMAP_MAYMOVE = 0x1
// MREMAP_FIXED = 0x2
// MREMAP_DONTUNMAP = 0x4
)
func mremap(data []byte, size int) ([]byte, error) {
header := (*reflect.SliceHeader)(unsafe.Pointer(&data))
mmapAddr, mmapSize, errno := unix.Syscall6(
unix.SYS_MREMAP,
header.Data,
uintptr(header.Len),
uintptr(size),
uintptr(MREMAP_MAYMOVE),
0,
0,
)
if errno != 0 {
return nil, fmt.Errorf("mremap failed with errno: %s", errno)
}
if mmapSize != uintptr(size) {
return nil, fmt.Errorf("mremap size mismatch: requested: %d got: %d", size, mmapSize)
}
header.Data = mmapAddr
header.Cap = size
header.Len = size
return data, nil
}
func main() {
log.SetFlags(log.LstdFlags | log.Lshortfile)
const mmPath = "/tmp/mm_test"
// create a file for mmap with 1 byte of data.
// this should take up 1 block on disk (4096 bytes).
err := ioutil.WriteFile(mmPath, []byte{0x1}, 0755)
if err != nil {
log.Fatal(err)
}
// open and stat the file.
file, err := os.OpenFile(mmPath, os.O_RDWR, 0)
if err != nil {
log.Fatal(err)
}
defer file.Close()
stat, err := file.Stat()
if err != nil {
log.Fatal(err)
}
// mmap the file and print the contents.
// this should print only one byte of data.
data, err := unix.Mmap(int(file.Fd()), 0, int(stat.Size()), unix.PROT_READ|unix.PROT_WRITE, unix.MAP_SHARED)
if err != nil {
log.Fatal(err)
}
fmt.Printf("mmap data: %+v\n", data)
// mremap the file to a size of 2 blocks.
data, err = mremap(data, 2*4096)
if err != nil {
log.Fatal(err)
}
// access the mremapped data.
fmt.Println(data[:4096]) // accessing the first block works.
fmt.Println(data[:4097]) // accessing the second block fails with `SIGBUS: unexpected fault address`.
}
I tried looking for other Go code that uses mremap, but I can't seem to find any. I would appreciate any input!
As #kostix mentioned in the comments, mmap is being used to map a regular file into memory. The reason that accessing the buffer results in a segfault is that the underlying file itself is not large enough. The solution is to truncate the file to the desired length before calling mremap:
if err := file.Truncate(2*4096); err != nil {
log.Fatal(err)
}
data, err = mremap(data, 2*4096)

Binary string to unicode

I'm not 100% sure why my binary string to unicode isn't working..can anyone point out the issue or help me patch it? Also the reason why i chunk out the binary is that it is too large for ParseInt to handle. See the playground link below for an example.
func binToString(s []byte) string {
var counter int
chunk := make([]byte, 7)
var buf bytes.Buffer
for i := range s {
if i%8 == 0 {
counter = 0
if i, err := strconv.ParseInt(string(chunk), 2, 64); err == nil {
buf.WriteString(string(i))
}
} else {
chunk[counter] = s[i] //i know i can use modulus here too but i was testing and an counter was easier to track and test for me
counter++
}
}
return buf.String()
}
It either seems to miss a character or add an character (or two) on conversion.
Here is a playground link showing an example of the function not working as expected.
Your function could be implemented in a simpler, more efficient manner:
func binToString(s []byte) string {
output := make([]byte, len(s)/8)
for i := 0; i < len(output); i++ {
val, err := strconv.ParseInt(string(s[i*8:(i+1)*8]), 2, 64)
if err == nil {
output[i] = byte(val)
}
}
return string(output)
}
https://play.golang.org/p/Fmo7I-rN3c

How to properly wait for an event/process to finish not being the parent?

I am using GO to check if a process (not been parent) has ben terminated, basically something like the pwait command in FreeBSD but written in go.
Currently I am trying a for loop with a kill -0, but I notice that the CPU usage is very high 99% with this approach, here is the code:
package main
import (
"fmt"
"os"
"strconv"
"syscall"
"time"
)
func main() {
if len(os.Args) != 2 {
fmt.Printf("usage: %s pid", os.Args[0])
os.Exit(1)
}
pid, err := strconv.ParseInt(os.Args[1], 10, 64)
if err != nil {
panic(err)
}
process, err := os.FindProcess(int(pid))
err = process.Signal(syscall.Signal(0))
for err == nil {
err = process.Signal(syscall.Signal(0))
time.Sleep(500 * time.Millisecond)
}
fmt.Println(err)
}
Any idea of how to improve or properly implement this.
Thanks in advance.
UPDATE
Adding a sleep within the loop like suggested, helps reducing the load.
From the provided links, seems to be possible to attach to the existing pid, I will give a try PtraceAttach but don't know if this may have side effects, any idea?
As suggested I was available to use kqueue:
package main
import (
"fmt"
"log"
"os"
"strconv"
"syscall"
)
func main() {
if len(os.Args) != 2 {
fmt.Printf("usage: %s pid", os.Args[0])
os.Exit(1)
}
pid, err := strconv.ParseInt(os.Args[1], 10, 64)
if err != nil {
panic(err)
}
process, _ := os.FindProcess(int(pid))
kq, err := syscall.Kqueue()
if err != nil {
fmt.Println(err)
}
ev1 := syscall.Kevent_t{
Ident: uint64(process.Pid),
Filter: syscall.EVFILT_PROC,
Flags: syscall.EV_ADD,
Fflags: syscall.NOTE_EXIT,
Data: 0,
Udata: nil,
}
for {
events := make([]syscall.Kevent_t, 1)
n, err := syscall.Kevent(kq, []syscall.Kevent_t{ev1}, events, nil)
if err != nil {
log.Println("Error creating kevent")
}
if n > 0 {
break
}
}
fmt.Println("fin")
}
Works fine, but wondering how to implement/achieve the same on linux since I think kqueue not available on it, any ideas ?
One solution would be to use the netlink proc connector, which is a socket the kernel uses to let userspace know about different process events. The official documentation is somewhat lacking, although there are a couple of good examples in C which are probably better to read.
The main caveat to using the proc connector is the process must be run as root. If running your program as a non-root user is a requirement, you should consider other options, such as periodically polling /proc to watch for changes. Any approach which uses polling, as others have pointed out, is susceptible to a race condition if the process is terminated and another one is started with the same PID in between polls.
Anyway, to use the proc connector in Go, we will have to do some translation from C. Specifically, we need to define the proc_event and exit_proc_event structs from cn_proc.h, and the cn_msg and cb_id structs from connector.h.
// CbID corresponds to cb_id in connector.h
type CbID struct {
Idx uint32
Val uint32
}
// CnMsg corresponds to cn_msg in connector.h
type CnMsg struct {
ID CbID
Seq uint32
Ack uint32
Len uint16
Flags uint16
}
// ProcEventHeader corresponds to proc_event in cn_proc.h
type ProcEventHeader struct {
What uint32
CPU uint32
Timestamp uint64
}
// ExitProcEvent corresponds to exit_proc_event in cn_proc.h
type ExitProcEvent struct {
ProcessPid uint32
ProcessTgid uint32
ExitCode uint32
ExitSignal uint32
}
We also need to make a netlink socket and call bind.
sock, err := unix.Socket(unix.AF_NETLINK, unix.SOCK_DGRAM, unix.NETLINK_CONNECTOR)
if err != nil {
fmt.Println("socket: %v", err)
return
}
addr := &unix.SockaddrNetlink{Family: unix.AF_NETLINK, Groups: C.CN_IDX_PROC, Pid: uint32(os.Getpid())}
err = unix.Bind(sock, addr)
if err != nil {
fmt.Printf("bind: %v\n", err)
return
}
Next, we have to send the PROC_CN_MCAST_LISTEN message to the kernel to let it know we want to receive events. We can import this directly from C, where it's defined as an enum, to save some typing, and put it in a function since we will have to call it again with PROC_CN_MCAST_IGNORE when we are done receiving data from the kernel.
// #include <linux/cn_proc.h>
// #include <linux/connector.h>
import "C"
func send(sock int, msg uint32) error {
destAddr := &unix.SockaddrNetlink{Family: unix.AF_NETLINK, Groups: C.CN_IDX_PROC, Pid: 0} // the kernel
cnMsg := CnMsg{}
header := unix.NlMsghdr{
Len: unix.NLMSG_HDRLEN + uint32(binary.Size(cnMsg) + binary.Size(msg)),
Type: uint16(unix.NLMSG_DONE),
Flags: 0,
Seq: 1,
Pid: uint32(unix.Getpid()),
}
msg.ID = CbID{Idx: C.CN_IDX_PROC, Val: C.CN_VAL_PROC}
msg.Len = uint16(binary.Size(msg))
msg.Ack = 0
msg.Seq = 1
buf := bytes.NewBuffer(make([]byte, 0, header.Len))
binary.Write(buf, binary.LittleEndian, header)
binary.Write(buf, binary.LittleEndian, cnMsg)
binary.Write(buf, binary.LittleEndian, msg)
return unix.Sendto(sock, buf.Bytes(), 0, destAddr)
}
After we let the kernel know we're ready to receive events, we can receive them on the socket we're created. Once we receive them, we need to parse them, and check for relevant data. We only care about messages that meet the following criteria:
Come from the kernel
Have a header type of NLMSG_DONE
Have a proc_event_header.what value of PROC_EVENT_EXIT
Match our PID
If they meet these criteria, we can extract the relevant process information into a proc_event_exit struct, which contains the PID of the process.
for {
p := make([]byte, 1024)
nr, from, err := unix.Recvfrom(sock, p, 0)
if sockaddrNl, ok := from.(*unix.SockaddrNetlink); !ok || sockaddrNl.Pid != 0 {
continue
}
if err != nil {
fmt.Printf("Recvfrom: %v\n", err)
continue
}
if nr < unix.NLMSG_HDRLEN {
continue
}
// the sys/unix package doesn't include the ParseNetlinkMessage function
nlmessages, err := syscall.ParseNetlinkMessage(p[:nr])
if err != nil {
fmt.Printf("ParseNetlinkMessage: %v\n", err)
continue
}
for _, m := range(nlmessages) {
if m.Header.Type == unix.NLMSG_DONE {
buf := bytes.NewBuffer(m.Data)
msg := &CnMsg{}
hdr := &ProcEventHeader{}
binary.Read(buf, binary.LittleEndian, msg)
binary.Read(buf, binary.LittleEndian, hdr)
if hdr.What == C.PROC_EVENT_EXIT {
event := &ExitProcEvent{}
binary.Read(buf, binary.LittleEndian, event)
pid := int(event.ProcessTgid)
fmt.Printf("%d just exited.\n", pid)
}
}
}
}
A full code example is here.

How do I dump the struct into the byte array without reflection?

I already found encoding/binary package to deal with it, but it depended on reflect package so it didn't work with uncapitalized(that is, unexported) struct fields. However I spent a week to find that problem out, I still have a question: if struct fields should not be exported, how do I dump them easily into binary data?
EDIT: Here's the example. If you capitalize the name of fields of Data struct, that works properly. But Data struct was intended to be an abstract type, so I don't want to export these fields.
package main
import (
"fmt"
"encoding/binary"
"bytes"
)
type Data struct {
id int32
name [16]byte
}
func main() {
d := Data{Id: 1}
copy(d.Name[:], []byte("tree"))
buffer := new(bytes.Buffer)
binary.Write(buffer, binary.LittleEndian, d)
// d was written properly
fmt.Println(buffer.Bytes())
// try to read...
buffer = bytes.NewBuffer(buffer.Bytes())
var e = new(Data)
err := binary.Read(buffer, binary.LittleEndian, e)
fmt.Println(e, err)
}
Your best option would probably be to use the gob package and let your struct implement the GobDecoder and GobEncoder interfaces in order to serialize and deserialize private fields.
This would be safe, platform independent, and efficient. And you have to add those GobEncode and GobDecode functions only on structs with unexported fields, which means you don't clutter the rest of your code.
func (d *Data) GobEncode() ([]byte, error) {
w := new(bytes.Buffer)
encoder := gob.NewEncoder(w)
err := encoder.Encode(d.id)
if err!=nil {
return nil, err
}
err = encoder.Encode(d.name)
if err!=nil {
return nil, err
}
return w.Bytes(), nil
}
func (d *Data) GobDecode(buf []byte) error {
r := bytes.NewBuffer(buf)
decoder := gob.NewDecoder(r)
err := decoder.Decode(&d.id)
if err!=nil {
return err
}
return decoder.Decode(&d.name)
}
func main() {
d := Data{id: 7}
copy(d.name[:], []byte("tree"))
buffer := new(bytes.Buffer)
// writing
enc := gob.NewEncoder(buffer)
err := enc.Encode(d)
if err != nil {
log.Fatal("encode error:", err)
}
// reading
buffer = bytes.NewBuffer(buffer.Bytes())
e := new(Data)
dec := gob.NewDecoder(buffer)
err = dec.Decode(e)
fmt.Println(e, err)
}

Resources