mirror of
https://github.com/restic/restic.git
synced 2026-08-03 04:32:17 +00:00
Use array instead of hash for backend.ID
Since backend.ID is always a slice of constant length, use an array
instead of a slice. Mostly, arrays behave as slices, except that an
array cannot be nil, so use `*backend.ID` insteaf of `backend.ID` in
places where the absence of an ID is possible (e.g. for the Subtree of a
Node, which may not present when the node is a file node).
This change allows to directly use backend.ID as the the key for a map,
so that arbitrary data structures (e.g. a Set implemented as a
map[backend.ID]struct{}) can easily be formed.
This commit is contained in:
+8
-9
@@ -1,17 +1,16 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/restic/restic/backend"
|
||||
)
|
||||
|
||||
type Blob struct {
|
||||
ID backend.ID `json:"id,omitempty"`
|
||||
Size uint64 `json:"size,omitempty"`
|
||||
Storage backend.ID `json:"sid,omitempty"` // encrypted ID
|
||||
StorageSize uint64 `json:"ssize,omitempty"` // encrypted Size
|
||||
ID *backend.ID `json:"id,omitempty"`
|
||||
Size uint64 `json:"size,omitempty"`
|
||||
Storage *backend.ID `json:"sid,omitempty"` // encrypted ID
|
||||
StorageSize uint64 `json:"ssize,omitempty"` // encrypted Size
|
||||
}
|
||||
|
||||
type Blobs []Blob
|
||||
@@ -32,15 +31,15 @@ func (b Blob) String() string {
|
||||
|
||||
// Compare compares two blobs by comparing the ID and the size. It returns -1,
|
||||
// 0, or 1.
|
||||
func (blob Blob) Compare(other Blob) int {
|
||||
if res := bytes.Compare(other.ID, blob.ID); res != 0 {
|
||||
func (b Blob) Compare(other Blob) int {
|
||||
if res := b.ID.Compare(*other.ID); res != 0 {
|
||||
return res
|
||||
}
|
||||
|
||||
if blob.Size < other.Size {
|
||||
if b.Size < other.Size {
|
||||
return -1
|
||||
}
|
||||
if blob.Size > other.Size {
|
||||
if b.Size > other.Size {
|
||||
return 1
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ func LoadConfig(r JSONUnpackedLoader) (Config, error) {
|
||||
cfg Config
|
||||
)
|
||||
|
||||
err := r.LoadJSONUnpacked(backend.Config, nil, &cfg)
|
||||
err := r.LoadJSONUnpacked(backend.Config, backend.ID{}, &cfg)
|
||||
if err != nil {
|
||||
return Config{}, err
|
||||
}
|
||||
|
||||
+7
-7
@@ -20,7 +20,7 @@ type Index struct {
|
||||
|
||||
type indexEntry struct {
|
||||
tpe pack.BlobType
|
||||
packID backend.ID
|
||||
packID *backend.ID
|
||||
offset uint
|
||||
length uint
|
||||
old bool
|
||||
@@ -33,7 +33,7 @@ func NewIndex() *Index {
|
||||
}
|
||||
}
|
||||
|
||||
func (idx *Index) store(t pack.BlobType, id, pack backend.ID, offset, length uint, old bool) {
|
||||
func (idx *Index) store(t pack.BlobType, id backend.ID, pack *backend.ID, offset, length uint, old bool) {
|
||||
idx.pack[id.String()] = indexEntry{
|
||||
tpe: t,
|
||||
packID: pack,
|
||||
@@ -44,7 +44,7 @@ func (idx *Index) store(t pack.BlobType, id, pack backend.ID, offset, length uin
|
||||
}
|
||||
|
||||
// Store remembers the id and pack in the index.
|
||||
func (idx *Index) Store(t pack.BlobType, id, pack backend.ID, offset, length uint) {
|
||||
func (idx *Index) Store(t pack.BlobType, id backend.ID, pack *backend.ID, offset, length uint) {
|
||||
idx.m.Lock()
|
||||
defer idx.m.Unlock()
|
||||
|
||||
@@ -68,7 +68,7 @@ func (idx *Index) Remove(packID backend.ID) {
|
||||
}
|
||||
|
||||
// Lookup returns the pack for the id.
|
||||
func (idx *Index) Lookup(id backend.ID) (packID backend.ID, tpe pack.BlobType, offset, length uint, err error) {
|
||||
func (idx *Index) Lookup(id backend.ID) (packID *backend.ID, tpe pack.BlobType, offset, length uint, err error) {
|
||||
idx.m.Lock()
|
||||
defer idx.m.Unlock()
|
||||
|
||||
@@ -155,7 +155,7 @@ func (idx *Index) Each(done chan struct{}) <-chan PackedBlob {
|
||||
Type: blob.tpe,
|
||||
Length: uint32(blob.length),
|
||||
},
|
||||
PackID: blob.packID,
|
||||
PackID: *blob.packID,
|
||||
}:
|
||||
}
|
||||
}
|
||||
@@ -206,7 +206,7 @@ func (idx *Index) generatePackList(selectFn func(indexEntry) bool) ([]*packJSON,
|
||||
|
||||
debug.Log("Index.generatePackList", "handle blob %q", id[:8])
|
||||
|
||||
if blob.packID == nil {
|
||||
if blob.packID.IsNull() {
|
||||
debug.Log("Index.generatePackList", "blob %q has no packID! (type %v, offset %v, length %v)",
|
||||
id[:8], blob.tpe, blob.offset, blob.length)
|
||||
return nil, fmt.Errorf("unable to serialize index: pack for blob %v hasn't been written yet", id)
|
||||
@@ -315,7 +315,7 @@ func DecodeIndex(rd io.Reader) (*Index, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
idx.store(blob.Type, blobID, packID, blob.Offset, blob.Length, true)
|
||||
idx.store(blob.Type, blobID, &packID, blob.Offset, blob.Length, true)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+10
-10
@@ -13,12 +13,12 @@ import (
|
||||
)
|
||||
|
||||
func randomID() backend.ID {
|
||||
buf := make([]byte, backend.IDSize)
|
||||
_, err := io.ReadFull(rand.Reader, buf)
|
||||
id := backend.ID{}
|
||||
_, err := io.ReadFull(rand.Reader, id[:])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return buf
|
||||
return id
|
||||
}
|
||||
|
||||
func TestIndexSerialize(t *testing.T) {
|
||||
@@ -40,7 +40,7 @@ func TestIndexSerialize(t *testing.T) {
|
||||
for j := 0; j < 20; j++ {
|
||||
id := randomID()
|
||||
length := uint(i*100 + j)
|
||||
idx.Store(pack.Data, id, packID, pos, length)
|
||||
idx.Store(pack.Data, id, &packID, pos, length)
|
||||
|
||||
tests = append(tests, testEntry{
|
||||
id: id,
|
||||
@@ -71,7 +71,7 @@ func TestIndexSerialize(t *testing.T) {
|
||||
packID, tpe, offset, length, err := idx.Lookup(testBlob.id)
|
||||
OK(t, err)
|
||||
|
||||
Equals(t, testBlob.pack, packID)
|
||||
Equals(t, testBlob.pack, *packID)
|
||||
Equals(t, testBlob.tpe, tpe)
|
||||
Equals(t, testBlob.offset, offset)
|
||||
Equals(t, testBlob.length, length)
|
||||
@@ -79,7 +79,7 @@ func TestIndexSerialize(t *testing.T) {
|
||||
packID, tpe, offset, length, err = idx2.Lookup(testBlob.id)
|
||||
OK(t, err)
|
||||
|
||||
Equals(t, testBlob.pack, packID)
|
||||
Equals(t, testBlob.pack, *packID)
|
||||
Equals(t, testBlob.tpe, tpe)
|
||||
Equals(t, testBlob.offset, offset)
|
||||
Equals(t, testBlob.length, length)
|
||||
@@ -94,7 +94,7 @@ func TestIndexSerialize(t *testing.T) {
|
||||
for j := 0; j < 10; j++ {
|
||||
id := randomID()
|
||||
length := uint(i*100 + j)
|
||||
idx2.Store(pack.Data, id, packID, pos, length)
|
||||
idx2.Store(pack.Data, id, &packID, pos, length)
|
||||
|
||||
newtests = append(newtests, testEntry{
|
||||
id: id,
|
||||
@@ -130,7 +130,7 @@ func TestIndexSerialize(t *testing.T) {
|
||||
packID, tpe, offset, length, err := idx3.Lookup(testBlob.id)
|
||||
OK(t, err)
|
||||
|
||||
Equals(t, testBlob.pack, packID)
|
||||
Equals(t, testBlob.pack, *packID)
|
||||
Equals(t, testBlob.tpe, tpe)
|
||||
Equals(t, testBlob.offset, offset)
|
||||
Equals(t, testBlob.length, length)
|
||||
@@ -149,7 +149,7 @@ func TestIndexSize(t *testing.T) {
|
||||
for j := 0; j < blobs; j++ {
|
||||
id := randomID()
|
||||
length := uint(i*100 + j)
|
||||
idx.Store(pack.Data, id, packID, pos, length)
|
||||
idx.Store(pack.Data, id, &packID, pos, length)
|
||||
|
||||
pos += length
|
||||
}
|
||||
@@ -217,7 +217,7 @@ func TestIndexUnserialize(t *testing.T) {
|
||||
packID, tpe, offset, length, err := idx.Lookup(test.id)
|
||||
OK(t, err)
|
||||
|
||||
Equals(t, test.packID, packID)
|
||||
Equals(t, test.packID, *packID)
|
||||
Equals(t, test.tpe, tpe)
|
||||
Equals(t, test.offset, offset)
|
||||
Equals(t, test.length, length)
|
||||
|
||||
+2
-1
@@ -3,6 +3,7 @@ package repository
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -199,7 +200,7 @@ func AddKey(s *Repository, password string, template *crypto.Key) (*Key, error)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
name := backend.ID(plainhw.Sum(nil)).String()
|
||||
name := hex.EncodeToString(plainhw.Sum(nil))
|
||||
|
||||
err = blob.Finalize(backend.Key, name)
|
||||
if err != nil {
|
||||
|
||||
+25
-21
@@ -257,7 +257,7 @@ func (r *Repository) savePacker(p *pack.Packer) error {
|
||||
// update blobs in the index
|
||||
for _, b := range p.Blobs() {
|
||||
debug.Log("Repo.savePacker", " updating blob %v to pack %v", b.ID.Str(), sid.Str())
|
||||
r.idx.Store(b.Type, b.ID, sid, b.Offset, uint(b.Length))
|
||||
r.idx.Store(b.Type, b.ID, &sid, b.Offset, uint(b.Length))
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -273,10 +273,11 @@ func (r *Repository) countPacker() int {
|
||||
|
||||
// SaveAndEncrypt encrypts data and stores it to the backend as type t. If data is small
|
||||
// enough, it will be packed together with other small blobs.
|
||||
func (r *Repository) SaveAndEncrypt(t pack.BlobType, data []byte, id backend.ID) (backend.ID, error) {
|
||||
func (r *Repository) SaveAndEncrypt(t pack.BlobType, data []byte, id *backend.ID) (backend.ID, error) {
|
||||
if id == nil {
|
||||
// compute plaintext hash
|
||||
id = backend.Hash(data)
|
||||
hashedID := backend.Hash(data)
|
||||
id = &hashedID
|
||||
}
|
||||
|
||||
debug.Log("Repo.Save", "save id %v (%v, %d bytes)", id.Str(), t, len(data))
|
||||
@@ -288,21 +289,21 @@ func (r *Repository) SaveAndEncrypt(t pack.BlobType, data []byte, id backend.ID)
|
||||
// encrypt blob
|
||||
ciphertext, err := r.Encrypt(ciphertext, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return backend.ID{}, err
|
||||
}
|
||||
|
||||
// find suitable packer and add blob
|
||||
packer, err := r.findPacker(uint(len(ciphertext)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return backend.ID{}, err
|
||||
}
|
||||
|
||||
// save ciphertext
|
||||
packer.Add(t, id, bytes.NewReader(ciphertext))
|
||||
packer.Add(t, *id, bytes.NewReader(ciphertext))
|
||||
|
||||
// add this id to the index, although we don't know yet in which pack it
|
||||
// will be saved, the entry will be updated when the pack is written.
|
||||
r.idx.Store(t, id, nil, 0, 0)
|
||||
r.idx.Store(t, *id, nil, 0, 0)
|
||||
debug.Log("Repo.Save", "saving stub for %v (%v) in index", id.Str, t)
|
||||
|
||||
// if the pack is not full enough and there are less than maxPackers
|
||||
@@ -310,15 +311,15 @@ func (r *Repository) SaveAndEncrypt(t pack.BlobType, data []byte, id backend.ID)
|
||||
if packer.Size() < minPackSize && r.countPacker() < maxPackers {
|
||||
debug.Log("Repo.Save", "pack is not full enough (%d bytes)", packer.Size())
|
||||
r.insertPacker(packer)
|
||||
return id, nil
|
||||
return *id, nil
|
||||
}
|
||||
|
||||
// else write the pack to the backend
|
||||
return id, r.savePacker(packer)
|
||||
return *id, r.savePacker(packer)
|
||||
}
|
||||
|
||||
// SaveFrom encrypts data read from rd and stores it in a pack in the backend as type t.
|
||||
func (r *Repository) SaveFrom(t pack.BlobType, id backend.ID, length uint, rd io.Reader) error {
|
||||
func (r *Repository) SaveFrom(t pack.BlobType, id *backend.ID, length uint, rd io.Reader) error {
|
||||
debug.Log("Repo.SaveFrom", "save id %v (%v, %d bytes)", id.Str(), t, length)
|
||||
if id == nil {
|
||||
return errors.New("id is nil")
|
||||
@@ -349,7 +350,7 @@ func (r *Repository) SaveJSON(t pack.BlobType, item interface{}) (backend.ID, er
|
||||
enc := json.NewEncoder(wr)
|
||||
err := enc.Encode(item)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("json.Encode: %v", err)
|
||||
return backend.ID{}, fmt.Errorf("json.Encode: %v", err)
|
||||
}
|
||||
|
||||
buf = wr.Bytes()
|
||||
@@ -362,7 +363,7 @@ func (r *Repository) SaveJSONUnpacked(t backend.Type, item interface{}) (backend
|
||||
// create file
|
||||
blob, err := r.be.Create()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return backend.ID{}, err
|
||||
}
|
||||
debug.Log("Repo.SaveJSONUnpacked", "create new blob %v", t)
|
||||
|
||||
@@ -375,21 +376,23 @@ func (r *Repository) SaveJSONUnpacked(t backend.Type, item interface{}) (backend
|
||||
enc := json.NewEncoder(ewr)
|
||||
err = enc.Encode(item)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("json.Encode: %v", err)
|
||||
return backend.ID{}, fmt.Errorf("json.Encode: %v", err)
|
||||
}
|
||||
|
||||
err = ewr.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return backend.ID{}, err
|
||||
}
|
||||
|
||||
// finalize blob in the backend
|
||||
sid := backend.ID(hw.Sum(nil))
|
||||
hash := hw.Sum(nil)
|
||||
sid := backend.ID{}
|
||||
copy(sid[:], hash)
|
||||
|
||||
err = blob.Finalize(t, sid.String())
|
||||
if err != nil {
|
||||
debug.Log("Repo.SaveJSONUnpacked", "error saving blob %v as %v: %v", t, sid, err)
|
||||
return nil, err
|
||||
return backend.ID{}, err
|
||||
}
|
||||
|
||||
debug.Log("Repo.SaveJSONUnpacked", "new blob %v saved as %v", t, sid)
|
||||
@@ -438,7 +441,7 @@ func (r *Repository) SaveIndex() (backend.ID, error) {
|
||||
// create blob
|
||||
blob, err := r.be.Create()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return backend.ID{}, err
|
||||
}
|
||||
|
||||
debug.Log("Repo.SaveIndex", "create new pack %p", blob)
|
||||
@@ -451,20 +454,21 @@ func (r *Repository) SaveIndex() (backend.ID, error) {
|
||||
|
||||
err = r.idx.Encode(ewr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return backend.ID{}, err
|
||||
}
|
||||
|
||||
err = ewr.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return backend.ID{}, err
|
||||
}
|
||||
|
||||
// finalize blob in the backend
|
||||
sid := backend.ID(hw.Sum(nil))
|
||||
sid := backend.ID{}
|
||||
copy(sid[:], hw.Sum(nil))
|
||||
|
||||
err = blob.Finalize(backend.Index, sid.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return backend.ID{}, err
|
||||
}
|
||||
|
||||
debug.Log("Repo.SaveIndex", "Saved index as %v", sid.Str())
|
||||
|
||||
@@ -39,7 +39,7 @@ func TestSaveJSON(t *testing.T) {
|
||||
id, err := repo.SaveJSON(pack.Tree, obj)
|
||||
OK(t, err)
|
||||
|
||||
Assert(t, bytes.Equal(h[:], id),
|
||||
Assert(t, h == id,
|
||||
"TestSaveJSON: wrong plaintext ID: expected %02x, got %02x",
|
||||
h, id)
|
||||
}
|
||||
@@ -62,7 +62,7 @@ func BenchmarkSaveJSON(t *testing.B) {
|
||||
id, err := repo.SaveJSON(pack.Tree, obj)
|
||||
OK(t, err)
|
||||
|
||||
Assert(t, bytes.Equal(h[:], id),
|
||||
Assert(t, h == id,
|
||||
"TestSaveJSON: wrong plaintext ID: expected %02x, got %02x",
|
||||
h, id)
|
||||
}
|
||||
@@ -114,13 +114,13 @@ func TestSaveFrom(t *testing.T) {
|
||||
id := backend.Hash(data)
|
||||
|
||||
// save
|
||||
err = repo.SaveFrom(pack.Data, id[:], uint(size), bytes.NewReader(data))
|
||||
err = repo.SaveFrom(pack.Data, &id, uint(size), bytes.NewReader(data))
|
||||
OK(t, err)
|
||||
|
||||
OK(t, repo.Flush())
|
||||
|
||||
// read back
|
||||
buf, err := repo.LoadBlob(pack.Data, id[:])
|
||||
buf, err := repo.LoadBlob(pack.Data, id)
|
||||
|
||||
Assert(t, len(buf) == len(data),
|
||||
"number of bytes read back does not match: expected %d, got %d",
|
||||
@@ -142,14 +142,14 @@ func BenchmarkSaveFrom(t *testing.B) {
|
||||
_, err := io.ReadFull(rand.Reader, data)
|
||||
OK(t, err)
|
||||
|
||||
id := sha256.Sum256(data)
|
||||
id := backend.ID(sha256.Sum256(data))
|
||||
|
||||
t.ResetTimer()
|
||||
t.SetBytes(int64(size))
|
||||
|
||||
for i := 0; i < t.N; i++ {
|
||||
// save
|
||||
err = repo.SaveFrom(pack.Data, id[:], uint(size), bytes.NewReader(data))
|
||||
err = repo.SaveFrom(pack.Data, &id, uint(size), bytes.NewReader(data))
|
||||
OK(t, err)
|
||||
}
|
||||
}
|
||||
@@ -167,7 +167,7 @@ func TestLoadJSONPack(t *testing.T) {
|
||||
OK(t, repo.Flush())
|
||||
|
||||
tree := restic.NewTree()
|
||||
err := repo.LoadJSONPack(pack.Tree, sn.Tree, &tree)
|
||||
err := repo.LoadJSONPack(pack.Tree, *sn.Tree, &tree)
|
||||
OK(t, err)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user