137 lines
2.8 KiB
Go
137 lines
2.8 KiB
Go
|
// Copyright (C) 2018 Storj Labs, Inc.
|
||
|
// See LICENSE for copying information.
|
||
|
|
||
|
package fpiece
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"io"
|
||
|
"os"
|
||
|
)
|
||
|
|
||
|
// Chunk - Section of data to be concurrently read
|
||
|
type Chunk struct {
|
||
|
file *os.File
|
||
|
start int64
|
||
|
final int64
|
||
|
currentPos int64
|
||
|
}
|
||
|
|
||
|
// NewChunk - Create Chunk
|
||
|
func NewChunk(file *os.File, offset int64, length int64) (*Chunk, error) {
|
||
|
if length < 0 {
|
||
|
return nil, errors.New("Invalid Length")
|
||
|
}
|
||
|
|
||
|
return &Chunk{file, offset, length + offset, offset}, nil
|
||
|
}
|
||
|
|
||
|
// Size - Get size of Chunk
|
||
|
func (f *Chunk) Size() int64 {
|
||
|
return f.final - f.start
|
||
|
}
|
||
|
|
||
|
// Close - Close file associated with chunk
|
||
|
func (f *Chunk) Close() error {
|
||
|
return f.file.Close()
|
||
|
}
|
||
|
|
||
|
// Read - Concurrently read from Chunk
|
||
|
func (f *Chunk) Read(b []byte) (n int, err error) {
|
||
|
if f.currentPos >= f.final {
|
||
|
return 0, io.EOF
|
||
|
}
|
||
|
|
||
|
var readLen int64 // starts at 0
|
||
|
if f.final-f.currentPos > int64(len(b)) {
|
||
|
readLen = int64(len(b))
|
||
|
} else {
|
||
|
readLen = f.final - f.currentPos
|
||
|
}
|
||
|
|
||
|
n, err = f.file.ReadAt(b[:readLen], f.currentPos)
|
||
|
f.currentPos += int64(n)
|
||
|
return n, err
|
||
|
}
|
||
|
|
||
|
// ReadAt - Concurrently read from Chunk at specific offset
|
||
|
func (f *Chunk) ReadAt(p []byte, off int64) (n int, err error) {
|
||
|
if off < 0 || off >= f.final-f.start {
|
||
|
return 0, io.EOF
|
||
|
}
|
||
|
|
||
|
off += f.start
|
||
|
|
||
|
if max := f.final - off; int64(len(p)) > max {
|
||
|
p = p[:max]
|
||
|
n, err = f.file.ReadAt(p, off)
|
||
|
if err == nil {
|
||
|
err = io.EOF
|
||
|
}
|
||
|
return n, err
|
||
|
}
|
||
|
return f.file.ReadAt(p, off)
|
||
|
}
|
||
|
|
||
|
// Write - Concurrently write to Chunk
|
||
|
func (f *Chunk) Write(b []byte) (n int, err error) {
|
||
|
if f.currentPos >= f.final {
|
||
|
return 0, io.EOF
|
||
|
}
|
||
|
|
||
|
var writeLen int64 // starts at 0
|
||
|
if f.final-f.currentPos > int64(len(b)) {
|
||
|
writeLen = int64(len(b))
|
||
|
} else {
|
||
|
writeLen = f.final - f.currentPos
|
||
|
}
|
||
|
|
||
|
n, err = f.file.WriteAt(b[:writeLen], f.currentPos)
|
||
|
f.currentPos += int64(n)
|
||
|
return n, err
|
||
|
}
|
||
|
|
||
|
// WriteAt - Concurrently write to Chunk at specific offset
|
||
|
func (f *Chunk) WriteAt(p []byte, off int64) (n int, err error) {
|
||
|
if off < 0 || off >= f.final-f.start {
|
||
|
return 0, io.EOF
|
||
|
}
|
||
|
|
||
|
off += f.start
|
||
|
|
||
|
if max := f.final - off; int64(len(p)) > max {
|
||
|
p = p[:max]
|
||
|
n, err = f.file.WriteAt(p, off)
|
||
|
if err == nil {
|
||
|
err = io.EOF
|
||
|
}
|
||
|
return n, err
|
||
|
}
|
||
|
return f.file.WriteAt(p, off)
|
||
|
}
|
||
|
|
||
|
var errWhence = errors.New("Seek: invalid whence")
|
||
|
var errOffset = errors.New("Seek: invalid offset")
|
||
|
|
||
|
// Seek - Seek to position of chunk for reading/writing at specific offset
|
||
|
func (f *Chunk) Seek(offset int64, whence int) (int64, error) {
|
||
|
switch whence {
|
||
|
default:
|
||
|
return 0, errWhence
|
||
|
case io.SeekStart:
|
||
|
offset += f.start
|
||
|
case io.SeekCurrent:
|
||
|
offset += f.currentPos
|
||
|
case io.SeekEnd:
|
||
|
offset += f.final
|
||
|
}
|
||
|
|
||
|
// Do not seek to where somewhere outside the chunk
|
||
|
if offset < f.start || offset > f.final {
|
||
|
return 0, errOffset
|
||
|
}
|
||
|
|
||
|
f.currentPos = offset
|
||
|
return offset - f.start, nil
|
||
|
}
|