forked from mdlayher/ethtool
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbitset_linux.go
84 lines (71 loc) · 2.23 KB
/
bitset_linux.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
//go:build linux
// +build linux
package ethtool
import (
"fmt"
"github.com/mdlayher/netlink"
"github.com/mdlayher/netlink/nlenc"
"golang.org/x/sys/unix"
)
// A bitset is a compact bitset used by ethtool netlink.
type bitset []uint32
// newBitset creates a bitset from a netlink attribute decoder by parsing the
// various fields and applying a mask to the set if needed.
func newBitset(ad *netlink.AttributeDecoder) (bitset, error) {
// Bitsets are represented as a slice of contiguous uint32 which each
// contain bits. By default, the mask bitset is applied to values unless
// we explicitly find the NOMASK flag.
var (
values, mask bitset
doMask = true
)
for ad.Next() {
switch ad.Type() {
case unix.ETHTOOL_A_BITSET_NOMASK:
doMask = false
case unix.ETHTOOL_A_BITSET_SIZE:
// Convert number of bits to number of bytes, rounded up to the
// nearest 32 bits for a uint32 boundary.
n := (ad.Uint32() + 31) / 32
values = make(bitset, n)
if doMask {
mask = make(bitset, n)
}
case unix.ETHTOOL_A_BITSET_VALUE:
ad.Do(values.decode)
case unix.ETHTOOL_A_BITSET_MASK:
ad.Do(mask.decode)
}
}
// Do a quick check for errors before making use of the bitsets. Normally
// this will be called in a nested attribute decoder context and we could
// skip this, but we don't want to return an invalid bitset.
if err := ad.Err(); err != nil {
return nil, err
}
// Mask by default unless the caller told us not to.
if doMask {
for i := 0; i < len(values); i++ {
values[i] &= mask[i]
}
}
return values, nil
}
// decode returns a function which parses a compact bitset into a preallocated
// bitset. The bitset must be preallocated with the appropriate length and
// capacity for the length of the input data.
func (bs *bitset) decode(b []byte) error {
if len(b)/4 != len(*bs) {
return fmt.Errorf("ethtool: cannot store %d bytes in bitset with length %d",
len(b), len(*bs))
}
for i := 0; i < len(*bs); i++ {
(*bs)[i] = nlenc.Uint32(b[i*4 : (i*4)+4])
}
return nil
}
// test is like the ethnl_bitmap32_test_bit() function in the Linux kernel: it
// reports whether the bit with the specified index is set in the bitset.
func (bs *bitset) test(idx int) bool {
return (*bs)[idx/32]&(1<<(idx%32)) != 0
}