package restic import ( "bytes" "encoding/json" "errors" "sort" "sync" "github.com/restic/restic/backend" ) type BlobList struct { list []Blob m sync.Mutex } var ErrBlobNotFound = errors.New("Blob not found") func NewBlobList() *BlobList { return &BlobList{ list: []Blob{}, } } func LoadBlobList(ch *ContentHandler, id backend.ID) (*BlobList, error) { bl := &BlobList{} err := ch.LoadJSONRaw(backend.Map, id, bl) if err != nil { return nil, err } return bl, nil } func (bl *BlobList) find(blob Blob) (int, Blob, error) { pos := sort.Search(len(bl.list), func(i int) bool { return blob.ID.Compare(bl.list[i].ID) >= 0 }) if pos < len(bl.list) && blob.ID.Compare(bl.list[pos].ID) == 0 { return pos, bl.list[pos], nil } return pos, Blob{}, ErrBlobNotFound } func (bl *BlobList) Find(blob Blob) (Blob, error) { bl.m.Lock() defer bl.m.Unlock() _, blob, err := bl.find(blob) return blob, err } func (bl *BlobList) Merge(other *BlobList) { bl.m.Lock() defer bl.m.Unlock() other.m.Lock() defer other.m.Unlock() for _, blob := range other.list { bl.insert(blob) } } func (bl *BlobList) insert(blob Blob) { pos, _, err := bl.find(blob) if err == nil { // already present return } // insert blob // https://code.google.com/p/go-wiki/wiki/bliceTricks bl.list = append(bl.list, Blob{}) copy(bl.list[pos+1:], bl.list[pos:]) bl.list[pos] = blob } func (bl *BlobList) Insert(blob Blob) { bl.m.Lock() defer bl.m.Unlock() bl.insert(blob) } func (bl BlobList) MarshalJSON() ([]byte, error) { return json.Marshal(bl.list) } func (bl *BlobList) UnmarshalJSON(data []byte) error { return json.Unmarshal(data, &bl.list) } // 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 { return res } if blob.Size < other.Size { return -1 } if blob.Size > other.Size { return 1 } return 0 }