Commit 957fd575 authored by Robert Griesemer's avatar Robert Griesemer

go/token: support to serialize file sets

R=rsc
CC=golang-dev
https://golang.org/cl/5024042
parent 3eb41fbe
......@@ -7,6 +7,7 @@ include ../../../Make.inc
TARG=go/token
GOFILES=\
position.go\
serialize.go\
token.go\
include ../../../Make.pkg
......@@ -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
}
}
}
......
// 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)
}
// 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)
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment