File size: 5,099 Bytes
e36aeda
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
// Copyright 2025 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package cgroup

import (
	"internal/bytealg"
)

// stringError is a trival implementation of error, equivalent to errors.New,
// which cannot be imported from a runtime package.
type stringError string

func (e stringError) Error() string {
	return string(e)
}

// All errors are explicit converted to type error in global initialization to
// ensure that the linker allocates a static interface value. This is necessary
// because these errors may be used before the allocator is available.

var (
	// The entire line did not fit into the scratch buffer.
	errIncompleteLine error = stringError("incomplete line")

	// A system call failed.
	errSyscallFailed error = stringError("syscall failed")

	// Reached EOF.
	errEOF error = stringError("end of file")
)

// lineReader reads line-by-line using only a single fixed scratch buffer.
//
// When a single line is too long for the scratch buffer, the remainder of the
// line will be skipped.
type lineReader struct {
	read    func(fd int, b []byte) (int, uintptr)
	fd      int
	scratch []byte

	n       int // bytes of scratch in use.
	newline int // index of the first newline in scratch.

	eof bool // read reached EOF.
}

// newLineReader returns a lineReader which reads lines from fd.
//
// fd is the file descriptor to read from.
//
// scratch is the scratch buffer to read into. Note that len(scratch) is the
// longest line that can be read. Lines longer than len(scratch) will have the
// remainder of the line skipped. See next for more details.
//
// read is the function used to read more bytes from fd. This is usually
// internal/runtime/syscall/linux.Read. Note that this follows syscall semantics (not
// io.Reader), so EOF is indicated with n=0, errno=0.
func newLineReader(fd int, scratch []byte, read func(fd int, b []byte) (n int, errno uintptr)) *lineReader {
	return &lineReader{
		read:    read,
		fd:      fd,
		scratch: scratch,
		n:       0,
		newline: -1,
	}
}

// next advances to the next line.
//
// May return errIncompleteLine if the scratch buffer is too small to hold the
// entire line, in which case [r.line] will return the beginning of the line. A
// subsequent call to next will skip the remainder of the incomplete line.
//
// N.B. this behavior is important for /proc/self/mountinfo. Some lines
// (mounts), such as overlayfs, may be extremely long due to long super-block
// options, but we don't care about those. The mount type will appear early in
// the line.
//
// Returns errEOF when there are no more lines.
func (r *lineReader) next() error {
	// Three cases:
	//
	// 1. First call, no data read.
	// 2. Previous call had a complete line. Drop it and look for the end
	//    of the next line.
	// 3. Previous call had an incomplete line. Find the end of that line
	//    (start of the next line), and the end of the next line.

	prevComplete := r.newline >= 0
	firstCall := r.n == 0

	for {
		if prevComplete {
			// Drop the previous line.
			copy(r.scratch, r.scratch[r.newline+1:r.n])
			r.n -= r.newline + 1

			r.newline = bytealg.IndexByte(r.scratch[:r.n], '\n')
			if r.newline >= 0 {
				// We have another line already in scratch. Done.
				return nil
			}
		}

		// No newline available.

		if !prevComplete {
			// If the previous line was incomplete, we are
			// searching for the end of that line and have no need
			// for any buffered data.
			r.n = 0
		}

		n, errno := r.read(r.fd, r.scratch[r.n:len(r.scratch)])
		if errno != 0 {
			return errSyscallFailed
		}
		r.n += n

		if r.n == 0 {
			// Nothing left.
			//
			// N.B. we can't immediately return EOF when read
			// returns 0 as we may still need to return an
			// incomplete line.
			return errEOF
		}

		r.newline = bytealg.IndexByte(r.scratch[:r.n], '\n')
		if prevComplete || firstCall {
			// Already have the start of the line, just need to find the end.

			if r.newline < 0 {
				// We filled the entire buffer or hit EOF, but
				// still no newline.
				return errIncompleteLine
			}

			// Found the end of the line. Done.
			return nil
		} else {
			// Don't have the start of the line. We are currently
			// looking for the end of the previous line.

			if r.newline < 0 {
				// Not there yet.
				if n == 0 {
					// No more to read.
					return errEOF
				}
				continue
			}

			// Found the end of the previous line. The next
			// iteration will drop the remainder of the previous
			// line and look for the next line.
			prevComplete = true
		}
	}
}

// line returns a view of the current line, excluding the trailing newline.
//
// If [r.next] returned errIncompleteLine, then this returns only the beginning
// of the line.
//
// Preconditions: [r.next] is called prior to the first call to line.
//
// Postconditions: The caller must not keep a reference to the returned slice.
func (r *lineReader) line() []byte {
	if r.newline < 0 {
		// Incomplete line
		return r.scratch[:r.n]
	}
	// Complete line.
	return r.scratch[:r.newline]
}