// Copyright 2024 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package modindex import ( "slices" "strconv" "strings" ) type Candidate struct { PkgName string Name string Dir string ImportPath string Type LexType // information for Funcs Results int16 // how many results Sig []Field // arg names and types } type Field struct { Arg, Type string } type LexType int8 const ( Const LexType = iota Var Type Func ) // Lookup finds all the symbols in the index with the given PkgName and name. // If prefix is true, it finds all of these with name as a prefix. func (ix *Index) Lookup(pkg, name string, prefix bool) []Candidate { loc, ok := slices.BinarySearchFunc(ix.Entries, pkg, func(e Entry, pkg string) int { return strings.Compare(e.PkgName, pkg) }) if !ok { return nil // didn't find the package } var ans []Candidate // loc is the first entry for this package name, but there may be severeal for i := loc; i < len(ix.Entries); i++ { e := ix.Entries[i] if e.PkgName != pkg { break // end of sorted package names } nloc, ok := slices.BinarySearchFunc(e.Names, name, func(s string, name string) int { if strings.HasPrefix(s, name) { return 0 } if s < name { return -1 } return 1 }) if !ok { continue // didn't find the name, nor any symbols with name as a prefix } for j := nloc; j < len(e.Names); j++ { nstr := e.Names[j] // benchmarks show this makes a difference when there are a lot of Possibilities flds := fastSplit(nstr) if !(flds[0] == name || prefix && strings.HasPrefix(flds[0], name)) { // past range of matching Names break } if len(flds) < 2 { continue // should never happen } px := Candidate{ PkgName: pkg, Name: flds[0], Dir: string(e.Dir), ImportPath: e.ImportPath, Type: asLexType(flds[1][0]), } if flds[1] == "F" { n, err := strconv.Atoi(flds[2]) if err != nil { continue // should never happen } px.Results = int16(n) if len(flds) >= 4 { sig := strings.Split(flds[3], " ") for i := 0; i < len(sig); i++ { // $ cannot otherwise occur. removing the spaces // almost works, but for chan struct{}, e.g. sig[i] = strings.Replace(sig[i], "$", " ", -1) } px.Sig = toFields(sig) } } ans = append(ans, px) } } return ans } func toFields(sig []string) []Field { ans := make([]Field, len(sig)/2) for i := 0; i < len(ans); i++ { ans[i] = Field{Arg: sig[2*i], Type: sig[2*i+1]} } return ans } // benchmarks show this is measurably better than strings.Split func fastSplit(x string) []string { ans := make([]string, 0, 4) nxt := 0 start := 0 for i := 0; i < len(x); i++ { if x[i] != ' ' { continue } ans = append(ans, x[start:i]) nxt++ start = i + 1 if nxt >= 3 { break } } ans = append(ans, x[start:]) return ans } func asLexType(c byte) LexType { switch c { case 'C': return Const case 'V': return Var case 'T': return Type case 'F': return Func } return -1 }