github.com/karrick/gorill@v1.10.3/nlc.go (about) 1 package gorill 2 3 import ( 4 "bytes" 5 "io" 6 ) 7 8 // NewlineCounter counts the number of lines from the io.Reader, returning the 9 // same number of lines read regardless of whether the final Read terminated in 10 // a newline character. 11 func NewlineCounter(ior io.Reader) (int, error) { 12 var newlines, total, n int 13 var isNotFinalNewline bool 14 var err error 15 var reserved [4096]byte // allocate buffer space on the call stack 16 buf := reserved[:] // create slice using pre-allocated array from reserved 17 18 for { 19 n, err = ior.Read(buf) 20 if n > 0 { 21 total += n 22 isNotFinalNewline = buf[n-1] != '\n' 23 var searchOffset int 24 for { 25 index := bytes.IndexRune(buf[searchOffset:n], '\n') 26 if index == -1 { 27 break // done counting newlines from this chunk 28 } 29 newlines++ // count this newline 30 searchOffset += index + 1 // start next search following this newline 31 } 32 } 33 if err != nil { 34 if err == io.EOF { 35 err = nil // io.EOF is expected at end of stream 36 } 37 break // do not try to read more if error 38 } 39 } 40 41 // Return the same number of lines read regardless of whether the final read 42 // terminated in a newline character. 43 if isNotFinalNewline { 44 newlines++ 45 } else if total == 1 { 46 newlines-- 47 } 48 return newlines, err 49 }