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