github.com/m3db/m3@v1.5.0/src/x/generics/hashmap/idkey/map_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 // This file was automatically generated by genny. 22 // Any changes will be lost if this file is regenerated. 23 // see https://github.com/mauricelam/genny 24 25 package idkey 26 27 import ( 28 "github.com/m3db/m3/src/x/ident" 29 ) 30 31 // Copyright (c) 2018 Uber Technologies, Inc. 32 // 33 // Permission is hereby granted, free of charge, to any person obtaining a copy 34 // of this software and associated documentation files (the "Software"), to deal 35 // in the Software without restriction, including without limitation the rights 36 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 37 // copies of the Software, and to permit persons to whom the Software is 38 // furnished to do so, subject to the following conditions: 39 // 40 // The above copyright notice and this permission notice shall be included in 41 // all copies or substantial portions of the Software. 42 // 43 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 44 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 45 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 46 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 47 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 48 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 49 // THE SOFTWARE. 50 51 // MapHash is the hash for a given map entry, this is public to support 52 // iterating over the map using a native Go for loop. 53 type MapHash uint64 54 55 // HashFn is the hash function to execute when hashing a key. 56 type HashFn func(ident.ID) MapHash 57 58 // EqualsFn is the equals key function to execute when detecting equality of a key. 59 type EqualsFn func(ident.ID, ident.ID) bool 60 61 // CopyFn is the copy key function to execute when copying the key. 62 type CopyFn func(ident.ID) ident.ID 63 64 // FinalizeFn is the finalize key function to execute when finished with a key. 65 type FinalizeFn func(ident.ID) 66 67 // Map uses the genny package to provide a generic hash map that can be specialized 68 // by running the following command from this root of the repository: 69 // ``` 70 // make hashmap-gen pkg=outpkg key_type=Type value_type=Type out_dir=/tmp 71 // ``` 72 // Or if you would like to use bytes or ident.ID as keys you can use the 73 // partially specialized maps to generate your own maps as well: 74 // ``` 75 // make byteshashmap-gen pkg=outpkg value_type=Type out_dir=/tmp 76 // make idhashmap-gen pkg=outpkg value_type=Type out_dir=/tmp 77 // ``` 78 // This will output to stdout the generated source file to use for your map. 79 // It uses linear probing by incrementing the number of the hash created when 80 // hashing the identifier if there is a collision. 81 // Map is a value type and not an interface to allow for less painful 82 // upgrades when adding/removing methods, it is not likely to need mocking so 83 // an interface would not be super useful either. 84 type Map struct { 85 mapOptions 86 87 // lookup uses hash of the identifier for the key and the MapEntry value 88 // wraps the value type and the key (used to ensure lookup is correct 89 // when dealing with collisions), we use uint64 for the hash partially 90 // because lookups of maps with uint64 keys has a fast path for Go. 91 lookup map[MapHash]MapEntry 92 } 93 94 // mapOptions is a set of options used when creating an identifier map, it is kept 95 // private so that implementers of the generated map can specify their own options 96 // that partially fulfill these options. 97 type mapOptions struct { 98 // hash is the hash function to execute when hashing a key. 99 hash HashFn 100 // equals is the equals key function to execute when detecting equality. 101 equals EqualsFn 102 // copy is the copy key function to execute when copying the key. 103 copy CopyFn 104 // finalize is the finalize key function to execute when finished with a 105 // key, this is optional to specify. 106 finalize FinalizeFn 107 // initialSize is the initial size for the map, use zero to use Go's std map 108 // initial size and consequently is optional to specify. 109 initialSize int 110 } 111 112 // MapEntry is an entry in the map, this is public to support iterating 113 // over the map using a native Go for loop. 114 type MapEntry struct { 115 // key is used to check equality on lookups to resolve collisions 116 key mapKey 117 // value type stored 118 value MapValue 119 } 120 121 type mapKey struct { 122 key ident.ID 123 finalize bool 124 } 125 126 // Key returns the map entry key. 127 func (e MapEntry) Key() ident.ID { 128 return e.key.key 129 } 130 131 // Value returns the map entry value. 132 func (e MapEntry) Value() MapValue { 133 return e.value 134 } 135 136 // mapAlloc is a non-exported function so that when generating the source code 137 // for the map you can supply a public constructor that sets the correct 138 // hash, equals, copy, finalize options without users of the map needing to 139 // implement them themselves. 140 func mapAlloc(opts mapOptions) *Map { 141 m := &Map{mapOptions: opts} 142 m.Reallocate() 143 return m 144 } 145 146 func (m *Map) newMapKey(k ident.ID, opts mapKeyOptions) mapKey { 147 key := mapKey{key: k, finalize: opts.finalizeKey} 148 if !opts.copyKey { 149 return key 150 } 151 152 key.key = m.copy(k) 153 return key 154 } 155 156 func (m *Map) removeMapKey(hash MapHash, key mapKey) { 157 delete(m.lookup, hash) 158 if key.finalize { 159 m.finalize(key.key) 160 } 161 } 162 163 // Get returns a value in the map for an identifier if found. 164 func (m *Map) Get(k ident.ID) (MapValue, bool) { 165 hash := m.hash(k) 166 for entry, ok := m.lookup[hash]; ok; entry, ok = m.lookup[hash] { 167 if m.equals(entry.key.key, k) { 168 return entry.value, true 169 } 170 // Linear probe to "next" to this entry (really a rehash) 171 hash++ 172 } 173 var empty MapValue 174 return empty, false 175 } 176 177 // Set will set the value for an identifier. 178 func (m *Map) Set(k ident.ID, v MapValue) { 179 m.set(k, v, mapKeyOptions{ 180 copyKey: true, 181 finalizeKey: m.finalize != nil, 182 }) 183 } 184 185 // SetUnsafeOptions is a set of options to use when setting a value with 186 // the SetUnsafe method. 187 type SetUnsafeOptions struct { 188 NoCopyKey bool 189 NoFinalizeKey bool 190 } 191 192 // SetUnsafe will set the value for an identifier with unsafe options for how 193 // the map treats the key. 194 func (m *Map) SetUnsafe(k ident.ID, v MapValue, opts SetUnsafeOptions) { 195 m.set(k, v, mapKeyOptions{ 196 copyKey: !opts.NoCopyKey, 197 finalizeKey: !opts.NoFinalizeKey, 198 }) 199 } 200 201 type mapKeyOptions struct { 202 copyKey bool 203 finalizeKey bool 204 } 205 206 func (m *Map) set(k ident.ID, v MapValue, opts mapKeyOptions) { 207 hash := m.hash(k) 208 for entry, ok := m.lookup[hash]; ok; entry, ok = m.lookup[hash] { 209 if m.equals(entry.key.key, k) { 210 m.lookup[hash] = MapEntry{ 211 key: entry.key, 212 value: v, 213 } 214 return 215 } 216 // Linear probe to "next" to this entry (really a rehash) 217 hash++ 218 } 219 220 m.lookup[hash] = MapEntry{ 221 key: m.newMapKey(k, opts), 222 value: v, 223 } 224 } 225 226 // Iter provides the underlying map to allow for using a native Go for loop 227 // to iterate the map, however callers should only ever read and not write 228 // the map. 229 func (m *Map) Iter() map[MapHash]MapEntry { 230 return m.lookup 231 } 232 233 // Len returns the number of map entries in the map. 234 func (m *Map) Len() int { 235 return len(m.lookup) 236 } 237 238 // Contains returns true if value exists for key, false otherwise, it is 239 // shorthand for a call to Get that doesn't return the value. 240 func (m *Map) Contains(k ident.ID) bool { 241 _, ok := m.Get(k) 242 return ok 243 } 244 245 // Delete will remove a value set in the map for the specified key. 246 func (m *Map) Delete(k ident.ID) { 247 hash := m.hash(k) 248 for entry, ok := m.lookup[hash]; ok; entry, ok = m.lookup[hash] { 249 if m.equals(entry.key.key, k) { 250 m.removeMapKey(hash, entry.key) 251 return 252 } 253 // Linear probe to "next" to this entry (really a rehash) 254 hash++ 255 } 256 } 257 258 // Reset will reset the map by simply deleting all keys to avoid 259 // allocating a new map. 260 func (m *Map) Reset() { 261 for hash, entry := range m.lookup { 262 m.removeMapKey(hash, entry.key) 263 } 264 } 265 266 // Reallocate will avoid deleting all keys and reallocate a new 267 // map, this is useful if you believe you have a large map and 268 // will not need to grow back to a similar size. 269 func (m *Map) Reallocate() { 270 if m.initialSize > 0 { 271 m.lookup = make(map[MapHash]MapEntry, m.initialSize) 272 } else { 273 m.lookup = make(map[MapHash]MapEntry) 274 } 275 }