github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/libkbfs/block_ref_map.go (about)

     1  // Copyright 2016 Keybase Inc. All rights reserved.
     2  // Use of this source code is governed by a BSD
     3  // license that can be found in the LICENSE file.
     4  
     5  package libkbfs
     6  
     7  import (
     8  	"fmt"
     9  
    10  	"github.com/keybase/client/go/kbfs/kbfsblock"
    11  	"github.com/keybase/client/go/protocol/keybase1"
    12  )
    13  
    14  type blockRefStatus int
    15  
    16  const (
    17  	unknownBlockRef  blockRefStatus = 0
    18  	liveBlockRef     blockRefStatus = 1
    19  	archivedBlockRef blockRefStatus = 2
    20  )
    21  
    22  func (brf blockRefStatus) toBlockStatus() keybase1.BlockStatus {
    23  	switch brf {
    24  	case unknownBlockRef:
    25  		return keybase1.BlockStatus_UNKNOWN
    26  	case liveBlockRef:
    27  		return keybase1.BlockStatus_LIVE
    28  	case archivedBlockRef:
    29  		return keybase1.BlockStatus_ARCHIVED
    30  	default:
    31  		panic(fmt.Sprintf("Unknown ref status: %d", brf))
    32  	}
    33  }
    34  
    35  func (brf blockRefStatus) String() string {
    36  	switch brf {
    37  	case unknownBlockRef:
    38  		return "unknown"
    39  	case liveBlockRef:
    40  		return "live"
    41  	case archivedBlockRef:
    42  		return "archived"
    43  	default:
    44  		panic(fmt.Sprintf("Unknown ref status: %d", brf))
    45  	}
    46  }
    47  
    48  type blockContextMismatchError struct {
    49  	expected, actual kbfsblock.Context
    50  }
    51  
    52  func (e blockContextMismatchError) Error() string {
    53  	return fmt.Sprintf(
    54  		"Context mismatch: expected %s, got %s", e.expected, e.actual)
    55  }
    56  
    57  // TODO: Support unknown fields.
    58  
    59  type blockRefEntry struct {
    60  	Status  blockRefStatus
    61  	Context kbfsblock.Context
    62  	// mostRecentTag, if non-nil, is used by callers to figure out
    63  	// if an entry has been modified by something else. See
    64  	// blockRefMap.remove.
    65  	MostRecentTag string
    66  }
    67  
    68  func (e blockRefEntry) checkContext(context kbfsblock.Context) error {
    69  	if e.Context != context {
    70  		return blockContextMismatchError{e.Context, context}
    71  	}
    72  	return nil
    73  }
    74  
    75  // blockRefMap is a map with additional checking methods.
    76  //
    77  // TODO: Make this into a struct type that supports unknown fields.
    78  type blockRefMap map[kbfsblock.RefNonce]blockRefEntry
    79  
    80  func (refs blockRefMap) hasNonArchivedRef() bool {
    81  	for _, refEntry := range refs {
    82  		if refEntry.Status == liveBlockRef {
    83  			return true
    84  		}
    85  	}
    86  	return false
    87  }
    88  
    89  func (refs blockRefMap) getLiveCount() (count int) {
    90  	for _, refEntry := range refs {
    91  		if refEntry.Status == liveBlockRef {
    92  			count++
    93  		}
    94  	}
    95  	return count
    96  }
    97  
    98  func (refs blockRefMap) checkExists(context kbfsblock.Context) (
    99  	bool, blockRefStatus, error) {
   100  	refEntry, ok := refs[context.GetRefNonce()]
   101  	if !ok {
   102  		return false, unknownBlockRef, nil
   103  	}
   104  
   105  	err := refEntry.checkContext(context)
   106  	if err != nil {
   107  		return false, unknownBlockRef, err
   108  	}
   109  
   110  	return true, refEntry.Status, nil
   111  }
   112  
   113  func (refs blockRefMap) put(context kbfsblock.Context, status blockRefStatus,
   114  	tag string) error {
   115  	refNonce := context.GetRefNonce()
   116  	if refEntry, ok := refs[refNonce]; ok {
   117  		err := refEntry.checkContext(context)
   118  		if err != nil {
   119  			return err
   120  		}
   121  	}
   122  
   123  	refs[refNonce] = blockRefEntry{
   124  		Status:        status,
   125  		Context:       context,
   126  		MostRecentTag: tag,
   127  	}
   128  	return nil
   129  }
   130  
   131  // remove removes the entry with the given context, if any. If tag is
   132  // non-empty, then the entry will be removed only if its most recent
   133  // tag (passed in to put) matches the given one.
   134  func (refs blockRefMap) remove(context kbfsblock.Context, tag string) error {
   135  	refNonce := context.GetRefNonce()
   136  	// If this check fails, this ref is already gone, which is not
   137  	// an error.
   138  	if refEntry, ok := refs[refNonce]; ok {
   139  		err := refEntry.checkContext(context)
   140  		if err != nil {
   141  			return err
   142  		}
   143  		if tag == "" || refEntry.MostRecentTag == tag {
   144  			delete(refs, refNonce)
   145  		}
   146  	}
   147  	return nil
   148  }
   149  
   150  func (refs blockRefMap) deepCopy() blockRefMap {
   151  	if len(refs) == 0 {
   152  		return nil
   153  	}
   154  	refsCopy := make(blockRefMap)
   155  	for k, v := range refs {
   156  		refsCopy[k] = v
   157  	}
   158  	return refsCopy
   159  }