github.com/metacubex/gvisor@v0.0.0-20240320004321-933faba989ec/pkg/sentry/fsimpl/cgroupfs/bitmap.go (about)

     1  // Copyright 2021 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package cgroupfs
    16  
    17  import (
    18  	"fmt"
    19  	"strconv"
    20  	"strings"
    21  
    22  	"github.com/metacubex/gvisor/pkg/bitmap"
    23  )
    24  
    25  // formatBitmap produces a string representation of b, which lists the indices
    26  // of set bits in the bitmap. Indices are separated by commas and ranges of
    27  // set bits are abbreviated. Example outputs: "0,2,4", "0,3-7,10", "0-10".
    28  //
    29  // Inverse of parseBitmap.
    30  func formatBitmap(b *bitmap.Bitmap) string {
    31  	ones := b.ToSlice()
    32  	if len(ones) == 0 {
    33  		return ""
    34  	}
    35  
    36  	elems := make([]string, 0, len(ones))
    37  	runStart := ones[0]
    38  	lastVal := ones[0]
    39  	inRun := false
    40  
    41  	for _, v := range ones[1:] {
    42  		last := lastVal
    43  		lastVal = v
    44  
    45  		if last+1 == v {
    46  			// In a contiguous block of ones.
    47  			if !inRun {
    48  				runStart = last
    49  				inRun = true
    50  			}
    51  
    52  			continue
    53  		}
    54  
    55  		// Non-contiguous bit.
    56  		if inRun {
    57  			// Render a run
    58  			elems = append(elems, fmt.Sprintf("%d-%d", runStart, last))
    59  			inRun = false
    60  			continue
    61  		}
    62  
    63  		// Lone non-contiguous bit.
    64  		elems = append(elems, fmt.Sprintf("%d", last))
    65  
    66  	}
    67  
    68  	// Process potential final run
    69  	if inRun {
    70  		elems = append(elems, fmt.Sprintf("%d-%d", runStart, lastVal))
    71  	} else {
    72  		elems = append(elems, fmt.Sprintf("%d", lastVal))
    73  	}
    74  
    75  	return strings.Join(elems, ",")
    76  }
    77  
    78  func parseToken(token string) (start, end uint32, err error) {
    79  	ts := strings.SplitN(token, "-", 2)
    80  	switch len(ts) {
    81  	case 0:
    82  		return 0, 0, fmt.Errorf("invalid token %q", token)
    83  	case 1:
    84  		val, err := strconv.ParseUint(ts[0], 10, 32)
    85  		if err != nil {
    86  			return 0, 0, err
    87  		}
    88  		return uint32(val), uint32(val), nil
    89  	case 2:
    90  		val1, err := strconv.ParseUint(ts[0], 10, 32)
    91  		if err != nil {
    92  			return 0, 0, err
    93  		}
    94  		val2, err := strconv.ParseUint(ts[1], 10, 32)
    95  		if err != nil {
    96  			return 0, 0, err
    97  		}
    98  		if val1 >= val2 {
    99  			return 0, 0, fmt.Errorf("start (%v) must be less than end (%v)", val1, val2)
   100  		}
   101  		return uint32(val1), uint32(val2), nil
   102  	default:
   103  		panic(fmt.Sprintf("Unreachable: got %d substrs", len(ts)))
   104  	}
   105  }
   106  
   107  // parseBitmap parses input as a bitmap. input should be a comma separated list
   108  // of indices, and ranges of set bits may be abbreviated. Examples: "0,2,4",
   109  // "0,3-7,10", "0-10". Input after the first newline or null byte is discarded.
   110  //
   111  // sizeHint sets the initial size of the bitmap, which may prevent reallocation
   112  // when growing the bitmap during parsing. Ideally sizeHint should be at least
   113  // as large as the bitmap represented by input, but this is not required.
   114  //
   115  // Inverse of formatBitmap.
   116  func parseBitmap(input string, sizeHint uint32) (*bitmap.Bitmap, error) {
   117  	b := bitmap.New(sizeHint)
   118  
   119  	if termIdx := strings.IndexAny(input, "\n\000"); termIdx != -1 {
   120  		input = input[:termIdx]
   121  	}
   122  	input = strings.TrimSpace(input)
   123  
   124  	if len(input) == 0 {
   125  		return &b, nil
   126  	}
   127  	tokens := strings.Split(input, ",")
   128  
   129  	for _, t := range tokens {
   130  		start, end, err := parseToken(strings.TrimSpace(t))
   131  		if err != nil {
   132  			return nil, err
   133  		}
   134  		for i := start; i <= end; i++ {
   135  			b.Add(i)
   136  		}
   137  	}
   138  	return &b, nil
   139  }