确定[]byte所需存储的更好方法

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

Better way to determine required storage for []byte

问题

我有一段代码,用于解包从UDP套接字读取的消息,包括设备的MAC地址(设备将其存储在消息本身中)。我发现,只是将[]byte切片分配给结构体成员会复制缓冲区中MAC地址的地址。我可以使用copy()原语复制值,但前提是首先在目标中分配存储空间。以下代码可以工作:

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

}

我不喜欢的部分是第19行(和第21行)

d.Mac = make([]byte, 6)

在我看来,这必须是一个常见的模式,应该有一种方法来获取所需的存储空间大小。由于我已经硬编码了要复制的切片长度,我想硬编码所需的存储空间也不会更糟,但从更一般的意义上说,我希望能做得更好。(在C中,我会使用sizeof运算符,但这不是C。)

谢谢!

英文:

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!

答案1

得分: 2

你可以使用 len() 函数。

newBuf := make([]byte, len(oldBuf))
copy(newBuf, oldBuf)

如果你有更复杂的需求,请解释清楚。

英文:

Can you use len()?

newBuf := make([]byte,len(oldBuf))
copy(newBuf, oldBuf)

If you have more complex requirements, please explain them.

答案2

得分: 2

在Go语言中,可以使用内置的len()函数。将切片的结果存储起来,并对其使用len()函数:

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
}

但是,如果你只想复制它,更简单/更短的方法是将其附加到一个nil切片上(内置的append()函数会根据需要分配存储空间):

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
}

或者可以直接在复合字面量中使用:

d := Device{
    Mac:        append([]byte(nil), buff[7:7+6]...),
    ReverseMac: append([]byte(nil), buff[7+12:7+6+12]...),
}

这里需要注意的一点是,append()函数可能会分配比所需更大的后备数组(它会为未来的附加操作进行“优化”),所以如果你想避免这种情况,第一种使用copy()的解决方案更好。

英文:

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.

答案3

得分: 0

谢谢 - len() 是关键。我用下面的代码替换了原始代码:

// copyMac 从切片中分配空间并复制 MAC 地址
func copyMac(dst *net.HardwareAddr, src []byte) {
*dst = make([]byte, len(src))
copy(*dst, src)
}
// 用于查找并发送命令到 Orvibo S20 WiFi 控制插座的代码
// 插座需要先加入网络(也称为配对)
// 有关此过程,请参见 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
}

我对这个代码比原来的代码更满意。(我确实需要修复那个已经与代码失去联系的注释。)

英文:

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

huangapple
  • 本文由 发表于 2017年4月18日 22:25:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/43474910.html
匿名

发表评论

匿名网友

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

确定