summaryrefslogtreecommitdiffhomepage
path: root/pkg/sentry/fsimpl/cgroupfs/bitmap.go
blob: 8074641db21ab61455e52011d189fbb36f85aa63 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
// Copyright 2021 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cgroupfs

import (
	"fmt"
	"strconv"
	"strings"

	"gvisor.dev/gvisor/pkg/bitmap"
)

// formatBitmap produces a string representation of b, which lists the indicies
// of set bits in the bitmap. Indicies are separated by commas and ranges of
// set bits are abbreviated. Example outputs: "0,2,4", "0,3-7,10", "0-10".
//
// Inverse of parseBitmap.
func formatBitmap(b *bitmap.Bitmap) string {
	ones := b.ToSlice()
	if len(ones) == 0 {
		return ""
	}

	elems := make([]string, 0, len(ones))
	runStart := ones[0]
	lastVal := ones[0]
	inRun := false

	for _, v := range ones[1:] {
		last := lastVal
		lastVal = v

		if last+1 == v {
			// In a contiguous block of ones.
			if !inRun {
				runStart = last
				inRun = true
			}

			continue
		}

		// Non-contiguous bit.
		if inRun {
			// Render a run
			elems = append(elems, fmt.Sprintf("%d-%d", runStart, last))
			inRun = false
			continue
		}

		// Lone non-contiguous bit.
		elems = append(elems, fmt.Sprintf("%d", last))

	}

	// Process potential final run
	if inRun {
		elems = append(elems, fmt.Sprintf("%d-%d", runStart, lastVal))
	} else {
		elems = append(elems, fmt.Sprintf("%d", lastVal))
	}

	return strings.Join(elems, ",")
}

func parseToken(token string) (start, end uint32, err error) {
	ts := strings.SplitN(token, "-", 2)
	switch len(ts) {
	case 0:
		return 0, 0, fmt.Errorf("invalid token %q", token)
	case 1:
		val, err := strconv.ParseUint(ts[0], 10, 32)
		if err != nil {
			return 0, 0, err
		}
		return uint32(val), uint32(val), nil
	case 2:
		val1, err := strconv.ParseUint(ts[0], 10, 32)
		if err != nil {
			return 0, 0, err
		}
		val2, err := strconv.ParseUint(ts[1], 10, 32)
		if err != nil {
			return 0, 0, err
		}
		if val1 >= val2 {
			return 0, 0, fmt.Errorf("start (%v) must be less than end (%v)", val1, val2)
		}
		return uint32(val1), uint32(val2), nil
	default:
		panic(fmt.Sprintf("Unreachable: got %d substrs", len(ts)))
	}
}

// parseBitmap parses input as a bitmap. input should be a comma separated list
// of indices, and ranges of set bits may be abbreviated. Examples: "0,2,4",
// "0,3-7,10", "0-10". Input after the first newline or null byte is discarded.
//
// sizeHint sets the initial size of the bitmap, which may prevent reallocation
// when growing the bitmap during parsing. Ideally sizeHint should be at least
// as large as the bitmap represented by input, but this is not required.
//
// Inverse of formatBitmap.
func parseBitmap(input string, sizeHint uint32) (*bitmap.Bitmap, error) {
	b := bitmap.New(sizeHint)

	if termIdx := strings.IndexAny(input, "\n\000"); termIdx != -1 {
		input = input[:termIdx]
	}
	input = strings.TrimSpace(input)

	if len(input) == 0 {
		return &b, nil
	}
	tokens := strings.Split(input, ",")

	for _, t := range tokens {
		start, end, err := parseToken(strings.TrimSpace(t))
		if err != nil {
			return nil, err
		}
		for i := start; i <= end; i++ {
			b.Add(i)
		}
	}
	return &b, nil
}