github.com/endophage/docker@v1.4.2-0.20161027011718-242853499895/pkg/ioutils/multireader.go (about)

     1  package ioutils
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"os"
     8  )
     9  
    10  type pos struct {
    11  	idx    int
    12  	offset int64
    13  }
    14  
    15  type multiReadSeeker struct {
    16  	readers []io.ReadSeeker
    17  	pos     *pos
    18  	posIdx  map[io.ReadSeeker]int
    19  }
    20  
    21  func (r *multiReadSeeker) Seek(offset int64, whence int) (int64, error) {
    22  	var tmpOffset int64
    23  	switch whence {
    24  	case os.SEEK_SET:
    25  		for i, rdr := range r.readers {
    26  			// get size of the current reader
    27  			s, err := rdr.Seek(0, os.SEEK_END)
    28  			if err != nil {
    29  				return -1, err
    30  			}
    31  
    32  			if offset > tmpOffset+s {
    33  				if i == len(r.readers)-1 {
    34  					rdrOffset := s + (offset - tmpOffset)
    35  					if _, err := rdr.Seek(rdrOffset, os.SEEK_SET); err != nil {
    36  						return -1, err
    37  					}
    38  					r.pos = &pos{i, rdrOffset}
    39  					return offset, nil
    40  				}
    41  
    42  				tmpOffset += s
    43  				continue
    44  			}
    45  
    46  			rdrOffset := offset - tmpOffset
    47  			idx := i
    48  
    49  			rdr.Seek(rdrOffset, os.SEEK_SET)
    50  			// make sure all following readers are at 0
    51  			for _, rdr := range r.readers[i+1:] {
    52  				rdr.Seek(0, os.SEEK_SET)
    53  			}
    54  
    55  			if rdrOffset == s && i != len(r.readers)-1 {
    56  				idx++
    57  				rdrOffset = 0
    58  			}
    59  			r.pos = &pos{idx, rdrOffset}
    60  			return offset, nil
    61  		}
    62  	case os.SEEK_END:
    63  		for _, rdr := range r.readers {
    64  			s, err := rdr.Seek(0, os.SEEK_END)
    65  			if err != nil {
    66  				return -1, err
    67  			}
    68  			tmpOffset += s
    69  		}
    70  		r.Seek(tmpOffset+offset, os.SEEK_SET)
    71  		return tmpOffset + offset, nil
    72  	case os.SEEK_CUR:
    73  		if r.pos == nil {
    74  			return r.Seek(offset, os.SEEK_SET)
    75  		}
    76  		// Just return the current offset
    77  		if offset == 0 {
    78  			return r.getCurOffset()
    79  		}
    80  
    81  		curOffset, err := r.getCurOffset()
    82  		if err != nil {
    83  			return -1, err
    84  		}
    85  		rdr, rdrOffset, err := r.getReaderForOffset(curOffset + offset)
    86  		if err != nil {
    87  			return -1, err
    88  		}
    89  
    90  		r.pos = &pos{r.posIdx[rdr], rdrOffset}
    91  		return curOffset + offset, nil
    92  	default:
    93  		return -1, fmt.Errorf("Invalid whence: %d", whence)
    94  	}
    95  
    96  	return -1, fmt.Errorf("Error seeking for whence: %d, offset: %d", whence, offset)
    97  }
    98  
    99  func (r *multiReadSeeker) getReaderForOffset(offset int64) (io.ReadSeeker, int64, error) {
   100  
   101  	var offsetTo int64
   102  
   103  	for _, rdr := range r.readers {
   104  		size, err := getReadSeekerSize(rdr)
   105  		if err != nil {
   106  			return nil, -1, err
   107  		}
   108  		if offsetTo+size > offset {
   109  			return rdr, offset - offsetTo, nil
   110  		}
   111  		if rdr == r.readers[len(r.readers)-1] {
   112  			return rdr, offsetTo + offset, nil
   113  		}
   114  		offsetTo += size
   115  	}
   116  
   117  	return nil, 0, nil
   118  }
   119  
   120  func (r *multiReadSeeker) getCurOffset() (int64, error) {
   121  	var totalSize int64
   122  	for _, rdr := range r.readers[:r.pos.idx+1] {
   123  		if r.posIdx[rdr] == r.pos.idx {
   124  			totalSize += r.pos.offset
   125  			break
   126  		}
   127  
   128  		size, err := getReadSeekerSize(rdr)
   129  		if err != nil {
   130  			return -1, fmt.Errorf("error getting seeker size: %v", err)
   131  		}
   132  		totalSize += size
   133  	}
   134  	return totalSize, nil
   135  }
   136  
   137  func (r *multiReadSeeker) getOffsetToReader(rdr io.ReadSeeker) (int64, error) {
   138  	var offset int64
   139  	for _, r := range r.readers {
   140  		if r == rdr {
   141  			break
   142  		}
   143  
   144  		size, err := getReadSeekerSize(rdr)
   145  		if err != nil {
   146  			return -1, err
   147  		}
   148  		offset += size
   149  	}
   150  	return offset, nil
   151  }
   152  
   153  func (r *multiReadSeeker) Read(b []byte) (int, error) {
   154  	if r.pos == nil {
   155  		r.pos = &pos{0, 0}
   156  	}
   157  
   158  	bCap := int64(cap(b))
   159  	buf := bytes.NewBuffer(nil)
   160  	var rdr io.ReadSeeker
   161  
   162  	for _, rdr = range r.readers[r.pos.idx:] {
   163  		readBytes, err := io.CopyN(buf, rdr, bCap)
   164  		if err != nil && err != io.EOF {
   165  			return -1, err
   166  		}
   167  		bCap -= readBytes
   168  
   169  		if bCap == 0 {
   170  			break
   171  		}
   172  	}
   173  
   174  	rdrPos, err := rdr.Seek(0, os.SEEK_CUR)
   175  	if err != nil {
   176  		return -1, err
   177  	}
   178  	r.pos = &pos{r.posIdx[rdr], rdrPos}
   179  	return buf.Read(b)
   180  }
   181  
   182  func getReadSeekerSize(rdr io.ReadSeeker) (int64, error) {
   183  	// save the current position
   184  	pos, err := rdr.Seek(0, os.SEEK_CUR)
   185  	if err != nil {
   186  		return -1, err
   187  	}
   188  
   189  	// get the size
   190  	size, err := rdr.Seek(0, os.SEEK_END)
   191  	if err != nil {
   192  		return -1, err
   193  	}
   194  
   195  	// reset the position
   196  	if _, err := rdr.Seek(pos, os.SEEK_SET); err != nil {
   197  		return -1, err
   198  	}
   199  	return size, nil
   200  }
   201  
   202  // MultiReadSeeker returns a ReadSeeker that's the logical concatenation of the provided
   203  // input readseekers. After calling this method the initial position is set to the
   204  // beginning of the first ReadSeeker. At the end of a ReadSeeker, Read always advances
   205  // to the beginning of the next ReadSeeker and returns EOF at the end of the last ReadSeeker.
   206  // Seek can be used over the sum of lengths of all readseekers.
   207  //
   208  // When a MultiReadSeeker is used, no Read and Seek operations should be made on
   209  // its ReadSeeker components. Also, users should make no assumption on the state
   210  // of individual readseekers while the MultiReadSeeker is used.
   211  func MultiReadSeeker(readers ...io.ReadSeeker) io.ReadSeeker {
   212  	if len(readers) == 1 {
   213  		return readers[0]
   214  	}
   215  	idx := make(map[io.ReadSeeker]int)
   216  	for i, rdr := range readers {
   217  		idx[rdr] = i
   218  	}
   219  	return &multiReadSeeker{
   220  		readers: readers,
   221  		posIdx:  idx,
   222  	}
   223  }