summaryrefslogtreecommitdiff
path: root/dataset/count.go
diff options
context:
space:
mode:
Diffstat (limited to 'dataset/count.go')
-rw-r--r--dataset/count.go92
1 files changed, 92 insertions, 0 deletions
diff --git a/dataset/count.go b/dataset/count.go
new file mode 100644
index 0000000..b2e6d14
--- /dev/null
+++ b/dataset/count.go
@@ -0,0 +1,92 @@
+package dataset
+
+import (
+ "bytes"
+ "context"
+ "io"
+ "os"
+ "runtime"
+ "sync/atomic"
+
+ "golang.org/x/sync/errgroup"
+)
+
+func countLinesAll(names []string, delimiter []byte) (int, error) {
+ g, ctx := errgroup.WithContext(context.Background())
+
+ g.SetLimit(runtime.NumCPU() - 1)
+
+ var total atomic.Int64
+
+ for _, name := range names {
+ g.Go(func() error {
+ select {
+ case <-ctx.Done():
+ return ctx.Err()
+ default:
+ }
+
+ var file *os.File
+
+ if f, err := os.Open(name); err != nil {
+ return err
+ } else {
+ file = f
+ }
+
+ if n, err := countLines(file, delimiter); err != nil {
+ return err
+ } else {
+ total.Add(n)
+ }
+
+ return nil
+ })
+ }
+
+ if err := g.Wait(); err != nil {
+ return 0, err
+ }
+
+ return int(total.Load()), nil
+}
+
+func countLines(r io.Reader, delimiter []byte) (int64, error) {
+ if len(delimiter) == 0 {
+ panic("empty delimiter")
+ }
+
+ l := len(delimiter)
+
+ buf := make([]byte, 128*1024)
+
+ var total int64
+
+ carry := 0
+
+ for {
+ n, err := r.Read(buf[carry:])
+
+ active := carry + n
+
+ if active >= l {
+ total += int64(bytes.Count(buf[:active], delimiter))
+
+ carry = l - 1
+
+ copy(buf[:carry], buf[active-carry:])
+ } else {
+ carry = active
+ }
+
+ if err == io.EOF {
+ break
+ }
+
+ if err != nil {
+ return 0, err
+ }
+ }
+
+ return total, nil
+}