package main import ( "encoding/binary" "errors" "sync" "time" "github.com/google/uuid" ) var ErrObjectNotFound = errors.New("object not found") type ObjectStore interface { AwaitObjects(ids []ObjectID, c chan ObjectResult, timeout *time.Duration) PutObject(object *Object) error MakeID() ObjectID } type ObjectResult struct { Object *Object Error error } type Object struct { ID ObjectID Data []byte } func GetObject(s ObjectStore, id ObjectID) ([]byte, error) { c := make(chan ObjectResult) defer close(c) ids := []ObjectID{id} go func() { s.AwaitObjects(ids, c, nil) }() obj, ok := <-c if !ok { return nil, errors.New("Couldn't get object") } if obj.Error != nil { return nil, obj.Error } return obj.Object.Data, nil } type MemObjectStore struct { sync.RWMutex db map[ObjectID][]byte prefix uint64 printer uint64 } func (mem *MemObjectStore) AwaitObjects(ids []ObjectID, c chan ObjectResult, timeout *time.Duration) { if timeout != nil { panic("timeout not yet implemented") } mem.RLock() defer mem.RUnlock() for _, id := range ids { v, ok := mem.db[id] if !ok { c <- ObjectResult{Error: ErrObjectNotFound} } else { c <- ObjectResult{&Object{ID: id, Data: v}, nil} } } close(c) } func (mem *MemObjectStore) PutObject(object *Object) error { mem.Lock() defer mem.Unlock() mem.db[object.ID] = object.Data return nil } func (mem *MemObjectStore) MakeID() ObjectID { id := mem.prefix + mem.printer mem.prefix++ return ObjectID(id) } func NewMemObjectStore() *MemObjectStore { prefixUuid, err := uuid.NewRandom() if err != nil { panic(err) } prefix := uint64(prefixUuid.ID()) << 32 return &MemObjectStore{ db: make(map[ObjectID][]byte), prefix: prefix, printer: 1, } } type ObjectID uint64 func serializeObjectID(id ObjectID) []byte { out := make([]byte, 8) binary.BigEndian.PutUint64(out, uint64(id)) return out } func deserializeObjectID(id_bytes []byte) ObjectID { id := binary.BigEndian.Uint64(id_bytes) return ObjectID(id) }