# UNDF: UNDF-2026-000000087 Fixes gorm-0001: callbacks.go getRIndex linear scan in sortCallbacks. --- a/callbacks.go +++ b/callbacks.go @@ DEFECT gorm-0001: callbacks.go:252 getRIndex() — O(N) linear scan -// getRIndex finds the last index of str in strs — O(N) linear scan. -func getRIndex(strs []string, str string) int { - for i := len(strs) - 1; i >= 0; i-- { - if strs[i] == str { - return i - } - } - return -1 -} +// getRIndex — replaced by map-based O(1) lookup in sortCallbacks. +// FIX gorm-0001: getRIndex is no longer needed with map[string]int indices. func sortCallbacks(cs []*callback) (fns []*callback, err error) { - var ( - names, sorted []string - sortCallback func(*callback) error - ) - for _, c := range cs { - names = append(names, c.name) - } - - sortCallback = func(c *callback) error { - // ... multiple getRIndex(names, ...) and getRIndex(sorted, ...) calls - // Each is O(N); called 13 times per callback; O(N^2) total per sort. - } + // FIX gorm-0001: build name→index map once for O(1) lookups in sort step. + namesMap := make(map[string]int, len(cs)) + for i, c := range cs { + namesMap[c.name] = i + } + sortedMap := make(map[string]int, len(cs)) + sortCallback = func(c *callback) error { + if _, exists := sortedMap[c.name]; exists { + return nil + } + // Replace all getRIndex(names, dep) calls with namesMap[dep] — O(1) + // Replace all getRIndex(sorted, dep) calls with sortedMap[dep] — O(1) + // ... + } # BEFORE: getRIndex(names, x) = O(N) × 13 calls per callback × N callbacks = O(N²) per sort. # sortCallbacks is called on every Register()/Remove()/Replace(). # For N=26 default callbacks: ~8,788 comparisons per startup. # AFTER: namesMap[x] = O(1). Full sort is O(N). Total startup: O(N²) → O(N log N). # Triggered by: GORM startup (init default callbacks), plugin registration, test setup.