godoc: update Index.{Write,Reader}, add tests

R=golang-dev, adg
CC=golang-dev
https://golang.org/cl/24490043
This commit is contained in:
Brad Fitzpatrick 2013-11-12 14:58:47 -08:00
Родитель f339918a70
Коммит 515bcdc536
2 изменённых файлов: 115 добавлений и 21 удалений

Просмотреть файл

@ -880,11 +880,22 @@ func NewIndex(c *Corpus, dirnames <-chan string, fulltextIndex bool, throttle fl
}
}
var ErrFileIndexVersion = errors.New("file index version out of date")
const fileIndexVersion = 2
// fileIndex is the subset of Index that's gob-encoded for use by
// Index.Write and Index.Read.
type fileIndex struct {
Words map[string]*LookupResult
Alts map[string]*AltWords
Snippets []*Snippet
Fulltext bool
Version int
Words map[string]*LookupResult
Alts map[string]*AltWords
Snippets []*Snippet
Fulltext bool
Stats Statistics
ImportCount map[string]int
PackagePath map[string]map[string]bool
Exports map[string]map[string]SpotKind
}
func (x *fileIndex) Write(w io.Writer) error {
@ -895,63 +906,79 @@ func (x *fileIndex) Read(r io.Reader) error {
return gob.NewDecoder(r).Decode(x)
}
// Write writes the index x to w.
func (x *Index) Write(w io.Writer) error {
// WriteTo writes the index x to w.
func (x *Index) WriteTo(w io.Writer) (n int64, err error) {
w = countingWriter{&n, w}
fulltext := false
if x.suffixes != nil {
fulltext = true
}
fx := fileIndex{
x.words,
x.alts,
x.snippets,
fulltext,
Version: fileIndexVersion,
Words: x.words,
Alts: x.alts,
Snippets: x.snippets,
Fulltext: fulltext,
Stats: x.stats,
ImportCount: x.importCount,
PackagePath: x.packagePath,
Exports: x.exports,
}
if err := fx.Write(w); err != nil {
return err
return 0, err
}
if fulltext {
encode := func(x interface{}) error {
return gob.NewEncoder(w).Encode(x)
}
if err := x.fset.Write(encode); err != nil {
return err
return 0, err
}
if err := x.suffixes.Write(w); err != nil {
return err
return 0, err
}
}
return nil
return n, nil
}
// Read reads the index from r into x; x must not be nil.
// If r does not also implement io.ByteReader, it will be wrapped in a bufio.Reader.
func (x *Index) Read(r io.Reader) error {
// If the index is from an old version, the error is ErrFileIndexVersion.
func (x *Index) ReadFrom(r io.Reader) (n int64, err error) {
// We use the ability to read bytes as a plausible surrogate for buffering.
if _, ok := r.(io.ByteReader); !ok {
r = bufio.NewReader(r)
}
r = countingReader{&n, r.(byteReader)}
var fx fileIndex
if err := fx.Read(r); err != nil {
return err
return n, err
}
if fx.Version != fileIndexVersion {
return 0, ErrFileIndexVersion
}
x.words = fx.Words
x.alts = fx.Alts
x.snippets = fx.Snippets
x.stats = fx.Stats
x.importCount = fx.ImportCount
x.packagePath = fx.PackagePath
x.exports = fx.Exports
if fx.Fulltext {
x.fset = token.NewFileSet()
decode := func(x interface{}) error {
return gob.NewDecoder(r).Decode(x)
}
if err := x.fset.Read(decode); err != nil {
return err
return n, err
}
x.suffixes = new(suffixarray.Index)
if err := x.suffixes.Read(r); err != nil {
return err
return n, err
}
}
return nil
return n, nil
}
// Stats returns index statistics.
@ -1204,7 +1231,7 @@ func (c *Corpus) readIndex(filenames string) error {
files = append(files, f)
}
x := new(Index)
if err := x.Read(io.MultiReader(files...)); err != nil {
if _, err := x.ReadFrom(io.MultiReader(files...)); err != nil {
return err
}
c.searchIndex.Set(x)
@ -1269,3 +1296,36 @@ func (c *Corpus) RunIndexer() {
time.Sleep(delay)
}
}
type countingWriter struct {
n *int64
w io.Writer
}
func (c countingWriter) Write(p []byte) (n int, err error) {
n, err = c.w.Write(p)
*c.n += int64(n)
return
}
type byteReader interface {
io.Reader
io.ByteReader
}
type countingReader struct {
n *int64
r byteReader
}
func (c countingReader) Read(p []byte) (n int, err error) {
n, err = c.r.Read(p)
*c.n += int64(n)
return
}
func (c countingReader) ReadByte() (b byte, err error) {
b, err = c.r.ReadByte()
*c.n += 1
return
}

Просмотреть файл

@ -5,6 +5,7 @@
package godoc
import (
"bytes"
"reflect"
"strings"
"testing"
@ -12,7 +13,7 @@ import (
"code.google.com/p/go.tools/godoc/vfs/mapfs"
)
func TestIndex(t *testing.T) {
func newCorpus(t *testing.T) *Corpus {
c := NewCorpus(mapfs.New(map[string]string{
"src/pkg/foo/foo.go": `// Package foo is an example.
package foo
@ -46,13 +47,46 @@ func Skip() {}
if err := c.Init(); err != nil {
t.Fatal(err)
}
return c
}
func TestIndex(t *testing.T) {
c := newCorpus(t)
c.UpdateIndex()
ix, _ := c.CurrentIndex()
if ix == nil {
t.Fatal("no index")
}
t.Logf("Got: %#v", ix)
testIndex(t, ix)
}
func TestIndexWriteRead(t *testing.T) {
c := newCorpus(t)
c.UpdateIndex()
ix, _ := c.CurrentIndex()
if ix == nil {
t.Fatal("no index")
}
var buf bytes.Buffer
nw, err := ix.WriteTo(&buf)
if err != nil {
t.Fatalf("Index.WriteTo: %v", err)
}
ix2 := new(Index)
nr, err := ix2.ReadFrom(&buf)
if err != nil {
t.Fatalf("Index.ReadFrom: %v", err)
}
if nr != nw {
t.Errorf("Wrote %d bytes to index but read %d", nw, nr)
}
testIndex(t, ix2)
}
func testIndex(t *testing.T, ix *Index) {
wantStats := Statistics{Bytes: 256, Files: 3, Lines: 16, Words: 6, Spots: 9}
if !reflect.DeepEqual(ix.Stats(), wantStats) {
t.Errorf("Stats = %#v; want %#v", ix.Stats(), wantStats)