github.com/ipld/go-ipld-prime@v0.21.0/storage/funcs.go (about)

     1  package storage
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"io"
     8  )
     9  
    10  /*
    11  	This file contains equivalents of every method that can be feature-detected on a storage system.
    12  	You can always call these functions, and give them the most basic storage interface,
    13  	and they'll attempt to feature-detect their way to the best possible implementation of the behavior,
    14  	or they'll fall back to synthesizing the same behavior from more basic interfaces.
    15  
    16  	Long story short: you can always use these functions as an end user, and get the behavior you want --
    17  	regardless of how much explicit support the storage implementation has for the exact behavior you requested.
    18  */
    19  
    20  func Has(ctx context.Context, store Storage, key string) (bool, error) {
    21  	// Okay, not much going on here -- this function is only here for consistency of style.
    22  	return store.Has(ctx, key)
    23  }
    24  
    25  func Get(ctx context.Context, store ReadableStorage, key string) ([]byte, error) {
    26  	// Okay, not much going on here -- this function is only here for consistency of style.
    27  	return store.Get(ctx, key)
    28  }
    29  
    30  func Put(ctx context.Context, store WritableStorage, key string, content []byte) error {
    31  	// Okay, not much going on here -- this function is only here for consistency of style.
    32  	return store.Put(ctx, key, content)
    33  }
    34  
    35  // GetStream returns a streaming reader.
    36  // This function will feature-detect the StreamingReadableStorage interface, and use that if possible;
    37  // otherwise it will fall back to using basic ReadableStorage methods transparently
    38  // (at the cost of loading all the data into memory at once and up front).
    39  func GetStream(ctx context.Context, store ReadableStorage, key string) (io.ReadCloser, error) {
    40  	// Prefer the feature itself, first.
    41  	if streamable, ok := store.(StreamingReadableStorage); ok {
    42  		return streamable.GetStream(ctx, key)
    43  	}
    44  	// Fallback to basic.
    45  	blob, err := store.Get(ctx, key)
    46  	return noopCloser{bytes.NewReader(blob)}, err
    47  }
    48  
    49  // PutStream returns an io.Writer and a WriteCommitter callback.
    50  // (See the docs on StreamingWritableStorage.PutStream for details on what that means.)
    51  // This function will feature-detect the StreamingWritableStorage interface, and use that if possible;
    52  // otherwise it will fall back to using basic WritableStorage methods transparently
    53  // (at the cost of needing to buffer all of the content in memory while the write is in progress).
    54  func PutStream(ctx context.Context, store WritableStorage) (io.Writer, func(key string) error, error) {
    55  	// Prefer the feature itself, first.
    56  	if streamable, ok := store.(StreamingWritableStorage); ok {
    57  		return streamable.PutStream(ctx)
    58  	}
    59  	// Fallback to basic.
    60  	var buf bytes.Buffer
    61  	var written bool
    62  	return &buf, func(key string) error {
    63  		if written {
    64  			return fmt.Errorf("WriteCommitter already used")
    65  		}
    66  		written = true
    67  		return store.Put(ctx, key, buf.Bytes())
    68  	}, nil
    69  }
    70  
    71  // PutVec is an API for writing several slices of bytes at once into storage.
    72  // This kind of API can be useful for maximizing performance in scenarios where
    73  // data is already loaded completely into memory, but scattered across several non-contiguous regions.
    74  // This function will feature-detect the VectorWritableStorage interface, and use that if possible;
    75  // otherwise it will fall back to using StreamingWritableStorage,
    76  // or failing that, fall further back to basic WritableStorage methods, transparently.
    77  func PutVec(ctx context.Context, store WritableStorage, key string, blobVec [][]byte) error {
    78  	// Prefer the feature itself, first.
    79  	if putvable, ok := store.(VectorWritableStorage); ok {
    80  		return putvable.PutVec(ctx, key, blobVec)
    81  	}
    82  	// Fallback to streaming mode.
    83  	// ... or, fallback to basic, and use emulated streaming.  Still presumably preferable to doing a big giant memcopy.
    84  	// Conveniently, the PutStream function makes that transparent for our implementation, too.
    85  	wr, wrcommit, err := PutStream(ctx, store)
    86  	if err != nil {
    87  		return err
    88  	}
    89  	for _, blob := range blobVec {
    90  		_, err := wr.Write(blob)
    91  		if err != nil {
    92  			return err
    93  		}
    94  	}
    95  	return wrcommit(key)
    96  }
    97  
    98  // Peek accessess the same data as Get, but indicates that the caller promises not to mutate the returned byte slice.
    99  // (By contrast, Get is expected to return a safe copy.)
   100  // This function will feature-detect the PeekableStorage interface, and use that if possible;
   101  // otherwise it will fall back to using basic ReadableStorage methods transparently
   102  // (meaning that a no-copy fastpath simply wasn't available).
   103  //
   104  // An io.Closer is returned along with the byte slice.
   105  // The Close method on the Closer must be called when the caller is done with the byte slice;
   106  // otherwise, memory leaks may result.
   107  // (Implementers of this interface may be expecting to reuse the byte slice after Close is called.)
   108  func Peek(ctx context.Context, store ReadableStorage, key string) ([]byte, io.Closer, error) {
   109  	// Prefer the feature itself, first.
   110  	if peekable, ok := store.(PeekableStorage); ok {
   111  		return peekable.Peek(ctx, key)
   112  	}
   113  	// Fallback to basic.
   114  	bs, err := store.Get(ctx, key)
   115  	return bs, noopCloser{nil}, err
   116  }
   117  
   118  type noopCloser struct {
   119  	io.Reader
   120  }
   121  
   122  func (noopCloser) Close() error { return nil }