initial import
This commit is contained in:
84
core/filex/file.go
Normal file
84
core/filex/file.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package filex
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
const bufSize = 1024
|
||||
|
||||
func FirstLine(filename string) (string, error) {
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
return firstLine(file)
|
||||
}
|
||||
|
||||
func LastLine(filename string) (string, error) {
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
return lastLine(filename, file)
|
||||
}
|
||||
|
||||
func firstLine(file *os.File) (string, error) {
|
||||
var first []byte
|
||||
var offset int64
|
||||
for {
|
||||
buf := make([]byte, bufSize)
|
||||
n, err := file.ReadAt(buf, offset)
|
||||
if err != nil && err != io.EOF {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
if buf[i] == '\n' {
|
||||
return string(append(first, buf[:i]...)), nil
|
||||
}
|
||||
}
|
||||
|
||||
first = append(first, buf[:n]...)
|
||||
offset += bufSize
|
||||
}
|
||||
}
|
||||
|
||||
func lastLine(filename string, file *os.File) (string, error) {
|
||||
info, err := os.Stat(filename)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var last []byte
|
||||
offset := info.Size()
|
||||
for {
|
||||
offset -= bufSize
|
||||
if offset < 0 {
|
||||
offset = 0
|
||||
}
|
||||
buf := make([]byte, bufSize)
|
||||
n, err := file.ReadAt(buf, offset)
|
||||
if err != nil && err != io.EOF {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if buf[n-1] == '\n' {
|
||||
buf = buf[:n-1]
|
||||
n -= 1
|
||||
} else {
|
||||
buf = buf[:n]
|
||||
}
|
||||
for n -= 1; n >= 0; n-- {
|
||||
if buf[n] == '\n' {
|
||||
return string(append(buf[n+1:], last...)), nil
|
||||
}
|
||||
}
|
||||
|
||||
last = append(buf, last...)
|
||||
}
|
||||
}
|
||||
116
core/filex/file_test.go
Normal file
116
core/filex/file_test.go
Normal file
@@ -0,0 +1,116 @@
|
||||
package filex
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"zero/core/fs"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const (
|
||||
longLine = `Quid securi etiam tamquam eu fugiat nulla pariatur. Nec dubitamus multa iter quae et nos invenerat. Non equidem invideo, miror magis posuere velit aliquet. Integer legentibus erat a ante historiarum dapibus. Prima luce, cum quibus mons aliud consensu ab eo.Quid securi etiam tamquam eu fugiat nulla pariatur. Nec dubitamus multa iter quae et nos invenerat. Non equidem invideo, miror magis posuere velit aliquet. Integer legentibus erat a ante historiarum dapibus. Prima luce, cum quibus mons aliud consensu ab eo.Quid securi etiam tamquam eu fugiat nulla pariatur. Nec dubitamus multa iter quae et nos invenerat. Non equidem invideo, miror magis posuere velit aliquet. Integer legentibus erat a ante historiarum dapibus. Prima luce, cum quibus mons aliud consensu ab eo.Quid securi etiam tamquam eu fugiat nulla pariatur. Nec dubitamus multa iter quae et nos invenerat. Non equidem invideo, miror magis posuere velit aliquet. Integer legentibus erat a ante historiarum dapibus. Prima luce, cum quibus mons aliud consensu ab eo.Quid securi etiam tamquam eu fugiat nulla pariatur. Nec dubitamus multa iter quae et nos invenerat. Non equidem invideo, miror magis posuere velit aliquet. Integer legentibus erat a ante historiarum dapibus. Prima luce, cum quibus mons aliud consensu ab eo.`
|
||||
longFirstLine = longLine + "\n" + text
|
||||
text = `first line
|
||||
Cum sociis natoque penatibus et magnis dis parturient. Phasellus laoreet lorem vel dolor tempus vehicula. Vivamus sagittis lacus vel augue laoreet rutrum faucibus. Integer legentibus erat a ante historiarum dapibus.
|
||||
Quisque ut dolor gravida, placerat libero vel, euismod. Quam temere in vitiis, legem sancimus haerentia. Qui ipsorum lingua Celtae, nostra Galli appellantur. Quis aute iure reprehenderit in voluptate velit esse. Fabio vel iudice vincam, sunt in culpa qui officia. Cras mattis iudicium purus sit amet fermentum.
|
||||
Quo usque tandem abutere, Catilina, patientia nostra? Gallia est omnis divisa in partes tres, quarum. Quam diu etiam furor iste tuus nos eludet? Quid securi etiam tamquam eu fugiat nulla pariatur. Curabitur blandit tempus ardua ridiculus sed magna.
|
||||
Magna pars studiorum, prodita quaerimus. Cum ceteris in veneratione tui montes, nascetur mus. Morbi odio eros, volutpat ut pharetra vitae, lobortis sed nibh. Plura mihi bona sunt, inclinet, amari petere vellent. Idque Caesaris facere voluntate liceret: sese habere. Tu quoque, Brute, fili mi, nihil timor populi, nihil!
|
||||
Tityre, tu patulae recubans sub tegmine fagi dolor. Inmensae subtilitatis, obscuris et malesuada fames. Quae vero auctorem tractata ab fiducia dicuntur.
|
||||
Cum sociis natoque penatibus et magnis dis parturient. Phasellus laoreet lorem vel dolor tempus vehicula. Vivamus sagittis lacus vel augue laoreet rutrum faucibus. Integer legentibus erat a ante historiarum dapibus.
|
||||
Quisque ut dolor gravida, placerat libero vel, euismod. Quam temere in vitiis, legem sancimus haerentia. Qui ipsorum lingua Celtae, nostra Galli appellantur. Quis aute iure reprehenderit in voluptate velit esse. Fabio vel iudice vincam, sunt in culpa qui officia. Cras mattis iudicium purus sit amet fermentum.
|
||||
Quo usque tandem abutere, Catilina, patientia nostra? Gallia est omnis divisa in partes tres, quarum. Quam diu etiam furor iste tuus nos eludet? Quid securi etiam tamquam eu fugiat nulla pariatur. Curabitur blandit tempus ardua ridiculus sed magna.
|
||||
Magna pars studiorum, prodita quaerimus. Cum ceteris in veneratione tui montes, nascetur mus. Morbi odio eros, volutpat ut pharetra vitae, lobortis sed nibh. Plura mihi bona sunt, inclinet, amari petere vellent. Idque Caesaris facere voluntate liceret: sese habere. Tu quoque, Brute, fili mi, nihil timor populi, nihil!
|
||||
Tityre, tu patulae recubans sub tegmine fagi dolor. Inmensae subtilitatis, obscuris et malesuada fames. Quae vero auctorem tractata ab fiducia dicuntur.
|
||||
Cum sociis natoque penatibus et magnis dis parturient. Phasellus laoreet lorem vel dolor tempus vehicula. Vivamus sagittis lacus vel augue laoreet rutrum faucibus. Integer legentibus erat a ante historiarum dapibus.
|
||||
Quisque ut dolor gravida, placerat libero vel, euismod. Quam temere in vitiis, legem sancimus haerentia. Qui ipsorum lingua Celtae, nostra Galli appellantur. Quis aute iure reprehenderit in voluptate velit esse. Fabio vel iudice vincam, sunt in culpa qui officia. Cras mattis iudicium purus sit amet fermentum.
|
||||
Quo usque tandem abutere, Catilina, patientia nostra? Gallia est omnis divisa in partes tres, quarum. Quam diu etiam furor iste tuus nos eludet? Quid securi etiam tamquam eu fugiat nulla pariatur. Curabitur blandit tempus ardua ridiculus sed magna.
|
||||
Magna pars studiorum, prodita quaerimus. Cum ceteris in veneratione tui montes, nascetur mus. Morbi odio eros, volutpat ut pharetra vitae, lobortis sed nibh. Plura mihi bona sunt, inclinet, amari petere vellent. Idque Caesaris facere voluntate liceret: sese habere. Tu quoque, Brute, fili mi, nihil timor populi, nihil!
|
||||
Tityre, tu patulae recubans sub tegmine fagi dolor. Inmensae subtilitatis, obscuris et malesuada fames. Quae vero auctorem tractata ab fiducia dicuntur.
|
||||
` + longLine
|
||||
textWithLastNewline = `first line
|
||||
Cum sociis natoque penatibus et magnis dis parturient. Phasellus laoreet lorem vel dolor tempus vehicula. Vivamus sagittis lacus vel augue laoreet rutrum faucibus. Integer legentibus erat a ante historiarum dapibus.
|
||||
Quisque ut dolor gravida, placerat libero vel, euismod. Quam temere in vitiis, legem sancimus haerentia. Qui ipsorum lingua Celtae, nostra Galli appellantur. Quis aute iure reprehenderit in voluptate velit esse. Fabio vel iudice vincam, sunt in culpa qui officia. Cras mattis iudicium purus sit amet fermentum.
|
||||
Quo usque tandem abutere, Catilina, patientia nostra? Gallia est omnis divisa in partes tres, quarum. Quam diu etiam furor iste tuus nos eludet? Quid securi etiam tamquam eu fugiat nulla pariatur. Curabitur blandit tempus ardua ridiculus sed magna.
|
||||
Magna pars studiorum, prodita quaerimus. Cum ceteris in veneratione tui montes, nascetur mus. Morbi odio eros, volutpat ut pharetra vitae, lobortis sed nibh. Plura mihi bona sunt, inclinet, amari petere vellent. Idque Caesaris facere voluntate liceret: sese habere. Tu quoque, Brute, fili mi, nihil timor populi, nihil!
|
||||
Tityre, tu patulae recubans sub tegmine fagi dolor. Inmensae subtilitatis, obscuris et malesuada fames. Quae vero auctorem tractata ab fiducia dicuntur.
|
||||
Cum sociis natoque penatibus et magnis dis parturient. Phasellus laoreet lorem vel dolor tempus vehicula. Vivamus sagittis lacus vel augue laoreet rutrum faucibus. Integer legentibus erat a ante historiarum dapibus.
|
||||
Quisque ut dolor gravida, placerat libero vel, euismod. Quam temere in vitiis, legem sancimus haerentia. Qui ipsorum lingua Celtae, nostra Galli appellantur. Quis aute iure reprehenderit in voluptate velit esse. Fabio vel iudice vincam, sunt in culpa qui officia. Cras mattis iudicium purus sit amet fermentum.
|
||||
Quo usque tandem abutere, Catilina, patientia nostra? Gallia est omnis divisa in partes tres, quarum. Quam diu etiam furor iste tuus nos eludet? Quid securi etiam tamquam eu fugiat nulla pariatur. Curabitur blandit tempus ardua ridiculus sed magna.
|
||||
Magna pars studiorum, prodita quaerimus. Cum ceteris in veneratione tui montes, nascetur mus. Morbi odio eros, volutpat ut pharetra vitae, lobortis sed nibh. Plura mihi bona sunt, inclinet, amari petere vellent. Idque Caesaris facere voluntate liceret: sese habere. Tu quoque, Brute, fili mi, nihil timor populi, nihil!
|
||||
Tityre, tu patulae recubans sub tegmine fagi dolor. Inmensae subtilitatis, obscuris et malesuada fames. Quae vero auctorem tractata ab fiducia dicuntur.
|
||||
Cum sociis natoque penatibus et magnis dis parturient. Phasellus laoreet lorem vel dolor tempus vehicula. Vivamus sagittis lacus vel augue laoreet rutrum faucibus. Integer legentibus erat a ante historiarum dapibus.
|
||||
Quisque ut dolor gravida, placerat libero vel, euismod. Quam temere in vitiis, legem sancimus haerentia. Qui ipsorum lingua Celtae, nostra Galli appellantur. Quis aute iure reprehenderit in voluptate velit esse. Fabio vel iudice vincam, sunt in culpa qui officia. Cras mattis iudicium purus sit amet fermentum.
|
||||
Quo usque tandem abutere, Catilina, patientia nostra? Gallia est omnis divisa in partes tres, quarum. Quam diu etiam furor iste tuus nos eludet? Quid securi etiam tamquam eu fugiat nulla pariatur. Curabitur blandit tempus ardua ridiculus sed magna.
|
||||
Magna pars studiorum, prodita quaerimus. Cum ceteris in veneratione tui montes, nascetur mus. Morbi odio eros, volutpat ut pharetra vitae, lobortis sed nibh. Plura mihi bona sunt, inclinet, amari petere vellent. Idque Caesaris facere voluntate liceret: sese habere. Tu quoque, Brute, fili mi, nihil timor populi, nihil!
|
||||
Tityre, tu patulae recubans sub tegmine fagi dolor. Inmensae subtilitatis, obscuris et malesuada fames. Quae vero auctorem tractata ab fiducia dicuntur.
|
||||
` + longLine + "\n"
|
||||
shortText = `first line
|
||||
second line
|
||||
last line`
|
||||
shortTextWithLastNewline = `first line
|
||||
second line
|
||||
last line
|
||||
`
|
||||
)
|
||||
|
||||
func TestFirstLine(t *testing.T) {
|
||||
filename, err := fs.TempFilenameWithText(longFirstLine)
|
||||
assert.Nil(t, err)
|
||||
defer os.Remove(filename)
|
||||
|
||||
val, err := FirstLine(filename)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, longLine, val)
|
||||
}
|
||||
|
||||
func TestFirstLineShort(t *testing.T) {
|
||||
filename, err := fs.TempFilenameWithText(shortText)
|
||||
assert.Nil(t, err)
|
||||
defer os.Remove(filename)
|
||||
|
||||
val, err := FirstLine(filename)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "first line", val)
|
||||
}
|
||||
|
||||
func TestLastLine(t *testing.T) {
|
||||
filename, err := fs.TempFilenameWithText(text)
|
||||
assert.Nil(t, err)
|
||||
defer os.Remove(filename)
|
||||
|
||||
val, err := LastLine(filename)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, longLine, val)
|
||||
}
|
||||
|
||||
func TestLastLineWithLastNewline(t *testing.T) {
|
||||
filename, err := fs.TempFilenameWithText(textWithLastNewline)
|
||||
assert.Nil(t, err)
|
||||
defer os.Remove(filename)
|
||||
|
||||
val, err := LastLine(filename)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, longLine, val)
|
||||
}
|
||||
|
||||
func TestLastLineShort(t *testing.T) {
|
||||
filename, err := fs.TempFilenameWithText(shortText)
|
||||
assert.Nil(t, err)
|
||||
defer os.Remove(filename)
|
||||
|
||||
val, err := LastLine(filename)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "last line", val)
|
||||
}
|
||||
|
||||
func TestLastLineWithLastNewlineShort(t *testing.T) {
|
||||
filename, err := fs.TempFilenameWithText(shortTextWithLastNewline)
|
||||
assert.Nil(t, err)
|
||||
defer os.Remove(filename)
|
||||
|
||||
val, err := LastLine(filename)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "last line", val)
|
||||
}
|
||||
105
core/filex/lookup.go
Normal file
105
core/filex/lookup.go
Normal file
@@ -0,0 +1,105 @@
|
||||
package filex
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
type OffsetRange struct {
|
||||
File string
|
||||
Start int64
|
||||
Stop int64
|
||||
}
|
||||
|
||||
func SplitLineChunks(filename string, chunks int) ([]OffsetRange, error) {
|
||||
info, err := os.Stat(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if chunks <= 1 {
|
||||
return []OffsetRange{
|
||||
{
|
||||
File: filename,
|
||||
Start: 0,
|
||||
Stop: info.Size(),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
var ranges []OffsetRange
|
||||
var offset int64
|
||||
// avoid the last chunk too few bytes
|
||||
preferSize := info.Size()/int64(chunks) + 1
|
||||
for {
|
||||
if offset+preferSize >= info.Size() {
|
||||
ranges = append(ranges, OffsetRange{
|
||||
File: filename,
|
||||
Start: offset,
|
||||
Stop: info.Size(),
|
||||
})
|
||||
break
|
||||
}
|
||||
|
||||
offsetRange, err := nextRange(file, offset, offset+preferSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ranges = append(ranges, offsetRange)
|
||||
if offsetRange.Stop < info.Size() {
|
||||
offset = offsetRange.Stop
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return ranges, nil
|
||||
}
|
||||
|
||||
func nextRange(file *os.File, start, stop int64) (OffsetRange, error) {
|
||||
offset, err := skipPartialLine(file, stop)
|
||||
if err != nil {
|
||||
return OffsetRange{}, err
|
||||
}
|
||||
|
||||
return OffsetRange{
|
||||
File: file.Name(),
|
||||
Start: start,
|
||||
Stop: offset,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func skipPartialLine(file *os.File, offset int64) (int64, error) {
|
||||
for {
|
||||
skipBuf := make([]byte, bufSize)
|
||||
n, err := file.ReadAt(skipBuf, offset)
|
||||
if err != nil && err != io.EOF {
|
||||
return 0, err
|
||||
}
|
||||
if n == 0 {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
if skipBuf[i] != '\r' && skipBuf[i] != '\n' {
|
||||
offset++
|
||||
} else {
|
||||
for ; i < n; i++ {
|
||||
if skipBuf[i] == '\r' || skipBuf[i] == '\n' {
|
||||
offset++
|
||||
} else {
|
||||
return offset, nil
|
||||
}
|
||||
}
|
||||
return offset, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
68
core/filex/lookup_test.go
Normal file
68
core/filex/lookup_test.go
Normal file
@@ -0,0 +1,68 @@
|
||||
package filex
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"zero/core/fs"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestSplitLineChunks(t *testing.T) {
|
||||
const text = `first line
|
||||
second line
|
||||
third line
|
||||
fourth line
|
||||
fifth line
|
||||
sixth line
|
||||
seventh line
|
||||
`
|
||||
fp, err := fs.TempFileWithText(text)
|
||||
assert.Nil(t, err)
|
||||
defer func() {
|
||||
fp.Close()
|
||||
os.Remove(fp.Name())
|
||||
}()
|
||||
|
||||
offsets, err := SplitLineChunks(fp.Name(), 3)
|
||||
assert.Nil(t, err)
|
||||
body := make([]byte, 512)
|
||||
for _, offset := range offsets {
|
||||
reader := NewRangeReader(fp, offset.Start, offset.Stop)
|
||||
n, err := reader.Read(body)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, uint8('\n'), body[n-1])
|
||||
}
|
||||
}
|
||||
|
||||
func TestSplitLineChunksNoFile(t *testing.T) {
|
||||
_, err := SplitLineChunks("nosuchfile", 2)
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
func TestSplitLineChunksFull(t *testing.T) {
|
||||
const text = `first line
|
||||
second line
|
||||
third line
|
||||
fourth line
|
||||
fifth line
|
||||
sixth line
|
||||
`
|
||||
fp, err := fs.TempFileWithText(text)
|
||||
assert.Nil(t, err)
|
||||
defer func() {
|
||||
fp.Close()
|
||||
os.Remove(fp.Name())
|
||||
}()
|
||||
|
||||
offsets, err := SplitLineChunks(fp.Name(), 1)
|
||||
assert.Nil(t, err)
|
||||
body := make([]byte, 512)
|
||||
for _, offset := range offsets {
|
||||
reader := NewRangeReader(fp, offset.Start, offset.Stop)
|
||||
n, err := reader.Read(body)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, []byte(text), body[:n])
|
||||
}
|
||||
}
|
||||
28
core/filex/progressscanner.go
Normal file
28
core/filex/progressscanner.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package filex
|
||||
|
||||
import "gopkg.in/cheggaaa/pb.v1"
|
||||
|
||||
type (
|
||||
Scanner interface {
|
||||
Scan() bool
|
||||
Text() string
|
||||
}
|
||||
|
||||
progressScanner struct {
|
||||
Scanner
|
||||
bar *pb.ProgressBar
|
||||
}
|
||||
)
|
||||
|
||||
func NewProgressScanner(scanner Scanner, bar *pb.ProgressBar) Scanner {
|
||||
return &progressScanner{
|
||||
Scanner: scanner,
|
||||
bar: bar,
|
||||
}
|
||||
}
|
||||
|
||||
func (ps *progressScanner) Text() string {
|
||||
s := ps.Scanner.Text()
|
||||
ps.bar.Add64(int64(len(s)) + 1) // take newlines into account
|
||||
return s
|
||||
}
|
||||
31
core/filex/progressscanner_test.go
Normal file
31
core/filex/progressscanner_test.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package filex
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/cheggaaa/pb.v1"
|
||||
)
|
||||
|
||||
func TestProgressScanner(t *testing.T) {
|
||||
const text = "hello, world"
|
||||
bar := pb.New(100)
|
||||
var builder strings.Builder
|
||||
builder.WriteString(text)
|
||||
scanner := NewProgressScanner(&mockedScanner{builder: &builder}, bar)
|
||||
assert.True(t, scanner.Scan())
|
||||
assert.Equal(t, text, scanner.Text())
|
||||
}
|
||||
|
||||
type mockedScanner struct {
|
||||
builder *strings.Builder
|
||||
}
|
||||
|
||||
func (s *mockedScanner) Scan() bool {
|
||||
return s.builder.Len() > 0
|
||||
}
|
||||
|
||||
func (s *mockedScanner) Text() string {
|
||||
return s.builder.String()
|
||||
}
|
||||
43
core/filex/rangereader.go
Normal file
43
core/filex/rangereader.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package filex
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
)
|
||||
|
||||
type RangeReader struct {
|
||||
file *os.File
|
||||
start int64
|
||||
stop int64
|
||||
}
|
||||
|
||||
func NewRangeReader(file *os.File, start, stop int64) *RangeReader {
|
||||
return &RangeReader{
|
||||
file: file,
|
||||
start: start,
|
||||
stop: stop,
|
||||
}
|
||||
}
|
||||
|
||||
func (rr *RangeReader) Read(p []byte) (n int, err error) {
|
||||
stat, err := rr.file.Stat()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if rr.stop < rr.start || rr.start >= stat.Size() {
|
||||
return 0, errors.New("exceed file size")
|
||||
}
|
||||
|
||||
if rr.stop-rr.start < int64(len(p)) {
|
||||
p = p[:rr.stop-rr.start]
|
||||
}
|
||||
|
||||
n, err = rr.file.ReadAt(p, rr.start)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
|
||||
rr.start += int64(n)
|
||||
return
|
||||
}
|
||||
45
core/filex/rangereader_test.go
Normal file
45
core/filex/rangereader_test.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package filex
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"zero/core/fs"
|
||||
)
|
||||
|
||||
func TestRangeReader(t *testing.T) {
|
||||
const text = `hello
|
||||
world`
|
||||
file, err := fs.TempFileWithText(text)
|
||||
assert.Nil(t, err)
|
||||
defer func() {
|
||||
file.Close()
|
||||
os.Remove(file.Name())
|
||||
}()
|
||||
|
||||
reader := NewRangeReader(file, 5, 8)
|
||||
buf := make([]byte, 10)
|
||||
n, err := reader.Read(buf)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 3, n)
|
||||
assert.Equal(t, `
|
||||
wo`, string(buf[:n]))
|
||||
}
|
||||
|
||||
func TestRangeReader_OutOfRange(t *testing.T) {
|
||||
const text = `hello
|
||||
world`
|
||||
file, err := fs.TempFileWithText(text)
|
||||
assert.Nil(t, err)
|
||||
defer func() {
|
||||
file.Close()
|
||||
os.Remove(file.Name())
|
||||
}()
|
||||
|
||||
reader := NewRangeReader(file, 50, 8)
|
||||
buf := make([]byte, 10)
|
||||
_, err = reader.Read(buf)
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
Reference in New Issue
Block a user