Home Download Docs Code Community
     1	/*
     2	Copyright 2011 The Perkeep Authors
     3	
     4	Licensed under the Apache License, Version 2.0 (the "License");
     5	you may not use this file except in compliance with the License.
     6	You may obtain a copy of the License at
     7	
     8	     http://www.apache.org/licenses/LICENSE-2.0
     9	
    10	Unless required by applicable law or agreed to in writing, software
    11	distributed under the License is distributed on an "AS IS" BASIS,
    12	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13	See the License for the specific language governing permissions and
    14	limitations under the License.
    15	*/
    16	
    17	package blob
    18	
    19	import (
    20		"context"
    21		"errors"
    22		"fmt"
    23		"io"
    24		"math"
    25		"os"
    26	)
    27	
    28	var (
    29		ErrNegativeSubFetch         = errors.New("invalid negative subfetch parameters")
    30		ErrOutOfRangeOffsetSubFetch = errors.New("subfetch offset greater than blob size")
    31	)
    32	
    33	// Fetcher is the minimal interface for retrieving a blob from storage.
    34	// The full storage interface is blobserver.Storage.
    35	type Fetcher interface {
    36		// Fetch returns a blob. If the blob is not found then
    37		// os.ErrNotExist should be returned for the error (not a wrapped
    38		// error with a ErrNotExist inside)
    39		//
    40		// The contents are not guaranteed to match the digest of the
    41		// provided Ref (e.g. when streamed over HTTP). Paranoid
    42		// callers should verify them.
    43		//
    44		// The caller must close blob.
    45		//
    46		// The provided context is used until blob is closed and its
    47		// cancellation should but may not necessarily cause reads from
    48		// blob to fail with an error.
    49		Fetch(context.Context, Ref) (blob io.ReadCloser, size uint32, err error)
    50	}
    51	
    52	// ErrUnimplemented is returned by optional interfaces when their
    53	// wrapped values don't implemented the optional interface.
    54	var ErrUnimplemented = errors.New("optional method not implemented")
    55	
    56	// A SubFetcher is a Fetcher that can retrieve part of a blob.
    57	type SubFetcher interface {
    58		// SubFetch returns part of a blob.
    59		// The caller must close the returned io.ReadCloser.
    60		// The Reader may return fewer than 'length' bytes. Callers should
    61		// check. The returned error should be: ErrNegativeSubFetch if any of
    62		// offset or length is negative, or os.ErrNotExist if the blob
    63		// doesn't exist, or ErrOutOfRangeOffsetSubFetch if offset goes over
    64		// the size of the blob. If the error is ErrUnimplemented, the caller should
    65		// treat this Fetcher as if it doesn't implement SubFetcher.
    66		SubFetch(ctx context.Context, ref Ref, offset, length int64) (io.ReadCloser, error)
    67	}
    68	
    69	func NewSerialFetcher(fetchers ...Fetcher) Fetcher {
    70		return &serialFetcher{fetchers}
    71	}
    72	
    73	func NewSimpleDirectoryFetcher(dir string) *DirFetcher {
    74		return &DirFetcher{dir, "camli"}
    75	}
    76	
    77	type serialFetcher struct {
    78		fetchers []Fetcher
    79	}
    80	
    81	func (sf *serialFetcher) Fetch(ctx context.Context, r Ref) (file io.ReadCloser, size uint32, err error) {
    82		for _, fetcher := range sf.fetchers {
    83			file, size, err = fetcher.Fetch(ctx, r)
    84			if err == nil {
    85				return
    86			}
    87		}
    88		return
    89	}
    90	
    91	type DirFetcher struct {
    92		directory, extension string
    93	}
    94	
    95	func (df *DirFetcher) Fetch(ctx context.Context, r Ref) (file io.ReadCloser, size uint32, err error) {
    96		fileName := fmt.Sprintf("%s/%s.%s", df.directory, r.String(), df.extension)
    97		var stat os.FileInfo
    98		stat, err = os.Stat(fileName)
    99		if err != nil {
   100			return
   101		}
   102		if stat.Size() > math.MaxUint32 {
   103			err = errors.New("file size too big")
   104			return
   105		}
   106		file, err = os.Open(fileName)
   107		if err != nil {
   108			return
   109		}
   110		size = uint32(stat.Size())
   111		return
   112	}
   113	
   114	// ReaderAt returns an io.ReaderAt of br, fetching against sf.
   115	// The context is stored in and used by the returned ReaderAt.
   116	func ReaderAt(ctx context.Context, sf SubFetcher, br Ref) io.ReaderAt {
   117		return readerAt{ctx, sf, br}
   118	}
   119	
   120	type readerAt struct {
   121		ctx context.Context
   122		sf  SubFetcher
   123		br  Ref
   124	}
   125	
   126	func (ra readerAt) ReadAt(p []byte, off int64) (n int, err error) {
   127		rc, err := ra.sf.SubFetch(ra.ctx, ra.br, off, int64(len(p)))
   128		if err != nil {
   129			return 0, err
   130		}
   131		defer rc.Close()
   132		return io.ReadFull(rc, p)
   133	}
Website layout inspired by memcached.
Content by the authors.