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 }