github.com/timstclair/heapster@v0.20.0-alpha1/Godeps/_workspace/src/google.golang.org/appengine/blobstore/read.go (about)

     1  // Copyright 2012 Google Inc. All rights reserved.
     2  // Use of this source code is governed by the Apache 2.0
     3  // license that can be found in the LICENSE file.
     4  
     5  package blobstore
     6  
     7  import (
     8  	"errors"
     9  	"fmt"
    10  	"io"
    11  	"os"
    12  	"sync"
    13  
    14  	"github.com/golang/protobuf/proto"
    15  	"golang.org/x/net/context"
    16  
    17  	"google.golang.org/appengine"
    18  	"google.golang.org/appengine/internal"
    19  
    20  	blobpb "google.golang.org/appengine/internal/blobstore"
    21  )
    22  
    23  // openBlob returns a reader for a blob. It always succeeds; if the blob does
    24  // not exist then an error will be reported upon first read.
    25  func openBlob(c context.Context, blobKey appengine.BlobKey) Reader {
    26  	return &reader{
    27  		c:       c,
    28  		blobKey: blobKey,
    29  	}
    30  }
    31  
    32  const readBufferSize = 256 * 1024
    33  
    34  // reader is a blob reader. It implements the Reader interface.
    35  type reader struct {
    36  	c context.Context
    37  
    38  	// Either blobKey or filename is set:
    39  	blobKey  appengine.BlobKey
    40  	filename string
    41  
    42  	closeFunc func() // is nil if unavailable or already closed.
    43  
    44  	// buf is the read buffer. r is how much of buf has been read.
    45  	// off is the offset of buf[0] relative to the start of the blob.
    46  	// An invariant is 0 <= r && r <= len(buf).
    47  	// Reads that don't require an RPC call will increment r but not off.
    48  	// Seeks may modify r without discarding the buffer, but only if the
    49  	// invariant can be maintained.
    50  	mu  sync.Mutex
    51  	buf []byte
    52  	r   int
    53  	off int64
    54  }
    55  
    56  func (r *reader) Close() error {
    57  	if f := r.closeFunc; f != nil {
    58  		f()
    59  	}
    60  	r.closeFunc = nil
    61  	return nil
    62  }
    63  
    64  func (r *reader) Read(p []byte) (int, error) {
    65  	if len(p) == 0 {
    66  		return 0, nil
    67  	}
    68  	r.mu.Lock()
    69  	defer r.mu.Unlock()
    70  	if r.r == len(r.buf) {
    71  		if err := r.fetch(r.off + int64(r.r)); err != nil {
    72  			return 0, err
    73  		}
    74  	}
    75  	n := copy(p, r.buf[r.r:])
    76  	r.r += n
    77  	return n, nil
    78  }
    79  
    80  func (r *reader) ReadAt(p []byte, off int64) (int, error) {
    81  	if len(p) == 0 {
    82  		return 0, nil
    83  	}
    84  	r.mu.Lock()
    85  	defer r.mu.Unlock()
    86  	// Convert relative offsets to absolute offsets.
    87  	ab0 := r.off + int64(r.r)
    88  	ab1 := r.off + int64(len(r.buf))
    89  	ap0 := off
    90  	ap1 := off + int64(len(p))
    91  	// Check if we can satisfy the read entirely out of the existing buffer.
    92  	if r.off <= ap0 && ap1 <= ab1 {
    93  		// Convert off from an absolute offset to a relative offset.
    94  		rp0 := int(ap0 - r.off)
    95  		return copy(p, r.buf[rp0:]), nil
    96  	}
    97  	// Restore the original Read/Seek offset after ReadAt completes.
    98  	defer r.seek(ab0)
    99  	// Repeatedly fetch and copy until we have filled p.
   100  	n := 0
   101  	for len(p) > 0 {
   102  		if err := r.fetch(off + int64(n)); err != nil {
   103  			return n, err
   104  		}
   105  		r.r = copy(p, r.buf)
   106  		n += r.r
   107  		p = p[r.r:]
   108  	}
   109  	return n, nil
   110  }
   111  
   112  func (r *reader) Seek(offset int64, whence int) (ret int64, err error) {
   113  	r.mu.Lock()
   114  	defer r.mu.Unlock()
   115  	switch whence {
   116  	case os.SEEK_SET:
   117  		ret = offset
   118  	case os.SEEK_CUR:
   119  		ret = r.off + int64(r.r) + offset
   120  	case os.SEEK_END:
   121  		return 0, errors.New("seeking relative to the end of a blob isn't supported")
   122  	default:
   123  		return 0, fmt.Errorf("invalid Seek whence value: %d", whence)
   124  	}
   125  	if ret < 0 {
   126  		return 0, errors.New("negative Seek offset")
   127  	}
   128  	return r.seek(ret)
   129  }
   130  
   131  // fetch fetches readBufferSize bytes starting at the given offset. On success,
   132  // the data is saved as r.buf.
   133  func (r *reader) fetch(off int64) error {
   134  	req := &blobpb.FetchDataRequest{
   135  		BlobKey:    proto.String(string(r.blobKey)),
   136  		StartIndex: proto.Int64(off),
   137  		EndIndex:   proto.Int64(off + readBufferSize - 1), // EndIndex is inclusive.
   138  	}
   139  	res := &blobpb.FetchDataResponse{}
   140  	if err := internal.Call(r.c, "blobstore", "FetchData", req, res); err != nil {
   141  		return err
   142  	}
   143  	if len(res.Data) == 0 {
   144  		return io.EOF
   145  	}
   146  	r.buf, r.r, r.off = res.Data, 0, off
   147  	return nil
   148  }
   149  
   150  // seek seeks to the given offset with an effective whence equal to SEEK_SET.
   151  // It discards the read buffer if the invariant cannot be maintained.
   152  func (r *reader) seek(off int64) (int64, error) {
   153  	delta := off - r.off
   154  	if delta >= 0 && delta < int64(len(r.buf)) {
   155  		r.r = int(delta)
   156  		return off, nil
   157  	}
   158  	r.buf, r.r, r.off = nil, 0, off
   159  	return off, nil
   160  }