github.com/uber-go/tally/v4@v4.1.17/key_gen.go (about) 1 // Copyright (c) 2021 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package tally 22 23 const ( 24 prefixSplitter = '+' 25 keyPairSplitter = ',' 26 keyNameSplitter = '=' 27 nilString = "" 28 ) 29 30 // KeyForStringMap generates a unique key for a map string set combination. 31 func KeyForStringMap( 32 stringMap map[string]string, 33 ) string { 34 return KeyForPrefixedStringMap(nilString, stringMap) 35 } 36 37 // KeyForPrefixedStringMap generates a unique key for a 38 // a prefix and a map string set combination. 39 func KeyForPrefixedStringMap( 40 prefix string, 41 stringMap map[string]string, 42 ) string { 43 return keyForPrefixedStringMaps(prefix, stringMap) 44 } 45 46 // keyForPrefixedStringMapsAsKey writes a key using the prefix and the tags in a canonical form to 47 // the given input byte slice and returns a reference to the byte slice. Callers of this method can 48 // use a stack allocated byte slice to remove heap allocation. 49 func keyForPrefixedStringMapsAsKey(buf []byte, prefix string, maps ...map[string]string) []byte { 50 // stack allocated 51 keys := make([]string, 0, 32) 52 for _, m := range maps { 53 for k := range m { 54 keys = append(keys, k) 55 } 56 } 57 58 insertionSort(keys) 59 60 if prefix != nilString { 61 buf = append(buf, prefix...) 62 buf = append(buf, prefixSplitter) 63 } 64 65 var lastKey string // last key written to the buffer 66 for _, k := range keys { 67 if len(lastKey) > 0 { 68 if k == lastKey { 69 // Already wrote this key. 70 continue 71 } 72 buf = append(buf, keyPairSplitter) 73 } 74 lastKey = k 75 76 buf = append(buf, k...) 77 buf = append(buf, keyNameSplitter) 78 79 // Find and write the value for this key. Rightmost map takes 80 // precedence. 81 for j := len(maps) - 1; j >= 0; j-- { 82 if v, ok := maps[j][k]; ok { 83 buf = append(buf, v...) 84 break 85 } 86 } 87 } 88 89 return buf 90 } 91 92 // keyForPrefixedStringMaps generates a unique key for a prefix and a series 93 // of maps containing tags. 94 // 95 // If a key occurs in multiple maps, keys on the right take precedence. 96 func keyForPrefixedStringMaps(prefix string, maps ...map[string]string) string { 97 return string(keyForPrefixedStringMapsAsKey(make([]byte, 0, 256), prefix, maps...)) 98 } 99 100 func insertionSort(keys []string) { 101 n := len(keys) 102 for i := 1; i < n; i++ { 103 for j := i; j > 0 && keys[j] < keys[j-1]; j-- { 104 keys[j], keys[j-1] = keys[j-1], keys[j] 105 } 106 } 107 }