mirror of
https://github.com/golang/go
synced 2024-11-21 15:54:43 -07:00
go/token: support to serialize file sets
R=rsc CC=golang-dev https://golang.org/cl/5024042
This commit is contained in:
parent
3eb41fbeb6
commit
957fd575fc
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
62
src/pkg/go/token/serialize.go
Normal file
62
src/pkg/go/token/serialize.go
Normal file
@ -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)
|
||||
}
|
105
src/pkg/go/token/serialize_test.go
Normal file
105
src/pkg/go/token/serialize_test.go
Normal file
@ -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)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user