github.com/rigado/snapd@v2.42.5-go-mod+incompatible/asserts/membackstore.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2016 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package asserts
    21  
    22  import (
    23  	"errors"
    24  	"sync"
    25  )
    26  
    27  type memoryBackstore struct {
    28  	top memBSBranch
    29  	mu  sync.RWMutex
    30  }
    31  
    32  type memBSNode interface {
    33  	put(assertType *AssertionType, key []string, assert Assertion) error
    34  	get(key []string, maxFormat int) (Assertion, error)
    35  	search(hint []string, found func(Assertion), maxFormat int)
    36  }
    37  
    38  type memBSBranch map[string]memBSNode
    39  
    40  type memBSLeaf map[string]map[int]Assertion
    41  
    42  func (br memBSBranch) put(assertType *AssertionType, key []string, assert Assertion) error {
    43  	key0 := key[0]
    44  	down := br[key0]
    45  	if down == nil {
    46  		if len(key) > 2 {
    47  			down = make(memBSBranch)
    48  		} else {
    49  			down = make(memBSLeaf)
    50  		}
    51  		br[key0] = down
    52  	}
    53  	return down.put(assertType, key[1:], assert)
    54  }
    55  
    56  func (leaf memBSLeaf) cur(key0 string, maxFormat int) (a Assertion) {
    57  	for formatnum, a1 := range leaf[key0] {
    58  		if formatnum <= maxFormat {
    59  			if a == nil || a1.Revision() > a.Revision() {
    60  				a = a1
    61  			}
    62  		}
    63  	}
    64  	return a
    65  }
    66  
    67  func (leaf memBSLeaf) put(assertType *AssertionType, key []string, assert Assertion) error {
    68  	key0 := key[0]
    69  	cur := leaf.cur(key0, assertType.MaxSupportedFormat())
    70  	if cur != nil {
    71  		rev := assert.Revision()
    72  		curRev := cur.Revision()
    73  		if curRev >= rev {
    74  			return &RevisionError{Current: curRev, Used: rev}
    75  		}
    76  	}
    77  	if _, ok := leaf[key0]; !ok {
    78  		leaf[key0] = make(map[int]Assertion)
    79  	}
    80  	leaf[key0][assert.Format()] = assert
    81  	return nil
    82  }
    83  
    84  // errNotFound is used internally by backends, it is converted to the richer
    85  // NotFoundError only at their public interface boundary
    86  var errNotFound = errors.New("assertion not found")
    87  
    88  func (br memBSBranch) get(key []string, maxFormat int) (Assertion, error) {
    89  	key0 := key[0]
    90  	down := br[key0]
    91  	if down == nil {
    92  		return nil, errNotFound
    93  	}
    94  	return down.get(key[1:], maxFormat)
    95  }
    96  
    97  func (leaf memBSLeaf) get(key []string, maxFormat int) (Assertion, error) {
    98  	key0 := key[0]
    99  	cur := leaf.cur(key0, maxFormat)
   100  	if cur == nil {
   101  		return nil, errNotFound
   102  	}
   103  	return cur, nil
   104  }
   105  
   106  func (br memBSBranch) search(hint []string, found func(Assertion), maxFormat int) {
   107  	hint0 := hint[0]
   108  	if hint0 == "" {
   109  		for _, down := range br {
   110  			down.search(hint[1:], found, maxFormat)
   111  		}
   112  		return
   113  	}
   114  	down := br[hint0]
   115  	if down != nil {
   116  		down.search(hint[1:], found, maxFormat)
   117  	}
   118  	return
   119  }
   120  
   121  func (leaf memBSLeaf) search(hint []string, found func(Assertion), maxFormat int) {
   122  	hint0 := hint[0]
   123  	if hint0 == "" {
   124  		for key := range leaf {
   125  			cand := leaf.cur(key, maxFormat)
   126  			if cand != nil {
   127  				found(cand)
   128  			}
   129  		}
   130  		return
   131  	}
   132  
   133  	cur := leaf.cur(hint0, maxFormat)
   134  	if cur != nil {
   135  		found(cur)
   136  	}
   137  }
   138  
   139  // NewMemoryBackstore creates a memory backed assertions backstore.
   140  func NewMemoryBackstore() Backstore {
   141  	return &memoryBackstore{
   142  		top: make(memBSBranch),
   143  	}
   144  }
   145  
   146  func (mbs *memoryBackstore) Put(assertType *AssertionType, assert Assertion) error {
   147  	mbs.mu.Lock()
   148  	defer mbs.mu.Unlock()
   149  
   150  	internalKey := make([]string, 1, 1+len(assertType.PrimaryKey))
   151  	internalKey[0] = assertType.Name
   152  	internalKey = append(internalKey, assert.Ref().PrimaryKey...)
   153  
   154  	err := mbs.top.put(assertType, internalKey, assert)
   155  	return err
   156  }
   157  
   158  func (mbs *memoryBackstore) Get(assertType *AssertionType, key []string, maxFormat int) (Assertion, error) {
   159  	mbs.mu.RLock()
   160  	defer mbs.mu.RUnlock()
   161  
   162  	internalKey := make([]string, 1+len(assertType.PrimaryKey))
   163  	internalKey[0] = assertType.Name
   164  	copy(internalKey[1:], key)
   165  
   166  	a, err := mbs.top.get(internalKey, maxFormat)
   167  	if err == errNotFound {
   168  		return nil, &NotFoundError{Type: assertType}
   169  	}
   170  	return a, err
   171  }
   172  
   173  func (mbs *memoryBackstore) Search(assertType *AssertionType, headers map[string]string, foundCb func(Assertion), maxFormat int) error {
   174  	mbs.mu.RLock()
   175  	defer mbs.mu.RUnlock()
   176  
   177  	hint := make([]string, 1+len(assertType.PrimaryKey))
   178  	hint[0] = assertType.Name
   179  	for i, name := range assertType.PrimaryKey {
   180  		hint[1+i] = headers[name]
   181  	}
   182  
   183  	candCb := func(a Assertion) {
   184  		if searchMatch(a, headers) {
   185  			foundCb(a)
   186  		}
   187  	}
   188  
   189  	mbs.top.search(hint, candCb, maxFormat)
   190  	return nil
   191  }