| // 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") |
| } |