gitlab.com/greut/eclint@v0.5.2-0.20240402114752-14681fe6e0bf/scanner.go (about) 1 package eclint 2 3 import ( 4 "bufio" 5 "io" 6 ) 7 8 // LineFunc is the callback for a line. 9 // 10 // It returns the line number starting from zero. 11 type LineFunc func(int, []byte, bool) error 12 13 // SplitLines works like bufio.ScanLines while keeping the line endings. 14 func SplitLines(data []byte, atEOF bool) (int, []byte, error) { 15 i := 0 16 for i < len(data) { 17 if data[i] == cr { 18 i++ 19 20 if i < len(data) && !atEOF { 21 // Request more data 22 return 0, nil, nil 23 } 24 25 if i < len(data) && data[i] == lf { 26 i++ 27 } 28 29 return i, data[0:i], nil 30 } else if data[i] == lf { 31 i++ 32 33 return i, data[0:i], nil 34 } 35 36 i++ 37 } 38 39 if !atEOF { 40 // Request more data 41 return 0, nil, nil 42 } 43 44 if atEOF && i != 0 { 45 return 0, data, bufio.ErrFinalToken 46 } 47 48 return 0, nil, io.EOF 49 } 50 51 // ReadLines consumes the reader and emit each line via the LineFunc 52 // 53 // Line numbering starts at 0. Scanner is pretty smart an will reuse 54 // its memory structure. This is something we explicitly avoid by copying 55 // the content to a new slice. 56 func ReadLines(r io.Reader, fileSize int64, fn LineFunc) []error { 57 errs := make([]error, 0) 58 sc := bufio.NewScanner(r) 59 sc.Split(SplitLines) 60 61 var read int64 62 63 i := 0 64 65 for sc.Scan() { 66 l := sc.Bytes() 67 line := make([]byte, len(l)) 68 69 copy(line, l) 70 71 read += int64(len(line)) 72 73 if err := fn(i, line, read == fileSize); err != nil { 74 errs = append(errs, err) 75 } 76 77 i++ 78 } 79 80 return errs 81 }