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