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 }