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

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