blob: ffd96cc7ef887ab3b329b2c1450a8c75d1bf1449 [file] [log] [blame]
// Copyright 2019 The Wuffs Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------
// Package readerat provides utilities for the io.ReaderAt type.
package readerat
import (
"errors"
"io"
)
var (
errInvalidSize = errors.New("readerat: invalid size")
errSeekToInvalidWhence = errors.New("readerat: seek to invalid whence")
errSeekToNegativePosition = errors.New("readerat: seek to negative position")
)
// ReadSeeker is an io.ReadSeeker implementation based on an io.ReaderAt (and
// an int64 size).
//
// For example, an os.File is both an io.ReaderAt and an io.ReadSeeker, but its
// io.ReadSeeker methods are not safe to use concurrently. In comparison,
// multiple readerat.ReadSeeker values (using the same os.File as their
// io.ReaderAt) are safe to use concurrently. Each can Read and Seek
// independently.
//
// A single readerat.ReadSeeker is not safe to use concurrently.
//
// Do not modify its exported fields after calling any of its methods.
type ReadSeeker struct {
ReaderAt io.ReaderAt
Size int64
offset int64
}
// Read implements io.Reader.
func (r *ReadSeeker) Read(p []byte) (int, error) {
if r.Size < 0 {
return 0, errInvalidSize
}
if r.Size <= r.offset {
return 0, io.EOF
}
length := r.Size - r.offset
if int64(len(p)) > length {
p = p[:length]
}
if len(p) == 0 {
return 0, nil
}
actual, err := r.ReaderAt.ReadAt(p, r.offset)
r.offset += int64(actual)
if (err == nil) && (r.offset == r.Size) {
err = io.EOF
}
return actual, err
}
// Seek implements io.Seeker.
func (r *ReadSeeker) Seek(offset int64, whence int) (int64, error) {
if r.Size < 0 {
return 0, errInvalidSize
}
switch whence {
case io.SeekStart:
// No-op.
case io.SeekCurrent:
offset += r.offset
case io.SeekEnd:
offset += r.Size
default:
return 0, errSeekToInvalidWhence
}
if r.offset < 0 {
return 0, errSeekToNegativePosition
}
r.offset = offset
return r.offset, nil
}