Browse code

vendor: github.com/opencontainers/selinux v1.6.0

full diff: https://github.com/opencontainers/selinux/compare/v1.5.2...v1.6.0

This also adds a new dependency: github.com/willf/bitset

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>

Sebastiaan van Stijn authored on 2020/08/31 22:20:51
Showing 21 changed files
... ...
@@ -169,7 +169,8 @@ github.com/morikuni/aec                             39771216ff4c63d11f5e604076f9
169 169
 # metrics
170 170
 github.com/docker/go-metrics                        b619b3592b65de4f087d9f16863a7e6ff905973c # v0.0.1
171 171
 
172
-github.com/opencontainers/selinux                   c688bba66d7ecb448819836b96f9c416da8b0746 # v1.5.2
172
+github.com/opencontainers/selinux                   25504e34a9826d481f6e2903963ecaa881749124 # v1.6.0
173
+github.com/willf/bitset                             559910e8471e48d76d9e5a1ba15842dee77ad45d # v1.1.11
173 174
 
174 175
 
175 176
 # archive/tar
... ...
@@ -4,7 +4,11 @@
4 4
 
5 5
 Common SELinux package used across the container ecosystem.
6 6
 
7
-Please see the [godoc](https://godoc.org/github.com/opencontainers/selinux) for more information.
7
+## Usage
8
+
9
+When compiling consumers of this project, the `selinux` build tag must be used to enable selinux functionality.
10
+
11
+For complete documentation, see [godoc](https://godoc.org/github.com/opencontainers/selinux).
8 12
 
9 13
 ## Code of Conduct
10 14
 
11 15
new file mode 100644
... ...
@@ -0,0 +1,21 @@
0
+/*
1
+Package selinux provides a high-level interface for interacting with selinux.
2
+
3
+This package uses a selinux build tag to enable the selinux functionality. This
4
+allows non-linux and linux users who do not have selinux support to still use
5
+tools that rely on this library.
6
+
7
+To compile with full selinux support use the -tags=selinux option in your build
8
+and test commands.
9
+
10
+Usage:
11
+
12
+	import "github.com/opencontainers/selinux/go-selinux"
13
+
14
+	// Ensure that selinux is enforcing mode.
15
+	if selinux.EnforceMode() != selinux.Enforcing {
16
+		selinux.SetEnforceMode(selinux.Enforcing)
17
+	}
18
+
19
+*/
20
+package selinux
... ...
@@ -73,9 +73,9 @@ func InitLabels(options []string) (plabel string, mlabel string, Err error) {
73 73
 				selinux.ReleaseLabel(processLabel)
74 74
 			}
75 75
 			processLabel = pcon.Get()
76
-			mountLabel = mcon.Get()
77 76
 			selinux.ReserveLabel(processLabel)
78 77
 		}
78
+		mountLabel = mcon.Get()
79 79
 	}
80 80
 	return processLabel, mountLabel, nil
81 81
 }
