mirror of https://github.com/golang/go.git
116 lines
2.7 KiB
Go
116 lines
2.7 KiB
Go
// Copyright 2010 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 line implements a Reader that reads lines delimited by '\n' or
|
|
// ' \r\n'.
|
|
package line
|
|
|
|
import (
|
|
"io"
|
|
"os"
|
|
)
|
|
|
|
// Reader reads lines, delimited by '\n' or \r\n', from an io.Reader.
|
|
type Reader struct {
|
|
buf []byte
|
|
consumed int
|
|
in io.Reader
|
|
err os.Error
|
|
}
|
|
|
|
// NewReader returns a new Reader that will read successive
|
|
// lines from the input Reader.
|
|
func NewReader(input io.Reader, maxLineLength int) *Reader {
|
|
return &Reader{
|
|
buf: make([]byte, 0, maxLineLength),
|
|
consumed: 0,
|
|
in: input,
|
|
}
|
|
}
|
|
|
|
// Read reads from any buffered data past the last line read, or from the underlying
|
|
// io.Reader if the buffer is empty.
|
|
func (l *Reader) Read(p []byte) (n int, err os.Error) {
|
|
l.removeConsumedFromBuffer()
|
|
if len(l.buf) > 0 {
|
|
n = copy(p, l.buf)
|
|
l.consumed += n
|
|
return
|
|
}
|
|
return l.in.Read(p)
|
|
}
|
|
|
|
func (l *Reader) removeConsumedFromBuffer() {
|
|
if l.consumed > 0 {
|
|
n := copy(l.buf, l.buf[l.consumed:])
|
|
l.buf = l.buf[:n]
|
|
l.consumed = 0
|
|
}
|
|
}
|
|
|
|
// ReadLine tries to return a single line, not including the end-of-line bytes.
|
|
// If the line was found to be longer than the maximum length then isPrefix is
|
|
// set and the beginning of the line is returned. The rest of the line will be
|
|
// returned from future calls. isPrefix will be false when returning the last
|
|
// fragment of the line. The returned buffer points into the internal state of
|
|
// the Reader and is only valid until the next call to ReadLine. ReadLine
|
|
// either returns a non-nil line or it returns an error, never both.
|
|
func (l *Reader) ReadLine() (line []byte, isPrefix bool, err os.Error) {
|
|
l.removeConsumedFromBuffer()
|
|
|
|
if len(l.buf) == 0 && l.err != nil {
|
|
err = l.err
|
|
return
|
|
}
|
|
|
|
scannedTo := 0
|
|
|
|
for {
|
|
i := scannedTo
|
|
for ; i < len(l.buf); i++ {
|
|
if l.buf[i] == '\r' && len(l.buf) > i+1 && l.buf[i+1] == '\n' {
|
|
line = l.buf[:i]
|
|
l.consumed = i + 2
|
|
return
|
|
} else if l.buf[i] == '\n' {
|
|
line = l.buf[:i]
|
|
l.consumed = i + 1
|
|
return
|
|
}
|
|
}
|
|
|
|
if i == cap(l.buf) {
|
|
line = l.buf[:i]
|
|
l.consumed = i
|
|
isPrefix = true
|
|
return
|
|
}
|
|
|
|
if l.err != nil {
|
|
line = l.buf
|
|
l.consumed = i
|
|
return
|
|
}
|
|
|
|
// We don't want to rescan the input that we just scanned.
|
|
// However, we need to back up one byte because the last byte
|
|
// could have been a '\r' and we do need to rescan that.
|
|
scannedTo = i
|
|
if scannedTo > 0 {
|
|
scannedTo--
|
|
}
|
|
oldLen := len(l.buf)
|
|
l.buf = l.buf[:cap(l.buf)]
|
|
n, readErr := l.in.Read(l.buf[oldLen:])
|
|
l.buf = l.buf[:oldLen+n]
|
|
if readErr != nil {
|
|
l.err = readErr
|
|
if len(l.buf) == 0 {
|
|
return nil, false, readErr
|
|
}
|
|
}
|
|
}
|
|
panic("unreachable")
|
|
}
|