Skip to content

Instantly share code, notes, and snippets.

@paulsc
Last active December 22, 2018 10:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save paulsc/6a0bf30a2a8d898f7a8086aedf6af1e1 to your computer and use it in GitHub Desktop.
Save paulsc/6a0bf30a2a8d898f7a8086aedf6af1e1 to your computer and use it in GitHub Desktop.
package util
import (
"io"
)
type LinePipe struct {
leftover []byte
scanner TextScanner
bytesWritten int
newLineChar string
}
type TextScanner interface {
Text() string
Err() error
Scan() bool
}
func NewLinePipe(scanner TextScanner) *LinePipe {
lp := &LinePipe{scanner: scanner}
lp.newLineChar = "\n"
return lp
}
func (lp *LinePipe) NewLineChar(newlineChar string) {
lp.newLineChar = newlineChar
}
// Write as much as possible from data to p, save leftover bytes for next round
func (lp *LinePipe) write(p []byte, data []byte) int {
end := lp.bytesWritten + len(data)
end = min(end, len(p))
written := copy(p[lp.bytesWritten:end], data)
lp.bytesWritten += written
lp.leftover = data[written:]
return written
}
func (lp *LinePipe) Read(p []byte) (n int, err error) {
lp.bytesWritten = 0
// First empty local buffer
if len(lp.leftover) > 0 {
toWrite := len(lp.leftover)
written := lp.write(p, lp.leftover)
if written < toWrite {
return lp.bytesWritten, nil
}
}
// If there's still room in p, write from scanner
for {
if !lp.scanner.Scan() {
if err := lp.scanner.Err(); err != nil {
return lp.bytesWritten, err
} else {
return lp.bytesWritten, io.EOF
}
}
line := lp.scanner.Text() + lp.newLineChar
data := []byte(line)
written := lp.write(p, data)
if written < len(data) {
break
}
}
return lp.bytesWritten, nil
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment