项目文件夹

文件
wehub-resource-sync a06f331eb8
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:33:42 +08:00

128 行
4.3 KiB
Go

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
package clones
import "sort"
// Length-stratified LSH bounds.
//
// The 0.82 Jaccard threshold implies that two clone bodies' token
// counts differ by at most ~22% — Jaccard(A,B) ≤ min(|A|,|B|) /
// max(|A|,|B|), so a 0.82 clone needs max ≤ min/0.82 ≈ 1.22·min. We
// length-bucket items into 5 overlapping geometric classes (each
// adjacent pair overlapping by ~50%) so any real clone pair lands in
// at least one shared class, while items more than ~1.6× apart in
// size never enter the same LSH pass. On a 150k-item graph this
// alone drops the candidate count by 5–10× because k8s-style
// boilerplate (options-builders, controllers, list-watchers) shares
// signatures within its size class but doesn't fan out across
// classes.
//
// Bounds are [lo, hi). The catch-all final class covers everything
// beyond 400 tokens. An item at token count tc belongs to every
// class i where lo[i] ≤ tc < hi[i] — usually two adjacent classes
// when tc is in the overlap region, one otherwise.
var lengthBucketBounds = []struct{ lo, hi int }{
{0, 80},
{50, 160},
{100, 320},
{200, 640},
{400, 1 << 31},
}
// lengthClassesOf returns the class indexes a tokenCount belongs to.
// A tokenCount of 0 ("unknown" — legacy items without TokenCount
// stamped) returns every class so the legacy single-bucket detection
// behaviour is preserved.
func lengthClassesOf(tokenCount int) []int {
if tokenCount <= 0 {
all := make([]int, len(lengthBucketBounds))
for i := range all {
all[i] = i
}
return all
}
var out []int
for i, b := range lengthBucketBounds {
if tokenCount >= b.lo && tokenCount < b.hi {
out = append(out, i)
}
}
return out
}
// DetectPairsStratifiedWithStats is the length-stratified variant of
// DetectPairsWithStats. It partitions items into overlapping length
// classes (lengthBucketBounds), runs the per-class LSH pass
// independently, and merges the results.
//
// Two LSH-level wins:
//
// 1. Cross-class candidate pairs never enter any band bucket — pairs
// more than ~1.6× apart in token count are dropped before the
// bucket-fan-out blow-up that hurts non-stratified LSH on huge
// graphs (the per-bucket-cap telemetry's main source).
// 2. Per-class LSH indexes are smaller, so the `seen` dedup map and
// the band buckets themselves use bounded memory regardless of
// the global item count.
//
// Pair output is merged across classes; an item that lands in two
// adjacent classes can have its pairs surface twice (once per class),
// so a canonical-pair dedup runs on the merge.
//
// Items whose TokenCount is 0 fall into every class, so callers that
// haven't migrated to stamping token counts get the unstratified
// behaviour back.
func DetectPairsStratifiedWithStats(items []Item, threshold float64) (pairs []Pair, skippedBuckets, skippedBucketItems int) {
if threshold <= 0 {
threshold = DefaultThreshold
}
if len(items) < 2 {
return nil, 0, 0
}
// Partition into length classes. An item in the overlap region
// of two classes appears in both.
classes := make([][]Item, len(lengthBucketBounds))
for _, it := range items {
for _, c := range lengthClassesOf(it.TokenCount) {
classes[c] = append(classes[c], it)
}
}
// Per-class LSH. Each class is independent — different classes
// share no band buckets and therefore produce no cross-class
// candidate pairs.
seen := make(map[[2]string]struct{})
for _, classItems := range classes {
if len(classItems) < 2 {
continue
}
classPairs, sb, sbi := DetectPairsWithStats(classItems, threshold)
skippedBuckets += sb
skippedBucketItems += sbi
for _, p := range classPairs {
// DetectPairsWithStats already canonicalises A < B inside
// each Pair, so the key matches across classes.
key := [2]string{p.A, p.B}
if _, ok := seen[key]; ok {
continue
}
seen[key] = struct{}{}
pairs = append(pairs, p)
}
}
// Stable output order so callers logging "found N clones" get
// reproducible numbers across runs. DetectPairsWithStats sorts by
// descending similarity within a class; the merge re-sorts globally.
sort.Slice(pairs, func(i, j int) bool {
if pairs[i].Similarity != pairs[j].Similarity {
return pairs[i].Similarity > pairs[j].Similarity
}
if pairs[i].A != pairs[j].A {
return pairs[i].A < pairs[j].A
}
return pairs[i].B < pairs[j].B
})
return pairs, skippedBuckets, skippedBucketItems
}