github.com/searKing/golang/go@v1.2.117/io/counter.go (about)

     1  package io
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"io"
     7  
     8  	bytes_ "github.com/searKing/golang/go/bytes"
     9  )
    10  
    11  // Count counts the number of non-overlapping instances of sep in r.
    12  // If sep is an empty slice, Count returns 1 + the number of UTF-8-encoded code points in s.
    13  // tailMatch returns true if tail bytes match sep
    14  func Count(r io.Reader, sep string) (cnt int, tailMatch bool, err error) {
    15  	return CountSize(r, sep, bufio.MaxScanTokenSize)
    16  }
    17  
    18  // CountSize counts the number of non-overlapping instances of sep in r.
    19  // tailMatch returns true if tail bytes match sep
    20  func CountSize(r io.Reader, sep string, size int) (cnt int, tailMatch bool, err error) {
    21  	// special case
    22  	if len(sep) == 0 || len(sep) == 1 {
    23  		return CountAnySize(r, sep, size)
    24  	}
    25  
    26  	if size < len(sep) {
    27  		size = len(sep)
    28  	}
    29  	var count int
    30  	buf := make([]byte, size)
    31  
    32  	// buffered bytes
    33  	var bufferedPos int
    34  	for {
    35  		n, err := r.Read(buf[bufferedPos:])
    36  		if n > 0 {
    37  			bufferedPos += n
    38  			cnt, index := bytes_.CountIndex(buf[:bufferedPos], []byte(sep))
    39  			count += cnt
    40  
    41  			// store the next index to do match
    42  			var tailIndex int
    43  			if index >= 0 {
    44  				// skip matched
    45  				tailIndex = index + len(sep)
    46  			} else {
    47  				// skip first byte
    48  				tailIndex = 1
    49  			}
    50  
    51  			copyCnt := bufferedPos - tailIndex
    52  			if copyCnt >= len(sep) {
    53  				copyCnt = len(sep) - 1
    54  			}
    55  
    56  			// buffer tail bytes if any
    57  			if copyCnt > 0 {
    58  				copy(buf[:copyCnt], buf[tailIndex:tailIndex+copyCnt])
    59  				bufferedPos = copyCnt
    60  			} else {
    61  				bufferedPos = 0
    62  			}
    63  		}
    64  		if err == io.EOF {
    65  			return count, bufferedPos == 0, nil
    66  		}
    67  		if err != nil {
    68  			return count, true, err
    69  		}
    70  	}
    71  }
    72  
    73  // CountAnySize counts the number of non-overlapping instances of sep in r.
    74  // If sep is an empty slice, Count returns 1 + the number of UTF-8-encoded code points in s.
    75  func CountAny(r io.Reader, sep string) (cnt int, tailMatch bool, err error) {
    76  	return CountAnySize(r, sep, bufio.MaxScanTokenSize)
    77  }
    78  
    79  // CountAnySize counts the number of non-overlapping instances of sep in r.
    80  // If sep is an empty slice, Count returns 1 + the number of UTF-8-encoded code points in s.
    81  func CountAnySize(r io.Reader, sep string, size int) (cnt int, tailMatch bool, err error) {
    82  	var count int
    83  	if size < 1 {
    84  		size = 1
    85  	}
    86  
    87  	buf := make([]byte, size)
    88  	var lastByte byte
    89  	for {
    90  		n, err := r.Read(buf)
    91  		if n > 0 {
    92  			lastByte = buf[n-1]
    93  			count += bytes.Count(buf[:n], []byte(sep))
    94  		}
    95  		if err == io.EOF {
    96  			if bytes.ContainsAny([]byte(sep), string(lastByte)) {
    97  				return count, true, nil
    98  			}
    99  			return count, false, nil
   100  		}
   101  		if err != nil {
   102  			return count, true, err
   103  		}
   104  	}
   105  }
   106  
   107  // CountLines counts the number of lines by \n.
   108  func CountLines(r io.Reader) (lines int, err error) {
   109  	return CountLinesSize(r, bufio.MaxScanTokenSize)
   110  }
   111  
   112  // CountLinesSize counts the number of lines by \n.
   113  func CountLinesSize(r io.Reader, size int) (lines int, err error) {
   114  	cnt, tailMatch, err := CountSize(r, "\n", size)
   115  	// take care of ending line without '\n'
   116  	if !tailMatch {
   117  		cnt++
   118  	}
   119  	return cnt, err
   120  }