summaryrefslogtreecommitdiff
path: root/workhorse/internal/lsif_transformer/parser/references.go
blob: 39c34105fd1473972fc13aa90d1a8def928fde61 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
package parser

import (
	"strconv"
)

type ReferencesOffset struct {
	Id  Id
	Len int32
}

type References struct {
	Items           *cache
	Offsets         *cache
	CurrentOffsetId Id
}

type SerializedReference struct {
	Path string `json:"path"`
}

func NewReferences(config Config) (*References, error) {
	tempPath := config.TempPath

	items, err := newCache(tempPath, "references", Item{})
	if err != nil {
		return nil, err
	}

	offsets, err := newCache(tempPath, "references-offsets", ReferencesOffset{})
	if err != nil {
		return nil, err
	}

	return &References{
		Items:           items,
		Offsets:         offsets,
		CurrentOffsetId: 0,
	}, nil
}

// Store is responsible for keeping track of references that will be used when
// serializing in `For`.
//
// The references are stored in a file to cache them. It is like
// `map[Id][]Item` (where `Id` is `refId`) but relies on caching the array and
// its offset in files for storage to reduce RAM usage. The items can be
// fetched by calling `getItems`.
func (r *References) Store(refId Id, references []Item) error {
	size := len(references)

	if size == 0 {
		return nil
	}

	items := append(r.getItems(refId), references...)
	err := r.Items.SetEntry(r.CurrentOffsetId, items)
	if err != nil {
		return err
	}

	size = len(items)
	r.Offsets.SetEntry(refId, ReferencesOffset{Id: r.CurrentOffsetId, Len: int32(size)})
	r.CurrentOffsetId += Id(size)

	return nil
}

func (r *References) For(docs map[Id]string, refId Id) []SerializedReference {
	references := r.getItems(refId)
	if references == nil {
		return nil
	}

	var serializedReferences []SerializedReference

	for _, reference := range references {
		serializedReference := SerializedReference{
			Path: docs[reference.DocId] + "#L" + strconv.Itoa(int(reference.Line)),
		}

		serializedReferences = append(serializedReferences, serializedReference)
	}

	return serializedReferences
}

func (r *References) Close() error {
	for _, err := range []error{
		r.Items.Close(),
		r.Offsets.Close(),
	} {
		if err != nil {
			return err
		}
	}
	return nil
}

func (r *References) getItems(refId Id) []Item {
	var offset ReferencesOffset
	if err := r.Offsets.Entry(refId, &offset); err != nil || offset.Len == 0 {
		return nil
	}

	items := make([]Item, offset.Len)
	if err := r.Items.Entry(offset.Id, &items); err != nil {
		return nil
	}

	return items
}