How to use bit instead of bool in golang structure?

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

How to use bit instead of bool in golang structure?

问题

你好!根据你提供的代码,你想将结构体类型的大小从字节转换为位。实际上,Go语言中的结构体字段大小是以字节为单位的,无法直接以位为单位表示。如果你想使用位来表示这些字段,你可以使用位掩码(bitmask)来实现。通过将每个字段映射到一个位,你可以使用位运算来设置和检查每个字段的状态。希望这可以帮助到你!

英文:
type EventPrefs struct {
	Call          bool
	Presence      bool 
	Endpoint      bool
	VoiceMail     bool
	CallRecording bool
}

Currently, the size of that struct type is 5 bytes but I would like to use bits. Is there any way to do that?

答案1

得分: 13

在Go语言中没有"bit"类型,所以如果你想将多个bool信息打包成位(bit),你需要自己实现。声明一个uint8(或uint16或其他整数类型)类型的字段,并提供获取/设置字段特定位的方法。

一般的位设置/清除操作如下所示:

var masks = []uint8{0x01, 0x02, 0x04, 0x08, 0x10}

func set(field, data uint8, b bool) uint8 {
    if b {
        return data | masks[field] // 设置位
    }
    return data ^ masks[field] // 清除位
}

func get(field, data uint8) bool {
    return data&masks[field] != 0
}

将5个bool字段打包到一个uint8值中:

type EventPrefs struct {
    data uint8
}

func (e *EventPrefs) SetCall(b bool) { e.data = set(0, e.data, b) }
func (e *EventPrefs) Call() bool     { return get(0, e.data) }

func (e *EventPrefs) SetPresence(b bool) { e.data = set(1, e.data, b) }
func (e *EventPrefs) Presence() bool     { return get(1, e.data) }

func (e *EventPrefs) SetEndpoint(b bool) { e.data = set(2, e.data, b) }
func (e *EventPrefs) Endpoint() bool     { return get(2, e.data) }

func (e *EventPrefs) SetVoiceMail(b bool) { e.data = set(3, e.data, b) }
func (e *EventPrefs) VoiceMail() bool     { return get(3, e.data) }

func (e *EventPrefs) SetCallRecording(b bool) { e.data = set(4, e.data, b) }
func (e *EventPrefs) CallRecording() bool     { return get(4, e.data) }

进行测试:

ep := &EventPrefs{}

fmt.Println("Calls:", ep.Call(), ep.data)
ep.SetCall(true)
fmt.Println("Calls:", ep.Call(), ep.data)

fmt.Println("Presence:", ep.Presence(), ep.data)
ep.SetPresence(true)
fmt.Println("Presence:", ep.Presence(), ep.data)
ep.SetPresence(false)
fmt.Println("Presence:", ep.Presence(), ep.data)

输出结果为:

Calls: false 0
Calls: true 1
Presence: false 1
Presence: true 3
Presence: false 1

是否值得为了节省4个字节而麻烦一番?很少情况下会这样做。

注意:上述解决方案有很多变种。例如,可以使用位移来"计算"掩码,set()get()函数可以成为EventPrefs的方法,这样就不需要data参数(set()可以直接设置EventPrefs.data字段,因此不需要返回值)。如果set()仍然是一个函数,data参数可以是一个指针,这样set()可以改变指向的值而不返回新的data等等。data字段可以有自己声明的类型,例如bitpack,并附加get()set()方法。

参考链接:https://stackoverflow.com/questions/28432398/difference-between-some-operators-golang/28433370#28433370

英文:

There is no "bit" type in Go, so if you want to pack multiple bool information into bits, you have to implement it yourself. Declare a field of type uint8 (or uint16 or any other integer type), and provide methods that get / set specific bits of the field.

General bit setting / clearing is as simple as this:

var masks = []uint8{0x01, 0x02, 0x04, 0x08, 0x10}

func set(field, data uint8, b bool) uint8 {
	if b {
		return data | masks[field] // Set bit
	}
	return data ^ masks[field] // Clear bit
}

func get(field, data uint8) bool {
	return data&masks[field] != 0
}

Packing your 5 bool fields into an uint8 value:

type EventPrefs struct {
	data uint8
}

func (e *EventPrefs) SetCall(b bool) { e.data = set(0, e.data, b) }
func (e *EventPrefs) Call() bool     { return get(0, e.data) }

func (e *EventPrefs) SetPresence(b bool) { e.data = set(1, e.data, b) }
func (e *EventPrefs) Presence() bool     { return get(1, e.data) }

func (e *EventPrefs) SetEndpoint(b bool) { e.data = set(2, e.data, b) }
func (e *EventPrefs) Endpoint() bool     { return get(2, e.data) }

func (e *EventPrefs) SetVoiceMail(b bool) { e.data = set(3, e.data, b) }
func (e *EventPrefs) VoiceMail() bool     { return get(3, e.data) }

func (e *EventPrefs) SetCallRecording(b bool) { e.data = set(4, e.data, b) }
func (e *EventPrefs) CallRecording() bool     { return get(4, e.data) }

Testing it:

ep := &EventPrefs{}

fmt.Println("Calls:", ep.Call(), ep.data)
ep.SetCall(true)
fmt.Println("Calls:", ep.Call(), ep.data)

fmt.Println("Presence:", ep.Presence(), ep.data)
ep.SetPresence(true)
fmt.Println("Presence:", ep.Presence(), ep.data)
ep.SetPresence(false)
fmt.Println("Presence:", ep.Presence(), ep.data)

Which outputs (try it on the Go Playground):

Calls: false 0
Calls: true 1
Presence: false 1
Presence: true 3
Presence: false 1

Is saving 4 bytes worth the hassle? Rarely.

Note: the above solution can have many variations. For example the masks can be "computed" using bitshifts, the set() and get() functions could be methods of EventPrefs and so the data parameter would not be needed (and set() could directly set the EventPrefs.data field so no return value would be needed either). If set() remains a function, the data param could be a pointer so set() could change the pointed value without returning the new data etc. The data field may have its own declared type e.g. bitpack with get() and set() methods attached to it.

See related: https://stackoverflow.com/questions/28432398/difference-between-some-operators-golang/28433370#28433370

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

发表评论

匿名网友

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

确定