package objstore
import (
"bytes"
"context"
"io"
"sort"
"strings"
"sync"
"time"
"github.com/pkg/errors"
)
var errNotFound = errors .New ("inmem: object not found" )
type InMemBucket struct {
mtx sync .RWMutex
objects map [string ][]byte
attrs map [string ]ObjectAttributes
}
func NewInMemBucket () *InMemBucket {
return &InMemBucket {
objects : map [string ][]byte {},
attrs : map [string ]ObjectAttributes {},
}
}
func (b *InMemBucket ) Objects () map [string ][]byte {
b .mtx .RLock ()
defer b .mtx .RUnlock ()
objs := make (map [string ][]byte )
for k , v := range b .objects {
objs [k ] = v
}
return objs
}
func (b *InMemBucket ) Iter (_ context .Context , dir string , f func (string ) error , options ...IterOption ) error {
unique := map [string ]struct {}{}
params := ApplyIterOptions (options ...)
var dirPartsCount int
dirParts := strings .SplitAfter (dir , DirDelim )
for _ , p := range dirParts {
if p == "" {
continue
}
dirPartsCount ++
}
b .mtx .RLock ()
for filename := range b .objects {
if !strings .HasPrefix (filename , dir ) || dir == filename {
continue
}
if params .Recursive {
unique [filename ] = struct {}{}
continue
}
parts := strings .SplitAfter (filename , DirDelim )
unique [strings .Join (parts [:dirPartsCount +1 ], "" )] = struct {}{}
}
b .mtx .RUnlock ()
var keys []string
for n := range unique {
keys = append (keys , n )
}
sort .Slice (keys , func (i , j int ) bool {
if strings .HasSuffix (keys [i ], DirDelim ) && strings .HasSuffix (keys [j ], DirDelim ) {
return strings .Compare (keys [i ], keys [j ]) < 0
}
if strings .HasSuffix (keys [i ], DirDelim ) {
return false
}
if strings .HasSuffix (keys [j ], DirDelim ) {
return true
}
return strings .Compare (keys [i ], keys [j ]) < 0
})
for _ , k := range keys {
if err := f (k ); err != nil {
return err
}
}
return nil
}
func (b *InMemBucket ) Get (_ context .Context , name string ) (io .ReadCloser , error ) {
if name == "" {
return nil , errors .New ("inmem: object name is empty" )
}
b .mtx .RLock ()
file , ok := b .objects [name ]
b .mtx .RUnlock ()
if !ok {
return nil , errNotFound
}
return io .NopCloser (bytes .NewReader (file )), nil
}
func (b *InMemBucket ) GetRange (_ context .Context , name string , off , length int64 ) (io .ReadCloser , error ) {
if name == "" {
return nil , errors .New ("inmem: object name is empty" )
}
b .mtx .RLock ()
file , ok := b .objects [name ]
b .mtx .RUnlock ()
if !ok {
return nil , errNotFound
}
if int64 (len (file )) < off {
return io .NopCloser (bytes .NewReader (nil )), nil
}
if length == -1 {
return io .NopCloser (bytes .NewReader (file [off :])), nil
}
if length <= 0 {
return io .NopCloser (bytes .NewReader (nil )), errors .New ("length cannot be smaller or equal 0" )
}
if int64 (len (file )) <= off +length {
length = int64 (len (file )) - off
}
return io .NopCloser (bytes .NewReader (file [off : off +length ])), nil
}
func (b *InMemBucket ) Exists (_ context .Context , name string ) (bool , error ) {
b .mtx .RLock ()
defer b .mtx .RUnlock ()
_ , ok := b .objects [name ]
return ok , nil
}
func (b *InMemBucket ) Attributes (_ context .Context , name string ) (ObjectAttributes , error ) {
b .mtx .RLock ()
attrs , ok := b .attrs [name ]
b .mtx .RUnlock ()
if !ok {
return ObjectAttributes {}, errNotFound
}
return attrs , nil
}
func (b *InMemBucket ) Upload (_ context .Context , name string , r io .Reader ) error {
b .mtx .Lock ()
defer b .mtx .Unlock ()
body , err := io .ReadAll (r )
if err != nil {
return err
}
b .objects [name ] = body
b .attrs [name ] = ObjectAttributes {
Size : int64 (len (body )),
LastModified : time .Now (),
}
return nil
}
func (b *InMemBucket ) Delete (_ context .Context , name string ) error {
b .mtx .Lock ()
defer b .mtx .Unlock ()
if _ , ok := b .objects [name ]; !ok {
return errNotFound
}
delete (b .objects , name )
delete (b .attrs , name )
return nil
}
func (b *InMemBucket ) IsObjNotFoundErr (err error ) bool {
return errors .Is (err , errNotFound )
}
func (b *InMemBucket ) IsAccessDeniedErr (err error ) bool {
return false
}
func (b *InMemBucket ) Close () error { return nil }
func (b *InMemBucket ) Name () string {
return "inmem"
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .