mirror of
https://github.com/GrandpaNutz/fafda.git
synced 2026-03-11 22:15:35 +00:00
141 lines
2.2 KiB
Go
141 lines
2.2 KiB
Go
package partedio
|
|
|
|
import "io"
|
|
|
|
type PartReader interface {
|
|
GetSize() int
|
|
GetReader(start, end int) (io.ReadCloser, error)
|
|
}
|
|
|
|
type PartReaders []PartReader
|
|
|
|
type Reader struct {
|
|
parts []PartReader
|
|
pos int64
|
|
curIdx int
|
|
closed bool
|
|
reader io.ReadCloser
|
|
size int64
|
|
|
|
partStarts []int64
|
|
partEnds []int64
|
|
}
|
|
|
|
func NewReader(parts PartReaders, pos int64) (*Reader, error) {
|
|
if len(parts) == 0 {
|
|
return nil, ErrNoParts
|
|
}
|
|
|
|
partStarts := make([]int64, len(parts))
|
|
partEnds := make([]int64, len(parts))
|
|
|
|
var offset int64
|
|
for i, part := range parts {
|
|
partStarts[i] = offset
|
|
partEnds[i] = offset + int64(part.GetSize()) - 1
|
|
offset = partEnds[i] + 1
|
|
}
|
|
|
|
if pos > offset {
|
|
return nil, io.EOF
|
|
}
|
|
|
|
startIdx := 0
|
|
for i := range parts {
|
|
if pos <= partEnds[i] {
|
|
startIdx = i
|
|
break
|
|
}
|
|
}
|
|
|
|
return &Reader{
|
|
parts: parts[startIdx:],
|
|
partStarts: partStarts[startIdx:],
|
|
partEnds: partEnds[startIdx:],
|
|
pos: pos,
|
|
size: offset,
|
|
curIdx: 0,
|
|
}, nil
|
|
}
|
|
|
|
func (r *Reader) Read(p []byte) (int, error) {
|
|
if r.closed {
|
|
return 0, ErrClosed
|
|
}
|
|
|
|
if len(p) == 0 {
|
|
return 0, nil
|
|
}
|
|
|
|
if r.reader == nil {
|
|
if err := r.readNextPart(); err != nil {
|
|
return 0, err
|
|
}
|
|
}
|
|
|
|
var totalRead int
|
|
for totalRead < len(p) {
|
|
nr, err := r.reader.Read(p[totalRead:])
|
|
totalRead += nr
|
|
r.pos += int64(nr)
|
|
|
|
if err == nil {
|
|
continue
|
|
}
|
|
|
|
if err == io.EOF {
|
|
r.curIdx++
|
|
if r.curIdx >= len(r.parts) {
|
|
return totalRead, io.EOF
|
|
}
|
|
|
|
if err = r.readNextPart(); err != nil {
|
|
return totalRead, err
|
|
}
|
|
continue
|
|
}
|
|
|
|
return totalRead, err
|
|
}
|
|
|
|
return totalRead, nil
|
|
}
|
|
|
|
func (r *Reader) Close() error {
|
|
if r.closed {
|
|
return ErrClosed
|
|
}
|
|
|
|
var err error
|
|
if r.reader != nil {
|
|
err = r.reader.Close()
|
|
}
|
|
|
|
r.closed = true
|
|
r.reader = nil
|
|
r.parts = nil // Help GC
|
|
r.partStarts = nil
|
|
r.partEnds = nil
|
|
return err
|
|
}
|
|
|
|
func (r *Reader) readNextPart() error {
|
|
if r.reader != nil {
|
|
if err := r.reader.Close(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
start := 0
|
|
if r.pos > r.partStarts[r.curIdx] {
|
|
start = int(r.pos - r.partStarts[r.curIdx])
|
|
}
|
|
|
|
reader, err := r.parts[r.curIdx].GetReader(start, r.parts[r.curIdx].GetSize()-1)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
r.reader = reader
|
|
return nil
|
|
}
|