lazyseek.go 1.21 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
package corehttp

import (
	"fmt"
	"io"
)

// The HTTP server uses seek to determine the file size. Actually _seeking_ can
// be slow so we wrap the seeker in a _lazy_ seeker.
type lazySeeker struct {
	reader io.ReadSeeker

	size       int64
	offset     int64
	realOffset int64
}

func (s *lazySeeker) Seek(offset int64, whence int) (int64, error) {
	switch whence {
	case io.SeekEnd:
		return s.Seek(s.size+offset, io.SeekStart)
	case io.SeekCurrent:
		return s.Seek(s.offset+offset, io.SeekStart)
	case io.SeekStart:
		if offset < 0 {
			return s.offset, fmt.Errorf("invalid seek offset")
		}
		s.offset = offset
		return s.offset, nil
	default:
		return s.offset, fmt.Errorf("invalid whence: %d", whence)
	}
}

func (s *lazySeeker) Read(b []byte) (int, error) {
	// If we're past the end, EOF.
	if s.offset >= s.size {
		return 0, io.EOF
	}

	// actually seek
	for s.offset != s.realOffset {
		off, err := s.reader.Seek(s.offset, io.SeekStart)
		if err != nil {
45
			return 0, err
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
		}
		s.realOffset = off
	}
	off, err := s.reader.Read(b)
	s.realOffset += int64(off)
	s.offset += int64(off)
	return off, err
}

func (s *lazySeeker) Close() error {
	if closer, ok := s.reader.(io.Closer); ok {
		return closer.Close()
	}
	return nil
}