... ...
@@ -30,7 +30,6 @@ func Relabel(path string, fileLabel string, shared bool) error {
30 30
 // DisableSecOpt returns a security opt that can disable labeling
31 31
 // support for future container processes
32 32
 func DisableSecOpt() []string {
33
-	// TODO the selinux.DisableSecOpt stub returns []string{"disable"} instead of "nil"
34 33
 	return nil
35 34
 }
36 35
 
37 36
new file mode 100644
... ...
@@ -0,0 +1,249 @@
0
+package selinux
1
+
2
+import (
3
+	"github.com/pkg/errors"
4
+)
5
+
6
+const (
7
+	// Enforcing constant indicate SELinux is in enforcing mode
8
+	Enforcing = 1
9
+	// Permissive constant to indicate SELinux is in permissive mode
10
+	Permissive = 0
11
+	// Disabled constant to indicate SELinux is disabled
12
+	Disabled = -1
13
+
14
+	// DefaultCategoryRange is the upper bound on the category range
15
+	DefaultCategoryRange = uint32(1024)
16
+)
17
+
18
+var (
19
+	// ErrMCSAlreadyExists is returned when trying to allocate a duplicate MCS.
20
+	ErrMCSAlreadyExists = errors.New("MCS label already exists")
21
+	// ErrEmptyPath is returned when an empty path has been specified.
22
+	ErrEmptyPath = errors.New("empty path")
23
+
24
+	// InvalidLabel is returned when an invalid label is specified.
25
+	InvalidLabel = errors.New("Invalid Label")
26
+
27
+	// ErrIncomparable is returned two levels are not comparable
28
+	ErrIncomparable = errors.New("incomparable levels")
29
+	// ErrLevelSyntax is returned when a sensitivity or category do not have correct syntax in a level
30
+	ErrLevelSyntax = errors.New("invalid level syntax")
31
+
32
+	// CategoryRange allows the upper bound on the category range to be adjusted
33
+	CategoryRange = DefaultCategoryRange
34
+)
35
+
36
+// Context is a representation of the SELinux label broken into 4 parts
37
+type Context map[string]string
38
+
39
+// SetDisabled disables SELinux support for the package
40
+func SetDisabled() {
41
+	setDisabled()
42
+}
43
+
44
+// GetEnabled returns whether SELinux is currently enabled.
45
+func GetEnabled() bool {
46
+	return getEnabled()
47
+}
48
+
49
+// ClassIndex returns the int index for an object class in the loaded policy,
50
+// or -1 and an error
51
+func ClassIndex(class string) (int, error) {
52
+	return classIndex(class)
53
+}
54
+
55
+// SetFileLabel sets the SELinux label for this path or returns an error.
56
+func SetFileLabel(fpath string, label string) error {
57
+	return setFileLabel(fpath, label)
58
+}
59
+
60
+// FileLabel returns the SELinux label for this path or returns an error.
61
+func FileLabel(fpath string) (string, error) {
62
+	return fileLabel(fpath)
63
+}
64
+
65
+// SetFSCreateLabel tells kernel the label to create all file system objects
66
+// created by this task. Setting label="" to return to default.
67
+func SetFSCreateLabel(label string) error {
68
+	return setFSCreateLabel(label)
69
+}
70
+
71
+// FSCreateLabel returns the default label the kernel which the kernel is using
72
+// for file system objects created by this task. "" indicates default.
73
+func FSCreateLabel() (string, error) {
74
+	return fsCreateLabel()
75
+}
76
+
77
+// CurrentLabel returns the SELinux label of the current process thread, or an error.
78
+func CurrentLabel() (string, error) {
79
+	return currentLabel()
80
+}
81
+
82
+// PidLabel returns the SELinux label of the given pid, or an error.
83
+func PidLabel(pid int) (string, error) {
84
+	return pidLabel(pid)
85
+}
86
+
87
+// ExecLabel returns the SELinux label that the kernel will use for any programs
88
+// that are executed by the current process thread, or an error.
89
+func ExecLabel() (string, error) {
90
+	return execLabel()
91
+}
92
+
93
+// CanonicalizeContext takes a context string and writes it to the kernel
94
+// the function then returns the context that the kernel will use. Use this
95
+// function to check if two contexts are equivalent
96
+func CanonicalizeContext(val string) (string, error) {
97
+	return canonicalizeContext(val)
98
+}
99
+
100
+// ComputeCreateContext requests the type transition from source to target for
101
+// class from the kernel.
102
+func ComputeCreateContext(source string, target string, class string) (string, error) {
103
+	return computeCreateContext(source, target, class)
104
+}
105
+
106
+// CalculateGlbLub computes the glb (greatest lower bound) and lub (least upper bound)
107
+// of a source and target range.
108
+// The glblub is calculated as the greater of the low sensitivities and
109
+// the lower of the high sensitivities and the and of each category bitset.
110
+func CalculateGlbLub(sourceRange, targetRange string) (string, error) {
111
+	return calculateGlbLub(sourceRange, targetRange)
112
+}
113
+
114
+// SetExecLabel sets the SELinux label that the kernel will use for any programs
115
+// that are executed by the current process thread, or an error.
116
+func SetExecLabel(label string) error {
117
+	return setExecLabel(label)
118
+}
119
+
120
+// SetTaskLabel sets the SELinux label for the current thread, or an error.
121
+// This requires the dyntransition permission.
122
+func SetTaskLabel(label string) error {
123
+	return setTaskLabel(label)
124
+}
125
+
126
+// SetSocketLabel takes a process label and tells the kernel to assign the
127
+// label to the next socket that gets created
128
+func SetSocketLabel(label string) error {
129
+	return setSocketLabel(label)
130
+}
131
+
132
+// SocketLabel retrieves the current socket label setting
133
+func SocketLabel() (string, error) {
134
+	return socketLabel()
135
+}
136
+
137
+// PeerLabel retrieves the label of the client on the other side of a socket
138
+func PeerLabel(fd uintptr) (string, error) {
139
+	return peerLabel(fd)
140
+}
141
+
142
+// SetKeyLabel takes a process label and tells the kernel to assign the
143
+// label to the next kernel keyring that gets created
144
+func SetKeyLabel(label string) error {
145
+	return setKeyLabel(label)
146
+}
147
+
148
+// KeyLabel retrieves the current kernel keyring label setting
149
+func KeyLabel() (string, error) {
150
+	return keyLabel()
151
+}
152
+
153
+// Get returns the Context as a string
154
+func (c Context) Get() string {
155
+	return c.get()
156
+}
157
+
158
+// NewContext creates a new Context struct from the specified label
159
+func NewContext(label string) (Context, error) {
160
+	return newContext(label)
161
+}
162
+
163
+// ClearLabels clears all reserved labels
164
+func ClearLabels() {
165
+	clearLabels()
166
+}
167
+
168
+// ReserveLabel reserves the MLS/MCS level component of the specified label
169
+func ReserveLabel(label string) {
170
+	reserveLabel(label)
171
+}
172
+
173
+// EnforceMode returns the current SELinux mode Enforcing, Permissive, Disabled
174
+func EnforceMode() int {
175
+	return enforceMode()
176
+}
177
+
178
+// SetEnforceMode sets the current SELinux mode Enforcing, Permissive.
179
+// Disabled is not valid, since this needs to be set at boot time.
180
+func SetEnforceMode(mode int) error {
181
+	return setEnforceMode(mode)
182
+}
183
+
184
+// DefaultEnforceMode returns the systems default SELinux mode Enforcing,
185
+// Permissive or Disabled. Note this is is just the default at boot time.
186
+// EnforceMode tells you the systems current mode.
187
+func DefaultEnforceMode() int {
188
+	return defaultEnforceMode()
189
+}
190
+
191
+// ReleaseLabel un-reserves the MLS/MCS Level field of the specified label,
192
+// allowing it to be used by another process.
193
+func ReleaseLabel(label string) {
194
+	releaseLabel(label)
195
+}
196
+
197
+// ROFileLabel returns the specified SELinux readonly file label
198
+func ROFileLabel() string {
199
+	return roFileLabel()
200
+}
201
+
202
+// KVMContainerLabels returns the default processLabel and mountLabel to be used
203
+// for kvm containers by the calling process.
204
+func KVMContainerLabels() (string, string) {
205
+	return kvmContainerLabels()
206
+}
207
+
208
+// InitContainerLabels returns the default processLabel and file labels to be
209
+// used for containers running an init system like systemd by the calling process.
210
+func InitContainerLabels() (string, string) {
211
+	return initContainerLabels()
212
+}
213
+
214
+// ContainerLabels returns an allocated processLabel and fileLabel to be used for
215
+// container labeling by the calling process.
216
+func ContainerLabels() (processLabel string, fileLabel string) {
217
+	return containerLabels()
218
+}
219
+
220
+// SecurityCheckContext validates that the SELinux label is understood by the kernel
221
+func SecurityCheckContext(val string) error {
222
+	return securityCheckContext(val)
223
+}
224
+
225
+// CopyLevel returns a label with the MLS/MCS level from src label replaced on
226
+// the dest label.
227
+func CopyLevel(src, dest string) (string, error) {
228
+	return copyLevel(src, dest)
229
+}
230
+
231
+// Chcon changes the fpath file object to the SELinux label label.
232
+// If fpath is a directory and recurse is true, then Chcon walks the
233
+// directory tree setting the label.
234
+func Chcon(fpath string, label string, recurse bool) error {
235
+	return chcon(fpath, label, recurse)
236
+}
237
+
238
+// DupSecOpt takes an SELinux process label and returns security options that
239
+// can be used to set the SELinux Type and Level for future container processes.
240
+func DupSecOpt(src string) ([]string, error) {
241
+	return dupSecOpt(src)
242
+}
243
+
244
+// DisableSecOpt returns a security opt that can be used to disable SELinux
245
+// labeling support for future container processes.
246
+func DisableSecOpt() []string {
247
+	return disableSecOpt()
248
+}
... ...
@@ -20,17 +20,12 @@ import (
20 20
 
21 21
 	"github.com/opencontainers/selinux/pkg/pwalk"
22 22
 	"github.com/pkg/errors"
23
+	"github.com/willf/bitset"
23 24
 	"golang.org/x/sys/unix"
24 25
 )
25 26
 
26 27
 const (
27
-	// Enforcing constant indicate SELinux is in enforcing mode
28
-	Enforcing = 1
29
-	// Permissive constant to indicate SELinux is in permissive mode
30
-	Permissive = 0
31
-	// Disabled constant to indicate SELinux is disabled
32
-	Disabled = -1
33
-
28
+	minSensLen       = 2
34 29
 	contextFile      = "/usr/share/containers/selinux/contexts"
35 30
 	selinuxDir       = "/etc/selinux/"
36 31
 	selinuxConfig    = selinuxDir + "config"
... ...
@@ -49,17 +44,27 @@ type selinuxState struct {
49 49
 	sync.Mutex
50 50
 }
51 51
 
52
+type level struct {
53
+	sens uint
54
+	cats *bitset.BitSet
55
+}
56
+
57
+type mlsRange struct {
58
+	low  *level
59
+	high *level
60
+}
61
+
62
+type levelItem byte
63
+
64
+const (
65
+	sensitivity levelItem = 's'
66
+	category    levelItem = 'c'
67
+)
68
+
52 69
 var (
53
-	// ErrMCSAlreadyExists is returned when trying to allocate a duplicate MCS.
54
-	ErrMCSAlreadyExists = errors.New("MCS label already exists")
55
-	// ErrEmptyPath is returned when an empty path has been specified.
56
-	ErrEmptyPath = errors.New("empty path")
57
-	// InvalidLabel is returned when an invalid label is specified.
58
-	InvalidLabel = errors.New("Invalid Label")
59
-
60
-	assignRegex = regexp.MustCompile(`^([^=]+)=(.*)$`)
61
-	roFileLabel string
62
-	state       = selinuxState{
70
+	assignRegex       = regexp.MustCompile(`^([^=]+)=(.*)$`)
71
+	readOnlyFileLabel string
72
+	state             = selinuxState{
63 73
 		mcsList: make(map[string]bool),
64 74
 	}
65 75
 
... ...
@@ -68,9 +73,6 @@ var (
68 68
 	haveThreadSelf bool
69 69
 )
70 70
 
71
-// Context is a representation of the SELinux label broken into 4 parts
72
-type Context map[string]string
73
-
74 71
 func (s *selinuxState) setEnable(enabled bool) bool {
75 72
 	s.Lock()
76 73
 	defer s.Unlock()
... ...
@@ -97,8 +99,8 @@ func (s *selinuxState) getEnabled() bool {
97 97
 	return s.setEnable(enabled)
98 98
 }
99 99
 
100
-// SetDisabled disables selinux support for the package
101
-func SetDisabled() {
100
+// setDisabled disables SELinux support for the package
101
+func setDisabled() {
102 102
 	state.setEnable(false)
103 103
 }
104 104
 
... ...
@@ -190,15 +192,15 @@ func (s *selinuxState) getSELinuxfs() string {
190 190
 
191 191
 // getSelinuxMountPoint returns the path to the mountpoint of an selinuxfs
192 192
 // filesystem or an empty string if no mountpoint is found.  Selinuxfs is
193
-// a proc-like pseudo-filesystem that exposes the selinux policy API to
193
+// a proc-like pseudo-filesystem that exposes the SELinux policy API to
194 194
 // processes.  The existence of an selinuxfs mount is used to determine
195
-// whether selinux is currently enabled or not.
195
+// whether SELinux is currently enabled or not.
196 196
 func getSelinuxMountPoint() string {
197 197
 	return state.getSELinuxfs()
198 198
 }
199 199
 
200
-// GetEnabled returns whether selinux is currently enabled.
201
-func GetEnabled() bool {
200
+// getEnabled returns whether SELinux is currently enabled.
201
+func getEnabled() bool {
202 202
 	return state.getEnabled()
203 203
 }
204 204
 
... ...
@@ -282,8 +284,9 @@ func readCon(fpath string) (string, error) {
282 282
 	return strings.Trim(retval, "\x00"), nil
283 283
 }
284 284
 
285
-// ClassIndex returns the int index for an object class in the loaded policy, or -1 and an error
286
-func ClassIndex(class string) (int, error) {
285
+// classIndex returns the int index for an object class in the loaded policy,
286
+// or -1 and an error
287
+func classIndex(class string) (int, error) {
287 288
 	permpath := fmt.Sprintf("class/%s/index", class)
288 289
 	indexpath := filepath.Join(getSelinuxMountPoint(), permpath)
289 290
 
... ...
@@ -299,8 +302,8 @@ func ClassIndex(class string) (int, error) {
299 299
 	return index, nil
300 300
 }
301 301
 
302
-// SetFileLabel sets the SELinux label for this path or returns an error.
303
-func SetFileLabel(fpath string, label string) error {
302
+// setFileLabel sets the SELinux label for this path or returns an error.
303
+func setFileLabel(fpath string, label string) error {
304 304
 	if fpath == "" {
305 305
 		return ErrEmptyPath
306 306
 	}
... ...
@@ -310,8 +313,8 @@ func SetFileLabel(fpath string, label string) error {
310 310
 	return nil
311 311
 }
312 312
 
313
-// FileLabel returns the SELinux label for this path or returns an error.
314
-func FileLabel(fpath string) (string, error) {
313
+// fileLabel returns the SELinux label for this path or returns an error.
314
+func fileLabel(fpath string) (string, error) {
315 315
 	if fpath == "" {
316 316
 		return "", ErrEmptyPath
317 317
 	}
... ...
@@ -327,37 +330,31 @@ func FileLabel(fpath string) (string, error) {
327 327
 	return string(label), nil
328 328
 }
329 329
 
330
-/*
331
-SetFSCreateLabel tells kernel the label to create all file system objects
332
-created by this task. Setting label="" to return to default.
333
-*/
334
-func SetFSCreateLabel(label string) error {
330
+// setFSCreateLabel tells kernel the label to create all file system objects
331
+// created by this task. Setting label="" to return to default.
332
+func setFSCreateLabel(label string) error {
335 333
 	return writeAttr("fscreate", label)
336 334
 }
337 335
 
338
-/*
339
-FSCreateLabel returns the default label the kernel which the kernel is using
340
-for file system objects created by this task. "" indicates default.
341
-*/
342
-func FSCreateLabel() (string, error) {
336
+// fsCreateLabel returns the default label the kernel which the kernel is using
337
+// for file system objects created by this task. "" indicates default.
338
+func fsCreateLabel() (string, error) {
343 339
 	return readAttr("fscreate")
344 340
 }
345 341
 
346
-// CurrentLabel returns the SELinux label of the current process thread, or an error.
347
-func CurrentLabel() (string, error) {
342
+// currentLabel returns the SELinux label of the current process thread, or an error.
343
+func currentLabel() (string, error) {
348 344
 	return readAttr("current")
349 345
 }
350 346
 
351
-// PidLabel returns the SELinux label of the given pid, or an error.
352
-func PidLabel(pid int) (string, error) {
347
+// pidLabel returns the SELinux label of the given pid, or an error.
348
+func pidLabel(pid int) (string, error) {
353 349
 	return readCon(fmt.Sprintf("/proc/%d/attr/current", pid))
354 350
 }
355 351
 
356
-/*
357
-ExecLabel returns the SELinux label that the kernel will use for any programs
358
-that are executed by the current process thread, or an error.
359
-*/
360
-func ExecLabel() (string, error) {
352
+// ExecLabel returns the SELinux label that the kernel will use for any programs
353
+// that are executed by the current process thread, or an error.
354
+func execLabel() (string, error) {
361 355
 	return readAttr("exec")
362 356
 }
363 357
 
... ...
@@ -366,7 +363,7 @@ func writeCon(fpath, val string) error {
366 366
 		return ErrEmptyPath
367 367
 	}
368 368
 	if val == "" {
369
-		if !GetEnabled() {
369
+		if !getEnabled() {
370 370
 			return nil
371 371
 		}
372 372
 	}
... ...
@@ -418,20 +415,17 @@ func writeAttr(attr, val string) error {
418 418
 	return writeCon(attrPath(attr), val)
419 419
 }
420 420
 
421
-/*
422
-CanonicalizeContext takes a context string and writes it to the kernel
423
-the function then returns the context that the kernel will use.  This function
424
-can be used to see if two contexts are equivalent
425
-*/
426
-func CanonicalizeContext(val string) (string, error) {
421
+// canonicalizeContext takes a context string and writes it to the kernel
422
+// the function then returns the context that the kernel will use. Use this
423
+// function to check if two contexts are equivalent
424
+func canonicalizeContext(val string) (string, error) {
427 425
 	return readWriteCon(filepath.Join(getSelinuxMountPoint(), "context"), val)
428 426
 }
429 427
 
430
-/*
431
-ComputeCreateContext requests the type transition from source to target for class  from the kernel.
432
-*/
433
-func ComputeCreateContext(source string, target string, class string) (string, error) {
434
-	classidx, err := ClassIndex(class)
428
+// computeCreateContext requests the type transition from source to target for
429
+// class from the kernel.
430
+func computeCreateContext(source string, target string, class string) (string, error) {
431
+	classidx, err := classIndex(class)
435 432
 	if err != nil {
436 433
 		return "", err
437 434
 	}
... ...
@@ -439,6 +433,217 @@ func ComputeCreateContext(source string, target string, class string) (string, e
439 439
 	return readWriteCon(filepath.Join(getSelinuxMountPoint(), "create"), fmt.Sprintf("%s %s %d", source, target, classidx))
440 440
 }
441 441
 
442
+// catsToBitset stores categories in a bitset.
443
+func catsToBitset(cats string) (*bitset.BitSet, error) {
444
+	bitset := &bitset.BitSet{}
445
+
446
+	catlist := strings.Split(cats, ",")
447
+	for _, r := range catlist {
448
+		ranges := strings.SplitN(r, ".", 2)
449
+		if len(ranges) > 1 {
450
+			catstart, err := parseLevelItem(ranges[0], category)
451
+			if err != nil {
452
+				return nil, err
453
+			}
454
+			catend, err := parseLevelItem(ranges[1], category)
455
+			if err != nil {
456
+				return nil, err
457
+			}
458
+			for i := catstart; i <= catend; i++ {
459
+				bitset.Set(i)
460
+			}
461
+		} else {
462
+			cat, err := parseLevelItem(ranges[0], category)
463
+			if err != nil {
464
+				return nil, err
465
+			}
466
+			bitset.Set(cat)
467
+		}
468
+	}
469
+
470
+	return bitset, nil
471
+}
472
+
473
+// parseLevelItem parses and verifies that a sensitivity or category are valid
474
+func parseLevelItem(s string, sep levelItem) (uint, error) {
475
+	if len(s) < minSensLen || levelItem(s[0]) != sep {
476
+		return 0, ErrLevelSyntax
477
+	}
478
+	val, err := strconv.ParseUint(s[1:], 10, 32)
479
+	if err != nil {
480
+		return 0, err
481
+	}
482
+
483
+	return uint(val), nil
484
+}
485
+
486
+// parseLevel fills a level from a string that contains
487
+// a sensitivity and categories
488
+func (l *level) parseLevel(levelStr string) error {
489
+	lvl := strings.SplitN(levelStr, ":", 2)
490
+	sens, err := parseLevelItem(lvl[0], sensitivity)
491
+	if err != nil {
492
+		return errors.Wrap(err, "failed to parse sensitivity")
493
+	}
494
+	l.sens = sens
495
+	if len(lvl) > 1 {
496
+		cats, err := catsToBitset(lvl[1])
497
+		if err != nil {
498
+			return errors.Wrap(err, "failed to parse categories")
499
+		}
500
+		l.cats = cats
501
+	}
502
+
503
+	return nil
504
+}
505
+
506
+// rangeStrToMLSRange marshals a string representation of a range.
507
+func rangeStrToMLSRange(rangeStr string) (*mlsRange, error) {
508
+	mlsRange := &mlsRange{}
509
+	levelSlice := strings.SplitN(rangeStr, "-", 2)
510
+
511
+	switch len(levelSlice) {
512
+	// rangeStr that has a low and a high level, e.g. s4:c0.c1023-s6:c0.c1023
513
+	case 2:
514
+		mlsRange.high = &level{}
515
+		if err := mlsRange.high.parseLevel(levelSlice[1]); err != nil {
516
+			return nil, errors.Wrapf(err, "failed to parse high level %q", levelSlice[1])
517
+		}
518
+		fallthrough
519
+	// rangeStr that is single level, e.g. s6:c0,c3,c5,c30.c1023
520
+	case 1:
521
+		mlsRange.low = &level{}
522
+		if err := mlsRange.low.parseLevel(levelSlice[0]); err != nil {
523
+			return nil, errors.Wrapf(err, "failed to parse low level %q", levelSlice[0])
524
+		}
525
+	}
526
+
527
+	if mlsRange.high == nil {
528
+		mlsRange.high = mlsRange.low
529
+	}
530
+
531
+	return mlsRange, nil
532
+}
533
+
534
+// bitsetToStr takes a category bitset and returns it in the
535
+// canonical selinux syntax
536
+func bitsetToStr(c *bitset.BitSet) string {
537
+	var str string
538
+	i, e := c.NextSet(0)
539
+	len := 0
540
+	for e {
541
+		if len == 0 {
542
+			if str != "" {
543
+				str += ","
544
+			}
545
+			str += "c" + strconv.Itoa(int(i))
546
+		}
547
+
548
+		next, e := c.NextSet(i + 1)
549
+		if e {
550
+			// consecutive cats
551
+			if next == i+1 {
552
+				len++
553
+				i = next
554
+				continue
555
+			}
556
+		}
557
+		if len == 1 {
558
+			str += ",c" + strconv.Itoa(int(i))
559
+		} else if len > 1 {
560
+			str += ".c" + strconv.Itoa(int(i))
561
+		}
562
+		if !e {
563
+			break
564
+		}
565
+		len = 0
566
+		i = next
567
+	}
568
+
569
+	return str
570
+}
571
+
572
+func (l1 *level) equal(l2 *level) bool {
573
+	if l2 == nil || l1 == nil {
574
+		return l1 == l2
575
+	}
576
+	if l1.sens != l2.sens {
577
+		return false
578
+	}
579
+	return l1.cats.Equal(l2.cats)
580
+}
581
+
582
+// String returns an mlsRange as a string.
583
+func (m mlsRange) String() string {
584
+	low := "s" + strconv.Itoa(int(m.low.sens))
585
+	if m.low.cats != nil && m.low.cats.Count() > 0 {
586
+		low += ":" + bitsetToStr(m.low.cats)
587
+	}
588
+
589
+	if m.low.equal(m.high) {
590
+		return low
591
+	}
592
+
593
+	high := "s" + strconv.Itoa(int(m.high.sens))
594
+	if m.high.cats != nil && m.high.cats.Count() > 0 {
595
+		high += ":" + bitsetToStr(m.high.cats)
596
+	}
597
+
598
+	return low + "-" + high
599
+}
600
+
601
+func max(a, b uint) uint {
602
+	if a > b {
603
+		return a
604
+	}
605
+	return b
606
+}
607
+
608
+func min(a, b uint) uint {
609
+	if a < b {
610
+		return a
611
+	}
612
+	return b
613
+}
614
+
615
+// calculateGlbLub computes the glb (greatest lower bound) and lub (least upper bound)
616
+// of a source and target range.
617
+// The glblub is calculated as the greater of the low sensitivities and
618
+// the lower of the high sensitivities and the and of each category bitset.
619
+func calculateGlbLub(sourceRange, targetRange string) (string, error) {
620
+	s, err := rangeStrToMLSRange(sourceRange)
621
+	if err != nil {
622
+		return "", err
623
+	}
624
+	t, err := rangeStrToMLSRange(targetRange)
625
+	if err != nil {
626
+		return "", err
627
+	}
628
+
629
+	if s.high.sens < t.low.sens || t.high.sens < s.low.sens {
630
+		/* these ranges have no common sensitivities */
631
+		return "", ErrIncomparable
632
+	}
633
+
634
+	outrange := &mlsRange{low: &level{}, high: &level{}}
635
+
636
+	/* take the greatest of the low */
637
+	outrange.low.sens = max(s.low.sens, t.low.sens)
638
+
639
+	/* take the least of the high */
640
+	outrange.high.sens = min(s.high.sens, t.high.sens)
641
+
642
+	/* find the intersecting categories */
643
+	if s.low.cats != nil && t.low.cats != nil {
644
+		outrange.low.cats = s.low.cats.Intersection(t.low.cats)
645
+	}
646
+	if s.high.cats != nil && t.high.cats != nil {
647
+		outrange.high.cats = s.high.cats.Intersection(t.high.cats)
648
+	}
649
+
650
+	return outrange.String(), nil
651
+}
652
+
442 653
 func readWriteCon(fpath string, val string) (string, error) {
443 654
 	if fpath == "" {
444 655
 		return "", ErrEmptyPath
... ...
@@ -461,41 +666,37 @@ func readWriteCon(fpath string, val string) (string, error) {
461 461
 	return strings.Trim(retval, "\x00"), nil
462 462
 }
463 463
 
464
-/*
465
-SetExecLabel sets the SELinux label that the kernel will use for any programs
466
-that are executed by the current process thread, or an error.
467
-*/
468
-func SetExecLabel(label string) error {
464
+// setExecLabel sets the SELinux label that the kernel will use for any programs
465
+// that are executed by the current process thread, or an error.
466
+func setExecLabel(label string) error {
469 467
 	return writeAttr("exec", label)
470 468
 }
471 469
 
472
-/*
473
-SetTaskLabel sets the SELinux label for the current thread, or an error.
474
-This requires the dyntransition permission.
475
-*/
476
-func SetTaskLabel(label string) error {
470
+// setTaskLabel sets the SELinux label for the current thread, or an error.
471
+// This requires the dyntransition permission.
472
+func setTaskLabel(label string) error {
477 473
 	return writeAttr("current", label)
478 474
 }
479 475
 
480
-// SetSocketLabel takes a process label and tells the kernel to assign the
476
+// setSocketLabel takes a process label and tells the kernel to assign the
481 477
 // label to the next socket that gets created
482
-func SetSocketLabel(label string) error {
478
+func setSocketLabel(label string) error {
483 479
 	return writeAttr("sockcreate", label)
484 480
 }
485 481
 
486
-// SocketLabel retrieves the current socket label setting
487
-func SocketLabel() (string, error) {
482
+// socketLabel retrieves the current socket label setting
483
+func socketLabel() (string, error) {
488 484
 	return readAttr("sockcreate")
489 485
 }
490 486
 
491
-// PeerLabel retrieves the label of the client on the other side of a socket
492
-func PeerLabel(fd uintptr) (string, error) {
487
+// peerLabel retrieves the label of the client on the other side of a socket
488
+func peerLabel(fd uintptr) (string, error) {
493 489
 	return unix.GetsockoptString(int(fd), unix.SOL_SOCKET, unix.SO_PEERSEC)
494 490
 }
495 491
 
496
-// SetKeyLabel takes a process label and tells the kernel to assign the
492
+// setKeyLabel takes a process label and tells the kernel to assign the
497 493
 // label to the next kernel keyring that gets created
498
-func SetKeyLabel(label string) error {
494
+func setKeyLabel(label string) error {
499 495
 	err := writeCon("/proc/self/attr/keycreate", label)
500 496
 	if os.IsNotExist(errors.Cause(err)) {
501 497
 		return nil
... ...
@@ -506,21 +707,21 @@ func SetKeyLabel(label string) error {
506 506
 	return err
507 507
 }
508 508
 
509
-// KeyLabel retrieves the current kernel keyring label setting
510
-func KeyLabel() (string, error) {
509
+// keyLabel retrieves the current kernel keyring label setting
510
+func keyLabel() (string, error) {
511 511
 	return readCon("/proc/self/attr/keycreate")
512 512
 }
513 513
 
514
-// Get returns the Context as a string
515
-func (c Context) Get() string {
514
+// get returns the Context as a string
515
+func (c Context) get() string {
516 516
 	if c["level"] != "" {
517 517
 		return fmt.Sprintf("%s:%s:%s:%s", c["user"], c["role"], c["type"], c["level"])
518 518
 	}
519 519
 	return fmt.Sprintf("%s:%s:%s", c["user"], c["role"], c["type"])
520 520
 }
521 521
 
522
-// NewContext creates a new Context struct from the specified label
523
-func NewContext(label string) (Context, error) {
522
+// newContext creates a new Context struct from the specified label
523
+func newContext(label string) (Context, error) {
524 524
 	c := make(Context)
525 525
 
526 526
 	if len(label) != 0 {
... ...
@@ -538,15 +739,15 @@ func NewContext(label string) (Context, error) {
538 538
 	return c, nil
539 539
 }
540 540
 
541
-// ClearLabels clears all reserved labels
542
-func ClearLabels() {
541
+// clearLabels clears all reserved labels
542
+func clearLabels() {
543 543
 	state.Lock()
544 544
 	state.mcsList = make(map[string]bool)
545 545
 	state.Unlock()
546 546
 }
547 547
 
548
-// ReserveLabel reserves the MLS/MCS level component of the specified label
549
-func ReserveLabel(label string) {
548
+// reserveLabel reserves the MLS/MCS level component of the specified label
549
+func reserveLabel(label string) {
550 550
 	if len(label) != 0 {
551 551
 		con := strings.SplitN(label, ":", 4)
552 552
 		if len(con) > 3 {
... ...
@@ -559,8 +760,8 @@ func selinuxEnforcePath() string {
559 559
 	return path.Join(getSelinuxMountPoint(), "enforce")
560 560
 }
561 561
 
562
-// EnforceMode returns the current SELinux mode Enforcing, Permissive, Disabled
563
-func EnforceMode() int {
562
+// enforceMode returns the current SELinux mode Enforcing, Permissive, Disabled
563
+func enforceMode() int {
564 564
 	var enforce int
565 565
 
566 566
 	enforceB, err := ioutil.ReadFile(selinuxEnforcePath())
... ...
@@ -574,20 +775,16 @@ func EnforceMode() int {
574 574
 	return enforce
575 575
 }
576 576
 
577
-/*
578
-SetEnforceMode sets the current SELinux mode Enforcing, Permissive.
579
-Disabled is not valid, since this needs to be set at boot time.
580
-*/
581
-func SetEnforceMode(mode int) error {
577
+// setEnforceMode sets the current SELinux mode Enforcing, Permissive.
578
+// Disabled is not valid, since this needs to be set at boot time.
579
+func setEnforceMode(mode int) error {
582 580
 	return ioutil.WriteFile(selinuxEnforcePath(), []byte(strconv.Itoa(mode)), 0644)
583 581
 }
584 582
 
585
-/*
586
-DefaultEnforceMode returns the systems default SELinux mode Enforcing,
587
-Permissive or Disabled. Note this is is just the default at boot time.
588
-EnforceMode tells you the systems current mode.
589
-*/
590
-func DefaultEnforceMode() int {
583
+// defaultEnforceMode returns the systems default SELinux mode Enforcing,
584
+// Permissive or Disabled. Note this is is just the default at boot time.
585
+// EnforceMode tells you the systems current mode.
586
+func defaultEnforceMode() int {
591 587
 	switch readConfig(selinuxTag) {
592 588
 	case "enforcing":
593 589
 		return Enforcing
... ...
@@ -667,11 +864,9 @@ func uniqMcs(catRange uint32) string {
667 667
 	return mcs
668 668
 }
669 669
 
670
-/*
671
-ReleaseLabel will unreserve the MLS/MCS Level field of the specified label.
672
-Allowing it to be used by another process.
673
-*/
674
-func ReleaseLabel(label string) {
670
+// releaseLabel un-reserves the MLS/MCS Level field of the specified label,
671
+// allowing it to be used by another process.
672
+func releaseLabel(label string) {
675 673
 	if len(label) != 0 {
676 674
 		con := strings.SplitN(label, ":", 4)
677 675
 		if len(con) > 3 {
... ...
@@ -680,9 +875,9 @@ func ReleaseLabel(label string) {
680 680
 	}
681 681
 }
682 682
 
683
-// ROFileLabel returns the specified SELinux readonly file label
684
-func ROFileLabel() string {
685
-	return roFileLabel
683
+// roFileLabel returns the specified SELinux readonly file label
684
+func roFileLabel() string {
685
+	return readOnlyFileLabel
686 686
 }
687 687
 
688 688
 func openContextFile() (*os.File, error) {
... ...
@@ -737,11 +932,9 @@ func loadLabels() map[string]string {
737 737
 	return labels
738 738
 }
739 739
 
740
-/*
741
-KVMContainerLabels returns the default processLabel and mountLabel to be used
742
-for kvm containers by the calling process.
743
-*/
744
-func KVMContainerLabels() (string, string) {
740
+// kvmContainerLabels returns the default processLabel and mountLabel to be used
741
+// for kvm containers by the calling process.
742
+func kvmContainerLabels() (string, string) {
745 743
 	processLabel := labels["kvm_process"]
746 744
 	if processLabel == "" {
747 745
 		processLabel = labels["process"]
... ...
@@ -750,11 +943,9 @@ func KVMContainerLabels() (string, string) {
750 750
 	return addMcs(processLabel, labels["file"])
751 751
 }
752 752
 
753
-/*
754
-InitContainerLabels returns the default processLabel and file labels to be
755
-used for containers running an init system like systemd by the calling process.
756
-*/
757
-func InitContainerLabels() (string, string) {
753
+// initContainerLabels returns the default processLabel and file labels to be
754
+// used for containers running an init system like systemd by the calling process.
755
+func initContainerLabels() (string, string) {
758 756
 	processLabel := labels["init_process"]
759 757
 	if processLabel == "" {
760 758
 		processLabel = labels["process"]
... ...
@@ -763,25 +954,23 @@ func InitContainerLabels() (string, string) {
763 763
 	return addMcs(processLabel, labels["file"])
764 764
 }
765 765
 
766
-/*
767
-ContainerLabels returns an allocated processLabel and fileLabel to be used for
768
-container labeling by the calling process.
769
-*/
770
-func ContainerLabels() (processLabel string, fileLabel string) {
771
-	if !GetEnabled() {
766
+// containerLabels returns an allocated processLabel and fileLabel to be used for
767
+// container labeling by the calling process.
768
+func containerLabels() (processLabel string, fileLabel string) {
769
+	if !getEnabled() {
772 770
 		return "", ""
773 771
 	}
774 772
 
775 773
 	processLabel = labels["process"]
776 774
 	fileLabel = labels["file"]
777
-	roFileLabel = labels["ro_file"]
775
+	readOnlyFileLabel = labels["ro_file"]
778 776
 
779 777
 	if processLabel == "" || fileLabel == "" {
780 778
 		return "", fileLabel
781 779
 	}
782 780
 
783
-	if roFileLabel == "" {
784
-		roFileLabel = fileLabel
781
+	if readOnlyFileLabel == "" {
782
+		readOnlyFileLabel = fileLabel
785 783
 	}
786 784
 
787 785
 	return addMcs(processLabel, fileLabel)
... ...
@@ -790,7 +979,7 @@ func ContainerLabels() (processLabel string, fileLabel string) {
790 790
 func addMcs(processLabel, fileLabel string) (string, string) {
791 791
 	scon, _ := NewContext(processLabel)
792 792
 	if scon["level"] != "" {
793
-		mcs := uniqMcs(1024)
793
+		mcs := uniqMcs(CategoryRange)
794 794
 		scon["level"] = mcs
795 795
 		processLabel = scon.Get()
796 796
 		scon, _ = NewContext(fileLabel)
... ...
@@ -800,16 +989,14 @@ func addMcs(processLabel, fileLabel string) (string, string) {
800 800
 	return processLabel, fileLabel
801 801
 }
802 802
 
803
-// SecurityCheckContext validates that the SELinux label is understood by the kernel
804
-func SecurityCheckContext(val string) error {
803
+// securityCheckContext validates that the SELinux label is understood by the kernel
804
+func securityCheckContext(val string) error {
805 805
 	return ioutil.WriteFile(path.Join(getSelinuxMountPoint(), "context"), []byte(val), 0644)
806 806
 }
807 807
 
808
-/*
809
-CopyLevel returns a label with the MLS/MCS level from src label replaced on
810
-the dest label.
811
-*/
812
-func CopyLevel(src, dest string) (string, error) {
808
+// copyLevel returns a label with the MLS/MCS level from src label replaced on
809
+// the dest label.
810
+func copyLevel(src, dest string) (string, error) {
813 811
 	if src == "" {
814 812
 		return "", nil
815 813
 	}
... ...
@@ -833,7 +1020,7 @@ func CopyLevel(src, dest string) (string, error) {
833 833
 	return tcon.Get(), nil
834 834
 }
835 835
 
836
-// Prevent users from relabing system files
836
+// Prevent users from relabeling system files
837 837
 func badPrefix(fpath string) error {
838 838
 	if fpath == "" {
839 839
 		return ErrEmptyPath
... ...
@@ -848,10 +1035,10 @@ func badPrefix(fpath string) error {
848 848
 	return nil
849 849
 }
850 850
 
851
-// Chcon changes the fpath file object to the SELinux label label.
852
-// If fpath is a directory and recurse is true, Chcon will walk the
851
+// chcon changes the fpath file object to the SELinux label label.
852
+// If fpath is a directory and recurse is true, then chcon walks the
853 853
 // directory tree setting the label.
854
-func Chcon(fpath string, label string, recurse bool) error {
854
+func chcon(fpath string, label string, recurse bool) error {
855 855
 	if fpath == "" {
856 856
 		return ErrEmptyPath
857 857
 	}
... ...
@@ -876,9 +1063,9 @@ func Chcon(fpath string, label string, recurse bool) error {
876 876
 	})
877 877
 }
878 878
 
879
-// DupSecOpt takes an SELinux process label and returns security options that
879
+// dupSecOpt takes an SELinux process label and returns security options that
880 880
 // can be used to set the SELinux Type and Level for future container processes.
881
-func DupSecOpt(src string) ([]string, error) {
881
+func dupSecOpt(src string) ([]string, error) {
882 882
 	if src == "" {
883 883
 		return nil, nil
884 884
 	}
... ...
@@ -903,8 +1090,8 @@ func DupSecOpt(src string) ([]string, error) {
903 903
 	return dup, nil
904 904
 }
905 905
 
906
-// DisableSecOpt returns a security opt that can be used to disable SELinux
906
+// disableSecOpt returns a security opt that can be used to disable SELinux
907 907
 // labeling support for future container processes.
908
-func DisableSecOpt() []string {
908
+func disableSecOpt() []string {
909 909
 	return []string{"disable"}
910 910
 }
... ...
@@ -2,253 +2,147 @@
2 2
 
3 3
 package selinux
4 4
 
5
-import (
6
-	"errors"
7
-)
8
-
9
-const (
10
-	// Enforcing constant indicate SELinux is in enforcing mode
11
-	Enforcing = 1
12
-	// Permissive constant to indicate SELinux is in permissive mode
13
-	Permissive = 0
14
-	// Disabled constant to indicate SELinux is disabled
15
-	Disabled = -1
16
-)
17
-
18
-var (
19
-	// ErrMCSAlreadyExists is returned when trying to allocate a duplicate MCS.
20
-	ErrMCSAlreadyExists = errors.New("MCS label already exists")
21
-	// ErrEmptyPath is returned when an empty path has been specified.
22
-	ErrEmptyPath = errors.New("empty path")
23
-)
24
-
25
-// Context is a representation of the SELinux label broken into 4 parts
26
-type Context map[string]string
27
-
28
-// SetDisabled disables selinux support for the package
29
-func SetDisabled() {
30
-	return
31
-}
32
-
33
-// GetEnabled returns whether selinux is currently enabled.
34
-func GetEnabled() bool {
5
+func setDisabled() {
6
+}
7
+
8
+func getEnabled() bool {
35 9
 	return false
36 10
 }
37 11
 
38
-// ClassIndex returns the int index for an object class in the loaded policy, or -1 and an error
39
-func ClassIndex(class string) (int, error) {
12
+func classIndex(class string) (int, error) {
40 13
 	return -1, nil
41 14
 }
42 15
 
43
-// SetFileLabel sets the SELinux label for this path or returns an error.
44
-func SetFileLabel(fpath string, label string) error {
16
+func setFileLabel(fpath string, label string) error {
45 17
 	return nil
46 18
 }
47 19
 
48
-// FileLabel returns the SELinux label for this path or returns an error.
49
-func FileLabel(fpath string) (string, error) {
20
+func fileLabel(fpath string) (string, error) {
50 21
 	return "", nil
51 22
 }
52 23
 
53
-/*
54
-SetFSCreateLabel tells kernel the label to create all file system objects
55
-created by this task. Setting label="" to return to default.
56
-*/
57
-func SetFSCreateLabel(label string) error {
24
+func setFSCreateLabel(label string) error {
58 25
 	return nil
59 26
 }
60 27
 
61
-/*
62
-FSCreateLabel returns the default label the kernel which the kernel is using
63
-for file system objects created by this task. "" indicates default.
64
-*/
65
-func FSCreateLabel() (string, error) {
28
+func fsCreateLabel() (string, error) {
29
+	return "", nil
30
+}
31
+
32
+func currentLabel() (string, error) {
66 33
 	return "", nil
67 34
 }
68 35
 
69
-// CurrentLabel returns the SELinux label of the current process thread, or an error.
70
-func CurrentLabel() (string, error) {
36
+func pidLabel(pid int) (string, error) {
71 37
 	return "", nil
72 38
 }
73 39
 
74
-// PidLabel returns the SELinux label of the given pid, or an error.
75
-func PidLabel(pid int) (string, error) {
40
+func execLabel() (string, error) {
76 41
 	return "", nil
77 42
 }
78 43
 
79
-/*
80
-ExecLabel returns the SELinux label that the kernel will use for any programs
81
-that are executed by the current process thread, or an error.
82
-*/
83
-func ExecLabel() (string, error) {
44
+func canonicalizeContext(val string) (string, error) {
84 45
 	return "", nil
85 46
 }
86 47
 
87
-/*
88
-CanonicalizeContext takes a context string and writes it to the kernel
89
-the function then returns the context that the kernel will use.  This function
90
-can be used to see if two contexts are equivalent
91
-*/
92
-func CanonicalizeContext(val string) (string, error) {
48
+func computeCreateContext(source string, target string, class string) (string, error) {
93 49
 	return "", nil
94 50
 }
95 51
 
96
-/*
97
-ComputeCreateContext requests the type transition from source to target for class  from the kernel.
98
-*/
99
-func ComputeCreateContext(source string, target string, class string) (string, error) {
52
+func calculateGlbLub(sourceRange, targetRange string) (string, error) {
100 53
 	return "", nil
101 54
 }
102 55
 
103
-/*
104
-SetExecLabel sets the SELinux label that the kernel will use for any programs
105
-that are executed by the current process thread, or an error.
106
-*/
107
-func SetExecLabel(label string) error {
56
+func setExecLabel(label string) error {
108 57
 	return nil
109 58
 }
110 59
 
111
-/*
112
-SetTaskLabel sets the SELinux label for the current thread, or an error.
113
-This requires the dyntransition permission.
114
-*/
115
-func SetTaskLabel(label string) error {
60
+func setTaskLabel(label string) error {
116 61
 	return nil
117 62
 }
118 63
 
119
-/*
120
-SetSocketLabel sets the SELinux label that the kernel will use for any programs
121
-that are executed by the current process thread, or an error.
122
-*/
123
-func SetSocketLabel(label string) error {
64
+func setSocketLabel(label string) error {
124 65
 	return nil
125 66
 }
126 67
 
127
-// SocketLabel retrieves the current socket label setting
128
-func SocketLabel() (string, error) {
68
+func socketLabel() (string, error) {
129 69
 	return "", nil
130 70
 }
131 71
 
132
-// PeerLabel retrieves the label of the client on the other side of a socket
133
-func PeerLabel(fd uintptr) (string, error) {
72
+func peerLabel(fd uintptr) (string, error) {
134 73
 	return "", nil
135 74
 }
136 75
 
137
-// SetKeyLabel takes a process label and tells the kernel to assign the
138
-// label to the next kernel keyring that gets created
139
-func SetKeyLabel(label string) error {
76
+func setKeyLabel(label string) error {
140 77
 	return nil
141 78
 }
142 79
 
143
-// KeyLabel retrieves the current kernel keyring label setting
144
-func KeyLabel() (string, error) {
80
+func keyLabel() (string, error) {
145 81
 	return "", nil
146 82
 }
147 83
 
148
-// Get returns the Context as a string
149
-func (c Context) Get() string {
84
+func (c Context) get() string {
150 85
 	return ""
151 86
 }
152 87
 
153
-// NewContext creates a new Context struct from the specified label
154
-func NewContext(label string) (Context, error) {
88
+func newContext(label string) (Context, error) {
155 89
 	c := make(Context)
156 90
 	return c, nil
157 91
 }
158 92
 
159
-// ClearLabels clears all reserved MLS/MCS levels
160
-func ClearLabels() {
161
-	return
93
+func clearLabels() {
162 94
 }
163 95
 
164
-// ReserveLabel reserves the MLS/MCS level component of the specified label
165
-func ReserveLabel(label string) {
166
-	return
96
+func reserveLabel(label string) {
167 97
 }
168 98
 
169
-// EnforceMode returns the current SELinux mode Enforcing, Permissive, Disabled
170
-func EnforceMode() int {
99
+func enforceMode() int {
171 100
 	return Disabled
172 101
 }
173 102
 
174
-/*
175
-SetEnforceMode sets the current SELinux mode Enforcing, Permissive.
176
-Disabled is not valid, since this needs to be set at boot time.
177
-*/
178
-func SetEnforceMode(mode int) error {
103
+func setEnforceMode(mode int) error {
179 104
 	return nil
180 105
 }
181 106
 
182
-/*
183
-DefaultEnforceMode returns the systems default SELinux mode Enforcing,
184
-Permissive or Disabled. Note this is is just the default at boot time.
185
-EnforceMode tells you the systems current mode.
186
-*/
187
-func DefaultEnforceMode() int {
107
+func defaultEnforceMode() int {
188 108
 	return Disabled
189 109
 }
190 110
 
191
-/*
192
-ReleaseLabel will unreserve the MLS/MCS Level field of the specified label.
193
-Allowing it to be used by another process.
194
-*/
195
-func ReleaseLabel(label string) {
196
-	return
111
+func releaseLabel(label string) {
197 112
 }
198 113
 
199
-// ROFileLabel returns the specified SELinux readonly file label
200
-func ROFileLabel() string {
114
+func roFileLabel() string {
201 115
 	return ""
202 116
 }
203 117
 
204
-// KVMContainerLabels returns the default processLabel and mountLabel to be used
205
-// for kvm containers by the calling process.
206
-func KVMContainerLabels() (string, string) {
118
+func kvmContainerLabels() (string, string) {
207 119
 	return "", ""
208 120
 }
209 121
 
210
-// InitContainerLabels returns the default processLabel and file labels to be
211
-// used for containers running an init system like systemd by the calling
212
-func InitContainerLabels() (string, string) {
122
+func initContainerLabels() (string, string) {
213 123
 	return "", ""
214 124
 }
215 125
 
216
-/*
217
-ContainerLabels returns an allocated processLabel and fileLabel to be used for
218
-container labeling by the calling process.
219
-*/
220
-func ContainerLabels() (processLabel string, fileLabel string) {
126
+func containerLabels() (processLabel string, fileLabel string) {
221 127
 	return "", ""
222 128
 }
223 129
 
224
-// SecurityCheckContext validates that the SELinux label is understood by the kernel
225
-func SecurityCheckContext(val string) error {
130
+func securityCheckContext(val string) error {
226 131
 	return nil
227 132
 }
228 133
 
229
-/*
230
-CopyLevel returns a label with the MLS/MCS level from src label replaced on
231
-the dest label.
232
-*/
233
-func CopyLevel(src, dest string) (string, error) {
134
+func copyLevel(src, dest string) (string, error) {
234 135
 	return "", nil
235 136
 }
236 137
 
237
-// Chcon changes the `fpath` file object to the SELinux label `label`.
238
-// If `fpath` is a directory and `recurse`` is true, Chcon will walk the
239
-// directory tree setting the label.
240
-func Chcon(fpath string, label string, recurse bool) error {
138
+func chcon(fpath string, label string, recurse bool) error {
241 139
 	return nil
242 140
 }
243 141
 
244
-// DupSecOpt takes an SELinux process label and returns security options that
245
-// can be used to set the SELinux Type and Level for future container processes.
246
-func DupSecOpt(src string) ([]string, error) {
142
+func dupSecOpt(src string) ([]string, error) {
247 143
 	return nil, nil
248 144
 }
249 145
 
250
-// DisableSecOpt returns a security opt that can be used to disable SELinux
251
-// labeling support for future container processes.
252
-func DisableSecOpt() []string {
146
+func disableSecOpt() []string {
253 147
 	return []string{"disable"}
254 148
 }
... ...
@@ -3,6 +3,7 @@ module github.com/opencontainers/selinux
3 3
 go 1.13
4 4
 
5 5
 require (
6
-	github.com/pkg/errors v0.8.1
6
+	github.com/pkg/errors v0.9.1
7
+	github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243
7 8
 	golang.org/x/sys v0.0.0-20191115151921-52ab43148777
8 9
 )
... ...
@@ -48,7 +48,11 @@ func WalkN(root string, walkFn WalkFunc, num int) error {
48 48
 	errCh := make(chan error, 1) // get the first error, ignore others
49 49
 
50 50
 	// Start walking a tree asap
51
-	var err error
51
+	var (
52
+		err error
53
+		wg  sync.WaitGroup
54
+	)
55
+	wg.Add(1)
52 56
 	go func() {
53 57
 		err = filepath.Walk(root, func(p string, info os.FileInfo, err error) error {
54 58
 			if err != nil {
... ...
@@ -68,9 +72,9 @@ func WalkN(root string, walkFn WalkFunc, num int) error {
68 68
 		if err == nil {
69 69
 			close(files)
70 70
 		}
71
+		wg.Done()
71 72
 	}()
72 73
 
73
-	var wg sync.WaitGroup
74 74
 	wg.Add(num)
75 75
 	for i := 0; i < num; i++ {
76 76
 		go func() {
77 77
new file mode 100644
... ...
@@ -0,0 +1,27 @@
0
+Copyright (c) 2014 Will Fitzgerald. All rights reserved.
1
+
2
+Redistribution and use in source and binary forms, with or without
3
+modification, are permitted provided that the following conditions are
4
+met:
5
+
6
+   * Redistributions of source code must retain the above copyright
7
+notice, this list of conditions and the following disclaimer.
8
+   * Redistributions in binary form must reproduce the above
9
+copyright notice, this list of conditions and the following disclaimer
10
+in the documentation and/or other materials provided with the
11
+distribution.
12
+   * Neither the name of Google Inc. nor the names of its
13
+contributors may be used to endorse or promote products derived from
14
+this software without specific prior written permission.
15
+
16
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0 27
new file mode 100644
... ...
@@ -0,0 +1,94 @@
0
+# bitset
1
+
2
+*Go language library to map between non-negative integers and boolean values*
3
+
4
+[![Test](https://github.com/willf/bitset/workflows/Test/badge.svg)](https://github.com/willf/bitset/actions?query=workflow%3ATest)
5
+[![Master Coverage Status](https://coveralls.io/repos/willf/bitset/badge.svg?branch=master&service=github)](https://coveralls.io/github/willf/bitset?branch=master)
6
+[![Go Report Card](https://goreportcard.com/badge/github.com/willf/bitset)](https://goreportcard.com/report/github.com/willf/bitset)
7
+[![PkgGoDev](https://pkg.go.dev/badge/github.com/willf/bitset?tab=doc)](https://pkg.go.dev/github.com/willf/bitset?tab=doc)
8
+
9
+
10
+## Description
11
+
12
+Package bitset implements bitsets, a mapping between non-negative integers and boolean values.
13
+It should be more efficient than map[uint] bool.
14
+
15
+It provides methods for setting, clearing, flipping, and testing individual integers.
16
+
17
+But it also provides set intersection, union, difference, complement, and symmetric operations, as well as tests to check whether any, all, or no bits are set, and querying a bitset's current length and number of positive bits.
18
+
19
+BitSets are expanded to the size of the largest set bit; the memory allocation is approximately Max bits, where Max is the largest set bit. BitSets are never shrunk. On creation, a hint can be given for the number of bits that will be used.
20
+
21
+Many of the methods, including Set, Clear, and Flip, return a BitSet pointer, which allows for chaining.
22
+
23
+### Example use:
24
+
25
+```go
26
+package main
27
+
28
+import (
29
+	"fmt"
30
+	"math/rand"
31
+
32
+	"github.com/willf/bitset"
33
+)
34
+
35
+func main() {
36
+	fmt.Printf("Hello from BitSet!\n")
37
+	var b bitset.BitSet
38
+	// play some Go Fish
39
+	for i := 0; i < 100; i++ {
40
+		card1 := uint(rand.Intn(52))
41
+		card2 := uint(rand.Intn(52))
42
+		b.Set(card1)
43
+		if b.Test(card2) {
44
+			fmt.Println("Go Fish!")
45
+		}
46
+		b.Clear(card1)
47
+	}
48
+
49
+	// Chaining
50
+	b.Set(10).Set(11)
51
+
52
+	for i, e := b.NextSet(0); e; i, e = b.NextSet(i + 1) {
53
+		fmt.Println("The following bit is set:", i)
54
+	}
55
+	if b.Intersection(bitset.New(100).Set(10)).Count() == 1 {
56
+		fmt.Println("Intersection works.")
57
+	} else {
58
+		fmt.Println("Intersection doesn't work???")
59
+	}
60
+}
61
+```
62
+
63
+As an alternative to BitSets, one should check out the 'big' package, which provides a (less set-theoretical) view of bitsets.
64
+
65
+Package documentation is at: https://pkg.go.dev/github.com/willf/bitset?tab=doc
66
+
67
+## Memory Usage
68
+
69
+The memory usage of a bitset using N bits is at least N/8 bytes. The number of bits in a bitset is at least as large as one plus the greatest bit index you have accessed. Thus it is possible to run out of memory while using a bitset. If you have lots of bits, you might prefer compressed bitsets, like the [Roaring bitmaps](http://roaringbitmap.org) and its [Go implementation](https://github.com/RoaringBitmap/roaring).
70
+
71
+## Implementation Note
72
+
73
+Go 1.9 introduced a native `math/bits` library. We provide backward compatibility to Go 1.7, which might be removed.
74
+
75
+It is possible that a later version will match the `math/bits` return signature for counts (which is `int`, rather than our library's `unit64`). If so, the version will be bumped.
76
+
77
+## Installation
78
+
79
+```bash
80
+go get github.com/willf/bitset
81
+```
82
+
83
+## Contributing
84
+
85
+If you wish to contribute to this project, please branch and issue a pull request against master ("[GitHub Flow](https://guides.github.com/introduction/flow/)")
86
+
87
+## Running all tests
88
+
89
+Before committing the code, please check if it passes tests, has adequate coverage, etc.
90
+```bash
91
+go test
92
+go test -cover
93
+```
0 94
new file mode 100644
... ...
@@ -0,0 +1,931 @@
0
+/*
1
+Package bitset implements bitsets, a mapping
2
+between non-negative integers and boolean values. It should be more
3
+efficient than map[uint] bool.
4
+
5
+It provides methods for setting, clearing, flipping, and testing
6
+individual integers.
7
+
8
+But it also provides set intersection, union, difference,
9
+complement, and symmetric operations, as well as tests to
10
+check whether any, all, or no bits are set, and querying a
11
+bitset's current length and number of positive bits.
12
+
13
+BitSets are expanded to the size of the largest set bit; the
14
+memory allocation is approximately Max bits, where Max is
15
+the largest set bit. BitSets are never shrunk. On creation,
16
+a hint can be given for the number of bits that will be used.
17
+
18
+Many of the methods, including Set,Clear, and Flip, return
19
+a BitSet pointer, which allows for chaining.
20
+
21
+Example use:
22
+
23
+	import "bitset"
24
+	var b BitSet
25
+	b.Set(10).Set(11)
26
+	if b.Test(1000) {
27
+		b.Clear(1000)
28
+	}
29
+	if B.Intersection(bitset.New(100).Set(10)).Count() > 1 {
30
+		fmt.Println("Intersection works.")
31
+	}
32
+
33
+As an alternative to BitSets, one should check out the 'big' package,
34
+which provides a (less set-theoretical) view of bitsets.
35
+
36
+*/
37
+package bitset
38
+
39
+import (
40
+	"bufio"
41
+	"bytes"
42
+	"encoding/base64"
43
+	"encoding/binary"
44
+	"encoding/json"
45
+	"errors"
46
+	"fmt"
47
+	"io"
48
+	"strconv"
49
+)
50
+
51
+// the wordSize of a bit set
52
+const wordSize = uint(64)
53
+
54
+// log2WordSize is lg(wordSize)
55
+const log2WordSize = uint(6)
56
+
57
+// allBits has every bit set
58
+const allBits uint64 = 0xffffffffffffffff
59
+
60
+// default binary BigEndian
61
+var binaryOrder binary.ByteOrder = binary.BigEndian
62
+
63
+// default json encoding base64.URLEncoding
64
+var base64Encoding = base64.URLEncoding
65
+
66
+// Base64StdEncoding Marshal/Unmarshal BitSet with base64.StdEncoding(Default: base64.URLEncoding)
67
+func Base64StdEncoding() { base64Encoding = base64.StdEncoding }
68
+
69
+// LittleEndian Marshal/Unmarshal Binary as Little Endian(Default: binary.BigEndian)
70
+func LittleEndian() { binaryOrder = binary.LittleEndian }
71
+
72
+// A BitSet is a set of bits. The zero value of a BitSet is an empty set of length 0.
73
+type BitSet struct {
74
+	length uint
75
+	set    []uint64
76
+}
77
+
78
+// Error is used to distinguish errors (panics) generated in this package.
79
+type Error string
80
+
81
+// safeSet will fixup b.set to be non-nil and return the field value
82
+func (b *BitSet) safeSet() []uint64 {
83
+	if b.set == nil {
84
+		b.set = make([]uint64, wordsNeeded(0))
85
+	}
86
+	return b.set
87
+}
88
+
89
+// From is a constructor used to create a BitSet from an array of integers
90
+func From(buf []uint64) *BitSet {
91
+	return &BitSet{uint(len(buf)) * 64, buf}
92
+}
93
+
94
+// Bytes returns the bitset as array of integers
95
+func (b *BitSet) Bytes() []uint64 {
96
+	return b.set
97
+}
98
+
99
+// wordsNeeded calculates the number of words needed for i bits
100
+func wordsNeeded(i uint) int {
101
+	if i > (Cap() - wordSize + 1) {
102
+		return int(Cap() >> log2WordSize)
103
+	}
104
+	return int((i + (wordSize - 1)) >> log2WordSize)
105
+}
106
+
107
+// New creates a new BitSet with a hint that length bits will be required
108
+func New(length uint) (bset *BitSet) {
109
+	defer func() {
110
+		if r := recover(); r != nil {
111
+			bset = &BitSet{
112
+				0,
113
+				make([]uint64, 0),
114
+			}
115
+		}
116
+	}()
117
+
118
+	bset = &BitSet{
119
+		length,
120
+		make([]uint64, wordsNeeded(length)),
121
+	}
122
+
123
+	return bset
124
+}
125
+
126
+// Cap returns the total possible capacity, or number of bits
127
+func Cap() uint {
128
+	return ^uint(0)
129
+}
130
+
131
+// Len returns the number of bits in the BitSet.
132
+// Note the difference to method Count, see example.
133
+func (b *BitSet) Len() uint {
134
+	return b.length
135
+}
136
+
137
+// extendSetMaybe adds additional words to incorporate new bits if needed
138
+func (b *BitSet) extendSetMaybe(i uint) {
139
+	if i >= b.length { // if we need more bits, make 'em
140
+		if i >= Cap() {
141
+			panic("You are exceeding the capacity")
142
+		}
143
+		nsize := wordsNeeded(i + 1)
144
+		if b.set == nil {
145
+			b.set = make([]uint64, nsize)
146
+		} else if cap(b.set) >= nsize {
147
+			b.set = b.set[:nsize] // fast resize
148
+		} else if len(b.set) < nsize {
149
+			newset := make([]uint64, nsize, 2*nsize) // increase capacity 2x
150
+			copy(newset, b.set)
151
+			b.set = newset
152
+		}
153
+		b.length = i + 1
154
+	}
155
+}
156
+
157
+// Test whether bit i is set.
158
+func (b *BitSet) Test(i uint) bool {
159
+	if i >= b.length {
160
+		return false
161
+	}
162
+	return b.set[i>>log2WordSize]&(1<<(i&(wordSize-1))) != 0
163
+}
164
+
165
+// Set bit i to 1, the capacity of the bitset is automatically
166
+// increased accordingly.
167
+// If i>= Cap(), this function will panic.
168
+// Warning: using a very large value for 'i'
169
+// may lead to a memory shortage and a panic: the caller is responsible
170
+// for providing sensible parameters in line with their memory capacity.
171
+func (b *BitSet) Set(i uint) *BitSet {
172
+	b.extendSetMaybe(i)
173
+	b.set[i>>log2WordSize] |= 1 << (i & (wordSize - 1))
174
+	return b
175
+}
176
+
177
+// Clear bit i to 0
178
+func (b *BitSet) Clear(i uint) *BitSet {
179
+	if i >= b.length {
180
+		return b
181
+	}
182
+	b.set[i>>log2WordSize] &^= 1 << (i & (wordSize - 1))
183
+	return b
184
+}
185
+
186
+// SetTo sets bit i to value.
187
+// If i>= Cap(), this function will panic.
188
+// Warning: using a very large value for 'i'
189
+// may lead to a memory shortage and a panic: the caller is responsible
190
+// for providing sensible parameters in line with their memory capacity.
191
+func (b *BitSet) SetTo(i uint, value bool) *BitSet {
192
+	if value {
193
+		return b.Set(i)
194
+	}
195
+	return b.Clear(i)
196
+}
197
+
198
+// Flip bit at i.
199
+// If i>= Cap(), this function will panic.
200
+// Warning: using a very large value for 'i'
201
+// may lead to a memory shortage and a panic: the caller is responsible
202
+// for providing sensible parameters in line with their memory capacity.
203
+func (b *BitSet) Flip(i uint) *BitSet {
204
+	if i >= b.length {
205
+		return b.Set(i)
206
+	}
207
+	b.set[i>>log2WordSize] ^= 1 << (i & (wordSize - 1))
208
+	return b
209
+}
210
+
211
+// Shrink shrinks BitSet so that the provided value is the last possible
212
+// set value. It clears all bits > the provided index and reduces the size
213
+// and length of the set.
214
+//
215
+// Note that the parameter value is not the new length in bits: it is the
216
+// maximal value that can be stored in the bitset after the function call.
217
+// The new length in bits is the parameter value + 1. Thus it is not possible
218
+// to use this function to set the length to 0, the minimal value of the length
219
+// after this function call is 1.
220
+//
221
+// A new slice is allocated to store the new bits, so you may see an increase in
222
+// memory usage until the GC runs. Normally this should not be a problem, but if you
223
+// have an extremely large BitSet its important to understand that the old BitSet will
224
+// remain in memory until the GC frees it.
225
+func (b *BitSet) Shrink(lastbitindex uint) *BitSet {
226
+	length := lastbitindex + 1
227
+	idx := wordsNeeded(length)
228
+	if idx > len(b.set) {
229
+		return b
230
+	}
231
+	shrunk := make([]uint64, idx)
232
+	copy(shrunk, b.set[:idx])
233
+	b.set = shrunk
234
+	b.length = length
235
+	b.set[idx-1] &= (allBits >> (uint64(64) - uint64(length&(wordSize-1))))
236
+	return b
237
+}
238
+
239
+// Compact shrinks BitSet to so that we preserve all set bits, while minimizing
240
+// memory usage. Compact calls Shrink.
241
+func (b *BitSet) Compact() *BitSet {
242
+	idx := len(b.set) - 1
243
+	for ; idx >= 0 && b.set[idx] == 0; idx-- {
244
+	}
245
+	newlength := uint((idx + 1) << log2WordSize)
246
+	if newlength >= b.length {
247
+		return b // nothing to do
248
+	}
249
+	if newlength > 0 {
250
+		return b.Shrink(newlength - 1)
251
+	}
252
+	// We preserve one word
253
+	return b.Shrink(63)
254
+}
255
+
256
+// InsertAt takes an index which indicates where a bit should be
257
+// inserted. Then it shifts all the bits in the set to the left by 1, starting
258
+// from the given index position, and sets the index position to 0.
259
+//
260
+// Depending on the size of your BitSet, and where you are inserting the new entry,
261
+// this method could be extremely slow and in some cases might cause the entire BitSet
262
+// to be recopied.
263
+func (b *BitSet) InsertAt(idx uint) *BitSet {
264
+	insertAtElement := (idx >> log2WordSize)
265
+
266
+	// if length of set is a multiple of wordSize we need to allocate more space first
267
+	if b.isLenExactMultiple() {
268
+		b.set = append(b.set, uint64(0))
269
+	}
270
+
271
+	var i uint
272
+	for i = uint(len(b.set) - 1); i > insertAtElement; i-- {
273
+		// all elements above the position where we want to insert can simply by shifted
274
+		b.set[i] <<= 1
275
+
276
+		// we take the most significant bit of the previous element and set it as
277
+		// the least significant bit of the current element
278
+		b.set[i] |= (b.set[i-1] & 0x8000000000000000) >> 63
279
+	}
280
+
281
+	// generate a mask to extract the data that we need to shift left
282
+	// within the element where we insert a bit
283
+	dataMask := ^(uint64(1)<<uint64(idx&(wordSize-1)) - 1)
284
+
285
+	// extract that data that we'll shift
286
+	data := b.set[i] & dataMask
287
+
288
+	// set the positions of the data mask to 0 in the element where we insert
289
+	b.set[i] &= ^dataMask
290
+
291
+	// shift data mask to the left and insert its data to the slice element
292
+	b.set[i] |= data << 1
293
+
294
+	// add 1 to length of BitSet
295
+	b.length++
296
+
297
+	return b
298
+}
299
+
300
+// String creates a string representation of the Bitmap
301
+func (b *BitSet) String() string {
302
+	// follows code from https://github.com/RoaringBitmap/roaring
303
+	var buffer bytes.Buffer
304
+	start := []byte("{")
305
+	buffer.Write(start)
306
+	counter := 0
307
+	i, e := b.NextSet(0)
308
+	for e {
309
+		counter = counter + 1
310
+		// to avoid exhausting the memory
311
+		if counter > 0x40000 {
312
+			buffer.WriteString("...")
313
+			break
314
+		}
315
+		buffer.WriteString(strconv.FormatInt(int64(i), 10))
316
+		i, e = b.NextSet(i + 1)
317
+		if e {
318
+			buffer.WriteString(",")
319
+		}
320
+	}
321
+	buffer.WriteString("}")
322
+	return buffer.String()
323
+}
324
+
325
+// DeleteAt deletes the bit at the given index position from
326
+// within the bitset
327
+// All the bits residing on the left of the deleted bit get
328
+// shifted right by 1
329
+// The running time of this operation may potentially be
330
+// relatively slow, O(length)
331
+func (b *BitSet) DeleteAt(i uint) *BitSet {
332
+	// the index of the slice element where we'll delete a bit
333
+	deleteAtElement := i >> log2WordSize
334
+
335
+	// generate a mask for the data that needs to be shifted right
336
+	// within that slice element that gets modified
337
+	dataMask := ^((uint64(1) << (i & (wordSize - 1))) - 1)
338
+
339
+	// extract the data that we'll shift right from the slice element
340
+	data := b.set[deleteAtElement] & dataMask
341
+
342
+	// set the masked area to 0 while leaving the rest as it is
343
+	b.set[deleteAtElement] &= ^dataMask
344
+
345
+	// shift the previously extracted data to the right and then
346
+	// set it in the previously masked area
347
+	b.set[deleteAtElement] |= (data >> 1) & dataMask
348
+
349
+	// loop over all the consecutive slice elements to copy each
350
+	// lowest bit into the highest position of the previous element,
351
+	// then shift the entire content to the right by 1
352
+	for i := int(deleteAtElement) + 1; i < len(b.set); i++ {
353
+		b.set[i-1] |= (b.set[i] & 1) << 63
354
+		b.set[i] >>= 1
355
+	}
356
+
357
+	b.length = b.length - 1
358
+
359
+	return b
360
+}
361
+
362
+// NextSet returns the next bit set from the specified index,
363
+// including possibly the current index
364
+// along with an error code (true = valid, false = no set bit found)
365
+// for i,e := v.NextSet(0); e; i,e = v.NextSet(i + 1) {...}
366
+//
367
+// Users concerned with performance may want to use NextSetMany to
368
+// retrieve several values at once.
369
+func (b *BitSet) NextSet(i uint) (uint, bool) {
370
+	x := int(i >> log2WordSize)
371
+	if x >= len(b.set) {
372
+		return 0, false
373
+	}
374
+	w := b.set[x]
375
+	w = w >> (i & (wordSize - 1))
376
+	if w != 0 {
377
+		return i + trailingZeroes64(w), true
378
+	}
379
+	x = x + 1
380
+	for x < len(b.set) {
381
+		if b.set[x] != 0 {
382
+			return uint(x)*wordSize + trailingZeroes64(b.set[x]), true
383
+		}
384
+		x = x + 1
385
+
386
+	}
387
+	return 0, false
388
+}
389
+
390
+// NextSetMany returns many next bit sets from the specified index,
391
+// including possibly the current index and up to cap(buffer).
392
+// If the returned slice has len zero, then no more set bits were found
393
+//
394
+//    buffer := make([]uint, 256) // this should be reused
395
+//    j := uint(0)
396
+//    j, buffer = bitmap.NextSetMany(j, buffer)
397
+//    for ; len(buffer) > 0; j, buffer = bitmap.NextSetMany(j,buffer) {
398
+//     for k := range buffer {
399
+//      do something with buffer[k]
400
+//     }
401
+//     j += 1
402
+//    }
403
+//
404
+//
405
+// It is possible to retrieve all set bits as follow:
406
+//
407
+//    indices := make([]uint, bitmap.Count())
408
+//    bitmap.NextSetMany(0, indices)
409
+//
410
+// However if bitmap.Count() is large, it might be preferable to
411
+// use several calls to NextSetMany, for performance reasons.
412
+func (b *BitSet) NextSetMany(i uint, buffer []uint) (uint, []uint) {
413
+	myanswer := buffer
414
+	capacity := cap(buffer)
415
+	x := int(i >> log2WordSize)
416
+	if x >= len(b.set) || capacity == 0 {
417
+		return 0, myanswer[:0]
418
+	}
419
+	skip := i & (wordSize - 1)
420
+	word := b.set[x] >> skip
421
+	myanswer = myanswer[:capacity]
422
+	size := int(0)
423
+	for word != 0 {
424
+		r := trailingZeroes64(word)
425
+		t := word & ((^word) + 1)
426
+		myanswer[size] = r + i
427
+		size++
428
+		if size == capacity {
429
+			goto End
430
+		}
431
+		word = word ^ t
432
+	}
433
+	x++
434
+	for idx, word := range b.set[x:] {
435
+		for word != 0 {
436
+			r := trailingZeroes64(word)
437
+			t := word & ((^word) + 1)
438
+			myanswer[size] = r + (uint(x+idx) << 6)
439
+			size++
440
+			if size == capacity {
441
+				goto End
442
+			}
443
+			word = word ^ t
444
+		}
445
+	}
446
+End:
447
+	if size > 0 {
448
+		return myanswer[size-1], myanswer[:size]
449
+	}
450
+	return 0, myanswer[:0]
451
+}
452
+
453
+// NextClear returns the next clear bit from the specified index,
454
+// including possibly the current index
455
+// along with an error code (true = valid, false = no bit found i.e. all bits are set)
456
+func (b *BitSet) NextClear(i uint) (uint, bool) {
457
+	x := int(i >> log2WordSize)
458
+	if x >= len(b.set) {
459
+		return 0, false
460
+	}
461
+	w := b.set[x]
462
+	w = w >> (i & (wordSize - 1))
463
+	wA := allBits >> (i & (wordSize - 1))
464
+	index := i + trailingZeroes64(^w)
465
+	if w != wA && index < b.length {
466
+		return index, true
467
+	}
468
+	x++
469
+	for x < len(b.set) {
470
+		index = uint(x)*wordSize + trailingZeroes64(^b.set[x])
471
+		if b.set[x] != allBits && index < b.length {
472
+			return index, true
473
+		}
474
+		x++
475
+	}
476
+	return 0, false
477
+}
478
+
479
+// ClearAll clears the entire BitSet
480
+func (b *BitSet) ClearAll() *BitSet {
481
+	if b != nil && b.set != nil {
482
+		for i := range b.set {
483
+			b.set[i] = 0
484
+		}
485
+	}
486
+	return b
487
+}
488
+
489
+// wordCount returns the number of words used in a bit set
490
+func (b *BitSet) wordCount() int {
491
+	return len(b.set)
492
+}
493
+
494
+// Clone this BitSet
495
+func (b *BitSet) Clone() *BitSet {
496
+	c := New(b.length)
497
+	if b.set != nil { // Clone should not modify current object
498
+		copy(c.set, b.set)
499
+	}
500
+	return c
501
+}
502
+
503
+// Copy into a destination BitSet
504
+// Returning the size of the destination BitSet
505
+// like array copy
506
+func (b *BitSet) Copy(c *BitSet) (count uint) {
507
+	if c == nil {
508
+		return
509
+	}
510
+	if b.set != nil { // Copy should not modify current object
511
+		copy(c.set, b.set)
512
+	}
513
+	count = c.length
514
+	if b.length < c.length {
515
+		count = b.length
516
+	}
517
+	return
518
+}
519
+
520
+// Count (number of set bits).
521
+// Also known as "popcount" or "popularity count".
522
+func (b *BitSet) Count() uint {
523
+	if b != nil && b.set != nil {
524
+		return uint(popcntSlice(b.set))
525
+	}
526
+	return 0
527
+}
528
+
529
+// Equal tests the equivalence of two BitSets.
530
+// False if they are of different sizes, otherwise true
531
+// only if all the same bits are set
532
+func (b *BitSet) Equal(c *BitSet) bool {
533
+	if c == nil || b == nil {
534
+		return c == b
535
+	}
536
+	if b.length != c.length {
537
+		return false
538
+	}
539
+	if b.length == 0 { // if they have both length == 0, then could have nil set
540
+		return true
541
+	}
542
+	// testing for equality shoud not transform the bitset (no call to safeSet)
543
+
544
+	for p, v := range b.set {
545
+		if c.set[p] != v {
546
+			return false
547
+		}
548
+	}
549
+	return true
550
+}
551
+
552
+func panicIfNull(b *BitSet) {
553
+	if b == nil {
554
+		panic(Error("BitSet must not be null"))
555
+	}
556
+}
557
+
558
+// Difference of base set and other set
559
+// This is the BitSet equivalent of &^ (and not)
560
+func (b *BitSet) Difference(compare *BitSet) (result *BitSet) {
561
+	panicIfNull(b)
562
+	panicIfNull(compare)
563
+	result = b.Clone() // clone b (in case b is bigger than compare)
564
+	l := int(compare.wordCount())
565
+	if l > int(b.wordCount()) {
566
+		l = int(b.wordCount())
567
+	}
568
+	for i := 0; i < l; i++ {
569
+		result.set[i] = b.set[i] &^ compare.set[i]
570
+	}
571
+	return
572
+}
573
+
574
+// DifferenceCardinality computes the cardinality of the differnce
575
+func (b *BitSet) DifferenceCardinality(compare *BitSet) uint {
576
+	panicIfNull(b)
577
+	panicIfNull(compare)
578
+	l := int(compare.wordCount())
579
+	if l > int(b.wordCount()) {
580
+		l = int(b.wordCount())
581
+	}
582
+	cnt := uint64(0)
583
+	cnt += popcntMaskSlice(b.set[:l], compare.set[:l])
584
+	cnt += popcntSlice(b.set[l:])
585
+	return uint(cnt)
586
+}
587
+
588
+// InPlaceDifference computes the difference of base set and other set
589
+// This is the BitSet equivalent of &^ (and not)
590
+func (b *BitSet) InPlaceDifference(compare *BitSet) {
591
+	panicIfNull(b)
592
+	panicIfNull(compare)
593
+	l := int(compare.wordCount())
594
+	if l > int(b.wordCount()) {
595
+		l = int(b.wordCount())
596
+	}
597
+	for i := 0; i < l; i++ {
598
+		b.set[i] &^= compare.set[i]
599
+	}
600
+}
601
+
602
+// Convenience function: return two bitsets ordered by
603
+// increasing length. Note: neither can be nil
604
+func sortByLength(a *BitSet, b *BitSet) (ap *BitSet, bp *BitSet) {
605
+	if a.length <= b.length {
606
+		ap, bp = a, b
607
+	} else {
608
+		ap, bp = b, a
609
+	}
610
+	return
611
+}
612
+
613
+// Intersection of base set and other set
614
+// This is the BitSet equivalent of & (and)
615
+func (b *BitSet) Intersection(compare *BitSet) (result *BitSet) {
616
+	panicIfNull(b)
617
+	panicIfNull(compare)
618
+	b, compare = sortByLength(b, compare)
619
+	result = New(b.length)
620
+	for i, word := range b.set {
621
+		result.set[i] = word & compare.set[i]
622
+	}
623
+	return
624
+}
625
+
626
+// IntersectionCardinality computes the cardinality of the union
627
+func (b *BitSet) IntersectionCardinality(compare *BitSet) uint {
628
+	panicIfNull(b)
629
+	panicIfNull(compare)
630
+	b, compare = sortByLength(b, compare)
631
+	cnt := popcntAndSlice(b.set, compare.set)
632
+	return uint(cnt)
633
+}
634
+
635
+// InPlaceIntersection destructively computes the intersection of
636
+// base set and the compare set.
637
+// This is the BitSet equivalent of & (and)
638
+func (b *BitSet) InPlaceIntersection(compare *BitSet) {
639
+	panicIfNull(b)
640
+	panicIfNull(compare)
641
+	l := int(compare.wordCount())
642
+	if l > int(b.wordCount()) {
643
+		l = int(b.wordCount())
644
+	}
645
+	for i := 0; i < l; i++ {
646
+		b.set[i] &= compare.set[i]
647
+	}
648
+	for i := l; i < len(b.set); i++ {
649
+		b.set[i] = 0
650
+	}
651
+	if compare.length > 0 {
652
+		b.extendSetMaybe(compare.length - 1)
653
+	}
654
+}
655
+
656
+// Union of base set and other set
657
+// This is the BitSet equivalent of | (or)
658
+func (b *BitSet) Union(compare *BitSet) (result *BitSet) {
659
+	panicIfNull(b)
660
+	panicIfNull(compare)
661
+	b, compare = sortByLength(b, compare)
662
+	result = compare.Clone()
663
+	for i, word := range b.set {
664
+		result.set[i] = word | compare.set[i]
665
+	}
666
+	return
667
+}
668
+
669
+// UnionCardinality computes the cardinality of the uniton of the base set
670
+// and the compare set.
671
+func (b *BitSet) UnionCardinality(compare *BitSet) uint {
672
+	panicIfNull(b)
673
+	panicIfNull(compare)
674
+	b, compare = sortByLength(b, compare)
675
+	cnt := popcntOrSlice(b.set, compare.set)
676
+	if len(compare.set) > len(b.set) {
677
+		cnt += popcntSlice(compare.set[len(b.set):])
678
+	}
679
+	return uint(cnt)
680
+}
681
+
682
+// InPlaceUnion creates the destructive union of base set and compare set.
683
+// This is the BitSet equivalent of | (or).
684
+func (b *BitSet) InPlaceUnion(compare *BitSet) {
685
+	panicIfNull(b)
686
+	panicIfNull(compare)
687
+	l := int(compare.wordCount())
688
+	if l > int(b.wordCount()) {
689
+		l = int(b.wordCount())
690
+	}
691
+	if compare.length > 0 {
692
+		b.extendSetMaybe(compare.length - 1)
693
+	}
694
+	for i := 0; i < l; i++ {
695
+		b.set[i] |= compare.set[i]
696
+	}
697
+	if len(compare.set) > l {
698
+		for i := l; i < len(compare.set); i++ {
699
+			b.set[i] = compare.set[i]
700
+		}
701
+	}
702
+}
703
+
704
+// SymmetricDifference of base set and other set
705
+// This is the BitSet equivalent of ^ (xor)
706
+func (b *BitSet) SymmetricDifference(compare *BitSet) (result *BitSet) {
707
+	panicIfNull(b)
708
+	panicIfNull(compare)
709
+	b, compare = sortByLength(b, compare)
710
+	// compare is bigger, so clone it
711
+	result = compare.Clone()
712
+	for i, word := range b.set {
713
+		result.set[i] = word ^ compare.set[i]
714
+	}
715
+	return
716
+}
717
+
718
+// SymmetricDifferenceCardinality computes the cardinality of the symmetric difference
719
+func (b *BitSet) SymmetricDifferenceCardinality(compare *BitSet) uint {
720
+	panicIfNull(b)
721
+	panicIfNull(compare)
722
+	b, compare = sortByLength(b, compare)
723
+	cnt := popcntXorSlice(b.set, compare.set)
724
+	if len(compare.set) > len(b.set) {
725
+		cnt += popcntSlice(compare.set[len(b.set):])
726
+	}
727
+	return uint(cnt)
728
+}
729
+
730
+// InPlaceSymmetricDifference creates the destructive SymmetricDifference of base set and other set
731
+// This is the BitSet equivalent of ^ (xor)
732
+func (b *BitSet) InPlaceSymmetricDifference(compare *BitSet) {
733
+	panicIfNull(b)
734
+	panicIfNull(compare)
735
+	l := int(compare.wordCount())
736
+	if l > int(b.wordCount()) {
737
+		l = int(b.wordCount())
738
+	}
739
+	if compare.length > 0 {
740
+		b.extendSetMaybe(compare.length - 1)
741
+	}
742
+	for i := 0; i < l; i++ {
743
+		b.set[i] ^= compare.set[i]
744
+	}
745
+	if len(compare.set) > l {
746
+		for i := l; i < len(compare.set); i++ {
747
+			b.set[i] = compare.set[i]
748
+		}
749
+	}
750
+}
751
+
752
+// Is the length an exact multiple of word sizes?
753
+func (b *BitSet) isLenExactMultiple() bool {
754
+	return b.length%wordSize == 0
755
+}
756
+
757
+// Clean last word by setting unused bits to 0
758
+func (b *BitSet) cleanLastWord() {
759
+	if !b.isLenExactMultiple() {
760
+		b.set[len(b.set)-1] &= allBits >> (wordSize - b.length%wordSize)
761
+	}
762
+}
763
+
764
+// Complement computes the (local) complement of a biset (up to length bits)
765
+func (b *BitSet) Complement() (result *BitSet) {
766
+	panicIfNull(b)
767
+	result = New(b.length)
768
+	for i, word := range b.set {
769
+		result.set[i] = ^word
770
+	}
771
+	result.cleanLastWord()
772
+	return
773
+}
774
+
775
+// All returns true if all bits are set, false otherwise. Returns true for
776
+// empty sets.
777
+func (b *BitSet) All() bool {
778
+	panicIfNull(b)
779
+	return b.Count() == b.length
780
+}
781
+
782
+// None returns true if no bit is set, false otherwise. Returns true for
783
+// empty sets.
784
+func (b *BitSet) None() bool {
785
+	panicIfNull(b)
786
+	if b != nil && b.set != nil {
787
+		for _, word := range b.set {
788
+			if word > 0 {
789
+				return false
790
+			}
791
+		}
792
+		return true
793
+	}
794
+	return true
795
+}
796
+
797
+// Any returns true if any bit is set, false otherwise
798
+func (b *BitSet) Any() bool {
799
+	panicIfNull(b)
800
+	return !b.None()
801
+}
802
+
803
+// IsSuperSet returns true if this is a superset of the other set
804
+func (b *BitSet) IsSuperSet(other *BitSet) bool {
805
+	for i, e := other.NextSet(0); e; i, e = other.NextSet(i + 1) {
806
+		if !b.Test(i) {
807
+			return false
808
+		}
809
+	}
810
+	return true
811
+}
812
+
813
+// IsStrictSuperSet returns true if this is a strict superset of the other set
814
+func (b *BitSet) IsStrictSuperSet(other *BitSet) bool {
815
+	return b.Count() > other.Count() && b.IsSuperSet(other)
816
+}
817
+
818
+// DumpAsBits dumps a bit set as a string of bits
819
+func (b *BitSet) DumpAsBits() string {
820
+	if b.set == nil {
821
+		return "."
822
+	}
823
+	buffer := bytes.NewBufferString("")
824
+	i := len(b.set) - 1
825
+	for ; i >= 0; i-- {
826
+		fmt.Fprintf(buffer, "%064b.", b.set[i])
827
+	}
828
+	return buffer.String()
829
+}
830
+
831
+// BinaryStorageSize returns the binary storage requirements
832
+func (b *BitSet) BinaryStorageSize() int {
833
+	return binary.Size(uint64(0)) + binary.Size(b.set)
834
+}
835
+
836
+// WriteTo writes a BitSet to a stream
837
+func (b *BitSet) WriteTo(stream io.Writer) (int64, error) {
838
+	length := uint64(b.length)
839
+
840
+	// Write length
841
+	err := binary.Write(stream, binaryOrder, length)
842
+	if err != nil {
843
+		return 0, err
844
+	}
845
+
846
+	// Write set
847
+	err = binary.Write(stream, binaryOrder, b.set)
848
+	return int64(b.BinaryStorageSize()), err
849
+}
850
+
851
+// ReadFrom reads a BitSet from a stream written using WriteTo
852
+func (b *BitSet) ReadFrom(stream io.Reader) (int64, error) {
853
+	var length uint64
854
+
855
+	// Read length first
856
+	err := binary.Read(stream, binaryOrder, &length)
857
+	if err != nil {
858
+		return 0, err
859
+	}
860
+	newset := New(uint(length))
861
+
862
+	if uint64(newset.length) != length {
863
+		return 0, errors.New("unmarshalling error: type mismatch")
864
+	}
865
+
866
+	// Read remaining bytes as set
867
+	err = binary.Read(stream, binaryOrder, newset.set)
868
+	if err != nil {
869
+		return 0, err
870
+	}
871
+
872
+	*b = *newset
873
+	return int64(b.BinaryStorageSize()), nil
874
+}
875
+
876
+// MarshalBinary encodes a BitSet into a binary form and returns the result.
877
+func (b *BitSet) MarshalBinary() ([]byte, error) {
878
+	var buf bytes.Buffer
879
+	writer := bufio.NewWriter(&buf)
880
+
881
+	_, err := b.WriteTo(writer)
882
+	if err != nil {
883
+		return []byte{}, err
884
+	}
885
+
886
+	err = writer.Flush()
887
+
888
+	return buf.Bytes(), err
889
+}
890
+
891
+// UnmarshalBinary decodes the binary form generated by MarshalBinary.
892
+func (b *BitSet) UnmarshalBinary(data []byte) error {
893
+	buf := bytes.NewReader(data)
894
+	reader := bufio.NewReader(buf)
895
+
896
+	_, err := b.ReadFrom(reader)
897
+
898
+	return err
899
+}
900
+
901
+// MarshalJSON marshals a BitSet as a JSON structure
902
+func (b *BitSet) MarshalJSON() ([]byte, error) {
903
+	buffer := bytes.NewBuffer(make([]byte, 0, b.BinaryStorageSize()))
904
+	_, err := b.WriteTo(buffer)
905
+	if err != nil {
906
+		return nil, err
907
+	}
908
+
909
+	// URLEncode all bytes
910
+	return json.Marshal(base64Encoding.EncodeToString(buffer.Bytes()))
911
+}
912
+
913
+// UnmarshalJSON unmarshals a BitSet from JSON created using MarshalJSON
914
+func (b *BitSet) UnmarshalJSON(data []byte) error {
915
+	// Unmarshal as string
916
+	var s string
917
+	err := json.Unmarshal(data, &s)
918
+	if err != nil {
919
+		return err
920
+	}
921
+
922
+	// URLDecode string
923
+	buf, err := base64Encoding.DecodeString(s)
924
+	if err != nil {
925
+		return err
926
+	}
927
+
928
+	_, err = b.ReadFrom(bytes.NewReader(buf))
929
+	return err
930
+}
0 931
new file mode 100644
... ...
@@ -0,0 +1,3 @@
0
+module github.com/willf/bitset
1
+
2
+go 1.14
0 3
new file mode 100644
... ...
@@ -0,0 +1,53 @@
0
+package bitset
1
+
2
+// bit population count, take from
3
+// https://code.google.com/p/go/issues/detail?id=4988#c11
4
+// credit: https://code.google.com/u/arnehormann/
5
+func popcount(x uint64) (n uint64) {
6
+	x -= (x >> 1) & 0x5555555555555555
7
+	x = (x>>2)&0x3333333333333333 + x&0x3333333333333333
8
+	x += x >> 4
9
+	x &= 0x0f0f0f0f0f0f0f0f
10
+	x *= 0x0101010101010101
11
+	return x >> 56
12
+}
13
+
14
+func popcntSliceGo(s []uint64) uint64 {
15
+	cnt := uint64(0)
16
+	for _, x := range s {
17
+		cnt += popcount(x)
18
+	}
19
+	return cnt
20
+}
21
+
22
+func popcntMaskSliceGo(s, m []uint64) uint64 {
23
+	cnt := uint64(0)
24
+	for i := range s {
25
+		cnt += popcount(s[i] &^ m[i])
26
+	}
27
+	return cnt
28
+}
29
+
30
+func popcntAndSliceGo(s, m []uint64) uint64 {
31
+	cnt := uint64(0)
32
+	for i := range s {
33
+		cnt += popcount(s[i] & m[i])
34
+	}
35
+	return cnt
36
+}
37
+
38
+func popcntOrSliceGo(s, m []uint64) uint64 {
39
+	cnt := uint64(0)
40
+	for i := range s {
41
+		cnt += popcount(s[i] | m[i])
42
+	}
43
+	return cnt
44
+}
45
+
46
+func popcntXorSliceGo(s, m []uint64) uint64 {
47
+	cnt := uint64(0)
48
+	for i := range s {
49
+		cnt += popcount(s[i] ^ m[i])
50
+	}
51
+	return cnt
52
+}
0 53
new file mode 100644
... ...
@@ -0,0 +1,45 @@
0
+// +build go1.9
1
+
2
+package bitset
3
+
4
+import "math/bits"
5
+
6
+func popcntSlice(s []uint64) uint64 {
7
+	var cnt int
8
+	for _, x := range s {
9
+		cnt += bits.OnesCount64(x)
10
+	}
11
+	return uint64(cnt)
12
+}
13
+
14
+func popcntMaskSlice(s, m []uint64) uint64 {
15
+	var cnt int
16
+	for i := range s {
17
+		cnt += bits.OnesCount64(s[i] &^ m[i])
18
+	}
19
+	return uint64(cnt)
20
+}
21
+
22
+func popcntAndSlice(s, m []uint64) uint64 {
23
+	var cnt int
24
+	for i := range s {
25
+		cnt += bits.OnesCount64(s[i] & m[i])
26
+	}
27
+	return uint64(cnt)
28
+}
29
+
30
+func popcntOrSlice(s, m []uint64) uint64 {
31
+	var cnt int
32
+	for i := range s {
33
+		cnt += bits.OnesCount64(s[i] | m[i])
34
+	}
35
+	return uint64(cnt)
36
+}
37
+
38
+func popcntXorSlice(s, m []uint64) uint64 {
39
+	var cnt int
40
+	for i := range s {
41
+		cnt += bits.OnesCount64(s[i] ^ m[i])
42
+	}
43
+	return uint64(cnt)
44
+}
0 45
new file mode 100644
... ...
@@ -0,0 +1,68 @@
0
+// +build !go1.9
1
+// +build amd64,!appengine
2
+
3
+package bitset
4
+
5
+// *** the following functions are defined in popcnt_amd64.s
6
+
7
+//go:noescape
8
+
9
+func hasAsm() bool
10
+
11
+// useAsm is a flag used to select the GO or ASM implementation of the popcnt function
12
+var useAsm = hasAsm()
13
+
14
+//go:noescape
15
+
16
+func popcntSliceAsm(s []uint64) uint64
17
+
18
+//go:noescape
19
+
20
+func popcntMaskSliceAsm(s, m []uint64) uint64
21
+
22
+//go:noescape
23
+
24
+func popcntAndSliceAsm(s, m []uint64) uint64
25
+
26
+//go:noescape
27
+
28
+func popcntOrSliceAsm(s, m []uint64) uint64
29
+
30
+//go:noescape
31
+
32
+func popcntXorSliceAsm(s, m []uint64) uint64
33
+
34
+func popcntSlice(s []uint64) uint64 {
35
+	if useAsm {
36
+		return popcntSliceAsm(s)
37
+	}
38
+	return popcntSliceGo(s)
39
+}
40
+
41
+func popcntMaskSlice(s, m []uint64) uint64 {
42
+	if useAsm {
43
+		return popcntMaskSliceAsm(s, m)
44
+	}
45
+	return popcntMaskSliceGo(s, m)
46
+}
47
+
48
+func popcntAndSlice(s, m []uint64) uint64 {
49
+	if useAsm {
50
+		return popcntAndSliceAsm(s, m)
51
+	}
52
+	return popcntAndSliceGo(s, m)
53
+}
54
+
55
+func popcntOrSlice(s, m []uint64) uint64 {
56
+	if useAsm {
57
+		return popcntOrSliceAsm(s, m)
58
+	}
59
+	return popcntOrSliceGo(s, m)
60
+}
61
+
62
+func popcntXorSlice(s, m []uint64) uint64 {
63
+	if useAsm {
64
+		return popcntXorSliceAsm(s, m)
65
+	}
66
+	return popcntXorSliceGo(s, m)
67
+}
0 68
new file mode 100644
... ...
@@ -0,0 +1,104 @@
0
+// +build !go1.9
1
+// +build amd64,!appengine
2
+
3
+TEXT ·hasAsm(SB),4,$0-1
4
+MOVQ $1, AX
5
+CPUID
6
+SHRQ $23, CX
7
+ANDQ $1, CX
8
+MOVB CX, ret+0(FP)
9
+RET
10
+
11
+#define POPCNTQ_DX_DX BYTE $0xf3; BYTE $0x48; BYTE $0x0f; BYTE $0xb8; BYTE $0xd2
12
+
13
+TEXT ·popcntSliceAsm(SB),4,$0-32
14
+XORQ	AX, AX
15
+MOVQ	s+0(FP), SI
16
+MOVQ	s_len+8(FP), CX
17
+TESTQ	CX, CX
18
+JZ		popcntSliceEnd
19
+popcntSliceLoop:
20
+BYTE $0xf3; BYTE $0x48; BYTE $0x0f; BYTE $0xb8; BYTE $0x16 // POPCNTQ (SI), DX
21
+ADDQ	DX, AX
22
+ADDQ	$8, SI
23
+LOOP	popcntSliceLoop
24
+popcntSliceEnd:
25
+MOVQ	AX, ret+24(FP)
26
+RET
27
+
28
+TEXT ·popcntMaskSliceAsm(SB),4,$0-56
29
+XORQ	AX, AX
30
+MOVQ	s+0(FP), SI
31
+MOVQ	s_len+8(FP), CX
32
+TESTQ	CX, CX
33
+JZ		popcntMaskSliceEnd
34
+MOVQ	m+24(FP), DI
35
+popcntMaskSliceLoop:
36
+MOVQ	(DI), DX
37
+NOTQ	DX
38
+ANDQ	(SI), DX
39
+POPCNTQ_DX_DX
40
+ADDQ	DX, AX
41
+ADDQ	$8, SI
42
+ADDQ	$8, DI
43
+LOOP	popcntMaskSliceLoop
44
+popcntMaskSliceEnd:
45
+MOVQ	AX, ret+48(FP)
46
+RET
47
+
48
+TEXT ·popcntAndSliceAsm(SB),4,$0-56
49
+XORQ	AX, AX
50
+MOVQ	s+0(FP), SI
51
+MOVQ	s_len+8(FP), CX
52
+TESTQ	CX, CX
53
+JZ		popcntAndSliceEnd
54
+MOVQ	m+24(FP), DI
55
+popcntAndSliceLoop:
56
+MOVQ	(DI), DX
57
+ANDQ	(SI), DX
58
+POPCNTQ_DX_DX
59
+ADDQ	DX, AX
60
+ADDQ	$8, SI
61
+ADDQ	$8, DI
62
+LOOP	popcntAndSliceLoop
63
+popcntAndSliceEnd:
64
+MOVQ	AX, ret+48(FP)
65
+RET
66
+
67
+TEXT ·popcntOrSliceAsm(SB),4,$0-56
68
+XORQ	AX, AX
69
+MOVQ	s+0(FP), SI
70
+MOVQ	s_len+8(FP), CX
71
+TESTQ	CX, CX
72
+JZ		popcntOrSliceEnd
73
+MOVQ	m+24(FP), DI
74
+popcntOrSliceLoop:
75
+MOVQ	(DI), DX
76
+ORQ		(SI), DX
77
+POPCNTQ_DX_DX
78
+ADDQ	DX, AX
79
+ADDQ	$8, SI
80
+ADDQ	$8, DI
81
+LOOP	popcntOrSliceLoop
82
+popcntOrSliceEnd:
83
+MOVQ	AX, ret+48(FP)
84
+RET
85
+
86
+TEXT ·popcntXorSliceAsm(SB),4,$0-56
87
+XORQ	AX, AX
88
+MOVQ	s+0(FP), SI
89
+MOVQ	s_len+8(FP), CX
90
+TESTQ	CX, CX
91
+JZ		popcntXorSliceEnd
92
+MOVQ	m+24(FP), DI
93
+popcntXorSliceLoop:
94
+MOVQ	(DI), DX
95
+XORQ	(SI), DX
96
+POPCNTQ_DX_DX
97
+ADDQ	DX, AX
98
+ADDQ	$8, SI
99
+ADDQ	$8, DI
100
+LOOP	popcntXorSliceLoop
101
+popcntXorSliceEnd:
102
+MOVQ	AX, ret+48(FP)
103
+RET
0 104
new file mode 100644
... ...
@@ -0,0 +1,24 @@
0
+// +build !go1.9
1
+// +build !amd64 appengine
2
+
3
+package bitset
4
+
5
+func popcntSlice(s []uint64) uint64 {
6
+	return popcntSliceGo(s)
7
+}
8
+
9
+func popcntMaskSlice(s, m []uint64) uint64 {
10
+	return popcntMaskSliceGo(s, m)
11
+}
12
+
13
+func popcntAndSlice(s, m []uint64) uint64 {
14
+	return popcntAndSliceGo(s, m)
15
+}
16
+
17
+func popcntOrSlice(s, m []uint64) uint64 {
18
+	return popcntOrSliceGo(s, m)
19
+}
20
+
21
+func popcntXorSlice(s, m []uint64) uint64 {
22
+	return popcntXorSliceGo(s, m)
23
+}
0 24
new file mode 100644
... ...
@@ -0,0 +1,14 @@
0
+// +build !go1.9
1
+
2
+package bitset
3
+
4
+var deBruijn = [...]byte{
5
+	0, 1, 56, 2, 57, 49, 28, 3, 61, 58, 42, 50, 38, 29, 17, 4,
6
+	62, 47, 59, 36, 45, 43, 51, 22, 53, 39, 33, 30, 24, 18, 12, 5,
7
+	63, 55, 48, 27, 60, 41, 37, 16, 46, 35, 44, 21, 52, 32, 23, 11,
8
+	54, 26, 40, 15, 34, 20, 31, 10, 25, 14, 19, 9, 13, 8, 7, 6,
9
+}
10
+
11
+func trailingZeroes64(v uint64) uint {
12
+	return uint(deBruijn[((v&-v)*0x03f79d71b4ca8b09)>>58])
13
+}
0 14
new file mode 100644
... ...
@@ -0,0 +1,9 @@
0
+// +build go1.9
1
+
2
+package bitset
3
+
4
+import "math/bits"
5
+
6
+func trailingZeroes64(v uint64) uint {
7
+	return uint(bits.TrailingZeros64(v))
8
+}