git/odb: implement odb.MemoryStorer for in-memory object cache
This commit is contained in:
parent
c86d50a47c
commit
0a4c74a463
79
git/odb/memory_storer.go
Normal file
79
git/odb/memory_storer.go
Normal file
@ -0,0 +1,79 @@
|
||||
package odb
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// MemoryStorer is an implementation of the Storer interface that holds data for
|
||||
// the object database in memory.
|
||||
type MemoryStorer struct {
|
||||
// mu guards reads and writes to the map "fs" below.
|
||||
mu *sync.Mutex
|
||||
// fs maps a hex-encoded SHA to a bytes.Buffer wrapped in a no-op closer
|
||||
// type.
|
||||
fs map[string]*bufCloser
|
||||
}
|
||||
|
||||
var _ Storer = (*MemoryStorer)(nil)
|
||||
|
||||
// NewMemoryStorer initializes a new MemoryStorer instance with the given
|
||||
// initial set.
|
||||
//
|
||||
// A value of "nil" is acceptable and indicates that no entries shall be added
|
||||
// to the memory storer at/during construction time.
|
||||
func NewMemoryStorer(m map[string]io.ReadWriter) *MemoryStorer {
|
||||
fs := make(map[string]*bufCloser, len(m))
|
||||
for n, rw := range m {
|
||||
fs[n] = &bufCloser{rw}
|
||||
}
|
||||
|
||||
return &MemoryStorer{
|
||||
mu: new(sync.Mutex),
|
||||
fs: fs,
|
||||
}
|
||||
}
|
||||
|
||||
// Create implements the Storere.Create function and returns an
|
||||
// io.ReadWriteCloser for the given SHA, provided that an object of that SHA is
|
||||
// not already indexed in the database.
|
||||
func (ms *MemoryStorer) Create(sha []byte) (f io.ReadWriteCloser, err error) {
|
||||
ms.mu.Lock()
|
||||
defer ms.mu.Unlock()
|
||||
|
||||
key := fmt.Sprintf("%x", sha)
|
||||
if _, ok := ms.fs[key]; ok {
|
||||
panic(fmt.Sprintf("git/odb: memory storage create %x, already exists", sha))
|
||||
} else {
|
||||
ms.fs[key] = &bufCloser{new(bytes.Buffer)}
|
||||
}
|
||||
return ms.fs[key], nil
|
||||
}
|
||||
|
||||
// Open implements the Storer.Open function, and returns a io.ReadWriteCloser
|
||||
// for the given SHA. If a reader for the given SHA does not exist an error will
|
||||
// be returned.
|
||||
func (ms *MemoryStorer) Open(sha []byte) (f io.ReadWriteCloser, err error) {
|
||||
ms.mu.Lock()
|
||||
defer ms.mu.Unlock()
|
||||
|
||||
key := fmt.Sprintf("%x", sha)
|
||||
if _, ok := ms.fs[key]; !ok {
|
||||
panic(fmt.Sprintf("git/odb: memory storage cannot open %x, doesn't exist", sha))
|
||||
}
|
||||
return ms.fs[key], nil
|
||||
}
|
||||
|
||||
// bufCloser wraps a type satisfying the io.ReadWriter interface with a no-op
|
||||
// Close() function, thus implementing the io.ReadWriteCloser composite
|
||||
// interface.
|
||||
type bufCloser struct {
|
||||
io.ReadWriter
|
||||
}
|
||||
|
||||
var _ io.ReadWriteCloser = (*bufCloser)(nil)
|
||||
|
||||
// Close implements io.Closer, and returns nothing.
|
||||
func (b *bufCloser) Close() error { return nil }
|
94
git/odb/memory_storer_test.go
Normal file
94
git/odb/memory_storer_test.go
Normal file
@ -0,0 +1,94 @@
|
||||
package odb
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestMemoryStorerIncludesGivenEntries(t *testing.T) {
|
||||
sha := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
hex, err := hex.DecodeString(sha)
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
ms := NewMemoryStorer(map[string]io.ReadWriter{
|
||||
sha: bytes.NewBuffer([]byte{0x1}),
|
||||
})
|
||||
|
||||
buf, err := ms.Open(hex)
|
||||
assert.Nil(t, err)
|
||||
|
||||
contents, err := ioutil.ReadAll(buf)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, []byte{0x1}, contents)
|
||||
}
|
||||
|
||||
func TestMemoryStorerAcceptsNilEntries(t *testing.T) {
|
||||
ms := NewMemoryStorer(nil)
|
||||
|
||||
assert.NotNil(t, ms)
|
||||
assert.Equal(t, 0, len(ms.fs))
|
||||
}
|
||||
|
||||
func TestMemoryStorerDoesntOpenMissingEntries(t *testing.T) {
|
||||
sha := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
|
||||
hex, err := hex.DecodeString(sha)
|
||||
assert.Nil(t, err)
|
||||
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
expeced := fmt.Sprintf("git/odb: memory storage cannot open %x, doesn't exist", hex)
|
||||
assert.Equal(t, expeced, err)
|
||||
} else {
|
||||
t.Fatal("expected panic()")
|
||||
}
|
||||
}()
|
||||
|
||||
ms := NewMemoryStorer(nil)
|
||||
|
||||
ms.Open(hex)
|
||||
}
|
||||
|
||||
func TestMemoryStorerCreatesNewEntries(t *testing.T) {
|
||||
hex, err := hex.DecodeString("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
|
||||
assert.Nil(t, err)
|
||||
|
||||
ms := NewMemoryStorer(nil)
|
||||
|
||||
assert.Equal(t, 0, len(ms.fs))
|
||||
|
||||
_, err = ms.Create(hex)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(ms.fs))
|
||||
}
|
||||
|
||||
func TestMemoryStorerCreatesNewEntriesExclusively(t *testing.T) {
|
||||
hex, err := hex.DecodeString("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
|
||||
assert.Nil(t, err)
|
||||
|
||||
ms := NewMemoryStorer(nil)
|
||||
|
||||
assert.Equal(t, 0, len(ms.fs))
|
||||
|
||||
_, err = ms.Create(hex)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(ms.fs))
|
||||
|
||||
defer func() {
|
||||
expected := fmt.Sprintf("git/odb: memory storage create %x, already exists", hex)
|
||||
if err := recover(); err != nil {
|
||||
assert.Equal(t, expected, err)
|
||||
} else {
|
||||
t.Fatal("expected panic()")
|
||||
}
|
||||
}()
|
||||
|
||||
ms.Create(hex)
|
||||
}
|
Loading…
Reference in New Issue
Block a user