github.com/moontrade/nogc@v0.1.7/collections/rhmap/map_int64_ptr.go (about)

     1  package rhmap
     2  
     3  import (
     4  	. "github.com/moontrade/nogc"
     5  	"github.com/moontrade/nogc/hash"
     6  	"unsafe"
     7  )
     8  
     9  // MapInt64 is a hashset that uses the robinhood algorithm. This
    10  // implementation is not concurrent safe.
    11  type MapInt64 struct {
    12  	// items are the slots of the hashmap for items.
    13  	items Pointer
    14  	end   uintptr
    15  	size  uintptr
    16  
    17  	// Number of keys in the Map.
    18  	count uintptr
    19  
    20  	// When any item's distance gets too large, Grow the Map.
    21  	// Defaults to 10.
    22  	maxDistance int32
    23  	growBy      int32
    24  	// Number of hash slots to Grow by
    25  	growthFactor float32
    26  
    27  	freeOnClose bool
    28  }
    29  
    30  // Item represents an entry in the Map.
    31  type mapInt64Item struct {
    32  	key      int64
    33  	value    uintptr
    34  	distance int32 // How far item is from its best position.
    35  }
    36  
    37  // NewMapInt64 returns a new robinhood hashmap.
    38  //goland:noinspection ALL
    39  func NewMapInt64(size uintptr) MapInt64 {
    40  	//items := AllocZeroed(unsafe.Sizeof(mapInt64Item{}) * size)
    41  	items := Calloc(uintptr(size), unsafe.Sizeof(mapInt64Item{}))
    42  	return MapInt64{
    43  		items:        items,
    44  		size:         size,
    45  		end:          uintptr(items) + (size * unsafe.Sizeof(mapInt64Item{})),
    46  		maxDistance:  10,
    47  		growBy:       64,
    48  		growthFactor: 2.0,
    49  	}
    50  }
    51  
    52  //goland:noinspection GoVetUnsafePointer
    53  func (ps *MapInt64) Close() error {
    54  	if ps == nil {
    55  		return nil
    56  	}
    57  	if ps.items == 0 {
    58  		return nil
    59  	}
    60  
    61  	Free(ps.items)
    62  	ps.items = 0
    63  	return nil
    64  }
    65  
    66  // Reset clears Map, where already allocated memory will be reused.
    67  //goland:noinspection ALL
    68  func (ps *MapInt64) Reset() {
    69  	ps.items.Zero(unsafe.Sizeof(mapInt64Item{}) * ps.size)
    70  	ps.count = 0
    71  }
    72  
    73  //goland:noinspection GoVetUnsafePointer
    74  func (ps *MapInt64) isCollision(key int64) bool {
    75  	return *(*uintptr)(unsafe.Pointer(
    76  		uintptr(ps.items) + (uintptr(hash.FNV64a(uint64(key))%uint64(ps.size)) * unsafe.Sizeof(mapInt64Item{})))) != 0
    77  }
    78  
    79  // Has returns whether the key exists in the Add.
    80  //goland:noinspection ALL
    81  func (ps *MapInt64) Has(key int64) bool {
    82  	var (
    83  		idx      = uintptr(ps.items) + (uintptr(hash.FNV64a(uint64(key))%uint64(ps.size)) * unsafe.Sizeof(mapInt64Item{}))
    84  		idxStart = idx
    85  	)
    86  	for {
    87  		entry := (*mapInt64Item)(unsafe.Pointer(idx))
    88  		if entry == nil {
    89  			return false
    90  		}
    91  		if entry.key == key {
    92  			return true
    93  		}
    94  		idx += unsafe.Sizeof(mapInt64Item{})
    95  		if idx >= ps.end {
    96  			idx = uintptr(ps.items)
    97  		}
    98  		// Went all the way around?
    99  		if idx == idxStart {
   100  			return false
   101  		}
   102  	}
   103  }
   104  
   105  // Get returns whether the key exists in the Add.
   106  //goland:noinspection ALL
   107  func (ps *MapInt64) Get(key int64) (uintptr, bool) {
   108  	var (
   109  		idx      = uintptr(ps.items) + (uintptr(hash.FNV64a(uint64(key))%uint64(ps.size)) * unsafe.Sizeof(mapInt64Item{}))
   110  		idxStart = idx
   111  	)
   112  	for {
   113  		entry := (*mapInt64Item)(unsafe.Pointer(idx))
   114  		if entry.key == key {
   115  			return entry.value, true
   116  		}
   117  		idx += unsafe.Sizeof(mapInt64Item{})
   118  		if idx >= ps.end {
   119  			idx = uintptr(ps.items)
   120  		}
   121  		// Went all the way around?
   122  		if idx == idxStart {
   123  			return 0, false
   124  		}
   125  	}
   126  }
   127  
   128  func (ps *MapInt64) Set(key int64, value uintptr) (uintptr, bool, bool) {
   129  	return ps.set(key, value, 0)
   130  }
   131  
   132  // Set inserts or updates a key into the Map. The returned
   133  // wasNew will be true if the mutation was on a newly seen, inserted
   134  // key, and wasNew will be false if the mutation was an update to an
   135  // existing key.
   136  //goland:noinspection GoVetUnsafePointer
   137  func (ps *MapInt64) set(key int64, value uintptr, depth int) (uintptr, bool, bool) {
   138  	var (
   139  		idx      = uintptr(ps.items) + (uintptr(hash.FNV64a(uint64(key))%uint64(ps.size)) * unsafe.Sizeof(mapInt64Item{}))
   140  		idxStart = idx
   141  		incoming = mapInt64Item{key, value, 0}
   142  	)
   143  	for {
   144  		entry := (*mapInt64Item)(unsafe.Pointer(idx))
   145  		//if entry.key == 0 {
   146  		//	*(*mapInt64Item)(unsafe.Pointer(idx)) = incoming
   147  		//	ps.count++
   148  		//	return 0, true, true
   149  		//}
   150  
   151  		if entry.key == incoming.key {
   152  			old := entry.value
   153  			entry.value = incoming.value
   154  			entry.distance = incoming.distance
   155  			return old, false, true
   156  		}
   157  
   158  		// Swap if the incoming item is further from its best idx.
   159  		if entry.distance < incoming.distance {
   160  			incoming, *(*mapInt64Item)(unsafe.Pointer(idx)) = *(*mapInt64Item)(unsafe.Pointer(idx)), incoming
   161  		}
   162  
   163  		// One step further away from best idx.
   164  		incoming.distance++
   165  
   166  		idx += unsafe.Sizeof(mapInt64Item{})
   167  		if idx >= ps.end {
   168  			idx = uintptr(ps.items)
   169  		}
   170  
   171  		// Grow if distances become big or we went all the way around.
   172  		if incoming.distance > ps.maxDistance || idx == idxStart {
   173  			if depth > 5 {
   174  				return 0, false, false
   175  			}
   176  			if !ps.Grow() {
   177  				return 0, false, false
   178  			}
   179  			return ps.set(incoming.key, incoming.value, depth+1)
   180  		}
   181  	}
   182  }
   183  
   184  // Delete removes a key from the Map.
   185  //goland:noinspection GoVetUnsafePointer
   186  func (ps *MapInt64) Delete(key int64) (uintptr, bool) {
   187  	var (
   188  		idx      = uintptr(ps.items) + (uintptr(hash.FNV64a(uint64(key))%uint64(ps.size)) * unsafe.Sizeof(mapInt64Item{}))
   189  		idxStart = idx
   190  		prev     uintptr
   191  	)
   192  	for {
   193  		entry := (*mapInt64Item)(unsafe.Pointer(idx))
   194  
   195  		if entry.key == key {
   196  			// Found the item.
   197  			prev = entry.value
   198  			break
   199  		}
   200  		idx += unsafe.Sizeof(mapInt64Item{})
   201  		if idx >= ps.end {
   202  			idx = uintptr(ps.items)
   203  		}
   204  		if idx == idxStart {
   205  			return 0, false
   206  		}
   207  	}
   208  	// Left-shift succeeding items in the linear chain.
   209  	for {
   210  		next := idx + unsafe.Sizeof(mapInt64Item{})
   211  		if next >= ps.end {
   212  			next = uintptr(ps.items)
   213  		}
   214  		// Went all the way around?
   215  		if next == idx {
   216  			break
   217  		}
   218  		f := (*mapInt64Item)(unsafe.Pointer(next))
   219  		if f.key == 0 || f.distance <= 0 {
   220  			break
   221  		}
   222  		f.distance--
   223  		*(*mapInt64Item)(unsafe.Pointer(idx)) = *f
   224  		idx = next
   225  	}
   226  	// Clear entry
   227  	*(*mapInt64Item)(unsafe.Pointer(idx)) = mapInt64Item{}
   228  	ps.count--
   229  	return prev, true
   230  }
   231  
   232  //goland:noinspection GoVetUnsafePointer
   233  func (ps *MapInt64) Grow() bool {
   234  	// Calculate new size
   235  	// ps.size + 128
   236  	if ps.growthFactor <= 1.0 {
   237  		ps.growthFactor = 2.0
   238  	}
   239  	//newSize := uintptr(float32(ps.size) * ps.growthFactor)
   240  	newSize := ps.size * 2
   241  
   242  	if _TRACE {
   243  		println("Map.Grow", "newSize", uint(newSize), "oldSize", uint(ps.size))
   244  	}
   245  
   246  	// Allocate new items table
   247  	items := Calloc(newSize, unsafe.Sizeof(mapInt64Item{}))
   248  	//items := AllocZeroed(newSize * unsafe.Sizeof(mapInt64Item{}))
   249  	// Calculate end
   250  	itemsEnd := uintptr(items) + (newSize * unsafe.Sizeof(mapInt64Item{}))
   251  	// Init next structure
   252  	next := MapInt64{
   253  		items:        items,
   254  		size:         newSize,
   255  		end:          itemsEnd,
   256  		count:        0,
   257  		growthFactor: ps.growthFactor,
   258  		maxDistance:  ps.maxDistance,
   259  	}
   260  
   261  	// Add all entries from old to next
   262  	var (
   263  		success bool
   264  		item    *mapInt64Item
   265  	)
   266  	for i := uintptr(ps.items); i < ps.end; i += unsafe.Sizeof(mapInt64Item{}) {
   267  		item = (*mapInt64Item)(unsafe.Pointer(i))
   268  		if _, _, success = next.set(item.key, item.value, 0); !success {
   269  			return false
   270  		}
   271  	}
   272  
   273  	// Free old items
   274  	Free(ps.items)
   275  	ps.items = 0
   276  
   277  	// Update to next
   278  	*ps = next
   279  	return true
   280  }