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>
| ... | ... |
@@ -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 |
} |
| ... | ... |
@@ -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 |
+[](https://github.com/willf/bitset/actions?query=workflow%3ATest) |
|
| 5 |
+[](https://coveralls.io/github/willf/bitset?branch=master) |
|
| 6 |
+[](https://goreportcard.com/report/github.com/willf/bitset) |
|
| 7 |
+[](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 | 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 |
+} |