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 }