родитель
							
								
									b02cea0321
								
							
						
					
					
						коммит
						7e05efa274
					
				
					 2 изменённых файлов: 156 добавлений и 28 удалений
				
			
		|  | @ -10,6 +10,13 @@ import ( | ||||||
| 	"tinygo.org/x/go-llvm" | 	"tinygo.org/x/go-llvm" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | // constants for hashmap algorithms; must match src/runtime/hashmap.go | ||||||
|  | const ( | ||||||
|  | 	hashmapAlgorithmBinary = iota | ||||||
|  | 	hashmapAlgorithmString | ||||||
|  | 	hashmapAlgorithmInterface | ||||||
|  | ) | ||||||
|  | 
 | ||||||
| // createMakeMap creates a new map object (runtime.hashmap) by allocating and | // createMakeMap creates a new map object (runtime.hashmap) by allocating and | ||||||
| // initializing an appropriately sized object. | // initializing an appropriately sized object. | ||||||
| func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) { | func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) { | ||||||
|  | @ -17,22 +24,27 @@ func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) { | ||||||
| 	keyType := mapType.Key().Underlying() | 	keyType := mapType.Key().Underlying() | ||||||
| 	llvmValueType := b.getLLVMType(mapType.Elem().Underlying()) | 	llvmValueType := b.getLLVMType(mapType.Elem().Underlying()) | ||||||
| 	var llvmKeyType llvm.Type | 	var llvmKeyType llvm.Type | ||||||
|  | 	var alg uint64 // must match values in src/runtime/hashmap.go | ||||||
| 	if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 { | 	if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 { | ||||||
| 		// String keys. | 		// String keys. | ||||||
| 		llvmKeyType = b.getLLVMType(keyType) | 		llvmKeyType = b.getLLVMType(keyType) | ||||||
|  | 		alg = hashmapAlgorithmString | ||||||
| 	} else if hashmapIsBinaryKey(keyType) { | 	} else if hashmapIsBinaryKey(keyType) { | ||||||
| 		// Trivially comparable keys. | 		// Trivially comparable keys. | ||||||
| 		llvmKeyType = b.getLLVMType(keyType) | 		llvmKeyType = b.getLLVMType(keyType) | ||||||
|  | 		alg = hashmapAlgorithmBinary | ||||||
| 	} else { | 	} else { | ||||||
| 		// All other keys. Implemented as map[interface{}]valueType for ease of | 		// All other keys. Implemented as map[interface{}]valueType for ease of | ||||||
| 		// implementation. | 		// implementation. | ||||||
| 		llvmKeyType = b.getLLVMRuntimeType("_interface") | 		llvmKeyType = b.getLLVMRuntimeType("_interface") | ||||||
|  | 		alg = hashmapAlgorithmInterface | ||||||
| 	} | 	} | ||||||
| 	keySize := b.targetData.TypeAllocSize(llvmKeyType) | 	keySize := b.targetData.TypeAllocSize(llvmKeyType) | ||||||
| 	valueSize := b.targetData.TypeAllocSize(llvmValueType) | 	valueSize := b.targetData.TypeAllocSize(llvmValueType) | ||||||
| 	llvmKeySize := llvm.ConstInt(b.ctx.Int8Type(), keySize, false) | 	llvmKeySize := llvm.ConstInt(b.ctx.Int8Type(), keySize, false) | ||||||
| 	llvmValueSize := llvm.ConstInt(b.ctx.Int8Type(), valueSize, false) | 	llvmValueSize := llvm.ConstInt(b.ctx.Int8Type(), valueSize, false) | ||||||
| 	sizeHint := llvm.ConstInt(b.uintptrType, 8, false) | 	sizeHint := llvm.ConstInt(b.uintptrType, 8, false) | ||||||
|  | 	algEnum := llvm.ConstInt(b.ctx.Int8Type(), alg, false) | ||||||
| 	if expr.Reserve != nil { | 	if expr.Reserve != nil { | ||||||
| 		sizeHint = b.getValue(expr.Reserve) | 		sizeHint = b.getValue(expr.Reserve) | ||||||
| 		var err error | 		var err error | ||||||
|  | @ -41,7 +53,7 @@ func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) { | ||||||
| 			return llvm.Value{}, err | 			return llvm.Value{}, err | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	hashmap := b.createRuntimeCall("hashmapMake", []llvm.Value{llvmKeySize, llvmValueSize, sizeHint}, "") | 	hashmap := b.createRuntimeCall("hashmapMake", []llvm.Value{llvmKeySize, llvmValueSize, sizeHint, algEnum}, "") | ||||||
| 	return hashmap, nil | 	return hashmap, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -12,14 +12,23 @@ import ( | ||||||
| 
 | 
 | ||||||
| // The underlying hashmap structure for Go. | // The underlying hashmap structure for Go. | ||||||
| type hashmap struct { | type hashmap struct { | ||||||
| 	next       *hashmap       // hashmap after evacuate (for iterators) |  | ||||||
| 	buckets    unsafe.Pointer // pointer to array of buckets | 	buckets    unsafe.Pointer // pointer to array of buckets | ||||||
| 	count      uintptr | 	count      uintptr | ||||||
| 	keySize    uint8 // maybe this can store the key type as well? E.g. keysize == 5 means string? | 	keySize    uint8 // maybe this can store the key type as well? E.g. keysize == 5 means string? | ||||||
| 	valueSize  uint8 | 	valueSize  uint8 | ||||||
| 	bucketBits uint8 | 	bucketBits uint8 | ||||||
|  | 	keyEqual   func(x, y unsafe.Pointer, n uintptr) bool | ||||||
|  | 	keyHash    func(key unsafe.Pointer, size uintptr) uint32 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | type hashmapAlgorithm uint8 | ||||||
|  | 
 | ||||||
|  | const ( | ||||||
|  | 	hashmapAlgorithmBinary hashmapAlgorithm = iota | ||||||
|  | 	hashmapAlgorithmString | ||||||
|  | 	hashmapAlgorithmInterface | ||||||
|  | ) | ||||||
|  | 
 | ||||||
| // A hashmap bucket. A bucket is a container of 8 key/value pairs: first the | // A hashmap bucket. A bucket is a container of 8 key/value pairs: first the | ||||||
| // following two entries, then the 8 keys, then the 8 values. This somewhat odd | // following two entries, then the 8 keys, then the 8 values. This somewhat odd | ||||||
| // ordering is to make sure the keys and values are well aligned when one of | // ordering is to make sure the keys and values are well aligned when one of | ||||||
|  | @ -32,6 +41,8 @@ type hashmapBucket struct { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type hashmapIterator struct { | type hashmapIterator struct { | ||||||
|  | 	buckets      unsafe.Pointer | ||||||
|  | 	numBuckets   uintptr | ||||||
| 	bucketNumber uintptr | 	bucketNumber uintptr | ||||||
| 	bucket       *hashmapBucket | 	bucket       *hashmapBucket | ||||||
| 	bucketIndex  uint8 | 	bucketIndex  uint8 | ||||||
|  | @ -48,7 +59,7 @@ func hashmapTopHash(hash uint32) uint8 { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Create a new hashmap with the given keySize and valueSize. | // Create a new hashmap with the given keySize and valueSize. | ||||||
| func hashmapMake(keySize, valueSize uint8, sizeHint uintptr) *hashmap { | func hashmapMake(keySize, valueSize uint8, sizeHint uintptr, alg uint8) *hashmap { | ||||||
| 	numBuckets := sizeHint / 8 | 	numBuckets := sizeHint / 8 | ||||||
| 	bucketBits := uint8(0) | 	bucketBits := uint8(0) | ||||||
| 	for numBuckets != 0 { | 	for numBuckets != 0 { | ||||||
|  | @ -57,14 +68,58 @@ func hashmapMake(keySize, valueSize uint8, sizeHint uintptr) *hashmap { | ||||||
| 	} | 	} | ||||||
| 	bucketBufSize := unsafe.Sizeof(hashmapBucket{}) + uintptr(keySize)*8 + uintptr(valueSize)*8 | 	bucketBufSize := unsafe.Sizeof(hashmapBucket{}) + uintptr(keySize)*8 + uintptr(valueSize)*8 | ||||||
| 	buckets := alloc(bucketBufSize*(1<<bucketBits), nil) | 	buckets := alloc(bucketBufSize*(1<<bucketBits), nil) | ||||||
|  | 
 | ||||||
|  | 	keyHash := hashmapKeyHashAlg(hashmapAlgorithm(alg)) | ||||||
|  | 	keyEqual := hashmapKeyEqualAlg(hashmapAlgorithm(alg)) | ||||||
|  | 
 | ||||||
| 	return &hashmap{ | 	return &hashmap{ | ||||||
| 		buckets:    buckets, | 		buckets:    buckets, | ||||||
| 		keySize:    keySize, | 		keySize:    keySize, | ||||||
| 		valueSize:  valueSize, | 		valueSize:  valueSize, | ||||||
| 		bucketBits: bucketBits, | 		bucketBits: bucketBits, | ||||||
|  | 		keyEqual:   keyEqual, | ||||||
|  | 		keyHash:    keyHash, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func hashmapKeyEqualAlg(alg hashmapAlgorithm) func(x, y unsafe.Pointer, n uintptr) bool { | ||||||
|  | 	switch alg { | ||||||
|  | 	case hashmapAlgorithmBinary: | ||||||
|  | 		return memequal | ||||||
|  | 	case hashmapAlgorithmString: | ||||||
|  | 		return hashmapStringEqual | ||||||
|  | 	case hashmapAlgorithmInterface: | ||||||
|  | 		return hashmapInterfaceEqual | ||||||
|  | 	default: | ||||||
|  | 		panic("unknown hashmap equal algorithm") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func hashmapKeyHashAlg(alg hashmapAlgorithm) func(key unsafe.Pointer, n uintptr) uint32 { | ||||||
|  | 	switch alg { | ||||||
|  | 	case hashmapAlgorithmBinary: | ||||||
|  | 		return hash32 | ||||||
|  | 	case hashmapAlgorithmString: | ||||||
|  | 		return hashmapStringPtrHash | ||||||
|  | 	case hashmapAlgorithmInterface: | ||||||
|  | 		return hashmapInterfacePtrHash | ||||||
|  | 	default: | ||||||
|  | 		panic("unknown hashmap hash algorithm") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func hashmapShouldGrow(m *hashmap) bool { | ||||||
|  | 	if m.bucketBits == 32 { | ||||||
|  | 		// can't grow any more | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// "maximum" number of elements is 0.75 * buckets * elements per bucket | ||||||
|  | 	max := uintptr((3 * ((1 << m.bucketBits) * 8)) / 4) | ||||||
|  | 
 | ||||||
|  | 	return m.count > max | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // Return the number of entries in this hashmap, called from the len builtin. | // Return the number of entries in this hashmap, called from the len builtin. | ||||||
| // A nil hashmap is defined as having length 0. | // A nil hashmap is defined as having length 0. | ||||||
| //go:inline | //go:inline | ||||||
|  | @ -83,7 +138,7 @@ func hashmapLenUnsafePointer(p unsafe.Pointer) int { | ||||||
| 
 | 
 | ||||||
| // Set a specified key to a given value. Grow the map if necessary. | // Set a specified key to a given value. Grow the map if necessary. | ||||||
| //go:nobounds | //go:nobounds | ||||||
| func hashmapSet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint32, keyEqual func(x, y unsafe.Pointer, n uintptr) bool) { | func hashmapSet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint32) { | ||||||
| 	tophash := hashmapTopHash(hash) | 	tophash := hashmapTopHash(hash) | ||||||
| 
 | 
 | ||||||
| 	if m.buckets == nil { | 	if m.buckets == nil { | ||||||
|  | @ -92,6 +147,10 @@ func hashmapSet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint3 | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	if hashmapShouldGrow(m) { | ||||||
|  | 		hashmapGrow(m) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	numBuckets := uintptr(1) << m.bucketBits | 	numBuckets := uintptr(1) << m.bucketBits | ||||||
| 	bucketNumber := (uintptr(hash) & (numBuckets - 1)) | 	bucketNumber := (uintptr(hash) & (numBuckets - 1)) | ||||||
| 	bucketSize := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*8 | 	bucketSize := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*8 | ||||||
|  | @ -118,7 +177,7 @@ func hashmapSet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint3 | ||||||
| 			} | 			} | ||||||
| 			if bucket.tophash[i] == tophash { | 			if bucket.tophash[i] == tophash { | ||||||
| 				// Could be an existing key that's the same. | 				// Could be an existing key that's the same. | ||||||
| 				if keyEqual(key, slotKey, uintptr(m.keySize)) { | 				if m.keyEqual(key, slotKey, uintptr(m.keySize)) { | ||||||
| 					// found same key, replace it | 					// found same key, replace it | ||||||
| 					memcpy(slotValue, value, uintptr(m.valueSize)) | 					memcpy(slotValue, value, uintptr(m.valueSize)) | ||||||
| 					return | 					return | ||||||
|  | @ -158,9 +217,35 @@ func hashmapInsertIntoNewBucket(m *hashmap, key, value unsafe.Pointer, tophash u | ||||||
| 	return bucket | 	return bucket | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func hashmapGrow(m *hashmap) { | ||||||
|  | 
 | ||||||
|  | 	// clone map as empty | ||||||
|  | 	n := *m | ||||||
|  | 	n.count = 0 | ||||||
|  | 
 | ||||||
|  | 	// allocate our new buckets twice as big | ||||||
|  | 	n.bucketBits = m.bucketBits + 1 | ||||||
|  | 	numBuckets := uintptr(1) << n.bucketBits | ||||||
|  | 	bucketBufSize := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*8 | ||||||
|  | 	n.buckets = alloc(bucketBufSize*numBuckets, nil) | ||||||
|  | 
 | ||||||
|  | 	// use a hashmap iterator to go through the old map | ||||||
|  | 	var it hashmapIterator | ||||||
|  | 
 | ||||||
|  | 	var key = alloc(uintptr(m.keySize), nil) | ||||||
|  | 	var value = alloc(uintptr(m.valueSize), nil) | ||||||
|  | 
 | ||||||
|  | 	for hashmapNext(m, &it, key, value) { | ||||||
|  | 		h := m.keyHash(key, uintptr(m.keySize)) | ||||||
|  | 		hashmapSet(&n, key, value, h) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	*m = n | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // Get the value of a specified key, or zero the value if not found. | // Get the value of a specified key, or zero the value if not found. | ||||||
| //go:nobounds | //go:nobounds | ||||||
| func hashmapGet(m *hashmap, key, value unsafe.Pointer, valueSize uintptr, hash uint32, keyEqual func(x, y unsafe.Pointer, n uintptr) bool) bool { | func hashmapGet(m *hashmap, key, value unsafe.Pointer, valueSize uintptr, hash uint32) bool { | ||||||
| 	if m == nil { | 	if m == nil { | ||||||
| 		// Getting a value out of a nil map is valid. From the spec: | 		// Getting a value out of a nil map is valid. From the spec: | ||||||
| 		// > if the map is nil or does not contain such an entry, a[x] is the | 		// > if the map is nil or does not contain such an entry, a[x] is the | ||||||
|  | @ -189,7 +274,7 @@ func hashmapGet(m *hashmap, key, value unsafe.Pointer, valueSize uintptr, hash u | ||||||
| 			slotValue := unsafe.Pointer(uintptr(unsafe.Pointer(bucket)) + slotValueOffset) | 			slotValue := unsafe.Pointer(uintptr(unsafe.Pointer(bucket)) + slotValueOffset) | ||||||
| 			if bucket.tophash[i] == tophash { | 			if bucket.tophash[i] == tophash { | ||||||
| 				// This could be the key we're looking for. | 				// This could be the key we're looking for. | ||||||
| 				if keyEqual(key, slotKey, uintptr(m.keySize)) { | 				if m.keyEqual(key, slotKey, uintptr(m.keySize)) { | ||||||
| 					// Found the key, copy it. | 					// Found the key, copy it. | ||||||
| 					memcpy(value, slotValue, uintptr(m.valueSize)) | 					memcpy(value, slotValue, uintptr(m.valueSize)) | ||||||
| 					return true | 					return true | ||||||
|  | @ -207,7 +292,7 @@ func hashmapGet(m *hashmap, key, value unsafe.Pointer, valueSize uintptr, hash u | ||||||
| // Delete a given key from the map. No-op when the key does not exist in the | // Delete a given key from the map. No-op when the key does not exist in the | ||||||
| // map. | // map. | ||||||
| //go:nobounds | //go:nobounds | ||||||
| func hashmapDelete(m *hashmap, key unsafe.Pointer, hash uint32, keyEqual func(x, y unsafe.Pointer, n uintptr) bool) { | func hashmapDelete(m *hashmap, key unsafe.Pointer, hash uint32) { | ||||||
| 	if m == nil { | 	if m == nil { | ||||||
| 		// The delete builtin is defined even when the map is nil. From the spec: | 		// The delete builtin is defined even when the map is nil. From the spec: | ||||||
| 		// > If the map m is nil or the element m[k] does not exist, delete is a | 		// > If the map m is nil or the element m[k] does not exist, delete is a | ||||||
|  | @ -233,7 +318,7 @@ func hashmapDelete(m *hashmap, key unsafe.Pointer, hash uint32, keyEqual func(x, | ||||||
| 			slotKey := unsafe.Pointer(uintptr(unsafe.Pointer(bucket)) + slotKeyOffset) | 			slotKey := unsafe.Pointer(uintptr(unsafe.Pointer(bucket)) + slotKeyOffset) | ||||||
| 			if bucket.tophash[i] == tophash { | 			if bucket.tophash[i] == tophash { | ||||||
| 				// This could be the key we're looking for. | 				// This could be the key we're looking for. | ||||||
| 				if keyEqual(key, slotKey, uintptr(m.keySize)) { | 				if m.keyEqual(key, slotKey, uintptr(m.keySize)) { | ||||||
| 					// Found the key, delete it. | 					// Found the key, delete it. | ||||||
| 					bucket.tophash[i] = 0 | 					bucket.tophash[i] = 0 | ||||||
| 					m.count-- | 					m.count-- | ||||||
|  | @ -249,13 +334,16 @@ func hashmapDelete(m *hashmap, key unsafe.Pointer, hash uint32, keyEqual func(x, | ||||||
| //go:nobounds | //go:nobounds | ||||||
| func hashmapNext(m *hashmap, it *hashmapIterator, key, value unsafe.Pointer) bool { | func hashmapNext(m *hashmap, it *hashmapIterator, key, value unsafe.Pointer) bool { | ||||||
| 	if m == nil { | 	if m == nil { | ||||||
| 		// Iterating over a nil slice appears to be allowed by the Go spec: | 		// From the spec: If the map is nil, the number of iterations is 0. | ||||||
| 		// https://groups.google.com/g/golang-nuts/c/gVgVLQU1FFE?pli=1 |  | ||||||
| 		// https://play.golang.org/p/S8jxAMytKDB |  | ||||||
| 		return false | 		return false | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	numBuckets := uintptr(1) << m.bucketBits | 	if it.buckets == nil { | ||||||
|  | 		// initialize iterator | ||||||
|  | 		it.buckets = m.buckets | ||||||
|  | 		it.numBuckets = uintptr(1) << m.bucketBits | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	for { | 	for { | ||||||
| 		if it.bucketIndex >= 8 { | 		if it.bucketIndex >= 8 { | ||||||
| 			// end of bucket, move to the next in the chain | 			// end of bucket, move to the next in the chain | ||||||
|  | @ -263,12 +351,12 @@ func hashmapNext(m *hashmap, it *hashmapIterator, key, value unsafe.Pointer) boo | ||||||
| 			it.bucket = it.bucket.next | 			it.bucket = it.bucket.next | ||||||
| 		} | 		} | ||||||
| 		if it.bucket == nil { | 		if it.bucket == nil { | ||||||
| 			if it.bucketNumber >= numBuckets { | 			if it.bucketNumber >= it.numBuckets { | ||||||
| 				// went through all buckets | 				// went through all buckets | ||||||
| 				return false | 				return false | ||||||
| 			} | 			} | ||||||
| 			bucketSize := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*8 | 			bucketSize := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*8 | ||||||
| 			bucketAddr := uintptr(m.buckets) + bucketSize*it.bucketNumber | 			bucketAddr := uintptr(it.buckets) + bucketSize*it.bucketNumber | ||||||
| 			it.bucket = (*hashmapBucket)(unsafe.Pointer(bucketAddr)) | 			it.bucket = (*hashmapBucket)(unsafe.Pointer(bucketAddr)) | ||||||
| 			it.bucketNumber++ // next bucket | 			it.bucketNumber++ // next bucket | ||||||
| 		} | 		} | ||||||
|  | @ -281,11 +369,29 @@ func hashmapNext(m *hashmap, it *hashmapIterator, key, value unsafe.Pointer) boo | ||||||
| 		bucketAddr := uintptr(unsafe.Pointer(it.bucket)) | 		bucketAddr := uintptr(unsafe.Pointer(it.bucket)) | ||||||
| 		slotKeyOffset := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*uintptr(it.bucketIndex) | 		slotKeyOffset := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*uintptr(it.bucketIndex) | ||||||
| 		slotKey := unsafe.Pointer(bucketAddr + slotKeyOffset) | 		slotKey := unsafe.Pointer(bucketAddr + slotKeyOffset) | ||||||
|  | 		memcpy(key, slotKey, uintptr(m.keySize)) | ||||||
|  | 
 | ||||||
|  | 		if it.buckets == m.buckets { | ||||||
|  | 			// Our view of the buckets is the same as the parent map. | ||||||
|  | 			// Just copy the value we have | ||||||
| 			slotValueOffset := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*uintptr(it.bucketIndex) | 			slotValueOffset := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*uintptr(it.bucketIndex) | ||||||
| 			slotValue := unsafe.Pointer(bucketAddr + slotValueOffset) | 			slotValue := unsafe.Pointer(bucketAddr + slotValueOffset) | ||||||
| 		memcpy(key, slotKey, uintptr(m.keySize)) |  | ||||||
| 			memcpy(value, slotValue, uintptr(m.valueSize)) | 			memcpy(value, slotValue, uintptr(m.valueSize)) | ||||||
| 			it.bucketIndex++ | 			it.bucketIndex++ | ||||||
|  | 		} else { | ||||||
|  | 			it.bucketIndex++ | ||||||
|  | 
 | ||||||
|  | 			// Our view of the buckets doesn't match the parent map. | ||||||
|  | 			// Look up the key in the new buckets and return that value if it exists | ||||||
|  | 			hash := m.keyHash(key, uintptr(m.keySize)) | ||||||
|  | 			ok := hashmapGet(m, key, value, uintptr(m.valueSize), hash) | ||||||
|  | 			if !ok { | ||||||
|  | 				// doesn't exist in parent map; try next key | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			// All good. | ||||||
|  | 		} | ||||||
| 
 | 
 | ||||||
| 		return true | 		return true | ||||||
| 	} | 	} | ||||||
|  | @ -296,7 +402,7 @@ func hashmapNext(m *hashmap, it *hashmapIterator, key, value unsafe.Pointer) boo | ||||||
| func hashmapBinarySet(m *hashmap, key, value unsafe.Pointer) { | func hashmapBinarySet(m *hashmap, key, value unsafe.Pointer) { | ||||||
| 	// TODO: detect nil map here and throw a better panic message? | 	// TODO: detect nil map here and throw a better panic message? | ||||||
| 	hash := hash32(key, uintptr(m.keySize)) | 	hash := hash32(key, uintptr(m.keySize)) | ||||||
| 	hashmapSet(m, key, value, hash, memequal) | 	hashmapSet(m, key, value, hash) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func hashmapBinaryGet(m *hashmap, key, value unsafe.Pointer, valueSize uintptr) bool { | func hashmapBinaryGet(m *hashmap, key, value unsafe.Pointer, valueSize uintptr) bool { | ||||||
|  | @ -305,7 +411,7 @@ func hashmapBinaryGet(m *hashmap, key, value unsafe.Pointer, valueSize uintptr) | ||||||
| 		return false | 		return false | ||||||
| 	} | 	} | ||||||
| 	hash := hash32(key, uintptr(m.keySize)) | 	hash := hash32(key, uintptr(m.keySize)) | ||||||
| 	return hashmapGet(m, key, value, valueSize, hash, memequal) | 	return hashmapGet(m, key, value, valueSize, hash) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func hashmapBinaryDelete(m *hashmap, key unsafe.Pointer) { | func hashmapBinaryDelete(m *hashmap, key unsafe.Pointer) { | ||||||
|  | @ -313,7 +419,7 @@ func hashmapBinaryDelete(m *hashmap, key unsafe.Pointer) { | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	hash := hash32(key, uintptr(m.keySize)) | 	hash := hash32(key, uintptr(m.keySize)) | ||||||
| 	hashmapDelete(m, key, hash, memequal) | 	hashmapDelete(m, key, hash) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Hashmap with string keys (a common case). | // Hashmap with string keys (a common case). | ||||||
|  | @ -327,19 +433,24 @@ func hashmapStringHash(s string) uint32 { | ||||||
| 	return hash32(unsafe.Pointer(_s.ptr), uintptr(_s.length)) | 	return hash32(unsafe.Pointer(_s.ptr), uintptr(_s.length)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func hashmapStringPtrHash(sptr unsafe.Pointer, size uintptr) uint32 { | ||||||
|  | 	_s := *(*_string)(sptr) | ||||||
|  | 	return hash32(unsafe.Pointer(_s.ptr), uintptr(_s.length)) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func hashmapStringSet(m *hashmap, key string, value unsafe.Pointer) { | func hashmapStringSet(m *hashmap, key string, value unsafe.Pointer) { | ||||||
| 	hash := hashmapStringHash(key) | 	hash := hashmapStringHash(key) | ||||||
| 	hashmapSet(m, unsafe.Pointer(&key), value, hash, hashmapStringEqual) | 	hashmapSet(m, unsafe.Pointer(&key), value, hash) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func hashmapStringGet(m *hashmap, key string, value unsafe.Pointer, valueSize uintptr) bool { | func hashmapStringGet(m *hashmap, key string, value unsafe.Pointer, valueSize uintptr) bool { | ||||||
| 	hash := hashmapStringHash(key) | 	hash := hashmapStringHash(key) | ||||||
| 	return hashmapGet(m, unsafe.Pointer(&key), value, valueSize, hash, hashmapStringEqual) | 	return hashmapGet(m, unsafe.Pointer(&key), value, valueSize, hash) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func hashmapStringDelete(m *hashmap, key string) { | func hashmapStringDelete(m *hashmap, key string) { | ||||||
| 	hash := hashmapStringHash(key) | 	hash := hashmapStringHash(key) | ||||||
| 	hashmapDelete(m, unsafe.Pointer(&key), hash, hashmapStringEqual) | 	hashmapDelete(m, unsafe.Pointer(&key), hash) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Hashmap with interface keys (for everything else). | // Hashmap with interface keys (for everything else). | ||||||
|  | @ -427,21 +538,26 @@ func hashmapInterfaceHash(itf interface{}) uint32 { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func hashmapInterfacePtrHash(iptr unsafe.Pointer, size uintptr) uint32 { | ||||||
|  | 	_i := *(*_interface)(iptr) | ||||||
|  | 	return hashmapInterfaceHash(_i) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func hashmapInterfaceEqual(x, y unsafe.Pointer, n uintptr) bool { | func hashmapInterfaceEqual(x, y unsafe.Pointer, n uintptr) bool { | ||||||
| 	return *(*interface{})(x) == *(*interface{})(y) | 	return *(*interface{})(x) == *(*interface{})(y) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func hashmapInterfaceSet(m *hashmap, key interface{}, value unsafe.Pointer) { | func hashmapInterfaceSet(m *hashmap, key interface{}, value unsafe.Pointer) { | ||||||
| 	hash := hashmapInterfaceHash(key) | 	hash := hashmapInterfaceHash(key) | ||||||
| 	hashmapSet(m, unsafe.Pointer(&key), value, hash, hashmapInterfaceEqual) | 	hashmapSet(m, unsafe.Pointer(&key), value, hash) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func hashmapInterfaceGet(m *hashmap, key interface{}, value unsafe.Pointer, valueSize uintptr) bool { | func hashmapInterfaceGet(m *hashmap, key interface{}, value unsafe.Pointer, valueSize uintptr) bool { | ||||||
| 	hash := hashmapInterfaceHash(key) | 	hash := hashmapInterfaceHash(key) | ||||||
| 	return hashmapGet(m, unsafe.Pointer(&key), value, valueSize, hash, hashmapInterfaceEqual) | 	return hashmapGet(m, unsafe.Pointer(&key), value, valueSize, hash) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func hashmapInterfaceDelete(m *hashmap, key interface{}) { | func hashmapInterfaceDelete(m *hashmap, key interface{}) { | ||||||
| 	hash := hashmapInterfaceHash(key) | 	hash := hashmapInterfaceHash(key) | ||||||
| 	hashmapDelete(m, unsafe.Pointer(&key), hash, hashmapInterfaceEqual) | 	hashmapDelete(m, unsafe.Pointer(&key), hash) | ||||||
| } | } | ||||||
|  |  | ||||||
		Загрузка…
	
	Создание таблицы
		
		Сослаться в новой задаче
	
	 Damian Gryski
						Damian Gryski