git/odb: implement odb.MemoryStorer for in-memory object cache

This commit is contained in:
Taylor Blau 2017-05-15 15:51:50 -06:00
parent c86d50a47c
commit 0a4c74a463
2 changed files with 173 additions and 0 deletions

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 }

@ -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)
}