this repo has no description
0
fork

Configure Feed

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

at 18058d1e151e780b7617d574c037b8a4e948816c 205 lines 4.9 kB view raw
1package gitdiff 2 3import ( 4 "errors" 5 "io" 6) 7 8// LineReaderAt is the interface that wraps the ReadLinesAt method. 9// 10// ReadLinesAt reads len(lines) into lines starting at line offset in the 11// input source. It returns number of full lines read (0 <= n <= len(lines)) 12// and any error encountered. Line numbers are zero-indexed. 13// 14// If n < len(lines), ReadLinesAt returns a non-nil error explaining why more 15// lines were not returned. 16// 17// Each full line includes the line ending character(s). If the last line of 18// the input does not have a line ending character, ReadLinesAt returns the 19// content of the line and io.EOF. 20// 21// If the content of the input source changes after the first call to 22// ReadLinesAt, the behavior of future calls is undefined. 23type LineReaderAt interface { 24 ReadLinesAt(lines [][]byte, offset int64) (n int, err error) 25} 26 27type lineReaderAt struct { 28 r io.ReaderAt 29 index []int64 30 eof bool 31} 32 33func (r *lineReaderAt) ReadLinesAt(lines [][]byte, offset int64) (n int, err error) { 34 // TODO(bkeyes): revisit variable names 35 // - it's generally not clear when something is bytes vs lines 36 // - offset is a good example of this 37 38 if len(lines) == 0 { 39 return 0, nil 40 } 41 42 endLine := offset + int64(len(lines)) 43 if endLine > int64(len(r.index)) && !r.eof { 44 if err := r.indexTo(endLine); err != nil { 45 return 0, err 46 } 47 } 48 if offset > int64(len(r.index)) { 49 return 0, io.EOF 50 } 51 52 size, readOffset := lookupLines(r.index, offset, int64(len(lines))) 53 54 b := make([]byte, size) 55 if _, err := r.r.ReadAt(b, readOffset); err != nil { 56 if err == io.EOF { 57 err = errors.New("ReadLinesAt: corrupt line index or changed source data") 58 } 59 return 0, err 60 } 61 62 for n = 0; n < len(lines) && offset+int64(n) < int64(len(r.index)); n++ { 63 i := offset + int64(n) 64 start, end := readOffset, r.index[i] 65 if i > 0 { 66 start = r.index[i-1] 67 } 68 lines[n] = b[start-readOffset : end-readOffset] 69 } 70 71 if n < len(lines) || b[size-1] != '\n' { 72 return n, io.EOF 73 } 74 return n, nil 75} 76 77// indexTo reads data and computes the line index until there is information 78// for line or a read returns io.EOF. It returns an error if and only if there 79// is an error reading data. 80func (r *lineReaderAt) indexTo(line int64) error { 81 var buf [1024]byte 82 83 var offset int64 84 if len(r.index) > 0 { 85 offset = r.index[len(r.index)-1] 86 } 87 88 for int64(len(r.index)) < line { 89 n, err := r.r.ReadAt(buf[:], offset) 90 if err != nil && err != io.EOF { 91 return err 92 } 93 for _, b := range buf[:n] { 94 offset++ 95 if b == '\n' { 96 r.index = append(r.index, offset) 97 } 98 } 99 if err == io.EOF { 100 if n > 0 && buf[n-1] != '\n' { 101 r.index = append(r.index, offset) 102 } 103 r.eof = true 104 break 105 } 106 } 107 return nil 108} 109 110// lookupLines gets the byte offset and size of a range of lines from an index 111// where the value at n is the offset of the first byte after line number n. 112func lookupLines(index []int64, start, n int64) (size int64, offset int64) { 113 if start > int64(len(index)) { 114 offset = index[len(index)-1] 115 } else if start > 0 { 116 offset = index[start-1] 117 } 118 if n > 0 { 119 if start+n > int64(len(index)) { 120 size = index[len(index)-1] - offset 121 } else { 122 size = index[start+n-1] - offset 123 } 124 } 125 return 126} 127 128func isLen(r io.ReaderAt, n int64) (bool, error) { 129 off := n - 1 130 if off < 0 { 131 off = 0 132 } 133 134 var b [2]byte 135 nr, err := r.ReadAt(b[:], off) 136 if err == io.EOF { 137 return (n == 0 && nr == 0) || (n > 0 && nr == 1), nil 138 } 139 return false, err 140} 141 142// copyFrom writes bytes starting from offset off in src to dst stopping at the 143// end of src or at the first error. copyFrom returns the number of bytes 144// written and any error. 145func copyFrom(dst io.Writer, src io.ReaderAt, off int64) (written int64, err error) { 146 buf := make([]byte, 32*1024) // stolen from io.Copy 147 for { 148 nr, rerr := src.ReadAt(buf, off) 149 if nr > 0 { 150 nw, werr := dst.Write(buf[0:nr]) 151 if nw > 0 { 152 written += int64(nw) 153 } 154 if werr != nil { 155 err = werr 156 break 157 } 158 if nr != nw { 159 err = io.ErrShortWrite 160 break 161 } 162 } 163 if rerr != nil { 164 if rerr != io.EOF { 165 err = rerr 166 } 167 break 168 } 169 } 170 return written, err 171} 172 173// copyLinesFrom writes lines starting from line off in src to dst stopping at 174// the end of src or at the first error. copyLinesFrom returns the number of 175// lines written and any error. 176func copyLinesFrom(dst io.Writer, src LineReaderAt, off int64) (written int64, err error) { 177 buf := make([][]byte, 32) 178ReadLoop: 179 for { 180 nr, rerr := src.ReadLinesAt(buf, off) 181 if nr > 0 { 182 for _, line := range buf[0:nr] { 183 nw, werr := dst.Write(line) 184 if nw > 0 { 185 written++ 186 } 187 if werr != nil { 188 err = werr 189 break ReadLoop 190 } 191 if len(line) != nw { 192 err = io.ErrShortWrite 193 break ReadLoop 194 } 195 } 196 } 197 if rerr != nil { 198 if rerr != io.EOF { 199 err = rerr 200 } 201 break 202 } 203 } 204 return written, err 205}