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 }