github.com/fawick/restic@v0.1.1-0.20171126184616-c02923fbfc79/internal/backend/mem/mem_backend.go (about)

     1  package mem
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"io"
     7  	"io/ioutil"
     8  	"sync"
     9  
    10  	"github.com/restic/restic/internal/errors"
    11  	"github.com/restic/restic/internal/restic"
    12  
    13  	"github.com/restic/restic/internal/debug"
    14  )
    15  
    16  type memMap map[restic.Handle][]byte
    17  
    18  // make sure that MemoryBackend implements backend.Backend
    19  var _ restic.Backend = &MemoryBackend{}
    20  
    21  var errNotFound = errors.New("not found")
    22  
    23  // MemoryBackend is a mock backend that uses a map for storing all data in
    24  // memory. This should only be used for tests.
    25  type MemoryBackend struct {
    26  	data memMap
    27  	m    sync.Mutex
    28  }
    29  
    30  // New returns a new backend that saves all data in a map in memory.
    31  func New() *MemoryBackend {
    32  	be := &MemoryBackend{
    33  		data: make(memMap),
    34  	}
    35  
    36  	debug.Log("created new memory backend")
    37  
    38  	return be
    39  }
    40  
    41  // Test returns whether a file exists.
    42  func (be *MemoryBackend) Test(ctx context.Context, h restic.Handle) (bool, error) {
    43  	be.m.Lock()
    44  	defer be.m.Unlock()
    45  
    46  	debug.Log("Test %v", h)
    47  
    48  	if _, ok := be.data[h]; ok {
    49  		return true, nil
    50  	}
    51  
    52  	return false, nil
    53  }
    54  
    55  // IsNotExist returns true if the file does not exist.
    56  func (be *MemoryBackend) IsNotExist(err error) bool {
    57  	return errors.Cause(err) == errNotFound
    58  }
    59  
    60  // Save adds new Data to the backend.
    61  func (be *MemoryBackend) Save(ctx context.Context, h restic.Handle, rd io.Reader) error {
    62  	if err := h.Valid(); err != nil {
    63  		return err
    64  	}
    65  
    66  	be.m.Lock()
    67  	defer be.m.Unlock()
    68  
    69  	if h.Type == restic.ConfigFile {
    70  		h.Name = ""
    71  	}
    72  
    73  	if _, ok := be.data[h]; ok {
    74  		return errors.New("file already exists")
    75  	}
    76  
    77  	buf, err := ioutil.ReadAll(rd)
    78  	if err != nil {
    79  		return err
    80  	}
    81  
    82  	be.data[h] = buf
    83  	debug.Log("saved %v bytes at %v", len(buf), h)
    84  
    85  	return nil
    86  }
    87  
    88  // Load returns a reader that yields the contents of the file at h at the
    89  // given offset. If length is nonzero, only a portion of the file is
    90  // returned. rd must be closed after use.
    91  func (be *MemoryBackend) Load(ctx context.Context, h restic.Handle, length int, offset int64) (io.ReadCloser, error) {
    92  	if err := h.Valid(); err != nil {
    93  		return nil, err
    94  	}
    95  
    96  	be.m.Lock()
    97  	defer be.m.Unlock()
    98  
    99  	if h.Type == restic.ConfigFile {
   100  		h.Name = ""
   101  	}
   102  
   103  	debug.Log("Load %v offset %v len %v", h, offset, length)
   104  
   105  	if offset < 0 {
   106  		return nil, errors.New("offset is negative")
   107  	}
   108  
   109  	if _, ok := be.data[h]; !ok {
   110  		return nil, errNotFound
   111  	}
   112  
   113  	buf := be.data[h]
   114  	if offset > int64(len(buf)) {
   115  		return nil, errors.New("offset beyond end of file")
   116  	}
   117  
   118  	buf = buf[offset:]
   119  	if length > 0 && len(buf) > length {
   120  		buf = buf[:length]
   121  	}
   122  
   123  	return ioutil.NopCloser(bytes.NewReader(buf)), nil
   124  }
   125  
   126  // Stat returns information about a file in the backend.
   127  func (be *MemoryBackend) Stat(ctx context.Context, h restic.Handle) (restic.FileInfo, error) {
   128  	be.m.Lock()
   129  	defer be.m.Unlock()
   130  
   131  	if err := h.Valid(); err != nil {
   132  		return restic.FileInfo{}, err
   133  	}
   134  
   135  	if h.Type == restic.ConfigFile {
   136  		h.Name = ""
   137  	}
   138  
   139  	debug.Log("stat %v", h)
   140  
   141  	e, ok := be.data[h]
   142  	if !ok {
   143  		return restic.FileInfo{}, errNotFound
   144  	}
   145  
   146  	return restic.FileInfo{Size: int64(len(e))}, nil
   147  }
   148  
   149  // Remove deletes a file from the backend.
   150  func (be *MemoryBackend) Remove(ctx context.Context, h restic.Handle) error {
   151  	be.m.Lock()
   152  	defer be.m.Unlock()
   153  
   154  	debug.Log("Remove %v", h)
   155  
   156  	if _, ok := be.data[h]; !ok {
   157  		return errNotFound
   158  	}
   159  
   160  	delete(be.data, h)
   161  
   162  	return nil
   163  }
   164  
   165  // List returns a channel which yields entries from the backend.
   166  func (be *MemoryBackend) List(ctx context.Context, t restic.FileType) <-chan string {
   167  	be.m.Lock()
   168  	defer be.m.Unlock()
   169  
   170  	ch := make(chan string)
   171  
   172  	var ids []string
   173  	for entry := range be.data {
   174  		if entry.Type != t {
   175  			continue
   176  		}
   177  		ids = append(ids, entry.Name)
   178  	}
   179  
   180  	debug.Log("list %v: %v", t, ids)
   181  
   182  	go func() {
   183  		defer close(ch)
   184  		for _, id := range ids {
   185  			select {
   186  			case ch <- id:
   187  			case <-ctx.Done():
   188  				return
   189  			}
   190  		}
   191  	}()
   192  
   193  	return ch
   194  }
   195  
   196  // Location returns the location of the backend (RAM).
   197  func (be *MemoryBackend) Location() string {
   198  	return "RAM"
   199  }
   200  
   201  // Delete removes all data in the backend.
   202  func (be *MemoryBackend) Delete(ctx context.Context) error {
   203  	be.m.Lock()
   204  	defer be.m.Unlock()
   205  
   206  	be.data = make(memMap)
   207  	return nil
   208  }
   209  
   210  // Close closes the backend.
   211  func (be *MemoryBackend) Close() error {
   212  	return nil
   213  }