github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/store/blobstore/inmem.go (about) 1 // Copyright 2019 Dolthub, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package blobstore 16 17 import ( 18 "bytes" 19 "context" 20 "io" 21 "io/ioutil" 22 "sync" 23 24 "github.com/google/uuid" 25 ) 26 27 type byteSliceReadCloser struct { 28 io.Reader 29 io.Closer 30 } 31 32 func newByteSliceReadCloser(data []byte) *byteSliceReadCloser { 33 reader := bytes.NewReader(data) 34 return &byteSliceReadCloser{reader, ioutil.NopCloser(reader)} 35 } 36 37 // InMemoryBlobstore provides an in memory implementation of the Blobstore interface 38 type InMemoryBlobstore struct { 39 mutex sync.Mutex 40 blobs map[string][]byte 41 versions map[string]string 42 } 43 44 // NewInMemoryBlobstore creates an instance of an InMemoryBlobstore 45 func NewInMemoryBlobstore() *InMemoryBlobstore { 46 return &InMemoryBlobstore{blobs: make(map[string][]byte), versions: make(map[string]string)} 47 } 48 49 // Get retrieves an io.reader for the portion of a blob specified by br along with 50 // its version 51 func (bs *InMemoryBlobstore) Get(ctx context.Context, key string, br BlobRange) (io.ReadCloser, string, error) { 52 bs.mutex.Lock() 53 defer bs.mutex.Unlock() 54 55 if val, ok := bs.blobs[key]; ok { 56 if ver, ok := bs.versions[key]; ok && ver != "" { 57 var byteRange []byte 58 if br.isAllRange() { 59 byteRange = val 60 } else { 61 posBR := br.positiveRange(int64(len(val))) 62 if posBR.length == 0 { 63 byteRange = val[posBR.offset:] 64 } else { 65 byteRange = val[posBR.offset : posBR.offset+posBR.length] 66 } 67 } 68 69 return newByteSliceReadCloser(byteRange), ver, nil 70 } 71 72 panic("Blob without version, or with invalid version, should no be possible.") 73 } 74 75 return nil, "", NotFound{key} 76 } 77 78 // Put sets the blob and the version for a key 79 func (bs *InMemoryBlobstore) Put(ctx context.Context, key string, reader io.Reader) (string, error) { 80 bs.mutex.Lock() 81 defer bs.mutex.Unlock() 82 83 ver := uuid.New().String() 84 data, err := ioutil.ReadAll(reader) 85 86 if err != nil { 87 return "", err 88 } 89 90 bs.blobs[key] = data 91 bs.versions[key] = ver 92 93 return ver, nil 94 } 95 96 // CheckAndPut will check the current version of a blob against an expectedVersion, and if the 97 // versions match it will update the data and version associated with the key 98 func (bs *InMemoryBlobstore) CheckAndPut(ctx context.Context, expectedVersion, key string, reader io.Reader) (string, error) { 99 bs.mutex.Lock() 100 defer bs.mutex.Unlock() 101 102 ver, ok := bs.versions[key] 103 check := !ok && expectedVersion == "" || ok && expectedVersion == ver 104 105 if !check { 106 return "", CheckAndPutError{key, expectedVersion, ver} 107 } 108 109 newVer := uuid.New().String() 110 data, err := ioutil.ReadAll(reader) 111 112 if err != nil { 113 return "", err 114 } 115 116 bs.blobs[key] = data 117 bs.versions[key] = newVer 118 119 return newVer, nil 120 } 121 122 // Exists returns true if a blob exists for the given key, and false if it does not. 123 // For InMemoryBlobstore instances error should never be returned (though other 124 // implementations of this interface can) 125 func (bs *InMemoryBlobstore) Exists(ctx context.Context, key string) (bool, error) { 126 _, ok := bs.blobs[key] 127 128 return ok, nil 129 }