Reputation: 332
I've got a bit of code that unpacks a message read from a UDP socket including the devices MAC address (which the device stores in the message itself). I have found that just assigning the []byte
slice to the struct member copies the address of the MAC address in the buffer. I can copy the value using the copy()
primitive and that only works if I first allocate storage in the destination. The following code works:
// You can edit this code!
// Click here and start typing.
package main
import (
"fmt"
"net"
)
// information about an Orvibo S20 IoT device
type Device struct {
Mac, ReverseMac net.HardwareAddr // MAC address (and reversed)
IsOn bool // power state
}
// function to unpack the info from the Discover reply
func unpackDiscoverResp(buff []byte) Device {
d := Device{}
d.Mac = make([]byte, 6)
copy(d.Mac, buff[7:7+6])
d.ReverseMac = make([]byte, 6)
copy(d.ReverseMac, buff[7+12:7+6+12])
d.IsOn = buff[41] != 0
return d
}
func main() {
s1 := []byte{
0x68, 0x64, 0x00, 0x2a, 0x71, 0x61, 0x00, 0xac, 0xcf, 0x23, 0x36, 0x02, 0x0e, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x0e, 0x02, 0x36, 0x23, 0xcf, 0xac, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x53,
0x4f, 0x43, 0x30, 0x30, 0x35, 0x2e, 0xe1, 0x9d, 0xdc, 0x00}
s2 := []byte{
0x68, 0x64, 0x00, 0x2a, 0x71, 0x61, 0x00, 0xac, 0xcf, 0x23, 0x55, 0xfe, 0x22, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x22, 0xfe, 0x55, 0x23, 0xcf, 0xac, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x53,
0x4f, 0x43, 0x30, 0x30, 0x35, 0x39, 0x87, 0x1b, 0xbc, 0x01}
msgs := [][]byte{s1, s2}
devices := make([]Device, 0)
for _, msg := range msgs {
d := unpackDiscoverResp(msg)
devices = append(devices, d)
fmt.Println(d)
}
//d1 := unpackDiscoverResp(s1)
//devices = append(devices, d1)
//d2 := unpackDiscoverResp(s2)
//devices = append(devices, d2)
//fmt.Println(d1)
fmt.Println(devices)
}
https://play.golang.org/p/vMdNlX5H2H The part I don't like is line 19 (and 21)
d.Mac = make([]byte, 6)
It seems to me that this must be common pattern and there should be a way to get the size required for the storage. Since I've hard coded the slice length I'm copying from I suppose hard coding the storage required is no worse, but in the more general sense I'd prefer to do better. (In C I'd use the sizeof
operator but this isn't C.)
Thanks!
Upvotes: 1
Views: 92
Reputation: 332
Thanks - len()
is the key. I replaced the original code with
// copyMac allocates space for and copies MAC addr from slice
func copyMac(dst *net.HardwareAddr, src []byte) {
*dst = make([]byte, len(src))
copy(*dst, src)
}
// Code to find and send commands to Orvibo S20 WiFi controlled outlets
// The outlets need to brought into the network first (AKA paired)
// and for that see pair.go
func unpackDiscoverResp(ip *net.UDPAddr, buff []byte) Device {
d := Device{IpAddr: *ip}
copyMac(&d.Mac, buff[7:7+6])
copyMac(&d.ReverseMac, buff[7+12:7+6+12])
d.IsOn = buff[41] != 0
return d
}
I'm happier with that than my original code. (I do need to fix that comment which has lost its connection to the code.)
Upvotes: 0
Reputation: 417797
In Go use the builtin len()
function. Store the result of slicing, and use len()
on it:
func unpackDiscoverResp(buff []byte) Device {
d := Device{}
mac := buff[7:7+6]
d.Mac = make([]byte, len(mac))
copy(d.Mac, mac)
reverseMac := buff[7+12:7+6+12]
d.ReverseMac = make([]byte, len(reverseMac))
copy(d.ReverseMac, reverseMac)
d.IsOn = buff[41] != 0
return d
}
But if you just want to make a copy of it, it's easier / shorter to just append to a nil
slice (the builtin append()
function allocates storage as needed):
func unpackDiscoverResp(buff []byte) Device {
d := Device{}
d.Mac = append(d.Mac, buff[7:7+6]...)
d.ReverseMac = append(d.ReverseMac, buff[7+12:7+6+12]...)
d.IsOn = buff[41] != 0
return d
}
Or simply in the composite literal:
d := Device{
Mac: append([]byte(nil), buff[7:7+6]...),
ReverseMac: append([]byte(nil), buff[7+12:7+6+12]...),
}
One thing to note here: append()
might allocate a bigger backing array than what is needed (it "optimizes" for future appends), so if you want to avoid that, the first solution with copy()
is better.
Upvotes: 2
Reputation: 79614
Can you use len()
?
newBuf := make([]byte,len(oldBuf))
copy(newBuf, oldBuf)
If you have more complex requirements, please explain them.
Upvotes: 2