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

3
  • 13
    You cannot, there are no bitfields in Go. You can do type event uint8 and do the bitfiddling yourself. Commented Jun 22, 2021 at 11:09
  • 5
    Have you gathered empirical evidence that the memory footprint of your struct type actually is a problem? How about making its fields unexported now so you can be free to change its implementation later? Commented Jun 22, 2021 at 11:29
  • 3
    Bits are not individually addressable at the hardware level, which is why you have to use bitmasking like in any other language. Commented Jun 22, 2021 at 12:30

1 Answer 1

14

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: Difference between some operators "|", "^", "&", "&^". Golang

Sign up to request clarification or add additional context in comments.

1 Comment

thanks... @icza will try this solution and hoping this will fulfil my requirements.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.