github.com/KEINOS/go-countline@v1.1.0/cl/_alt/alt2.go (about)

     1  //nolint:revive,stylecheck
     2  package _alt
     3  
     4  import (
     5  	"bufio"
     6  	"bytes"
     7  	"io"
     8  	"sync"
     9  	"sync/atomic"
    10  
    11  	"github.com/pkg/errors"
    12  )
    13  
    14  // ----------------------------------------------------------------------------
    15  //  CountLinesAlt2
    16  // ----------------------------------------------------------------------------
    17  
    18  // CountLinesAlt2 uses bufio.Reader and goroutines to count the number of lines.
    19  func CountLinesAlt2(inputReader io.Reader) (int, error) {
    20  	if inputReader == nil {
    21  		return 0, errors.New("given reader is nil")
    22  	}
    23  
    24  	const double = 2
    25  
    26  	bufSizeBase := 1024
    27  	count := uint64(0)
    28  	wg := new(sync.WaitGroup) //nolint:varnamelen
    29  	bufReader := bufio.NewReader(inputReader)
    30  	lastBuf := []byte{}
    31  	numIte := 0
    32  
    33  	for {
    34  		numIte++
    35  		buf := make([]byte, bufSizeBase*(numIte*double))
    36  
    37  		numRead, err := bufReader.Read(buf)
    38  		if err != nil {
    39  			if err == io.EOF {
    40  				break
    41  			}
    42  
    43  			return 0, errors.Wrap(err, "failed to read from reader")
    44  		}
    45  
    46  		task := buf[:numRead]
    47  		lastBuf = task
    48  
    49  		wg.Add(1)
    50  
    51  		go func() {
    52  			found := bytes.Count(task, []byte{'\n'})
    53  
    54  			atomic.AddUint64(&count, uint64(found)) // count++ safely
    55  
    56  			wg.Done()
    57  		}()
    58  	}
    59  
    60  	wg.Wait()
    61  
    62  	// Detect the file ends without a line break and count up if so.
    63  	lenLastBuf := len(lastBuf)
    64  	hasFragment := false
    65  
    66  	for i := lenLastBuf; i > 0; i-- {
    67  		tmpChar := lastBuf[i-1]
    68  		if tmpChar == '\x00' {
    69  			continue
    70  		}
    71  
    72  		if tmpChar == '\n' {
    73  			break
    74  		}
    75  
    76  		hasFragment = true
    77  	}
    78  
    79  	if hasFragment {
    80  		atomic.AddUint64(&count, 1) // count++ safely
    81  	}
    82  
    83  	return int(count), nil
    84  }