aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Griesemer <gri@golang.org>2011-09-14 20:45:45 -0700
committerRobert Griesemer <gri@golang.org>2011-09-14 20:45:45 -0700
commit957fd575fc2941402c964f088330e109d02740f7 (patch)
treee2a5466c1509624c22b9ac718e81fb88cfaaac44
parent3eb41fbeb6157c043a1c848fe670dd1fd762e177 (diff)
downloadgo-957fd575fc2941402c964f088330e109d02740f7.tar.gz
go-957fd575fc2941402c964f088330e109d02740f7.zip
go/token: support to serialize file sets
R=rsc CC=golang-dev https://golang.org/cl/5024042
-rw-r--r--src/pkg/go/token/Makefile1
-rw-r--r--src/pkg/go/token/position.go20
-rw-r--r--src/pkg/go/token/serialize.go62
-rw-r--r--src/pkg/go/token/serialize_test.go105
4 files changed, 180 insertions, 8 deletions
diff --git a/src/pkg/go/token/Makefile b/src/pkg/go/token/Makefile
index 4a4e64dc8e..b13b0442be 100644
--- a/src/pkg/go/token/Makefile
+++ b/src/pkg/go/token/Makefile
@@ -7,6 +7,7 @@ include ../../../Make.inc
TARG=go/token
GOFILES=\
position.go\
+ serialize.go\
token.go\
include ../../../Make.pkg
diff --git a/src/pkg/go/token/position.go b/src/pkg/go/token/position.go
index c559e19f88..9155b501d4 100644
--- a/src/pkg/go/token/position.go
+++ b/src/pkg/go/token/position.go
@@ -136,10 +136,14 @@ func (s *FileSet) Position(p Pos) (pos Position) {
return
}
+// A lineInfo object describes alternative file and line number
+// information (such as provided via a //line comment in a .go
+// file) for a given file offset.
type lineInfo struct {
- offset int
- filename string
- line int
+ // fields are exported to make them accessible to gob
+ Offset int
+ Filename string
+ Line int
}
// AddLineInfo adds alternative file and line number information for
@@ -152,7 +156,7 @@ type lineInfo struct {
//
func (f *File) AddLineInfo(offset int, filename string, line int) {
f.set.mutex.Lock()
- if i := len(f.infos); i == 0 || f.infos[i-1].offset < offset && offset < f.size {
+ if i := len(f.infos); i == 0 || f.infos[i-1].Offset < offset && offset < f.size {
f.infos = append(f.infos, lineInfo{offset, filename, line})
}
f.set.mutex.Unlock()
@@ -317,7 +321,7 @@ func searchInts(a []int, x int) int {
}
func searchLineInfos(a []lineInfo, x int) int {
- return sort.Search(len(a), func(i int) bool { return a[i].offset > x }) - 1
+ return sort.Search(len(a), func(i int) bool { return a[i].Offset > x }) - 1
}
// info returns the file name, line, and column number for a file offset.
@@ -330,9 +334,9 @@ func (f *File) info(offset int) (filename string, line, column int) {
// almost no files have extra line infos
if i := searchLineInfos(f.infos, offset); i >= 0 {
alt := &f.infos[i]
- filename = alt.filename
- if i := searchInts(f.lines, alt.offset); i >= 0 {
- line += alt.line - i - 1
+ filename = alt.Filename
+ if i := searchInts(f.lines, alt.Offset); i >= 0 {
+ line += alt.Line - i - 1
}
}
}
diff --git a/src/pkg/go/token/serialize.go b/src/pkg/go/token/serialize.go
new file mode 100644
index 0000000000..80a3323f94
--- /dev/null
+++ b/src/pkg/go/token/serialize.go
@@ -0,0 +1,62 @@
+// Copyright 2011 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 token
+
+import (
+ "gob"
+ "io"
+ "os"
+)
+
+type serializedFile struct {
+ // fields correspond 1:1 to fields with same (lower-case) name in File
+ Name string
+ Base int
+ Size int
+ Lines []int
+ Infos []lineInfo
+}
+
+type serializedFileSet struct {
+ Base int
+ Files []serializedFile
+}
+
+// Read reads the fileset from r into s; s must not be nil.
+func (s *FileSet) Read(r io.Reader) os.Error {
+ var ss serializedFileSet
+ if err := gob.NewDecoder(r).Decode(&ss); err != nil {
+ return err
+ }
+
+ s.mutex.Lock()
+ s.base = ss.Base
+ files := make([]*File, len(ss.Files))
+ for i := 0; i < len(ss.Files); i++ {
+ f := &ss.Files[i]
+ files[i] = &File{s, f.Name, f.Base, f.Size, f.Lines, f.Infos}
+ }
+ s.files = files
+ s.last = nil
+ s.mutex.Unlock()
+
+ return nil
+}
+
+// Write writes the fileset s to w.
+func (s *FileSet) Write(w io.Writer) os.Error {
+ var ss serializedFileSet
+
+ s.mutex.Lock()
+ ss.Base = s.base
+ files := make([]serializedFile, len(s.files))
+ for i, f := range s.files {
+ files[i] = serializedFile{f.name, f.base, f.size, f.lines, f.infos}
+ }
+ ss.Files = files
+ s.mutex.Unlock()
+
+ return gob.NewEncoder(w).Encode(ss)
+}
diff --git a/src/pkg/go/token/serialize_test.go b/src/pkg/go/token/serialize_test.go
new file mode 100644
index 0000000000..24e419abf6
--- /dev/null
+++ b/src/pkg/go/token/serialize_test.go
@@ -0,0 +1,105 @@
+// Copyright 2011 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 token
+
+import (
+ "bytes"
+ "fmt"
+ "os"
+ "testing"
+)
+
+// equal returns nil if p and q describe the same file set;
+// otherwise it returns an error describing the discrepancy.
+func equal(p, q *FileSet) os.Error {
+ if p == q {
+ // avoid deadlock if p == q
+ return nil
+ }
+
+ // not strictly needed for the test
+ p.mutex.Lock()
+ q.mutex.Lock()
+ defer q.mutex.Unlock()
+ defer p.mutex.Unlock()
+
+ if p.base != q.base {
+ return fmt.Errorf("different bases: %d != %d", p.base, q.base)
+ }
+
+ if len(p.files) != len(q.files) {
+ return fmt.Errorf("different number of files: %d != %d", len(p.files), len(q.files))
+ }
+
+ for i, f := range p.files {
+ g := q.files[i]
+ if f.set != p {
+ return fmt.Errorf("wrong fileset for %q", f.name)
+ }
+ if g.set != q {
+ return fmt.Errorf("wrong fileset for %q", g.name)
+ }
+ if f.name != g.name {
+ return fmt.Errorf("different filenames: %q != %q", f.name, g.name)
+ }
+ if f.base != g.base {
+ return fmt.Errorf("different base for %q: %d != %d", f.name, f.base, g.base)
+ }
+ if f.size != g.size {
+ return fmt.Errorf("different size for %q: %d != %d", f.name, f.size, g.size)
+ }
+ for j, l := range f.lines {
+ m := g.lines[j]
+ if l != m {
+ return fmt.Errorf("different offsets for %q", f.name)
+ }
+ }
+ for j, l := range f.infos {
+ m := g.infos[j]
+ if l.Offset != m.Offset || l.Filename != m.Filename || l.Line != m.Line {
+ return fmt.Errorf("different infos for %q", f.name)
+ }
+ }
+ }
+
+ // we don't care about .last - it's just a cache
+ return nil
+}
+
+func checkSerialize(t *testing.T, p *FileSet) {
+ var buf bytes.Buffer
+ if err := p.Write(&buf); err != nil {
+ t.Errorf("writing fileset failed: %s", err)
+ return
+ }
+ q := NewFileSet()
+ if err := q.Read(&buf); err != nil {
+ t.Errorf("reading fileset failed: %s", err)
+ return
+ }
+ if err := equal(p, q); err != nil {
+ t.Errorf("filesets not identical: %s", err)
+ }
+}
+
+func TestSerialization(t *testing.T) {
+ p := NewFileSet()
+ checkSerialize(t, p)
+ // add some files
+ for i := 0; i < 10; i++ {
+ f := p.AddFile(fmt.Sprintf("file%d", i), p.Base()+i, i*100)
+ checkSerialize(t, p)
+ // add some lines and alternative file infos
+ line := 1000
+ for offs := 0; offs < f.Size(); offs += 40 + i {
+ f.AddLine(offs)
+ if offs%7 == 0 {
+ f.AddLineInfo(offs, fmt.Sprintf("file%d", offs), line)
+ line += 33
+ }
+ }
+ checkSerialize(t, p)
+ }
+}