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  }