1package s3mem 2 3import ( 4 "bytes" 5 "io" 6 "time" 7 8 "github.com/johannesboyne/gofakes3" 9 "github.com/johannesboyne/gofakes3/internal/s3io" 10 "github.com/ryszard/goskiplist/skiplist" 11) 12 13type versionGenFunc func() gofakes3.VersionID 14 15type versioningStatus int 16 17type bucket struct { 18 name string 19 versioning gofakes3.VersioningStatus 20 versionGen versionGenFunc 21 creationDate gofakes3.ContentTime 22 23 objects *skiplist.SkipList 24} 25 26func newBucket(name string, at time.Time, versionGen versionGenFunc) *bucket { 27 return &bucket{ 28 name: name, 29 creationDate: gofakes3.NewContentTime(at), 30 versionGen: versionGen, 31 objects: skiplist.NewStringMap(), 32 } 33} 34 35type bucketObject struct { 36 name string 37 data *bucketData 38 versions *skiplist.SkipList 39} 40 41func (b *bucketObject) Iterator() *bucketObjectIterator { 42 var iter skiplist.Iterator 43 if b.versions != nil { 44 iter = b.versions.Iterator() 45 } 46 47 return &bucketObjectIterator{ 48 data: b.data, 49 iter: iter, 50 } 51} 52 53type bucketObjectIterator struct { 54 data *bucketData 55 iter skiplist.Iterator 56 cur *bucketData 57 seenData bool 58 done bool 59} 60 61func (b *bucketObjectIterator) Seek(key gofakes3.VersionID) bool { 62 if b.iter.Seek(key) { 63 return true 64 } 65 66 b.iter = nil 67 if b.data != nil && b.data.versionID == key { 68 return true 69 } 70 71 b.data = nil 72 b.done = true 73 74 return false 75} 76 77func (b *bucketObjectIterator) Next() bool { 78 if b.done { 79 return false 80 } 81 82 if b.iter != nil { 83 iterAlive := b.iter.Next() 84 if iterAlive { 85 b.cur = b.iter.Value().(*bucketData) 86 return true 87 } 88 89 b.iter.Close() 90 b.iter = nil 91 } 92 93 if b.data != nil { 94 b.cur = b.data 95 b.data = nil 96 return true 97 } 98 99 b.done = true 100 return false 101} 102 103func (b *bucketObjectIterator) Close() { 104 if b.iter != nil { 105 b.iter.Close() 106 } 107 b.done = true 108} 109 110func (b *bucketObjectIterator) Value() *bucketData { 111 return b.cur 112} 113 114type bucketData struct { 115 name string 116 lastModified time.Time 117 versionID gofakes3.VersionID 118 deleteMarker bool 119 body []byte 120 hash []byte 121 etag string 122 metadata map[string]string 123} 124 125func (bi *bucketData) toObject(rangeRequest *gofakes3.ObjectRangeRequest, withBody bool) (obj *gofakes3.Object, err error) { 126 sz := int64(len(bi.body)) 127 data := bi.body 128 129 var contents io.ReadCloser 130 var rnge *gofakes3.ObjectRange 131 132 if withBody { 133 // In case of a range request the correct part of the slice is extracted: 134 rnge, err = rangeRequest.Range(sz) 135 if err != nil { 136 return nil, err 137 } 138 139 if rnge != nil { 140 data = data[rnge.Start : rnge.Start+rnge.Length] 141 } 142 143 // The data slice should be completely replaced if the bucket item is edited, so 144 // it should be safe to return the data slice directly. 145 contents = s3io.ReaderWithDummyCloser{bytes.NewReader(data)} 146 147 } else { 148 contents = s3io.NoOpReadCloser{} 149 } 150 151 return &gofakes3.Object{ 152 Name: bi.name, 153 Hash: bi.hash, 154 Metadata: bi.metadata, 155 Size: sz, 156 Range: rnge, 157 IsDeleteMarker: bi.deleteMarker, 158 VersionID: bi.versionID, 159 Contents: contents, 160 }, nil 161} 162 163func (b *bucket) setVersioning(enabled bool) { 164 if enabled { 165 b.versioning = gofakes3.VersioningEnabled 166 } else if b.versioning == gofakes3.VersioningEnabled { 167 b.versioning = gofakes3.VersioningSuspended 168 } 169} 170 171func (b *bucket) object(objectName string) (obj *bucketObject) { 172 objIface, _ := b.objects.Get(objectName) 173 if objIface == nil { 174 return nil 175 } 176 obj, _ = objIface.(*bucketObject) 177 return obj 178} 179 180func (b *bucket) objectVersion(objectName string, versionID gofakes3.VersionID) (*bucketData, error) { 181 obj := b.object(objectName) 182 if obj == nil { 183 return nil, gofakes3.KeyNotFound(objectName) 184 } 185 186 if obj.data != nil && obj.data.versionID == versionID { 187 return obj.data, nil 188 } 189 if obj.versions == nil { 190 return nil, gofakes3.ErrNoSuchVersion 191 } 192 versionIface, _ := obj.versions.Get(versionID) 193 if versionIface == nil { 194 return nil, gofakes3.ErrNoSuchVersion 195 } 196 197 return versionIface.(*bucketData), nil 198} 199 200func (b *bucket) put(name string, item *bucketData) { 201 // Always generate a version for convenience; we can just mask it on return. 202 item.versionID = b.versionGen() 203 204 object := b.object(name) 205 if object == nil { 206 object = &bucketObject{name: name} 207 b.objects.Set(name, object) 208 } 209 210 if b.versioning == gofakes3.VersioningEnabled { 211 if object.data != nil { 212 if object.versions == nil { 213 object.versions = skiplist.NewCustomMap(func(l, r interface{}) bool { 214 return l.(gofakes3.VersionID) < r.(gofakes3.VersionID) 215 }) 216 } 217 object.versions.Set(object.data.versionID, object.data) 218 } 219 } 220 221 object.data = item 222} 223 224func (b *bucket) rm(name string, at time.Time) (result gofakes3.ObjectDeleteResult, rerr error) { 225 object := b.object(name) 226 if object == nil { 227 // S3 does not report an error when attemping to delete a key that does not exist 228 return result, nil 229 } 230 231 if b.versioning == gofakes3.VersioningEnabled { 232 item := &bucketData{lastModified: at, name: name, deleteMarker: true} 233 b.put(name, item) 234 result.IsDeleteMarker = true 235 result.VersionID = item.versionID 236 237 } else { 238 object.data = nil 239 if object.versions == nil || object.versions.Len() == 0 { 240 b.objects.Delete(name) 241 } 242 } 243 244 return result, nil 245} 246 247func (b *bucket) rmVersion(name string, versionID gofakes3.VersionID, at time.Time) (result gofakes3.ObjectDeleteResult, rerr error) { 248 object := b.object(name) 249 if object == nil { 250 return result, nil 251 252 } else if object.data != nil && object.data.versionID == versionID { 253 result.VersionID = versionID 254 result.IsDeleteMarker = object.data.deleteMarker 255 object.data = nil 256 257 } else if object.versions != nil { 258 versionIface, ok := object.versions.Delete(versionID) 259 if !ok { 260 // S3 does not report an error when attemping to delete a key that does not exist 261 return result, nil 262 } 263 264 version := versionIface.(*bucketData) 265 result.VersionID = version.versionID 266 result.IsDeleteMarker = version.deleteMarker 267 } 268 269 if object.data == nil && (object.versions == nil || object.versions.Len() == 0) { 270 b.objects.Delete(name) 271 } 272 273 return result, nil 274} 275