forked from owenthereal/ccat
-
Notifications
You must be signed in to change notification settings - Fork 0
/
unit_files_index.go
184 lines (162 loc) · 4.35 KB
/
unit_files_index.go
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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
package store
import (
"fmt"
"io"
"path"
"github.com/jingweno/ccat/Godeps/_workspace/src/github.com/alecthomas/binary"
"github.com/jingweno/ccat/Godeps/_workspace/src/sourcegraph.com/sourcegraph/srclib/store/phtable"
"github.com/jingweno/ccat/Godeps/_workspace/src/sourcegraph.com/sourcegraph/srclib/unit"
)
// NOTE(sqs): There is a lot of duplication here with defFilesIndex.
// unitFilesIndex makes it fast to determine which source units
// contain a file (or files in a dir).
type unitFilesIndex struct {
phtable *phtable.CHD
ready bool
}
var _ interface {
Index
persistedIndex
unitIndexBuilder
unitIndex
} = (*unitFilesIndex)(nil)
var c_unitFilesIndex_getByPath = 0 // counter
func (x *unitFilesIndex) String() string { return fmt.Sprintf("unitFilesIndex(ready=%v)", x.ready) }
// getByFile returns a list of source units that contain the file
// specified by the path. The path can also be a directory, in which
// case all source units that contain files underneath that directory
// are returned.
func (x *unitFilesIndex) getByPath(path string) ([]unit.ID2, bool, error) {
vlog.Printf("unitFilesIndex.getByPath(%s)", path)
c_unitFilesIndex_getByPath++
if x.phtable == nil {
panic("phtable not built/read")
}
v := x.phtable.Get([]byte(path))
if v == nil {
return nil, false, nil
}
var us []unit.ID2
if err := binary.Unmarshal(v, &us); err != nil {
return nil, true, err
}
return us, true, nil
}
// Covers implements unitIndex.
func (x *unitFilesIndex) Covers(filters interface{}) int {
cov := 0
for _, f := range storeFilters(filters) {
if _, ok := f.(ByFilesFilter); ok {
cov++
}
}
return cov
}
// Units implements unitIndex.
func (x *unitFilesIndex) Units(fs ...UnitFilter) ([]unit.ID2, error) {
for _, f := range fs {
if ff, ok := f.(ByFilesFilter); ok {
files := ff.ByFiles()
umap := map[unit.ID2]struct{}{}
for _, file := range files {
u, _, err := x.getByPath(file)
if err != nil {
return nil, err
}
for _, uu := range u {
umap[uu] = struct{}{}
}
}
us := make([]unit.ID2, 0, len(umap))
for u := range umap {
us = append(us, u)
}
vlog.Printf("unitFilesIndex(%v): Found units %v using index.", fs, us)
return us, nil
}
}
return nil, nil
}
// Build implements unitIndexBuilder.
func (x *unitFilesIndex) Build(units []*unit.SourceUnit) error {
vlog.Printf("unitFilesIndex: building index...")
f2u := make(filesToUnits, len(units)*10)
for _, u := range units {
for _, f := range u.Files {
f2u.add(f, u.ID2())
}
}
b := phtable.Builder(len(f2u))
for file, fileUnits := range f2u {
ub, err := binary.Marshal(fileUnits)
if err != nil {
return err
}
b.Add([]byte(file), ub)
}
h, err := b.Build()
if err != nil {
return err
}
x.phtable = h
x.ready = true
vlog.Printf("unitFilesIndex: done building index.")
return nil
}
// filesToUnits is a helper type used by unitFilesIndex.Build that
// adds parent dirs of each file to the mapping as well.
//
// TODO(sqs): lots of duplication with filesToDefOfs
type filesToUnits map[string][]unit.ID2
// add appends u to file's list of units, as well as the list of units
// for each of file's ancestor dirs.
func (v filesToUnits) add(file string, u unit.ID2) {
file = path.Clean(file)
v[file] = append(v[file], u)
for _, dir := range ancestorDirsExceptRoot(file) {
v.addIfNotExists(dir, u)
}
}
// addIfNotExists appends u to dir's list of units, if u is not
// already present in the list.
func (v filesToUnits) addIfNotExists(dir string, u unit.ID2) {
for _, uu := range v[dir] {
if u == uu {
return
}
}
v[dir] = append(v[dir], u)
}
// ancestorDirsExceptRoot returns a list of p's ancestor directories
// excluding the root ("." or "/").
func ancestorDirsExceptRoot(p string) []string {
if p == "" {
return nil
}
if len(p) == 1 && (p[0] == '.' || p[0] == '/') {
return nil
}
var dirs []string
for i, c := range p {
if c == '/' {
dirs = append(dirs, p[:i])
}
}
return dirs
}
// Write implements persistedIndex.
func (x *unitFilesIndex) Write(w io.Writer) error {
if x.phtable == nil {
panic("no phtable to write")
}
return x.phtable.Write(w)
}
// Read implements persistedIndex.
func (x *unitFilesIndex) Read(r io.Reader) error {
var err error
x.phtable, err = phtable.Read(r)
x.ready = (err == nil)
return err
}
// Ready implements persistedIndex.
func (x *unitFilesIndex) Ready() bool { return x.ready }