1// Copyright 2015 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package elf
6
7import (
8	"io"
9	"os"
10)
11
12// errorReader returns error from all operations.
13type errorReader struct {
14	error
15}
16
17func (r errorReader) Read(p []byte) (n int, err error) {
18	return 0, r.error
19}
20
21func (r errorReader) ReadAt(p []byte, off int64) (n int, err error) {
22	return 0, r.error
23}
24
25func (r errorReader) Seek(offset int64, whence int) (int64, error) {
26	return 0, r.error
27}
28
29func (r errorReader) Close() error {
30	return r.error
31}
32
33// readSeekerFromReader converts an io.Reader into an io.ReadSeeker.
34// In general Seek may not be efficient, but it is optimized for
35// common cases such as seeking to the end to find the length of the
36// data.
37type readSeekerFromReader struct {
38	reset  func() (io.Reader, error)
39	r      io.Reader
40	size   int64
41	offset int64
42}
43
44func (r *readSeekerFromReader) start() {
45	x, err := r.reset()
46	if err != nil {
47		r.r = errorReader{err}
48	} else {
49		r.r = x
50	}
51	r.offset = 0
52}
53
54func (r *readSeekerFromReader) Read(p []byte) (n int, err error) {
55	if r.r == nil {
56		r.start()
57	}
58	n, err = r.r.Read(p)
59	r.offset += int64(n)
60	return n, err
61}
62
63func (r *readSeekerFromReader) Seek(offset int64, whence int) (int64, error) {
64	var newOffset int64
65	switch whence {
66	case io.SeekStart:
67		newOffset = offset
68	case io.SeekCurrent:
69		newOffset = r.offset + offset
70	case io.SeekEnd:
71		newOffset = r.size + offset
72	default:
73		return 0, os.ErrInvalid
74	}
75
76	switch {
77	case newOffset == r.offset:
78		return newOffset, nil
79
80	case newOffset < 0, newOffset > r.size:
81		return 0, os.ErrInvalid
82
83	case newOffset == 0:
84		r.r = nil
85
86	case newOffset == r.size:
87		r.r = errorReader{io.EOF}
88
89	default:
90		if newOffset < r.offset {
91			// Restart at the beginning.
92			r.start()
93		}
94		// Read until we reach offset.
95		var buf [512]byte
96		for r.offset < newOffset {
97			b := buf[:]
98			if newOffset-r.offset < int64(len(buf)) {
99				b = buf[:newOffset-r.offset]
100			}
101			if _, err := r.Read(b); err != nil {
102				return 0, err
103			}
104		}
105	}
106	r.offset = newOffset
107	return r.offset, nil
108}
109