Wim Brand
6 years ago
3 changed files with 94 additions and 51 deletions
-
72asyncreader.go
-
2build.sh
-
71main.go
@ -0,0 +1,72 @@ |
|||||
|
package main |
||||
|
|
||||
|
import ( |
||||
|
"io" |
||||
|
"sync" |
||||
|
|
||||
|
"golang.org/x/exp/mmap" |
||||
|
) |
||||
|
|
||||
|
const lf = byte('\n') |
||||
|
const null = byte(0) |
||||
|
|
||||
|
type AsyncLineReader struct { |
||||
|
offset int64 |
||||
|
eof bool |
||||
|
file *mmap.ReaderAt |
||||
|
mu *sync.Mutex |
||||
|
remainder []byte |
||||
|
} |
||||
|
|
||||
|
func NewAsyncLineReader(name string) (*AsyncLineReader, error) { |
||||
|
var a = &AsyncLineReader{ |
||||
|
offset: 0, |
||||
|
eof: false, |
||||
|
mu: &sync.Mutex{}, |
||||
|
} |
||||
|
|
||||
|
var err error |
||||
|
a.file, err = mmap.Open(name) |
||||
|
|
||||
|
return a, err |
||||
|
} |
||||
|
|
||||
|
func (a *AsyncLineReader) Read(b []byte) (int, error) { |
||||
|
if a.eof { |
||||
|
return 0, io.EOF |
||||
|
} |
||||
|
|
||||
|
a.mu.Lock() |
||||
|
defer a.mu.Unlock() |
||||
|
|
||||
|
var n, err = a.file.ReadAt(b, a.offset) |
||||
|
if err == io.EOF { |
||||
|
a.eof = true |
||||
|
return 0, err |
||||
|
} |
||||
|
if n == 0 { |
||||
|
return 0, err |
||||
|
} |
||||
|
|
||||
|
// find the last line feed in the batch
|
||||
|
for { |
||||
|
n-- |
||||
|
|
||||
|
if b[n] == lf { |
||||
|
// selected character is a line feed, we're done
|
||||
|
break |
||||
|
} else if n == 0 { |
||||
|
break // result is at start of file
|
||||
|
} |
||||
|
|
||||
|
b[n] = null // Discard this byte so it doesn't get read multiple times
|
||||
|
} |
||||
|
|
||||
|
a.offset += int64(n) |
||||
|
|
||||
|
return n, err |
||||
|
} |
||||
|
|
||||
|
func (a *AsyncLineReader) Close() error { |
||||
|
return a.file.Close() |
||||
|
} |
@ -1,3 +1,3 @@ |
|||||
#!/bin/sh |
#!/bin/sh |
||||
|
|
||||
go build -o rapidgrep -ldflags="-s -w" main.go |
|
||||
|
go build -o rapidgrep -ldflags="-s -w" asyncreader.go main.go |
Write
Preview
Loading…
Cancel
Save
Reference in new issue