github.com/intel/goresctrl@v0.5.0/pkg/rdt/bitmask.go (about)

     1  /*
     2  Copyright 2019 Intel Corporation
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package rdt
    18  
    19  import (
    20  	"fmt"
    21  	"math/bits"
    22  	"strconv"
    23  	"strings"
    24  )
    25  
    26  // bitmask represents a generic 64 bit wide bitmask
    27  type bitmask uint64
    28  
    29  // MarshalJSON implements the Marshaler interface of "encoding/json"
    30  func (b bitmask) MarshalJSON() ([]byte, error) {
    31  	return []byte(fmt.Sprintf("\"%#x\"", b)), nil
    32  }
    33  
    34  // listStr prints the bitmask in human-readable format, similar to e.g. the
    35  // cpuset format of the Linux kernel
    36  func (b bitmask) listStr() string {
    37  	str := ""
    38  	sep := ""
    39  
    40  	shift := int(0)
    41  	lsbOne := b.lsbOne()
    42  
    43  	// Process "ranges of ones"
    44  	for lsbOne != -1 {
    45  		b >>= uint(lsbOne)
    46  
    47  		// Get range lenght from the position of the first zero
    48  		numOnes := b.lsbZero()
    49  
    50  		if numOnes == 1 {
    51  			str += sep + strconv.Itoa(lsbOne+shift)
    52  		} else {
    53  			str += sep + strconv.Itoa(lsbOne+shift) + "-" + strconv.Itoa(lsbOne+numOnes-1+shift)
    54  		}
    55  
    56  		// Shift away the bits that have been processed
    57  		b >>= uint(numOnes)
    58  		shift += lsbOne + numOnes
    59  
    60  		// Get next bit that is set (if any)
    61  		lsbOne = b.lsbOne()
    62  
    63  		sep = ","
    64  	}
    65  
    66  	return str
    67  }
    68  
    69  // listStrToBitmask parses a string containing a human-readable list of bit
    70  // numbers into a bitmask
    71  func listStrToBitmask(str string) (bitmask, error) {
    72  	b := bitmask(0)
    73  
    74  	// Empty bitmask
    75  	if len(str) == 0 {
    76  		return b, nil
    77  	}
    78  
    79  	ranges := strings.Split(str, ",")
    80  	for _, ran := range ranges {
    81  		split := strings.SplitN(ran, "-", 2)
    82  
    83  		bitNum, err := strconv.ParseUint(split[0], 10, 6)
    84  		if err != nil {
    85  			return b, fmt.Errorf("invalid bitmask %q: %v", str, err)
    86  		}
    87  
    88  		if len(split) == 1 {
    89  			b |= 1 << bitNum
    90  		} else {
    91  			endNum, err := strconv.ParseUint(split[1], 10, 6)
    92  			if err != nil {
    93  				return b, fmt.Errorf("invalid bitmask %q: %v", str, err)
    94  			}
    95  			if endNum <= bitNum {
    96  				return b, fmt.Errorf("invalid range %q in bitmask %q", ran, str)
    97  			}
    98  			b |= (1<<(endNum-bitNum+1) - 1) << bitNum
    99  		}
   100  	}
   101  	return b, nil
   102  }
   103  
   104  func (b bitmask) lsbOne() int {
   105  	if b == 0 {
   106  		return -1
   107  	}
   108  	return bits.TrailingZeros64(uint64(b))
   109  }
   110  
   111  func (b bitmask) msbOne() int {
   112  	// Returns -1 for b == 0
   113  	return 63 - bits.LeadingZeros64(uint64(b))
   114  }
   115  
   116  func (b bitmask) lsbZero() int {
   117  	return bits.TrailingZeros64(^uint64(b))
   118  }