]> Cypherpunks repositories - gostls13.git/commitdiff
go/token: support to serialize file sets
authorRobert Griesemer <gri@golang.org>
Thu, 15 Sep 2011 03:45:45 +0000 (20:45 -0700)
committerRobert Griesemer <gri@golang.org>
Thu, 15 Sep 2011 03:45:45 +0000 (20:45 -0700)
R=rsc
CC=golang-dev
https://golang.org/cl/5024042

src/pkg/go/token/Makefile
src/pkg/go/token/position.go
src/pkg/go/token/serialize.go [new file with mode: 0644]
src/pkg/go/token/serialize_test.go [new file with mode: 0644]

index 4a4e64dc8e8e26632664a28a611269dd148e0169..b13b0442be07a6015ffa1658578c3fbd8b7ae740 100644 (file)
@@ -7,6 +7,7 @@ include ../../../Make.inc
 TARG=go/token
 GOFILES=\
        position.go\
+       serialize.go\
        token.go\
 
 include ../../../Make.pkg
index c559e19f8865867b93735876cf946eeac89a176d..9155b501d4d0419415682dcd2b29162cb13c1b96 100644 (file)
@@ -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 (file)
index 0000000..80a3323
--- /dev/null
@@ -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 (file)
index 0000000..24e419a
--- /dev/null
@@ -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)
+       }
+}