mirror of
https://github.com/bluekeyes/go-gitdiff
synced 2026-06-08 13:18:30 +00:00
Support adjustable read-ahead in parser
At the moment, we need only to read three lines, but now the value is easy to adjust as needed. I've only seen the Git implementation read two lines ahead so far, but it has the whole input in memory and may read more in other places.
This commit is contained in:
@@ -17,14 +17,13 @@ func (p *parser) ParseGitFileHeader(f *File, header string) error {
|
||||
}
|
||||
|
||||
for err = p.Next(); err == nil; err = p.Next() {
|
||||
end, err := parseGitHeaderData(f, p.Line(), defaultName)
|
||||
end, err := parseGitHeaderData(f, p.Line(0), defaultName)
|
||||
if err != nil {
|
||||
return p.Errorf(1, "git file header: %v", err)
|
||||
}
|
||||
if end {
|
||||
break
|
||||
}
|
||||
p.Line()
|
||||
}
|
||||
if err != nil && err != io.EOF {
|
||||
return err
|
||||
|
||||
@@ -147,7 +147,7 @@ index deadbeef
|
||||
p.Next()
|
||||
|
||||
var f File
|
||||
err := p.ParseGitFileHeader(&f, p.Line())
|
||||
err := p.ParseGitFileHeader(&f, p.Line(0))
|
||||
if test.Err {
|
||||
if err == nil {
|
||||
t.Fatalf("expected error parsing git file header, got nil")
|
||||
|
||||
+21
-24
@@ -44,7 +44,7 @@ type parser struct {
|
||||
|
||||
eof bool
|
||||
lineno int64
|
||||
lines [2]string
|
||||
lines [3]string
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -63,7 +63,7 @@ func (p *parser) ParseNextFileHeader() (file *File, err error) {
|
||||
// based on find_header() in git/apply.c
|
||||
|
||||
for err = p.Next(); err == nil; err = p.Next() {
|
||||
line := p.Line()
|
||||
line := p.Line(0)
|
||||
|
||||
// check for disconnected fragment headers (corrupt patch)
|
||||
if isMaybeFragmentHeader(line) {
|
||||
@@ -85,16 +85,12 @@ func (p *parser) ParseNextFileHeader() (file *File, err error) {
|
||||
}
|
||||
|
||||
// check for a "traditional" patch
|
||||
if strings.HasPrefix(line, oldFilePrefix) && strings.HasPrefix(p.PeekLine(), newFilePrefix) {
|
||||
if err = p.Next(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if strings.HasPrefix(line, oldFilePrefix) && strings.HasPrefix(p.Line(1), newFilePrefix) {
|
||||
oldFileLine := line
|
||||
newFileLine := p.Line()
|
||||
newFileLine := p.Line(1)
|
||||
|
||||
// only a file header if followed by a (probable) unified fragment header
|
||||
if !isMaybeFragmentHeader(p.PeekLine()) {
|
||||
if !isMaybeFragmentHeader(p.Line(2)) {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -127,9 +123,11 @@ func (p *parser) Next() error {
|
||||
}
|
||||
|
||||
if p.lineno == 0 {
|
||||
// on the first call, need extra shift to initialize both slots
|
||||
if err := p.shiftLines(); err != nil && err != io.EOF {
|
||||
return err
|
||||
// on first call to next, need to shift in all lines
|
||||
for i := 0; i < len(p.lines)-1; i++ {
|
||||
if err := p.shiftLines(); err != nil && err != io.EOF {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,21 +143,20 @@ func (p *parser) Next() error {
|
||||
}
|
||||
|
||||
func (p *parser) shiftLines() (err error) {
|
||||
p.lines[0] = p.lines[1]
|
||||
p.lines[1], err = p.r.ReadString('\n')
|
||||
for i := 0; i < len(p.lines)-1; i++ {
|
||||
p.lines[i] = p.lines[i+1]
|
||||
}
|
||||
p.lines[len(p.lines)-1], err = p.r.ReadString('\n')
|
||||
return
|
||||
}
|
||||
|
||||
// Line returns the current line or an empty string if Next has returned io.EOF.
|
||||
func (p *parser) Line() string {
|
||||
return p.lines[0]
|
||||
}
|
||||
|
||||
// PeekLine returns the line following the current line or an empty string if
|
||||
// the current line is the final line. If PeekLine returns an empty string,
|
||||
// Next will return io.EOF on the next call.
|
||||
func (p *parser) PeekLine() string {
|
||||
return p.lines[1]
|
||||
// Line returns a line from the parser without advancing it. A delta of 0
|
||||
// returns the current line, while higher deltas return read-ahead lines. It
|
||||
// returns an empty string if the delta is higher than the available lines,
|
||||
// either because of the buffer size or because the parser reached the end of
|
||||
// the input. Valid lines always contain at least a newline character.
|
||||
func (p *parser) Line(delta uint) string {
|
||||
return p.lines[delta]
|
||||
}
|
||||
|
||||
// Errorf generates an error and appends the current line information.
|
||||
|
||||
+21
-8
@@ -2,6 +2,7 @@ package gitdiff
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -14,14 +15,14 @@ func TestLineOperations(t *testing.T) {
|
||||
return &parser{r: bufio.NewReader(strings.NewReader(content))}
|
||||
}
|
||||
|
||||
t.Run("readLine", func(t *testing.T) {
|
||||
t.Run("read", func(t *testing.T) {
|
||||
p := newParser()
|
||||
|
||||
if err := p.Next(); err != nil {
|
||||
t.Fatalf("error advancing parser: %v", err)
|
||||
}
|
||||
|
||||
line := p.Line()
|
||||
line := p.Line(0)
|
||||
if line != "the first line\n" {
|
||||
t.Fatalf("incorrect first line: %s", line)
|
||||
}
|
||||
@@ -30,30 +31,42 @@ func TestLineOperations(t *testing.T) {
|
||||
t.Fatalf("error advancing parser: %v", err)
|
||||
}
|
||||
|
||||
line = p.Line()
|
||||
if p.Line() != "the second line\n" {
|
||||
line = p.Line(0)
|
||||
if line != "the second line\n" {
|
||||
t.Fatalf("incorrect second line: %s", line)
|
||||
}
|
||||
|
||||
if err := p.Next(); err != nil {
|
||||
t.Fatalf("error advancing parser: %v", err)
|
||||
}
|
||||
|
||||
line = p.Line(0)
|
||||
if line != "the third line\n" {
|
||||
t.Fatalf("incorrect third line: %s", line)
|
||||
}
|
||||
|
||||
if err := p.Next(); err != io.EOF {
|
||||
t.Fatalf("expected EOF, but got: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("peekLine", func(t *testing.T) {
|
||||
t.Run("peek", func(t *testing.T) {
|
||||
p := newParser()
|
||||
|
||||
if err := p.Next(); err != nil {
|
||||
t.Fatalf("error advancing parser: %v", err)
|
||||
}
|
||||
|
||||
line := p.PeekLine()
|
||||
line := p.Line(1)
|
||||
if line != "the second line\n" {
|
||||
t.Fatalf("incorrect peek line: %s", line)
|
||||
}
|
||||
|
||||
// test that reading the line returns the same value
|
||||
if err := p.Next(); err != nil {
|
||||
t.Fatalf("error advancing parser: %v", err)
|
||||
}
|
||||
|
||||
line = p.Line()
|
||||
line = p.Line(0)
|
||||
if line != "the second line\n" {
|
||||
t.Fatalf("incorrect line: %s", line)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user