rsc.io/go@v0.0.0-20150416155037-e040fd465409/src/runtime/hashmap_fast.go (about)

     1  // Copyright 2014 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package runtime
     6  
     7  import (
     8  	"unsafe"
     9  )
    10  
    11  func mapaccess1_fast32(t *maptype, h *hmap, key uint32) unsafe.Pointer {
    12  	if raceenabled && h != nil {
    13  		callerpc := getcallerpc(unsafe.Pointer(&t))
    14  		racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess1_fast32))
    15  	}
    16  	if h == nil || h.count == 0 {
    17  		return unsafe.Pointer(t.elem.zero)
    18  	}
    19  	var b *bmap
    20  	if h.B == 0 {
    21  		// One-bucket table.  No need to hash.
    22  		b = (*bmap)(h.buckets)
    23  	} else {
    24  		hash := t.key.alg.hash(noescape(unsafe.Pointer(&key)), uintptr(h.hash0))
    25  		m := uintptr(1)<<h.B - 1
    26  		b = (*bmap)(add(h.buckets, (hash&m)*uintptr(t.bucketsize)))
    27  		if c := h.oldbuckets; c != nil {
    28  			oldb := (*bmap)(add(c, (hash&(m>>1))*uintptr(t.bucketsize)))
    29  			if !evacuated(oldb) {
    30  				b = oldb
    31  			}
    32  		}
    33  	}
    34  	for {
    35  		for i := uintptr(0); i < bucketCnt; i++ {
    36  			k := *((*uint32)(add(unsafe.Pointer(b), dataOffset+i*4)))
    37  			if k != key {
    38  				continue
    39  			}
    40  			x := *((*uint8)(add(unsafe.Pointer(b), i))) // b.topbits[i] without the bounds check
    41  			if x == empty {
    42  				continue
    43  			}
    44  			return add(unsafe.Pointer(b), dataOffset+bucketCnt*4+i*uintptr(t.valuesize))
    45  		}
    46  		b = b.overflow(t)
    47  		if b == nil {
    48  			return unsafe.Pointer(t.elem.zero)
    49  		}
    50  	}
    51  }
    52  
    53  func mapaccess2_fast32(t *maptype, h *hmap, key uint32) (unsafe.Pointer, bool) {
    54  	if raceenabled && h != nil {
    55  		callerpc := getcallerpc(unsafe.Pointer(&t))
    56  		racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess2_fast32))
    57  	}
    58  	if h == nil || h.count == 0 {
    59  		return unsafe.Pointer(t.elem.zero), false
    60  	}
    61  	var b *bmap
    62  	if h.B == 0 {
    63  		// One-bucket table.  No need to hash.
    64  		b = (*bmap)(h.buckets)
    65  	} else {
    66  		hash := t.key.alg.hash(noescape(unsafe.Pointer(&key)), uintptr(h.hash0))
    67  		m := uintptr(1)<<h.B - 1
    68  		b = (*bmap)(add(h.buckets, (hash&m)*uintptr(t.bucketsize)))
    69  		if c := h.oldbuckets; c != nil {
    70  			oldb := (*bmap)(add(c, (hash&(m>>1))*uintptr(t.bucketsize)))
    71  			if !evacuated(oldb) {
    72  				b = oldb
    73  			}
    74  		}
    75  	}
    76  	for {
    77  		for i := uintptr(0); i < bucketCnt; i++ {
    78  			k := *((*uint32)(add(unsafe.Pointer(b), dataOffset+i*4)))
    79  			if k != key {
    80  				continue
    81  			}
    82  			x := *((*uint8)(add(unsafe.Pointer(b), i))) // b.topbits[i] without the bounds check
    83  			if x == empty {
    84  				continue
    85  			}
    86  			return add(unsafe.Pointer(b), dataOffset+bucketCnt*4+i*uintptr(t.valuesize)), true
    87  		}
    88  		b = b.overflow(t)
    89  		if b == nil {
    90  			return unsafe.Pointer(t.elem.zero), false
    91  		}
    92  	}
    93  }
    94  
    95  func mapaccess1_fast64(t *maptype, h *hmap, key uint64) unsafe.Pointer {
    96  	if raceenabled && h != nil {
    97  		callerpc := getcallerpc(unsafe.Pointer(&t))
    98  		racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess1_fast64))
    99  	}
   100  	if h == nil || h.count == 0 {
   101  		return unsafe.Pointer(t.elem.zero)
   102  	}
   103  	var b *bmap
   104  	if h.B == 0 {
   105  		// One-bucket table.  No need to hash.
   106  		b = (*bmap)(h.buckets)
   107  	} else {
   108  		hash := t.key.alg.hash(noescape(unsafe.Pointer(&key)), uintptr(h.hash0))
   109  		m := uintptr(1)<<h.B - 1
   110  		b = (*bmap)(add(h.buckets, (hash&m)*uintptr(t.bucketsize)))
   111  		if c := h.oldbuckets; c != nil {
   112  			oldb := (*bmap)(add(c, (hash&(m>>1))*uintptr(t.bucketsize)))
   113  			if !evacuated(oldb) {
   114  				b = oldb
   115  			}
   116  		}
   117  	}
   118  	for {
   119  		for i := uintptr(0); i < bucketCnt; i++ {
   120  			k := *((*uint64)(add(unsafe.Pointer(b), dataOffset+i*8)))
   121  			if k != key {
   122  				continue
   123  			}
   124  			x := *((*uint8)(add(unsafe.Pointer(b), i))) // b.topbits[i] without the bounds check
   125  			if x == empty {
   126  				continue
   127  			}
   128  			return add(unsafe.Pointer(b), dataOffset+bucketCnt*8+i*uintptr(t.valuesize))
   129  		}
   130  		b = b.overflow(t)
   131  		if b == nil {
   132  			return unsafe.Pointer(t.elem.zero)
   133  		}
   134  	}
   135  }
   136  
   137  func mapaccess2_fast64(t *maptype, h *hmap, key uint64) (unsafe.Pointer, bool) {
   138  	if raceenabled && h != nil {
   139  		callerpc := getcallerpc(unsafe.Pointer(&t))
   140  		racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess2_fast64))
   141  	}
   142  	if h == nil || h.count == 0 {
   143  		return unsafe.Pointer(t.elem.zero), false
   144  	}
   145  	var b *bmap
   146  	if h.B == 0 {
   147  		// One-bucket table.  No need to hash.
   148  		b = (*bmap)(h.buckets)
   149  	} else {
   150  		hash := t.key.alg.hash(noescape(unsafe.Pointer(&key)), uintptr(h.hash0))
   151  		m := uintptr(1)<<h.B - 1
   152  		b = (*bmap)(add(h.buckets, (hash&m)*uintptr(t.bucketsize)))
   153  		if c := h.oldbuckets; c != nil {
   154  			oldb := (*bmap)(add(c, (hash&(m>>1))*uintptr(t.bucketsize)))
   155  			if !evacuated(oldb) {
   156  				b = oldb
   157  			}
   158  		}
   159  	}
   160  	for {
   161  		for i := uintptr(0); i < bucketCnt; i++ {
   162  			k := *((*uint64)(add(unsafe.Pointer(b), dataOffset+i*8)))
   163  			if k != key {
   164  				continue
   165  			}
   166  			x := *((*uint8)(add(unsafe.Pointer(b), i))) // b.topbits[i] without the bounds check
   167  			if x == empty {
   168  				continue
   169  			}
   170  			return add(unsafe.Pointer(b), dataOffset+bucketCnt*8+i*uintptr(t.valuesize)), true
   171  		}
   172  		b = b.overflow(t)
   173  		if b == nil {
   174  			return unsafe.Pointer(t.elem.zero), false
   175  		}
   176  	}
   177  }
   178  
   179  func mapaccess1_faststr(t *maptype, h *hmap, ky string) unsafe.Pointer {
   180  	if raceenabled && h != nil {
   181  		callerpc := getcallerpc(unsafe.Pointer(&t))
   182  		racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess1_faststr))
   183  	}
   184  	if h == nil || h.count == 0 {
   185  		return unsafe.Pointer(t.elem.zero)
   186  	}
   187  	key := (*stringStruct)(unsafe.Pointer(&ky))
   188  	if h.B == 0 {
   189  		// One-bucket table.
   190  		b := (*bmap)(h.buckets)
   191  		if key.len < 32 {
   192  			// short key, doing lots of comparisons is ok
   193  			for i := uintptr(0); i < bucketCnt; i++ {
   194  				x := *((*uint8)(add(unsafe.Pointer(b), i))) // b.topbits[i] without the bounds check
   195  				if x == empty {
   196  					continue
   197  				}
   198  				k := (*stringStruct)(add(unsafe.Pointer(b), dataOffset+i*2*ptrSize))
   199  				if k.len != key.len {
   200  					continue
   201  				}
   202  				if k.str == key.str || memeq(k.str, key.str, uintptr(key.len)) {
   203  					return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*ptrSize+i*uintptr(t.valuesize))
   204  				}
   205  			}
   206  			return unsafe.Pointer(t.elem.zero)
   207  		}
   208  		// long key, try not to do more comparisons than necessary
   209  		keymaybe := uintptr(bucketCnt)
   210  		for i := uintptr(0); i < bucketCnt; i++ {
   211  			x := *((*uint8)(add(unsafe.Pointer(b), i))) // b.topbits[i] without the bounds check
   212  			if x == empty {
   213  				continue
   214  			}
   215  			k := (*stringStruct)(add(unsafe.Pointer(b), dataOffset+i*2*ptrSize))
   216  			if k.len != key.len {
   217  				continue
   218  			}
   219  			if k.str == key.str {
   220  				return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*ptrSize+i*uintptr(t.valuesize))
   221  			}
   222  			// check first 4 bytes
   223  			// TODO: on amd64/386 at least, make this compile to one 4-byte comparison instead of
   224  			// four 1-byte comparisons.
   225  			if *((*[4]byte)(key.str)) != *((*[4]byte)(k.str)) {
   226  				continue
   227  			}
   228  			// check last 4 bytes
   229  			if *((*[4]byte)(add(key.str, uintptr(key.len)-4))) != *((*[4]byte)(add(k.str, uintptr(key.len)-4))) {
   230  				continue
   231  			}
   232  			if keymaybe != bucketCnt {
   233  				// Two keys are potential matches.  Use hash to distinguish them.
   234  				goto dohash
   235  			}
   236  			keymaybe = i
   237  		}
   238  		if keymaybe != bucketCnt {
   239  			k := (*stringStruct)(add(unsafe.Pointer(b), dataOffset+keymaybe*2*ptrSize))
   240  			if memeq(k.str, key.str, uintptr(key.len)) {
   241  				return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*ptrSize+keymaybe*uintptr(t.valuesize))
   242  			}
   243  		}
   244  		return unsafe.Pointer(t.elem.zero)
   245  	}
   246  dohash:
   247  	hash := t.key.alg.hash(noescape(unsafe.Pointer(&ky)), uintptr(h.hash0))
   248  	m := uintptr(1)<<h.B - 1
   249  	b := (*bmap)(add(h.buckets, (hash&m)*uintptr(t.bucketsize)))
   250  	if c := h.oldbuckets; c != nil {
   251  		oldb := (*bmap)(add(c, (hash&(m>>1))*uintptr(t.bucketsize)))
   252  		if !evacuated(oldb) {
   253  			b = oldb
   254  		}
   255  	}
   256  	top := uint8(hash >> (ptrSize*8 - 8))
   257  	if top < minTopHash {
   258  		top += minTopHash
   259  	}
   260  	for {
   261  		for i := uintptr(0); i < bucketCnt; i++ {
   262  			x := *((*uint8)(add(unsafe.Pointer(b), i))) // b.topbits[i] without the bounds check
   263  			if x != top {
   264  				continue
   265  			}
   266  			k := (*stringStruct)(add(unsafe.Pointer(b), dataOffset+i*2*ptrSize))
   267  			if k.len != key.len {
   268  				continue
   269  			}
   270  			if k.str == key.str || memeq(k.str, key.str, uintptr(key.len)) {
   271  				return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*ptrSize+i*uintptr(t.valuesize))
   272  			}
   273  		}
   274  		b = b.overflow(t)
   275  		if b == nil {
   276  			return unsafe.Pointer(t.elem.zero)
   277  		}
   278  	}
   279  }
   280  
   281  func mapaccess2_faststr(t *maptype, h *hmap, ky string) (unsafe.Pointer, bool) {
   282  	if raceenabled && h != nil {
   283  		callerpc := getcallerpc(unsafe.Pointer(&t))
   284  		racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapaccess2_faststr))
   285  	}
   286  	if h == nil || h.count == 0 {
   287  		return unsafe.Pointer(t.elem.zero), false
   288  	}
   289  	key := (*stringStruct)(unsafe.Pointer(&ky))
   290  	if h.B == 0 {
   291  		// One-bucket table.
   292  		b := (*bmap)(h.buckets)
   293  		if key.len < 32 {
   294  			// short key, doing lots of comparisons is ok
   295  			for i := uintptr(0); i < bucketCnt; i++ {
   296  				x := *((*uint8)(add(unsafe.Pointer(b), i))) // b.topbits[i] without the bounds check
   297  				if x == empty {
   298  					continue
   299  				}
   300  				k := (*stringStruct)(add(unsafe.Pointer(b), dataOffset+i*2*ptrSize))
   301  				if k.len != key.len {
   302  					continue
   303  				}
   304  				if k.str == key.str || memeq(k.str, key.str, uintptr(key.len)) {
   305  					return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*ptrSize+i*uintptr(t.valuesize)), true
   306  				}
   307  			}
   308  			return unsafe.Pointer(t.elem.zero), false
   309  		}
   310  		// long key, try not to do more comparisons than necessary
   311  		keymaybe := uintptr(bucketCnt)
   312  		for i := uintptr(0); i < bucketCnt; i++ {
   313  			x := *((*uint8)(add(unsafe.Pointer(b), i))) // b.topbits[i] without the bounds check
   314  			if x == empty {
   315  				continue
   316  			}
   317  			k := (*stringStruct)(add(unsafe.Pointer(b), dataOffset+i*2*ptrSize))
   318  			if k.len != key.len {
   319  				continue
   320  			}
   321  			if k.str == key.str {
   322  				return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*ptrSize+i*uintptr(t.valuesize)), true
   323  			}
   324  			// check first 4 bytes
   325  			if *((*[4]byte)(key.str)) != *((*[4]byte)(k.str)) {
   326  				continue
   327  			}
   328  			// check last 4 bytes
   329  			if *((*[4]byte)(add(key.str, uintptr(key.len)-4))) != *((*[4]byte)(add(k.str, uintptr(key.len)-4))) {
   330  				continue
   331  			}
   332  			if keymaybe != bucketCnt {
   333  				// Two keys are potential matches.  Use hash to distinguish them.
   334  				goto dohash
   335  			}
   336  			keymaybe = i
   337  		}
   338  		if keymaybe != bucketCnt {
   339  			k := (*stringStruct)(add(unsafe.Pointer(b), dataOffset+keymaybe*2*ptrSize))
   340  			if memeq(k.str, key.str, uintptr(key.len)) {
   341  				return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*ptrSize+keymaybe*uintptr(t.valuesize)), true
   342  			}
   343  		}
   344  		return unsafe.Pointer(t.elem.zero), false
   345  	}
   346  dohash:
   347  	hash := t.key.alg.hash(noescape(unsafe.Pointer(&ky)), uintptr(h.hash0))
   348  	m := uintptr(1)<<h.B - 1
   349  	b := (*bmap)(add(h.buckets, (hash&m)*uintptr(t.bucketsize)))
   350  	if c := h.oldbuckets; c != nil {
   351  		oldb := (*bmap)(add(c, (hash&(m>>1))*uintptr(t.bucketsize)))
   352  		if !evacuated(oldb) {
   353  			b = oldb
   354  		}
   355  	}
   356  	top := uint8(hash >> (ptrSize*8 - 8))
   357  	if top < minTopHash {
   358  		top += minTopHash
   359  	}
   360  	for {
   361  		for i := uintptr(0); i < bucketCnt; i++ {
   362  			x := *((*uint8)(add(unsafe.Pointer(b), i))) // b.topbits[i] without the bounds check
   363  			if x != top {
   364  				continue
   365  			}
   366  			k := (*stringStruct)(add(unsafe.Pointer(b), dataOffset+i*2*ptrSize))
   367  			if k.len != key.len {
   368  				continue
   369  			}
   370  			if k.str == key.str || memeq(k.str, key.str, uintptr(key.len)) {
   371  				return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*ptrSize+i*uintptr(t.valuesize)), true
   372  			}
   373  		}
   374  		b = b.overflow(t)
   375  		if b == nil {
   376  			return unsafe.Pointer(t.elem.zero), false
   377  		}
   378  	}
   379  }