github.com/stchris/docker@v1.4.2-0.20150106053530-1510a324dbd5/pkg/truncindex/truncindex.go (about) 1 package truncindex 2 3 import ( 4 "errors" 5 "fmt" 6 "strings" 7 "sync" 8 9 "github.com/tchap/go-patricia/patricia" 10 ) 11 12 var ( 13 // ErrNoID is thrown when attempting to use empty prefixes 14 ErrNoID = errors.New("prefix can't be empty") 15 errDuplicateID = errors.New("multiple IDs were found") 16 ) 17 18 func init() { 19 // Change patricia max prefix per node length, 20 // because our len(ID) always 64 21 patricia.MaxPrefixPerNode = 64 22 } 23 24 // TruncIndex allows the retrieval of string identifiers by any of their unique prefixes. 25 // This is used to retrieve image and container IDs by more convenient shorthand prefixes. 26 type TruncIndex struct { 27 sync.RWMutex 28 trie *patricia.Trie 29 ids map[string]struct{} 30 } 31 32 // NewTruncIndex creates a new TruncIndex and initializes with a list of IDs 33 func NewTruncIndex(ids []string) (idx *TruncIndex) { 34 idx = &TruncIndex{ 35 ids: make(map[string]struct{}), 36 trie: patricia.NewTrie(), 37 } 38 for _, id := range ids { 39 idx.addID(id) 40 } 41 return 42 } 43 44 func (idx *TruncIndex) addID(id string) error { 45 if strings.Contains(id, " ") { 46 return fmt.Errorf("illegal character: ' '") 47 } 48 if id == "" { 49 return ErrNoID 50 } 51 if _, exists := idx.ids[id]; exists { 52 return fmt.Errorf("id already exists: '%s'", id) 53 } 54 idx.ids[id] = struct{}{} 55 if inserted := idx.trie.Insert(patricia.Prefix(id), struct{}{}); !inserted { 56 return fmt.Errorf("failed to insert id: %s", id) 57 } 58 return nil 59 } 60 61 // Add adds a new ID to the TruncIndex 62 func (idx *TruncIndex) Add(id string) error { 63 idx.Lock() 64 defer idx.Unlock() 65 if err := idx.addID(id); err != nil { 66 return err 67 } 68 return nil 69 } 70 71 // Delete removes an ID from the TruncIndex. If there are multiple IDs 72 // with the given prefix, an error is thrown. 73 func (idx *TruncIndex) Delete(id string) error { 74 idx.Lock() 75 defer idx.Unlock() 76 if _, exists := idx.ids[id]; !exists || id == "" { 77 return fmt.Errorf("no such id: '%s'", id) 78 } 79 delete(idx.ids, id) 80 if deleted := idx.trie.Delete(patricia.Prefix(id)); !deleted { 81 return fmt.Errorf("no such id: '%s'", id) 82 } 83 return nil 84 } 85 86 // Get retrieves an ID from the TruncIndex. If there are multiple IDs 87 // with the given prefix, an error is thrown. 88 func (idx *TruncIndex) Get(s string) (string, error) { 89 idx.RLock() 90 defer idx.RUnlock() 91 var ( 92 id string 93 ) 94 if s == "" { 95 return "", ErrNoID 96 } 97 subTreeVisitFunc := func(prefix patricia.Prefix, item patricia.Item) error { 98 if id != "" { 99 // we haven't found the ID if there are two or more IDs 100 id = "" 101 return errDuplicateID 102 } 103 id = string(prefix) 104 return nil 105 } 106 107 if err := idx.trie.VisitSubtree(patricia.Prefix(s), subTreeVisitFunc); err != nil { 108 return "", fmt.Errorf("no such id: %s", s) 109 } 110 if id != "" { 111 return id, nil 112 } 113 return "", fmt.Errorf("no such id: %s", s) 114 }