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