this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

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.

+44 -35
+1 -2
gitdiff/file_header.go
··· 17 17 } 18 18 19 19 for err = p.Next(); err == nil; err = p.Next() { 20 - end, err := parseGitHeaderData(f, p.Line(), defaultName) 20 + end, err := parseGitHeaderData(f, p.Line(0), defaultName) 21 21 if err != nil { 22 22 return p.Errorf(1, "git file header: %v", err) 23 23 } 24 24 if end { 25 25 break 26 26 } 27 - p.Line() 28 27 } 29 28 if err != nil && err != io.EOF { 30 29 return err
+1 -1
gitdiff/file_header_test.go
··· 147 147 p.Next() 148 148 149 149 var f File 150 - err := p.ParseGitFileHeader(&f, p.Line()) 150 + err := p.ParseGitFileHeader(&f, p.Line(0)) 151 151 if test.Err { 152 152 if err == nil { 153 153 t.Fatalf("expected error parsing git file header, got nil")
+21 -24
gitdiff/parser.go
··· 44 44 45 45 eof bool 46 46 lineno int64 47 - lines [2]string 47 + lines [3]string 48 48 } 49 49 50 50 const ( ··· 63 63 // based on find_header() in git/apply.c 64 64 65 65 for err = p.Next(); err == nil; err = p.Next() { 66 - line := p.Line() 66 + line := p.Line(0) 67 67 68 68 // check for disconnected fragment headers (corrupt patch) 69 69 if isMaybeFragmentHeader(line) { ··· 85 85 } 86 86 87 87 // check for a "traditional" patch 88 - if strings.HasPrefix(line, oldFilePrefix) && strings.HasPrefix(p.PeekLine(), newFilePrefix) { 89 - if err = p.Next(); err != nil { 90 - return nil, err 91 - } 92 - 88 + if strings.HasPrefix(line, oldFilePrefix) && strings.HasPrefix(p.Line(1), newFilePrefix) { 93 89 oldFileLine := line 94 - newFileLine := p.Line() 90 + newFileLine := p.Line(1) 95 91 96 92 // only a file header if followed by a (probable) unified fragment header 97 - if !isMaybeFragmentHeader(p.PeekLine()) { 93 + if !isMaybeFragmentHeader(p.Line(2)) { 98 94 continue 99 95 } 100 96 ··· 127 123 } 128 124 129 125 if p.lineno == 0 { 130 - // on the first call, need extra shift to initialize both slots 131 - if err := p.shiftLines(); err != nil && err != io.EOF { 132 - return err 126 + // on first call to next, need to shift in all lines 127 + for i := 0; i < len(p.lines)-1; i++ { 128 + if err := p.shiftLines(); err != nil && err != io.EOF { 129 + return err 130 + } 133 131 } 134 132 } 135 133 ··· 145 143 } 146 144 147 145 func (p *parser) shiftLines() (err error) { 148 - p.lines[0] = p.lines[1] 149 - p.lines[1], err = p.r.ReadString('\n') 146 + for i := 0; i < len(p.lines)-1; i++ { 147 + p.lines[i] = p.lines[i+1] 148 + } 149 + p.lines[len(p.lines)-1], err = p.r.ReadString('\n') 150 150 return 151 151 } 152 152 153 - // Line returns the current line or an empty string if Next has returned io.EOF. 154 - func (p *parser) Line() string { 155 - return p.lines[0] 156 - } 157 - 158 - // PeekLine returns the line following the current line or an empty string if 159 - // the current line is the final line. If PeekLine returns an empty string, 160 - // Next will return io.EOF on the next call. 161 - func (p *parser) PeekLine() string { 162 - return p.lines[1] 153 + // Line returns a line from the parser without advancing it. A delta of 0 154 + // returns the current line, while higher deltas return read-ahead lines. It 155 + // returns an empty string if the delta is higher than the available lines, 156 + // either because of the buffer size or because the parser reached the end of 157 + // the input. Valid lines always contain at least a newline character. 158 + func (p *parser) Line(delta uint) string { 159 + return p.lines[delta] 163 160 } 164 161 165 162 // Errorf generates an error and appends the current line information.
+21 -8
gitdiff/parser_test.go
··· 2 2 3 3 import ( 4 4 "bufio" 5 + "io" 5 6 "reflect" 6 7 "strings" 7 8 "testing" ··· 14 15 return &parser{r: bufio.NewReader(strings.NewReader(content))} 15 16 } 16 17 17 - t.Run("readLine", func(t *testing.T) { 18 + t.Run("read", func(t *testing.T) { 18 19 p := newParser() 19 20 20 21 if err := p.Next(); err != nil { 21 22 t.Fatalf("error advancing parser: %v", err) 22 23 } 23 24 24 - line := p.Line() 25 + line := p.Line(0) 25 26 if line != "the first line\n" { 26 27 t.Fatalf("incorrect first line: %s", line) 27 28 } ··· 30 31 t.Fatalf("error advancing parser: %v", err) 31 32 } 32 33 33 - line = p.Line() 34 - if p.Line() != "the second line\n" { 34 + line = p.Line(0) 35 + if line != "the second line\n" { 35 36 t.Fatalf("incorrect second line: %s", line) 36 37 } 38 + 39 + if err := p.Next(); err != nil { 40 + t.Fatalf("error advancing parser: %v", err) 41 + } 42 + 43 + line = p.Line(0) 44 + if line != "the third line\n" { 45 + t.Fatalf("incorrect third line: %s", line) 46 + } 47 + 48 + if err := p.Next(); err != io.EOF { 49 + t.Fatalf("expected EOF, but got: %v", err) 50 + } 37 51 }) 38 52 39 - t.Run("peekLine", func(t *testing.T) { 53 + t.Run("peek", func(t *testing.T) { 40 54 p := newParser() 41 55 42 56 if err := p.Next(); err != nil { 43 57 t.Fatalf("error advancing parser: %v", err) 44 58 } 45 59 46 - line := p.PeekLine() 60 + line := p.Line(1) 47 61 if line != "the second line\n" { 48 62 t.Fatalf("incorrect peek line: %s", line) 49 63 } 50 64 51 - // test that reading the line returns the same value 52 65 if err := p.Next(); err != nil { 53 66 t.Fatalf("error advancing parser: %v", err) 54 67 } 55 68 56 - line = p.Line() 69 + line = p.Line(0) 57 70 if line != "the second line\n" { 58 71 t.Fatalf("incorrect line: %s", line) 59 72 }