kythe.io@v0.0.68-0.20240422202219-7225dbc01741/kythe/go/serving/xrefs/assemble/assemble.go (about)

     1  /*
     2   * Copyright 2015 The Kythe Authors. All rights reserved.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *   http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  // Package assemble provides functions to build the various components (nodes,
    18  // edges, and decorations) of an xrefs serving table.
    19  package assemble // import "kythe.io/kythe/go/serving/xrefs/assemble"
    20  
    21  import (
    22  	"context"
    23  	"errors"
    24  	"fmt"
    25  	"sort"
    26  	"strconv"
    27  
    28  	"kythe.io/kythe/go/services/graphstore"
    29  	"kythe.io/kythe/go/services/xrefs"
    30  	"kythe.io/kythe/go/storage/stream"
    31  	"kythe.io/kythe/go/util/compare"
    32  	"kythe.io/kythe/go/util/encoding/text"
    33  	"kythe.io/kythe/go/util/kytheuri"
    34  	"kythe.io/kythe/go/util/log"
    35  	"kythe.io/kythe/go/util/pager"
    36  	"kythe.io/kythe/go/util/schema/edges"
    37  	"kythe.io/kythe/go/util/schema/facts"
    38  	"kythe.io/kythe/go/util/schema/nodes"
    39  	"kythe.io/kythe/go/util/schema/tickets"
    40  	"kythe.io/kythe/go/util/span"
    41  	cpb "kythe.io/kythe/proto/common_go_proto"
    42  	ipb "kythe.io/kythe/proto/internal_go_proto"
    43  	srvpb "kythe.io/kythe/proto/serving_go_proto"
    44  	spb "kythe.io/kythe/proto/storage_go_proto"
    45  )
    46  
    47  // Node returns the Source as a srvpb.Node.
    48  func Node(s *ipb.Source) *srvpb.Node {
    49  	facts := make([]*cpb.Fact, 0, len(s.Facts))
    50  	for name, value := range s.Facts {
    51  		facts = append(facts, &cpb.Fact{Name: name, Value: value})
    52  	}
    53  	sort.Sort(xrefs.ByName(facts))
    54  	return &srvpb.Node{
    55  		Ticket: s.Ticket,
    56  		Fact:   facts,
    57  	}
    58  }
    59  
    60  // AppendEntry adds the given Entry to the Source's facts or edges.  It is
    61  // assumed that src.Ticket == kytheuri.ToString(e.Source).
    62  func AppendEntry(src *ipb.Source, e *spb.Entry) {
    63  	if graphstore.IsEdge(e) {
    64  		kind, ordinal, _ := edges.ParseOrdinal(e.EdgeKind)
    65  		group, ok := src.EdgeGroups[kind]
    66  		if !ok {
    67  			group = &ipb.Source_EdgeGroup{}
    68  			src.EdgeGroups[kind] = group
    69  		}
    70  
    71  		ticket := kytheuri.ToString(e.Target)
    72  
    73  		ord := int32(ordinal)
    74  		for _, edge := range group.Edges {
    75  			if edge.Ticket == ticket && edge.Ordinal == ord {
    76  				// Don't add duplicate edge
    77  				return
    78  			}
    79  		}
    80  
    81  		group.Edges = append(group.Edges, &ipb.Source_Edge{
    82  			Ticket:  ticket,
    83  			Ordinal: ord,
    84  		})
    85  	} else {
    86  		src.Facts[e.FactName] = e.FactValue
    87  	}
    88  }
    89  
    90  // Sources constructs a new Source for every contiguous set of entries sharing
    91  // the same Source, calling f for each.
    92  func Sources(rd stream.EntryReader, f func(*ipb.Source) error) error {
    93  	var source *spb.VName
    94  	var src *ipb.Source
    95  	if err := rd(func(entry *spb.Entry) error {
    96  		if src != nil && !compare.VNamesEqual(source, entry.Source) {
    97  			if err := f(src); err != nil {
    98  				return err
    99  			}
   100  			src = nil
   101  		}
   102  		if src == nil {
   103  			source = entry.Source
   104  			src = &ipb.Source{
   105  				Ticket:     kytheuri.ToString(entry.Source),
   106  				Facts:      make(map[string][]byte),
   107  				EdgeGroups: make(map[string]*ipb.Source_EdgeGroup),
   108  			}
   109  		}
   110  		AppendEntry(src, entry)
   111  		return nil
   112  	}); err != nil {
   113  		return err
   114  	}
   115  	if src != nil {
   116  		return f(src)
   117  	}
   118  	return nil
   119  }
   120  
   121  // SourceFromEntries returns a new Source from the given a set of entries with
   122  // the same source VName.
   123  func SourceFromEntries(entries []*spb.Entry) *ipb.Source {
   124  	if len(entries) == 0 {
   125  		return nil
   126  	}
   127  
   128  	src := &ipb.Source{
   129  		Ticket:     kytheuri.ToString(entries[0].Source),
   130  		Facts:      make(map[string][]byte),
   131  		EdgeGroups: make(map[string]*ipb.Source_EdgeGroup),
   132  	}
   133  
   134  	for _, e := range entries {
   135  		AppendEntry(src, e)
   136  	}
   137  
   138  	for _, group := range src.EdgeGroups {
   139  		sort.Sort(byOrdinal(group.Edges))
   140  	}
   141  
   142  	return src
   143  }
   144  
   145  // FactsToMap returns a map from fact name to value.
   146  func FactsToMap(facts []*cpb.Fact) map[string][]byte {
   147  	m := make(map[string][]byte, len(facts))
   148  	for _, f := range facts {
   149  		m[f.Name] = f.Value
   150  	}
   151  	return m
   152  }
   153  
   154  // GetFact returns the value of the first fact in facts with the given name; otherwise returns nil.
   155  func GetFact(facts []*cpb.Fact, name string) []byte {
   156  	for _, f := range facts {
   157  		if f.Name == name {
   158  			return f.Value
   159  		}
   160  	}
   161  	return nil
   162  }
   163  
   164  // PartialReverseEdges returns the set of partial reverse edges from the given source.  Each
   165  // reversed Edge has its Target fully populated and its Source will have no facts.  To ensure every
   166  // node has at least 1 Edge, the first Edge will be a self-edge without a Kind or Target.  To reduce
   167  // the size of edge sets, each Target will have any text facts filtered (see FilterTextFacts).
   168  func PartialReverseEdges(src *ipb.Source) []*srvpb.Edge {
   169  	node := Node(src)
   170  
   171  	result := []*srvpb.Edge{{
   172  		Source: node, // self-edge to ensure every node has at least 1 edge
   173  	}}
   174  
   175  	targetNode := FilterTextFacts(node)
   176  
   177  	for kind, group := range src.EdgeGroups {
   178  		rev := edges.Mirror(kind)
   179  		for _, target := range group.Edges {
   180  			result = append(result, &srvpb.Edge{
   181  				Source:  &srvpb.Node{Ticket: target.Ticket},
   182  				Kind:    rev,
   183  				Ordinal: target.Ordinal,
   184  				Target:  targetNode,
   185  			})
   186  		}
   187  	}
   188  
   189  	return result
   190  }
   191  
   192  // FilterTextFacts returns a new Node without any text facts.
   193  func FilterTextFacts(n *srvpb.Node) *srvpb.Node {
   194  	res := &srvpb.Node{
   195  		Ticket: n.Ticket,
   196  		Fact:   make([]*cpb.Fact, 0, len(n.Fact)),
   197  	}
   198  	for _, f := range n.Fact {
   199  		switch f.Name {
   200  		case facts.Text, facts.TextEncoding:
   201  			// Skip large text facts for targets
   202  		default:
   203  			res.Fact = append(res.Fact, f)
   204  		}
   205  	}
   206  	return res
   207  }
   208  
   209  // DecorationFragmentBuilder builds pieces of FileDecorations given an ordered (see AddEdge) stream
   210  // of completed Edges.  Each fragment constructed (either by AddEdge or Flush) will be emitted using
   211  // the Output function in the builder.  There are two types of fragments: file fragments (which have
   212  // their SourceText, FileTicket, and Encoding set) and decoration fragments (which have only
   213  // Decoration set).
   214  type DecorationFragmentBuilder struct {
   215  	Output func(ctx context.Context, file string, fragment *srvpb.FileDecorations) error
   216  
   217  	anchor  *srvpb.RawAnchor
   218  	targets map[string]*srvpb.Node
   219  	decor   []*srvpb.FileDecorations_Decoration
   220  	parents []string
   221  }
   222  
   223  // AddEdge adds the given edge to the current fragment (or emits some fragments and starts a new
   224  // fragment with e).  AddEdge must be called in GraphStore sorted order of the Edges with the
   225  // beginning to every set of edges with the same Source having a signaling Edge with only its Source
   226  // set (no Kind or Target).  Otherwise, every Edge must have a completed Source, Kind, and Target.
   227  // Flush must be called after every call to AddEdge in order to output any remaining fragments.
   228  func (b *DecorationFragmentBuilder) AddEdge(ctx context.Context, e *srvpb.Edge) error {
   229  	if e.Target == nil {
   230  		// Beginning of a set of edges with a new Source
   231  		if err := b.Flush(ctx); err != nil {
   232  			return err
   233  		}
   234  
   235  		srcFacts := FactsToMap(e.Source.Fact)
   236  
   237  		switch string(srcFacts[facts.NodeKind]) {
   238  		case nodes.File:
   239  			if err := b.Output(ctx, e.Source.Ticket, &srvpb.FileDecorations{
   240  				File: &srvpb.File{
   241  					Ticket:   e.Source.Ticket,
   242  					Text:     srcFacts[facts.Text],
   243  					Encoding: string(srcFacts[facts.TextEncoding]),
   244  				},
   245  			}); err != nil {
   246  				return err
   247  			}
   248  		case nodes.Anchor:
   249  			// Implicit anchors don't belong in file decorations.
   250  			if string(srcFacts[facts.Subkind]) == nodes.Implicit {
   251  				return nil
   252  			}
   253  			anchorStart, err := strconv.Atoi(string(srcFacts[facts.AnchorStart]))
   254  			if err != nil {
   255  				log.ErrorContextf(ctx, "parsing anchor start offset %q: %v",
   256  					string(srcFacts[facts.AnchorStart]), err)
   257  				return nil
   258  			}
   259  			anchorEnd, err := strconv.Atoi(string(srcFacts[facts.AnchorEnd]))
   260  			if err != nil {
   261  				log.ErrorContextf(ctx, "parsing anchor end offset %q: %v",
   262  					string(srcFacts[facts.AnchorEnd]), err)
   263  				return nil
   264  			}
   265  			// Record the parent file for the anchor.
   266  			parentFile, err := tickets.AnchorFile(e.Source.Ticket)
   267  			if err != nil {
   268  				log.ErrorContextf(ctx, "deriving anchor ticket for %q: %v", e.Source.Ticket, err)
   269  			} else {
   270  				b.parents = append(b.parents, parentFile)
   271  			}
   272  
   273  			// Ignore errors; offsets will just be zero
   274  			snippetStart, _ := strconv.Atoi(string(srcFacts[facts.SnippetStart]))
   275  			snippetEnd, _ := strconv.Atoi(string(srcFacts[facts.SnippetEnd]))
   276  
   277  			b.anchor = &srvpb.RawAnchor{
   278  				Ticket:       e.Source.Ticket,
   279  				StartOffset:  int32(anchorStart),
   280  				EndOffset:    int32(anchorEnd),
   281  				SnippetStart: int32(snippetStart),
   282  				SnippetEnd:   int32(snippetEnd),
   283  			}
   284  			b.targets = make(map[string]*srvpb.Node)
   285  		}
   286  		return nil
   287  	} else if b.anchor == nil {
   288  		// We don't care about edges for non-anchors
   289  		return nil
   290  	}
   291  
   292  	if e.Kind != edges.ChildOf {
   293  		b.decor = append(b.decor, &srvpb.FileDecorations_Decoration{
   294  			Anchor: b.anchor,
   295  			Kind:   e.Kind,
   296  			Target: e.Target.Ticket,
   297  		})
   298  
   299  		if _, ok := b.targets[e.Target.Ticket]; !ok {
   300  			b.targets[e.Target.Ticket] = e.Target
   301  		}
   302  
   303  		if len(b.parents) > 0 {
   304  			fd := &srvpb.FileDecorations{Decoration: b.decor}
   305  			for _, n := range b.targets {
   306  				fd.Target = append(fd.Target, n)
   307  			}
   308  			sort.Sort(ByTicket(fd.Target))
   309  			for _, parent := range b.parents {
   310  				if err := b.Output(ctx, parent, fd); err != nil {
   311  					return err
   312  				}
   313  			}
   314  			b.decor = nil
   315  			b.targets = make(map[string]*srvpb.Node)
   316  		}
   317  	}
   318  
   319  	return nil
   320  }
   321  
   322  // Flush outputs any remaining fragments that are being built.  It is safe, but usually unnecessary,
   323  // to call Flush in between sets of Edges with the same Source.  This also means that
   324  // DecorationFragmentBuilder can be used to construct decoration fragments in parallel by
   325  // partitioning edges along the same boundaries.
   326  func (b *DecorationFragmentBuilder) Flush(ctx context.Context) error {
   327  	defer func() {
   328  		b.anchor = nil
   329  		b.decor = nil
   330  		b.parents = nil
   331  	}()
   332  
   333  	if len(b.decor) > 0 && len(b.parents) > 0 {
   334  		fd := &srvpb.FileDecorations{Decoration: b.decor}
   335  		for _, parent := range b.parents {
   336  			if err := b.Output(ctx, parent, fd); err != nil {
   337  				return err
   338  			}
   339  		}
   340  	}
   341  	return nil
   342  }
   343  
   344  // ByOffset sorts file decorations by their byte offsets.
   345  type ByOffset []*srvpb.FileDecorations_Decoration
   346  
   347  func (s ByOffset) Len() int      { return len(s) }
   348  func (s ByOffset) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
   349  func (s ByOffset) Less(i, j int) bool {
   350  	if s[i].Anchor.StartOffset == s[j].Anchor.StartOffset {
   351  		if s[i].Anchor.EndOffset == s[j].Anchor.EndOffset {
   352  			if s[i].Kind == s[j].Kind {
   353  				if s[i].Target == s[j].Target {
   354  					return s[i].Anchor.Ticket < s[j].Anchor.Ticket
   355  				}
   356  				return s[i].Target < s[j].Target
   357  			}
   358  			return s[i].Kind < s[j].Kind
   359  		}
   360  		return s[i].Anchor.EndOffset < s[j].Anchor.EndOffset
   361  	}
   362  	return s[i].Anchor.StartOffset < s[j].Anchor.StartOffset
   363  }
   364  
   365  // ByTicket sorts nodes by their ticket.
   366  type ByTicket []*srvpb.Node
   367  
   368  func (s ByTicket) Len() int           { return len(s) }
   369  func (s ByTicket) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
   370  func (s ByTicket) Less(i, j int) bool { return s[i].Ticket < s[j].Ticket }
   371  
   372  // ByAnchorTicket sorts anchors by their ticket.
   373  type ByAnchorTicket []*srvpb.ExpandedAnchor
   374  
   375  func (s ByAnchorTicket) Len() int           { return len(s) }
   376  func (s ByAnchorTicket) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
   377  func (s ByAnchorTicket) Less(i, j int) bool { return s[i].Ticket < s[j].Ticket }
   378  
   379  // EdgeSetBuilder constructs a set of PagedEdgeSets and EdgePages from a
   380  // sequence of Nodes and EdgeSet_Groups.  For each set of groups with the same
   381  // source, a call to StartEdgeSet must precede.  All EdgeSet_Groups for the same
   382  // source are then assumed to be given sequentially to AddGroup, secondarily
   383  // ordered by the group's edge kind.  If given in this order, Output will be
   384  // given exactly 1 PagedEdgeSet per source with as few EdgeSet_Group per edge
   385  // kind as to satisfy MaxEdgePageSize (MaxEdgePageSize == 0 indicates that there
   386  // will be exactly 1 edge group per edge kind).  If not given in this order, no
   387  // guarantees can be made.  Flush must be called after the final call to
   388  // AddGroup.
   389  type EdgeSetBuilder struct {
   390  	// MaxEdgePageSize is maximum number of edges that are allowed in the
   391  	// PagedEdgeSet and any EdgePage.  If MaxEdgePageSize <= 0, no paging is
   392  	// attempted.
   393  	MaxEdgePageSize int
   394  
   395  	// Output is used to emit each PagedEdgeSet constructed.
   396  	Output func(context.Context, *srvpb.PagedEdgeSet) error
   397  	// OutputPage is used to emit each EdgePage constructed.
   398  	OutputPage func(context.Context, *srvpb.EdgePage) error
   399  
   400  	pager *pager.SetPager
   401  }
   402  
   403  func (b *EdgeSetBuilder) constructPager() *pager.SetPager {
   404  	// Head:  *srvpb.Node
   405  	// Set:   *srvpb.PagedEdgeSet
   406  	// Group: *srvpb.EdgeGroup
   407  	return &pager.SetPager{
   408  		MaxPageSize: b.MaxEdgePageSize,
   409  
   410  		NewSet: func(hd pager.Head) pager.Set {
   411  			return &srvpb.PagedEdgeSet{
   412  				Source: hd.(*srvpb.Node),
   413  			}
   414  		},
   415  		Combine: func(l, r pager.Group) pager.Group {
   416  			lg, rg := l.(*srvpb.EdgeGroup), r.(*srvpb.EdgeGroup)
   417  			if lg.Kind != rg.Kind {
   418  				return nil
   419  			}
   420  			lg.Edge = append(lg.Edge, rg.Edge...)
   421  			return lg
   422  		},
   423  		Split: func(sz int, g pager.Group) (l, r pager.Group) {
   424  			eg := g.(*srvpb.EdgeGroup)
   425  			neg := &srvpb.EdgeGroup{
   426  				Kind: eg.Kind,
   427  				Edge: eg.Edge[:sz],
   428  			}
   429  			eg.Edge = eg.Edge[sz:]
   430  			return neg, eg
   431  		},
   432  		Size: func(g pager.Group) int { return len(g.(*srvpb.EdgeGroup).Edge) },
   433  
   434  		OutputSet: func(ctx context.Context, total int, s pager.Set, grps []pager.Group) error {
   435  			pes := s.(*srvpb.PagedEdgeSet)
   436  
   437  			// pes.Group = []*srvpb.EdgeGroup(grps)
   438  			pes.Group = make([]*srvpb.EdgeGroup, len(grps))
   439  			for i, g := range grps {
   440  				pes.Group[i] = g.(*srvpb.EdgeGroup)
   441  			}
   442  
   443  			sort.Sort(byEdgeKind(pes.Group))
   444  			sort.Sort(byPageKind(pes.PageIndex))
   445  			pes.TotalEdges = int32(total)
   446  
   447  			return b.Output(ctx, pes)
   448  		},
   449  		OutputPage: func(ctx context.Context, s pager.Set, g pager.Group) error {
   450  			pes := s.(*srvpb.PagedEdgeSet)
   451  			eviction := g.(*srvpb.EdgeGroup)
   452  
   453  			src := pes.Source.Ticket
   454  			key := newPageKey(src, len(pes.PageIndex))
   455  
   456  			// Output the EdgePage and add it to the page indices
   457  			if err := b.OutputPage(ctx, &srvpb.EdgePage{
   458  				PageKey:      key,
   459  				SourceTicket: src,
   460  				EdgesGroup:   eviction,
   461  			}); err != nil {
   462  				return fmt.Errorf("error emitting EdgePage: %v", err)
   463  			}
   464  			pes.PageIndex = append(pes.PageIndex, &srvpb.PageIndex{
   465  				PageKey:   key,
   466  				EdgeKind:  eviction.Kind,
   467  				EdgeCount: int32(len(eviction.Edge)),
   468  			})
   469  			return nil
   470  		},
   471  	}
   472  }
   473  
   474  // StartEdgeSet begins a new EdgeSet for the given source node, possibly
   475  // emitting a PagedEdgeSet for the previous EdgeSet.  Each following call to
   476  // AddGroup adds the group to this new EdgeSet until another call to
   477  // StartEdgeSet is made.
   478  func (b *EdgeSetBuilder) StartEdgeSet(ctx context.Context, src *srvpb.Node) error {
   479  	if b.pager == nil {
   480  		b.pager = b.constructPager()
   481  	}
   482  	return b.pager.StartSet(ctx, src)
   483  }
   484  
   485  // AddGroup adds a EdgeSet_Group to current EdgeSet being built, possibly
   486  // emitting a new PagedEdgeSet and/or EdgePage.  StartEdgeSet must be called
   487  // before any calls to this method.  See EdgeSetBuilder's documentation for the
   488  // assumed order of the groups and this method's relation to StartEdgeSet.
   489  func (b *EdgeSetBuilder) AddGroup(ctx context.Context, eg *srvpb.EdgeGroup) error {
   490  	return b.pager.AddGroup(ctx, eg)
   491  }
   492  
   493  // Flush signals the end of the current PagedEdgeSet being built, flushing it,
   494  // and its EdgeSet_Groups to the output function.  This should be called after
   495  // the final call to AddGroup.  Manually calling Flush at any other time is
   496  // unnecessary.
   497  func (b *EdgeSetBuilder) Flush(ctx context.Context) error { return b.pager.Flush(ctx) }
   498  
   499  // CrossReferencesBuilder is a type wrapper around a pager.SetPager that emits
   500  // *srvpb.PagedCrossReferences and *srvpb.PagedCrossReferences_Pages.  Each
   501  // PagedCrossReferences_Group added the builder should be in sorted order so
   502  // that groups of the same kind are added sequentially.  Before each set of
   503  // like-kinded groups, StartSet should be called with the source ticket of the
   504  // proceeding groups.  See also EdgeSetBuilder.
   505  type CrossReferencesBuilder struct {
   506  	MaxPageSize int
   507  
   508  	Output     func(context.Context, *srvpb.PagedCrossReferences) error
   509  	OutputPage func(context.Context, *srvpb.PagedCrossReferences_Page) error
   510  
   511  	pager *pager.SetPager
   512  }
   513  
   514  func (b *CrossReferencesBuilder) constructPager() *pager.SetPager {
   515  	// Head:  *srvpb.Node
   516  	// Set:   *srvpb.PagedCrossReferences
   517  	// Group: *srvpb.PagedCrossReferences_Group
   518  	// Page:  *srvpb.PagedCrossReferences_Page
   519  	return &pager.SetPager{
   520  		MaxPageSize: b.MaxPageSize,
   521  
   522  		NewSet: func(hd pager.Head) pager.Set {
   523  			n := hd.(*srvpb.Node)
   524  			var incomplete bool
   525  			for _, f := range n.Fact {
   526  				if f.Name == facts.Complete && string(f.Value) != "definition" {
   527  					incomplete = true
   528  				}
   529  			}
   530  			return &srvpb.PagedCrossReferences{
   531  				SourceTicket: n.Ticket,
   532  				Incomplete:   incomplete,
   533  			}
   534  		},
   535  		Combine: func(l, r pager.Group) pager.Group {
   536  			lg, rg := l.(*srvpb.PagedCrossReferences_Group), r.(*srvpb.PagedCrossReferences_Group)
   537  			if lg.Kind != rg.Kind {
   538  				return nil
   539  			}
   540  			lg.Anchor = append(lg.Anchor, rg.Anchor...)
   541  			return lg
   542  		},
   543  		Split: func(sz int, g pager.Group) (l, r pager.Group) {
   544  			og := g.(*srvpb.PagedCrossReferences_Group)
   545  			ng := &srvpb.PagedCrossReferences_Group{
   546  				Kind:   og.Kind,
   547  				Anchor: og.Anchor[:sz],
   548  			}
   549  			og.Anchor = og.Anchor[sz:]
   550  			return ng, og
   551  		},
   552  		Size: func(g pager.Group) int { return len(g.(*srvpb.PagedCrossReferences_Group).Anchor) },
   553  
   554  		OutputSet: func(ctx context.Context, total int, s pager.Set, grps []pager.Group) error {
   555  			xs := s.(*srvpb.PagedCrossReferences)
   556  
   557  			// xs.Group = grps.([]*srvpb.PagedCrossReferences_Group)
   558  			xs.Group = make([]*srvpb.PagedCrossReferences_Group, len(grps))
   559  			for i, g := range grps {
   560  				xs.Group[i] = g.(*srvpb.PagedCrossReferences_Group)
   561  			}
   562  
   563  			sort.Sort(byRefKind(xs.Group))
   564  			sort.Sort(byRefPageKind(xs.PageIndex))
   565  			xs.TotalReferences = int32(total)
   566  
   567  			return b.Output(ctx, xs)
   568  		},
   569  		OutputPage: func(ctx context.Context, s pager.Set, g pager.Group) error {
   570  			xs, xg := s.(*srvpb.PagedCrossReferences), g.(*srvpb.PagedCrossReferences_Group)
   571  
   572  			key := newPageKey(xs.SourceTicket, len(xs.PageIndex))
   573  
   574  			pg := &srvpb.PagedCrossReferences_Page{
   575  				PageKey:      key,
   576  				SourceTicket: xs.SourceTicket,
   577  				Group:        xg,
   578  			}
   579  			xs.PageIndex = append(xs.PageIndex, &srvpb.PagedCrossReferences_PageIndex{
   580  				PageKey: key,
   581  				Kind:    xg.Kind,
   582  				Count:   int32(len(xg.Anchor)),
   583  			})
   584  			return b.OutputPage(ctx, pg)
   585  		},
   586  	}
   587  }
   588  
   589  // StartSet begins a new *srvpb.PagedCrossReferences.  As a side-effect, a
   590  // previously-built srvpb.PagedCrossReferences may be emitted.
   591  func (b *CrossReferencesBuilder) StartSet(ctx context.Context, src *srvpb.Node) error {
   592  	if b.pager == nil {
   593  		b.pager = b.constructPager()
   594  	}
   595  	return b.pager.StartSet(ctx, src)
   596  }
   597  
   598  // AddGroup add the given group of cross-references to the currently being built
   599  // *srvpb.PagedCrossReferences.  The group should share the same source ticket
   600  // as given to the mostly recent invocation to StartSet.
   601  func (b *CrossReferencesBuilder) AddGroup(ctx context.Context, g *srvpb.PagedCrossReferences_Group) error {
   602  	return b.pager.AddGroup(ctx, g)
   603  }
   604  
   605  // Flush emits any *srvpb.PagedCrossReferences and
   606  // *srvpb.PagedCrossReferences_Page currently being built.
   607  func (b *CrossReferencesBuilder) Flush(ctx context.Context) error { return b.pager.Flush(ctx) }
   608  
   609  func newPageKey(src string, n int) string { return fmt.Sprintf("%s.%.10d", src, n) }
   610  
   611  // CrossReference returns a (Referent, TargetAnchor) *ipb.CrossReference
   612  // equivalent to the given decoration.  The decoration's anchor is expanded
   613  // given its parent file and associated Normalizer.
   614  func CrossReference(file *srvpb.File, norm *span.Normalizer, d *srvpb.FileDecorations_Decoration, tgt *srvpb.Node) (*ipb.CrossReference, error) {
   615  	if file == nil || norm == nil {
   616  		return nil, errors.New("missing decoration's parent file")
   617  	}
   618  
   619  	ea, err := ExpandAnchor(d.Anchor, file, norm, edges.Mirror(d.Kind))
   620  	if err != nil {
   621  		return nil, fmt.Errorf("error expanding anchor {%+v}: %v", d.Anchor, err)
   622  	}
   623  	// Throw away most of the referent's facts.  They are not needed.
   624  	var selected []*cpb.Fact
   625  	if tgt != nil {
   626  		for _, fact := range tgt.Fact {
   627  			if fact.Name == facts.Complete {
   628  				selected = append(selected, fact)
   629  			}
   630  		}
   631  	}
   632  	return &ipb.CrossReference{
   633  		Referent: &srvpb.Node{
   634  			Ticket: d.Target,
   635  			Fact:   selected,
   636  		},
   637  		TargetAnchor: ea,
   638  	}, nil
   639  }
   640  
   641  // ExpandAnchor returns the ExpandedAnchor equivalent of the given RawAnchor
   642  // where file (and its associated Normalizer) must be the anchor's parent file.
   643  func ExpandAnchor(anchor *srvpb.RawAnchor, file *srvpb.File, norm *span.Normalizer, kind string) (*srvpb.ExpandedAnchor, error) {
   644  	if err := checkSpan(len(file.Text), anchor.StartOffset, anchor.EndOffset); err != nil {
   645  		return nil, fmt.Errorf("invalid text offsets: %v", err)
   646  	}
   647  
   648  	sp := norm.ByteOffset(anchor.StartOffset)
   649  	ep := norm.ByteOffset(anchor.EndOffset)
   650  	txt, err := getText(sp, ep, file)
   651  	if err != nil {
   652  		return nil, fmt.Errorf("error getting anchor text: %v", err)
   653  	}
   654  
   655  	var snippet string
   656  	var ssp, sep *cpb.Point
   657  	if anchor.SnippetStart != 0 || anchor.SnippetEnd != 0 {
   658  		if err := checkSpan(len(file.Text), anchor.SnippetStart, anchor.SnippetEnd); err != nil {
   659  			return nil, fmt.Errorf("invalid snippet offsets: %v", err)
   660  		}
   661  
   662  		ssp = norm.ByteOffset(anchor.SnippetStart)
   663  		sep = norm.ByteOffset(anchor.SnippetEnd)
   664  		snippet, err = getText(ssp, sep, file)
   665  		if err != nil {
   666  			return nil, fmt.Errorf("error getting text for snippet: %v", err)
   667  		}
   668  	} else {
   669  		// fallback to a line-based snippet if the indexer did not provide its own snippet offsets
   670  		ssp = &cpb.Point{
   671  			ByteOffset: sp.ByteOffset - sp.ColumnOffset,
   672  			LineNumber: sp.LineNumber,
   673  		}
   674  		nextLine := norm.Point(&cpb.Point{LineNumber: sp.LineNumber + 1})
   675  		if nextLine.ByteOffset <= ssp.ByteOffset { // double-check ssp != EOF
   676  			return nil, errors.New("anchor past EOF")
   677  		}
   678  		sep = &cpb.Point{
   679  			ByteOffset:   nextLine.ByteOffset - 1,
   680  			LineNumber:   sp.LineNumber,
   681  			ColumnOffset: sp.ColumnOffset + (nextLine.ByteOffset - sp.ByteOffset - 1),
   682  		}
   683  		snippet, err = getText(ssp, sep, file)
   684  		if err != nil {
   685  			return nil, fmt.Errorf("error getting text for line snippet: %v", err)
   686  		}
   687  	}
   688  
   689  	return &srvpb.ExpandedAnchor{
   690  		Ticket: anchor.Ticket,
   691  		Kind:   kind,
   692  
   693  		Text: txt,
   694  		Span: &cpb.Span{
   695  			Start: p2p(sp),
   696  			End:   p2p(ep),
   697  		},
   698  
   699  		Snippet: snippet,
   700  		SnippetSpan: &cpb.Span{
   701  			Start: p2p(ssp),
   702  			End:   p2p(sep),
   703  		},
   704  
   705  		BuildConfiguration: anchor.BuildConfiguration,
   706  	}, nil
   707  }
   708  
   709  func checkSpan(textLen int, start, end int32) error {
   710  	if int(end) > textLen {
   711  		return fmt.Errorf("span past EOF %d: [%d, %d)", textLen, start, end)
   712  	} else if start < 0 {
   713  		return fmt.Errorf("negative span: [%d, %d)", start, end)
   714  	} else if start > end {
   715  		return fmt.Errorf("crossed span: [%d, %d)", start, end)
   716  	}
   717  	return nil
   718  }
   719  
   720  func getText(sp, ep *cpb.Point, file *srvpb.File) (string, error) {
   721  	txt, err := text.ToUTF8(file.Encoding, file.Text[sp.ByteOffset:ep.ByteOffset])
   722  	if err != nil {
   723  		return "", fmt.Errorf("unable to decode file text: %v", err)
   724  	}
   725  	return txt, nil
   726  }
   727  
   728  func p2p(p *cpb.Point) *cpb.Point {
   729  	return &cpb.Point{
   730  		ByteOffset:   p.ByteOffset,
   731  		LineNumber:   p.LineNumber,
   732  		ColumnOffset: p.ColumnOffset,
   733  	}
   734  }
   735  
   736  var edgeOrdering = []string{
   737  	edges.Defines,
   738  	edges.Documents,
   739  	edges.Ref,
   740  	edges.Named,
   741  	edges.Typed,
   742  }
   743  
   744  func edgeKindLess(kind1, kind2 string) bool {
   745  	// General ordering:
   746  	//   anchor edge kinds before non-anchor edge kinds
   747  	//   forward edges before reverse edges
   748  	//   edgeOrdering[i] (and variants) before edgeOrdering[i+1:]
   749  	//   edge variants after root edge kind (ordered lexicographically)
   750  	//   otherwise, order lexicographically
   751  
   752  	if kind1 == kind2 {
   753  		return false
   754  	} else if a1, a2 := edges.IsAnchorEdge(kind1), edges.IsAnchorEdge(kind2); a1 != a2 {
   755  		return a1
   756  	} else if d1, d2 := edges.IsForward(kind1), edges.IsForward(kind2); d1 != d2 {
   757  		return d1
   758  	}
   759  	kind1, kind2 = edges.Canonical(kind1), edges.Canonical(kind2)
   760  	for _, kind := range edgeOrdering {
   761  		if kind1 == kind {
   762  			return true
   763  		} else if kind2 == kind {
   764  			return false
   765  		} else if v1, v2 := edges.IsVariant(kind1, kind), edges.IsVariant(kind2, kind); v1 != v2 {
   766  			return v1
   767  		} else if v1 {
   768  			return kind1 < kind2
   769  		}
   770  	}
   771  	return kind1 < kind2
   772  }
   773  
   774  // byPageKind implements the sort.Interface
   775  type byPageKind []*srvpb.PageIndex
   776  
   777  // Implement the sort.Interface using edgeKindLess
   778  func (s byPageKind) Len() int           { return len(s) }
   779  func (s byPageKind) Less(i, j int) bool { return edgeKindLess(s[i].EdgeKind, s[j].EdgeKind) }
   780  func (s byPageKind) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
   781  
   782  // byEdgeKind implements the sort.Interface
   783  type byEdgeKind []*srvpb.EdgeGroup
   784  
   785  // Implement the sort.Interface using edgeKindLess
   786  func (s byEdgeKind) Len() int           { return len(s) }
   787  func (s byEdgeKind) Less(i, j int) bool { return edgeKindLess(s[i].Kind, s[j].Kind) }
   788  func (s byEdgeKind) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
   789  
   790  // byRefPageKind implements the sort.Interface
   791  type byRefPageKind []*srvpb.PagedCrossReferences_PageIndex
   792  
   793  // Implement the sort.Interface using edgeKindLess
   794  func (s byRefPageKind) Len() int           { return len(s) }
   795  func (s byRefPageKind) Less(i, j int) bool { return edgeKindLess(s[i].Kind, s[j].Kind) }
   796  func (s byRefPageKind) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
   797  
   798  // byRefKind implements the sort.Interface
   799  type byRefKind []*srvpb.PagedCrossReferences_Group
   800  
   801  // Implement the sort.Interface using edgeKindLess
   802  func (s byRefKind) Len() int           { return len(s) }
   803  func (s byRefKind) Less(i, j int) bool { return edgeKindLess(s[i].Kind, s[j].Kind) }
   804  func (s byRefKind) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
   805  
   806  // byOrdinal sorts edges by their ordinals
   807  type byOrdinal []*ipb.Source_Edge
   808  
   809  func (s byOrdinal) Len() int      { return len(s) }
   810  func (s byOrdinal) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
   811  func (s byOrdinal) Less(i, j int) bool {
   812  	if s[i].Ordinal == s[j].Ordinal {
   813  		return s[i].Ticket < s[j].Ticket
   814  	}
   815  	return s[i].Ordinal < s[j].Ordinal
   816  }