diff --git a/internal/checker/checker.go b/internal/checker/checker.go index 6ca4a88e3b9..6f93b4f0852 100644 --- a/internal/checker/checker.go +++ b/internal/checker/checker.go @@ -2,6 +2,7 @@ package checker import ( "context" + "encoding/binary" "fmt" "iter" "maps" @@ -17352,30 +17353,6 @@ func (c *Checker) isThislessInterface(symbol *ast.Symbol) bool { return true } -func hashWrite32[T ~int32 | ~uint32](h *xxh3.Hasher, value T) { - v := uint32(value) - _, _ = h.Write([]byte{ - byte(v), - byte(v >> 8), - byte(v >> 16), - byte(v >> 24), - }) -} - -func hashWrite64[T ~int | ~uint | ~int64 | ~uint64](h *xxh3.Hasher, value T) { - v := uint64(value) - _, _ = h.Write([]byte{ - byte(v), - byte(v >> 8), - byte(v >> 16), - byte(v >> 24), - byte(v >> 32), - byte(v >> 40), - byte(v >> 48), - byte(v >> 56), - }) -} - type CacheHashKey xxh3.Uint128 func (k CacheHashKey) IsZero() bool { @@ -17383,31 +17360,70 @@ func (k CacheHashKey) IsZero() bool { } type keyBuilder struct { - h xxh3.Hasher + inlineLength int + overflowBuffer []byte + inlineBuffer [192]byte } func (b *keyBuilder) hash() CacheHashKey { - return CacheHashKey(b.h.Sum128()) + if b.overflowBuffer == nil { + return CacheHashKey(xxh3.Hash128(b.inlineBuffer[:b.inlineLength])) + } + return CacheHashKey(xxh3.Hash128(append(b.overflowBuffer, b.inlineBuffer[:b.inlineLength]...))) +} + +// spill moves the buffered bytes onto the end of overflowBuffer, so the key's byte +// stream stays overflowBuffer followed by inlineBuffer. +func (b *keyBuilder) spill() { + b.overflowBuffer = append(b.overflowBuffer, b.inlineBuffer[:b.inlineLength]...) + b.inlineLength = 0 } func (b *keyBuilder) writeByte(c byte) { - _, _ = b.h.Write([]byte{c}) + if b.inlineLength == len(b.inlineBuffer) { + b.spill() + } + b.inlineBuffer[b.inlineLength] = c + b.inlineLength++ } func (b *keyBuilder) writeString(s string) { - _, _ = b.h.WriteString(s) + if b.inlineLength+len(s) > len(b.inlineBuffer) { + b.spill() + if len(s) > len(b.inlineBuffer) { + b.overflowBuffer = append(b.overflowBuffer, s...) + return + } + } + b.inlineLength += copy(b.inlineBuffer[b.inlineLength:], s) +} + +func (b *keyBuilder) writeUint32(v uint32) { + if b.inlineLength+4 > len(b.inlineBuffer) { + b.spill() + } + binary.LittleEndian.PutUint32(b.inlineBuffer[b.inlineLength:], v) + b.inlineLength += 4 +} + +func (b *keyBuilder) writeUint64(v uint64) { + if b.inlineLength+8 > len(b.inlineBuffer) { + b.spill() + } + binary.LittleEndian.PutUint64(b.inlineBuffer[b.inlineLength:], v) + b.inlineLength += 8 } func (b *keyBuilder) writeInt(value int) { - hashWrite64(&b.h, value) + b.writeUint64(uint64(value)) } func (b *keyBuilder) writeSymbol(s *ast.Symbol) { - hashWrite64(&b.h, ast.GetSymbolId(s)) + b.writeUint64(uint64(ast.GetSymbolId(s))) } func (b *keyBuilder) writeType(t *Type) { - hashWrite32(&b.h, t.id) + b.writeUint32(uint32(t.id)) } func (b *keyBuilder) writeTypes(types []*Type) { @@ -17463,7 +17479,7 @@ func (b *keyBuilder) writeGenericTypeReferences(source *Type, target *Type, igno } func (b *keyBuilder) writeNodeId(id ast.NodeId) { - hashWrite64(&b.h, id) + b.writeUint64(uint64(id)) } func (b *keyBuilder) writeNode(node *ast.Node) { @@ -17560,7 +17576,7 @@ func getIndexedAccessKey(objectType *Type, indexType *Type, accessFlags AccessFl var b keyBuilder b.writeType(objectType) b.writeType(indexType) - hashWrite32(&b.h, accessFlags) + b.writeUint32(uint32(accessFlags)) b.writeAlias(alias) return b.hash() } @@ -17603,7 +17619,7 @@ func getRelationKey(source *Type, target *Type, intersectionState IntersectionSt b.writeType(source) b.writeType(target) } - hashWrite32(&b.h, intersectionState) + b.writeUint32(uint32(intersectionState)) return b.hash(), constrained }