github.com/golang/dep@v0.5.4/gps/typed_radix.go (about) 1 // Copyright 2017 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package gps 6 7 import ( 8 "strings" 9 "sync" 10 11 "github.com/armon/go-radix" 12 ) 13 14 // Typed implementations of radix trees. These are just simple wrappers that let 15 // us avoid having to type assert anywhere else, cleaning up other code a bit. 16 // 17 // Some of the more annoying things to implement (like walks) aren't 18 // implemented. They can be added if/when we actually need them. 19 // 20 // Oh generics, where art thou... 21 22 type deducerTrie struct { 23 sync.RWMutex 24 t *radix.Tree 25 } 26 27 func newDeducerTrie() *deducerTrie { 28 return &deducerTrie{ 29 t: radix.New(), 30 } 31 } 32 33 // Suppress unused warning. 34 var _ = (*deducerTrie)(nil).Delete 35 36 // Delete is used to delete a key, returning the previous value and if it was deleted 37 func (t *deducerTrie) Delete(s string) (pathDeducer, bool) { 38 t.Lock() 39 defer t.Unlock() 40 if d, had := t.t.Delete(s); had { 41 return d.(pathDeducer), had 42 } 43 return nil, false 44 } 45 46 // Insert is used to add a newentry or update an existing entry. Returns if updated. 47 func (t *deducerTrie) Insert(s string, d pathDeducer) (pathDeducer, bool) { 48 t.Lock() 49 defer t.Unlock() 50 if d2, had := t.t.Insert(s, d); had { 51 return d2.(pathDeducer), had 52 } 53 return nil, false 54 } 55 56 // LongestPrefix is like Get, but instead of an exact match, it will return the 57 // longest prefix match. 58 func (t *deducerTrie) LongestPrefix(s string) (string, pathDeducer, bool) { 59 t.RLock() 60 defer t.RUnlock() 61 if p, d, has := t.t.LongestPrefix(s); has { 62 return p, d.(pathDeducer), has 63 } 64 return "", nil, false 65 } 66 67 // isPathPrefixOrEqual is an additional helper check to ensure that the literal 68 // string prefix returned from a radix tree prefix match is also a path tree 69 // match. 70 // 71 // The radix tree gets it mostly right, but we have to guard against 72 // possibilities like this: 73 // 74 // github.com/sdboyer/foo 75 // github.com/sdboyer/foobar/baz 76 // 77 // The latter would incorrectly be conflated with the former. As we know we're 78 // operating on strings that describe import paths, guard against this case by 79 // verifying that either the input is the same length as the match (in which 80 // case we know they're equal), or that the next character is a "/". (Import 81 // paths are defined to always use "/", not the OS-specific path separator.) 82 func isPathPrefixOrEqual(pre, path string) bool { 83 prflen, pathlen := len(pre), len(path) 84 if pathlen == prflen+1 { 85 // this can never be the case 86 return false 87 } 88 89 // we assume something else (a trie) has done equality check up to the point 90 // of the prefix, so we just check len 91 return prflen == pathlen || strings.Index(path[prflen:], "/") == 0 92 }