]> Cypherpunks repositories - gostls13.git/commitdiff
gob: better debugging, commentary
authorRob Pike <r@golang.org>
Fri, 21 Jan 2011 19:28:53 +0000 (11:28 -0800)
committerRob Pike <r@golang.org>
Fri, 21 Jan 2011 19:28:53 +0000 (11:28 -0800)
Re-implement the debugging helper to be independent of the existing
implementation.  This is preparatory to a rewrite to clean up issue 1416.
Include a definition of the grammar of the data stream.

R=rsc
CC=golang-dev
https://golang.org/cl/3970045

src/pkg/gob/codec_test.go
src/pkg/gob/debug.go
src/pkg/gob/decode.go
src/pkg/gob/decoder.go
src/pkg/gob/doc.go

index af941c629cd9f4da31fb4d659df6229124900c1c..4b8bd347e8d23c1cd481f0effd22772c588c7c70 100644 (file)
@@ -1307,6 +1307,31 @@ func TestUnexportedFields(t *testing.T) {
        }
 }
 
+var singletons = []interface{}{
+       true,
+       7,
+       3.2,
+       "hello",
+       [3]int{11, 22, 33},
+       []float32{0.5, 0.25, 0.125},
+       map[string]int{"one": 1, "two": 2},
+}
+
+func TestDebugSingleton(t *testing.T) {
+       if debugFunc == nil {
+               return
+       }
+       b := new(bytes.Buffer)
+       // Accumulate a number of values and print them out all at once.
+       for _, x := range singletons {
+               err := NewEncoder(b).Encode(x)
+               if err != nil {
+                       t.Fatal("encode:", err)
+               }
+       }
+       debugFunc(b)
+}
+
 // A type that won't be defined in the gob until we send it in an interface value.
 type OnTheFly struct {
        A int
@@ -1325,7 +1350,7 @@ type DT struct {
        S     []string
 }
 
-func TestDebug(t *testing.T) {
+func TestDebugStruct(t *testing.T) {
        if debugFunc == nil {
                return
        }
index f3632a0807ece14a0aa17e4f0ae95580c367cafd..ba55cb497878f79f9c74fae1937cd5d5d91d958d 100644 (file)
@@ -2,309 +2,683 @@ package gob
 
 // This file is not normally included in the gob package.  Used only for debugging the package itself.
 // Add debug.go to the files listed in the Makefile to add Debug to the gob package.
+// Except for reading uints, it is an implementation of a reader that is independent of
+// the one implemented by Decoder.
 
 import (
        "bytes"
        "fmt"
        "io"
        "os"
-       "reflect"
-       "runtime"
+       "strings"
+       "sync"
 )
 
-var dump = false // If true, print the remaining bytes in the input buffer at each item.
+var dumpBytes = false // If true, print the remaining bytes in the input buffer at each item.
 
 // Init installs the debugging facility. If this file is not compiled in the
-// package, the test in codec_test.go is a no-op.
+// package, the tests in codec_test.go are no-ops.
 func init() {
        debugFunc = Debug
 }
 
-// Debug prints a human-readable representation of the gob data read from r.
-func Debug(r io.Reader) {
-       defer func() {
-               if e := recover(); e != nil {
-                       if _, ok := e.(runtime.Error); ok {
-                               panic(e)
-                       }
-                       fmt.Printf("error during debugging: %v\n", e)
+var (
+       blanks = bytes.Repeat([]byte{' '}, 3*10)
+       empty  = []byte(": <empty>\n")
+       tabs   = strings.Repeat("\t", 100)
+)
+
+// tab indents itself when printed.
+type tab int
+
+func (t tab) String() string {
+       n := int(t)
+       if n > len(tabs) {
+               n = len(tabs)
+       }
+       return tabs[0:n]
+}
+
+func (t tab) print() {
+       fmt.Fprint(os.Stderr, t)
+}
+
+// A peekReader wraps an io.Reader, allowing one to peek ahead to see
+// what's coming without stealing the data from the client of the Reader.
+type peekReader struct {
+       r    io.Reader
+       data []byte // read-ahead data
+}
+
+// newPeekReader returns a peekReader that wraps r.
+func newPeekReader(r io.Reader) *peekReader {
+       return &peekReader{r: r}
+}
+
+// Read is the usual method. It will first take data that has been read ahead.
+func (p *peekReader) Read(b []byte) (n int, err os.Error) {
+       if len(p.data) == 0 {
+               return p.r.Read(b)
+       }
+       // Satisfy what's possible from the read-ahead data.
+       n = copy(b, p.data)
+       // Move data down to beginning of slice, to avoid endless growth
+       copy(p.data, p.data[n:])
+       p.data = p.data[:len(p.data)-n]
+       return
+}
+
+// peek returns as many bytes as possible from the unread
+// portion of the stream, up to the length of b.
+func (p *peekReader) peek(b []byte) (n int, err os.Error) {
+       if len(p.data) > 0 {
+               n = copy(b, p.data)
+               if n == len(b) {
+                       return
                }
-       }()
-       NewDecoder(r).debug()
+               b = b[n:]
+       }
+       if len(b) == 0 {
+               return
+       }
+       m, e := io.ReadFull(p.r, b)
+       if m > 0 {
+               p.data = append(p.data, b[:m]...)
+       }
+       n += m
+       if e == io.ErrUnexpectedEOF {
+               // That means m > 0 but we reached EOF. If we got data
+               // we won't complain about not being able to peek enough.
+               if n > 0 {
+                       e = nil
+               } else {
+                       e = os.EOF
+               }
+       }
+       return n, e
 }
 
-// debugRecv is like recv but prints what it sees.
-func (dec *Decoder) debugRecv() {
-       if dec.byteBuffer != nil && dec.byteBuffer.Len() != 0 {
-               fmt.Printf("error in recv: %d bytes left in input buffer\n", dec.byteBuffer.Len())
+// dump prints the next nBytes of the input.
+// It arranges to print the output aligned from call to
+// call, to make it easy to see what has been consumed.
+func (deb *debugger) dump(nBytes int, format string, args ...interface{}) {
+       if !dumpBytes {
+               return
+       }
+       fmt.Fprintf(os.Stderr, format+" ", args...)
+       if nBytes < 0 {
+               fmt.Fprintf(os.Stderr, "nbytes is negative! %d\n", nBytes)
                return
        }
-       // Read a count.
-       var nbytes uint64
-       nbytes, dec.err = decodeUintReader(dec.r, dec.countBuf[0:])
-       if dec.err != nil {
-               fmt.Printf("receiver error on count: %s\n", dec.err)
+       data := make([]byte, nBytes)
+       n, _ := deb.r.peek(data)
+       if n == 0 {
+               os.Stderr.Write(empty)
                return
        }
-       // Allocate the buffer.
-       if nbytes > uint64(len(dec.buf)) {
-               dec.buf = make([]byte, nbytes+1000)
+       b := new(bytes.Buffer)
+       fmt.Fprint(b, "{\n")
+       // Blanks until first byte
+       lineLength := 0
+       if n := len(data); n%10 != 0 {
+               lineLength = 10 - n%10
+               fmt.Fprintf(b, "\t%s", blanks[:lineLength*3])
        }
-       dec.byteBuffer = bytes.NewBuffer(dec.buf[0:nbytes])
-
-       // Read the data
-       _, dec.err = io.ReadFull(dec.r, dec.buf[0:nbytes])
-       if dec.err != nil {
-               fmt.Printf("receiver error on data: %s\n", dec.err)
-               if dec.err == os.EOF {
-                       dec.err = io.ErrUnexpectedEOF
+       // 10 bytes per line
+       for len(data) > 0 {
+               if lineLength == 0 {
+                       fmt.Fprint(b, "\t")
                }
-               return
+               m := 10 - lineLength
+               lineLength = 0
+               if m > len(data) {
+                       m = len(data)
+               }
+               fmt.Fprintf(b, "% x\n", data[:m])
+               data = data[m:]
        }
-       if dump {
-               fmt.Printf("received %d bytes:\n\t% x\n", nbytes, dec.byteBuffer.Bytes())
+       fmt.Fprint(b, "}\n")
+       os.Stderr.Write(b.Bytes())
+}
+
+type debugger struct {
+       mutex    sync.Mutex
+       r        *peekReader
+       wireType map[typeId]*wireType
+       tmp      []byte // scratch space for decoding uints.
+}
+
+// Debug prints a human-readable representation of the gob data read from r.
+func Debug(r io.Reader) {
+       fmt.Fprintln(os.Stderr, "Start of debugging")
+       deb := &debugger{
+               r:        newPeekReader(r),
+               wireType: make(map[typeId]*wireType),
+               tmp:      make([]byte, 16),
        }
+       deb.gobStream()
 }
 
+// toInt turns an encoded uint64 into an int, according to the marshaling rules.
+func toInt(x uint64) int64 {
+       i := int64(x >> 1)
+       if x&1 != 0 {
+               i = ^i
+       }
+       return i
+}
+
+// readInt returns the next int, which must be present,
+// and the number of bytes it consumed.
+// Don't call this if you could be at EOF.
+func (deb *debugger) readInt() (i int64, w int) {
+       var u uint64
+       u, w = deb.readUint()
+       return toInt(u), w
+}
 
-// debug is like Decode but just prints what it finds.  It should be safe even for corrupted data.
-func (dec *Decoder) debug() {
+// readUint returns the next uint, which must be present.
+// and the number of bytes it consumed.
+// Don't call this if you could be at EOF.
+// TODO: handle errors better.
+func (deb *debugger) readUint() (x uint64, w int) {
+       n, w, err := decodeUintReader(deb.r, deb.tmp)
+       if err != nil {
+               errorf("debug: read error: %s", err)
+       }
+       return n, w
+}
+
+// GobStream:
+//     DelimitedMessage* (until EOF)
+func (deb *debugger) gobStream() {
        // Make sure we're single-threaded through here.
-       dec.mutex.Lock()
-       defer dec.mutex.Unlock()
+       deb.mutex.Lock()
+       defer deb.mutex.Unlock()
 
-       dec.err = nil
-       dec.debugRecv()
-       if dec.err != nil {
-               return
+       for deb.delimitedMessage(0) {
        }
-       dec.debugFromBuffer(0, false)
 }
 
-// printFromBuffer prints the next value.  The buffer contains data, but it may
-// be a type descriptor and we may need to load more data to see the value;
-// printType takes care of that.
-func (dec *Decoder) debugFromBuffer(indent int, countPresent bool) {
-       for dec.state.b.Len() > 0 {
-               // Receive a type id.
-               id := typeId(dec.state.decodeInt())
+// DelimitedMessage:
+//     uint(lengthOfMessage) Message
+func (deb *debugger) delimitedMessage(indent tab) bool {
+       for {
+               n := deb.loadBlock(true)
+               if n < 0 {
+                       return false
+               }
+               deb.dump(int(n), "Message of length %d", n)
+               deb.message(indent, n)
+       }
+       return true
+}
 
-               // Is it a new type?
-               if id < 0 { // 0 is the error state, handled above
-                       // If the id is negative, we have a type.
-                       dec.debugRecvType(-id)
-                       if dec.err != nil {
-                               break
-                       }
-                       continue
+// loadBlock preps us to read a message
+// of the length specified next in the input. It returns
+// the length of the block. The argument tells whether
+// an EOF is acceptable now.  If it is and one is found,
+// the return value is negative.
+func (deb *debugger) loadBlock(eofOK bool) int {
+       n64, _, err := decodeUintReader(deb.r, deb.tmp)
+       if err != nil {
+               if eofOK && err == os.EOF {
+                       return -1
                }
+               errorf("debug: unexpected error: %s", err)
+       }
+       n := int(n64)
+       if n < 0 {
+               errorf("huge value for message length: %d", n64)
+       }
+       return n
+}
 
-               // No, it's a value.
-               // Make sure the type has been defined already or is a builtin type (for
-               // top-level singleton values).
-               if dec.wireType[id] == nil && builtinIdToType[id] == nil {
-                       dec.err = errBadType
+// Message:
+//     TypeSequence TypedValue
+// TypeSequence
+//     (TypeDefinition DelimitedTypeDefinition*)?
+// DelimitedTypeDefinition:
+//     uint(lengthOfTypeDefinition) TypeDefinition
+// TypedValue:
+//     int(typeId) Value
+func (deb *debugger) message(indent tab, n int) bool {
+       for {
+               // Convert the uint64 to a signed integer typeId
+               uid, w := deb.readInt()
+               id := typeId(uid)
+               n -= w
+               deb.dump(n, "type id=%d", id)
+               if id < 0 {
+                       n -= deb.typeDefinition(indent, -id, n)
+                       n = deb.loadBlock(false)
+                       deb.dump(n, "Message of length %d", n)
+                       continue
+               } else {
+                       deb.value(indent, id, n)
                        break
                }
-               if countPresent {
-                       dec.state.decodeUint()
-               }
-               dec.debugPrint(indent, id)
-               break
        }
+       return true
 }
 
-func (dec *Decoder) debugRecvType(id typeId) {
-       // Have we already seen this type?  That's an error
-       if _, alreadySeen := dec.wireType[id]; alreadySeen {
-               dec.err = os.ErrorString("gob: duplicate type received")
-               return
+// TypeDefinition:
+//     [int(-typeId) (already read)] encodingOfWireType
+func (deb *debugger) typeDefinition(indent tab, id typeId, n int) int {
+       deb.dump(n, "type definition for id %d", id)
+       // Encoding is of a wireType. Decode the structure as usual
+       fieldNum := -1
+       m := 0
+
+       // Closures to make it easy to scan.
+
+       // Read a uint from the input
+       getUint := func() uint {
+               i, w := deb.readUint()
+               m += w
+               n -= w
+               return uint(i)
+       }
+       // Read an int from the input
+       getInt := func() int {
+               i, w := deb.readInt()
+               m += w
+               n -= w
+               return int(i)
+       }
+       // Read a string from the input
+       getString := func() string {
+               u, w := deb.readUint()
+               x := int(u)
+               m += w
+               n -= w
+               b := make([]byte, x)
+               nb, _ := deb.r.Read(b)
+               if nb != x {
+                       errorf("corrupted type")
+               }
+               m += x
+               n -= x
+               return string(b)
+       }
+       // Read a typeId from the input
+       getTypeId := func() typeId {
+               return typeId(getInt())
+       }
+       // Read a delta from the input.
+       getDelta := func(expect int) int {
+               u, w := deb.readUint()
+               m += w
+               n -= w
+               delta := int(u)
+               if delta < 0 || (expect >= 0 && delta != expect) {
+                       errorf("gob decode: corrupted type: delta %d expected %d", delta, expect)
+               }
+               return int(u)
+       }
+       // Read a CommonType from the input
+       common := func() CommonType {
+               fieldNum := -1
+               name := ""
+               id := typeId(0)
+               for {
+                       delta := getDelta(-1)
+                       if delta == 0 {
+                               break
+                       }
+                       fieldNum += delta
+                       switch fieldNum {
+                       case 0:
+                               name = getString()
+                       case 1:
+                               // Id typeId
+                               id = getTypeId()
+                       default:
+                               errorf("corrupted CommonType")
+                       }
+               }
+               return CommonType{name, id}
        }
 
-       // Type:
        wire := new(wireType)
-       dec.err = dec.decode(tWireType, reflect.NewValue(wire))
-       if dec.err == nil {
-               printWireType(wire)
-       }
-       // Remember we've seen this type.
-       dec.wireType[id] = wire
+       // A wireType defines a single field.
+       delta := getDelta(-1)
+       fieldNum += delta
+       switch fieldNum {
+       case 0: // array type, one field of {{Common}, elem, length}
+               // Field number 0 is CommonType
+               getDelta(1)
+               com := common()
+               // Field number 1 is type Id of elem
+               getDelta(1)
+               id := getTypeId()
+               // Field number 3 is length
+               getDelta(1)
+               length := getInt()
+               wire.ArrayT = &arrayType{com, id, length}
 
-       // Load the next parcel.
-       dec.debugRecv()
-}
+       case 1: // slice type, one field of {{Common}, elem}
+               // Field number 0 is CommonType
+               getDelta(1)
+               com := common()
+               // Field number 1 is type Id of elem
+               getDelta(1)
+               id := getTypeId()
+               wire.SliceT = &sliceType{com, id}
 
-func printWireType(wire *wireType) {
-       fmt.Printf("type definition {\n")
-       switch {
-       case wire.ArrayT != nil:
-               printCommonType("array", &wire.ArrayT.CommonType)
-               fmt.Printf("\tlen %d\n\telemid %d\n", wire.ArrayT.Len, wire.ArrayT.Elem)
-       case wire.MapT != nil:
-               printCommonType("map", &wire.MapT.CommonType)
-               fmt.Printf("\tkeyid %d\n", wire.MapT.Key)
-               fmt.Printf("\telemid %d\n", wire.MapT.Elem)
-       case wire.SliceT != nil:
-               printCommonType("slice", &wire.SliceT.CommonType)
-               fmt.Printf("\telemid %d\n", wire.SliceT.Elem)
-       case wire.StructT != nil:
-               printCommonType("struct", &wire.StructT.CommonType)
-               for i, field := range wire.StructT.Field {
-                       fmt.Printf("\tfield %d:\t%s\tid=%d\n", i, field.Name, field.Id)
+       case 2: // struct type, one field of {{Common}, []fieldType}
+               // Field number 0 is CommonType
+               getDelta(1)
+               com := common()
+               // Field number 1 is slice of FieldType
+               getDelta(1)
+               numField := int(getUint())
+               field := make([]*fieldType, numField)
+               for i := 0; i < numField; i++ {
+                       field[i] = new(fieldType)
+                       getDelta(1) // field 0 of fieldType: name
+                       field[i].Name = getString()
+                       getDelta(1) // field 1 of fieldType: id
+                       field[i].Id = getTypeId()
+                       getDelta(0) // end of fieldType
                }
+               wire.StructT = &structType{com, field}
+
+       case 3: // map type, one field of {{Common}, key, elem}
+               // Field number 0 is CommonType
+               getDelta(1)
+               com := common()
+               // Field number 1 is type Id of key
+               getDelta(1)
+               keyId := getTypeId()
+               wire.SliceT = &sliceType{com, id}
+               // Field number 2 is type Id of elem
+               getDelta(1)
+               elemId := getTypeId()
+               wire.MapT = &mapType{com, keyId, elemId}
+       default:
+               errorf("bad field in type %d", fieldNum)
        }
-       fmt.Printf("}\n")
+       deb.printWireType(indent, wire)
+       getDelta(0) // end inner type (arrayType, etc.)
+       getDelta(0) // end wireType
+       // Remember we've seen this type.
+       deb.wireType[id] = wire
+       return m
 }
 
-func printCommonType(kind string, common *CommonType) {
-       fmt.Printf("\t%s %q\n\tid: %d\n", kind, common.Name, common.Id)
+
+// Value:
+//     ConcreteValue | InterfaceValue
+func (deb *debugger) value(indent tab, id typeId, n int) int {
+       if id == tInterface {
+               return deb.interfaceValue(indent, n)
+       }
+       return deb.concreteValue(indent, id, n)
 }
 
-func (dec *Decoder) debugPrint(indent int, id typeId) {
-       wire, ok := dec.wireType[id]
+
+// ConcreteValue:
+//     SingletonValue | StructValue
+func (deb *debugger) concreteValue(indent tab, id typeId, n int) int {
+       wire, ok := deb.wireType[id]
        if ok && wire.StructT != nil {
-               dec.debugStruct(indent+1, id, wire)
-       } else {
-               dec.debugSingle(indent+1, id, wire)
+               return deb.structValue(indent, id, n)
        }
+       return deb.singletonValue(indent, id, n)
 }
 
-func (dec *Decoder) debugSingle(indent int, id typeId, wire *wireType) {
+// SingletonValue:
+//     int(0) FieldValue
+func (deb *debugger) singletonValue(indent tab, id typeId, n int) int {
+       deb.dump(n, "Singleton value")
        // is it a builtin type?
+       wire := deb.wireType[id]
        _, ok := builtinIdToType[id]
        if !ok && wire == nil {
-               errorf("type id %d not defined\n", id)
+               errorf("type id %d not defined", id)
+       }
+       m, w := deb.readInt()
+       if m != 0 {
+               errorf("expected zero; got %d", n)
        }
-       dec.state.decodeUint()
-       dec.printItem(indent, id)
+       return w + deb.fieldValue(indent, id, n-w)
 }
 
-func (dec *Decoder) printItem(indent int, id typeId) {
-       if dump {
-               fmt.Printf("print item %d bytes: % x\n", dec.state.b.Len(), dec.state.b.Bytes())
+// InterfaceValue:
+//     NilInterfaceValue | NonNilInterfaceValue
+func (deb *debugger) interfaceValue(indent tab, n int) int {
+       deb.dump(n, "Start of interface value")
+       nameLen, w := deb.readUint()
+       n -= w
+       if n == 0 {
+               return w + deb.nilInterfaceValue(indent)
        }
-       _, ok := builtinIdToType[id]
-       if ok {
-               dec.printBuiltin(indent, id)
-               return
-       }
-       wire, ok := dec.wireType[id]
-       if !ok {
-               errorf("type id %d not defined\n", id)
+       return w + deb.nonNilInterfaceValue(indent, int(nameLen), n)
+}
+
+// NilInterfaceValue:
+//     uint(0) [already read]
+func (deb *debugger) nilInterfaceValue(indent tab) int {
+       fmt.Fprintf(os.Stderr, "%snil interface\n", indent)
+       return 0
+}
+
+
+// NonNilInterfaceValue:
+//     ConcreteTypeName TypeSequence InterfaceContents
+// ConcreteTypeName:
+//     uint(lengthOfName) [already read=n] name
+// InterfaceContents:
+//     int(concreteTypeId) DelimitedValue
+// DelimitedValue:
+//     uint(length) Value
+func (deb *debugger) nonNilInterfaceValue(indent tab, nameLen, n int) int {
+       // ConcreteTypeName
+       b := make([]byte, nameLen)
+       deb.r.Read(b) // TODO: CHECK THESE READS!!
+       w := nameLen
+       n -= nameLen
+       name := string(b)
+       fmt.Fprintf(os.Stderr, "%sinterface value, type %q length %d\n", indent, name, n)
+
+       for {
+               x, width := deb.readInt()
+               n -= w
+               w += width
+               id := typeId(x)
+               if id < 0 {
+                       deb.typeDefinition(indent, -id, n)
+                       n = deb.loadBlock(false)
+                       deb.dump(n, "Message of length %d", n)
+               } else {
+                       // DelimitedValue
+                       x, width := deb.readUint() // in case we want to ignore the value; we don't.
+                       n -= w
+                       w += width
+                       fmt.Fprintf(os.Stderr, "%sinterface value, type %q id=%d; length %d\n", indent, name, id, x)
+                       ZZ := w + deb.value(indent, id, int(x))
+                       return ZZ
+               }
        }
+       panic("not reached")
+}
+
+// printCommonType prints a common type; used by printWireType.
+func (deb *debugger) printCommonType(indent tab, kind string, common *CommonType) {
+       indent.print()
+       fmt.Fprintf(os.Stderr, "%s %q id=%d\n", kind, common.Name, common.Id)
+}
+
+// printWireType prints the contents of a wireType.
+func (deb *debugger) printWireType(indent tab, wire *wireType) {
+       fmt.Fprintf(os.Stderr, "%stype definition {\n", indent)
+       indent++
        switch {
        case wire.ArrayT != nil:
-               dec.printArray(indent, wire)
+               deb.printCommonType(indent, "array", &wire.ArrayT.CommonType)
+               fmt.Fprintf(os.Stderr, "%slen %d\n", indent+1, wire.ArrayT.Len)
+               fmt.Fprintf(os.Stderr, "%selemid %d\n", indent+1, wire.ArrayT.Elem)
        case wire.MapT != nil:
-               dec.printMap(indent, wire)
+               deb.printCommonType(indent, "map", &wire.MapT.CommonType)
+               fmt.Fprintf(os.Stderr, "%skey id=%d\n", indent+1, wire.MapT.Key)
+               fmt.Fprintf(os.Stderr, "%selem id=%d\n", indent+1, wire.MapT.Elem)
        case wire.SliceT != nil:
-               dec.printSlice(indent, wire)
+               deb.printCommonType(indent, "slice", &wire.SliceT.CommonType)
+               fmt.Fprintf(os.Stderr, "%selem id=%d\n", indent+1, wire.SliceT.Elem)
        case wire.StructT != nil:
-               dec.debugStruct(indent, id, wire)
+               deb.printCommonType(indent, "struct", &wire.StructT.CommonType)
+               for i, field := range wire.StructT.Field {
+                       fmt.Fprintf(os.Stderr, "%sfield %d:\t%s\tid=%d\n", indent+1, i, field.Name, field.Id)
+               }
        }
+       indent--
+       fmt.Fprintf(os.Stderr, "%s}\n", indent)
 }
 
-func (dec *Decoder) printArray(indent int, wire *wireType) {
-       elemId := wire.ArrayT.Elem
-       n := int(dec.state.decodeUint())
-       for i := 0; i < n && dec.err == nil; i++ {
-               dec.printItem(indent, elemId)
-       }
-       if n != wire.ArrayT.Len {
-               tab(indent)
-               fmt.Printf("(wrong length for array: %d should be %d)\n", n, wire.ArrayT.Len)
+// fieldValue prints a value of any type, such as a struct field.
+func (deb *debugger) fieldValue(indent tab, id typeId, n int) int {
+       _, ok := builtinIdToType[id]
+       if ok {
+               return deb.printBuiltin(indent, id, n)
        }
-}
-
-func (dec *Decoder) printMap(indent int, wire *wireType) {
-       keyId := wire.MapT.Key
-       elemId := wire.MapT.Elem
-       n := int(dec.state.decodeUint())
-       for i := 0; i < n && dec.err == nil; i++ {
-               dec.printItem(indent, keyId)
-               dec.printItem(indent+1, elemId)
+       wire, ok := deb.wireType[id]
+       if !ok {
+               errorf("type id %d not defined", id)
        }
-}
-
-func (dec *Decoder) printSlice(indent int, wire *wireType) {
-       elemId := wire.SliceT.Elem
-       n := int(dec.state.decodeUint())
-       for i := 0; i < n && dec.err == nil; i++ {
-               dec.printItem(indent, elemId)
+       switch {
+       case wire.ArrayT != nil:
+               return deb.arrayValue(indent, wire, n)
+       case wire.MapT != nil:
+               return deb.mapValue(indent, wire, n)
+       case wire.SliceT != nil:
+               return deb.sliceValue(indent, wire, n)
+       case wire.StructT != nil:
+               return deb.structValue(indent, id, n)
        }
+       panic("unreached")
 }
 
-func (dec *Decoder) printBuiltin(indent int, id typeId) {
-       tab(indent)
+// printBuiltin prints a value not of a fundamental type, that is,
+// one whose type is known to gobs at bootstrap time.
+// That includes interfaces, although they may require
+// more unpacking down the line.
+func (deb *debugger) printBuiltin(indent tab, id typeId, n int) int {
        switch id {
        case tBool:
-               if dec.state.decodeInt() == 0 {
-                       fmt.Printf("false\n")
+               x, w := deb.readInt()
+               if x == 0 {
+                       fmt.Fprintf(os.Stderr, "%sfalse\n", indent)
                } else {
-                       fmt.Printf("true\n")
+                       fmt.Fprintf(os.Stderr, "%strue\n", indent)
                }
+               return w
        case tInt:
-               fmt.Printf("%d\n", dec.state.decodeInt())
+               x, w := deb.readInt()
+               fmt.Fprintf(os.Stderr, "%s%d\n", indent, x)
+               return w
        case tUint:
-               fmt.Printf("%d\n", dec.state.decodeUint())
+               x, w := deb.readInt()
+               fmt.Fprintf(os.Stderr, "%s%d\n", indent, x)
+               return w
        case tFloat:
-               fmt.Printf("%g\n", floatFromBits(dec.state.decodeUint()))
+               x, w := deb.readUint()
+               fmt.Fprintf(os.Stderr, "%s%g\n", indent, floatFromBits(x))
+               return w
        case tBytes:
-               b := make([]byte, dec.state.decodeUint())
-               dec.state.b.Read(b)
-               fmt.Printf("% x\n", b)
+               x, w := deb.readUint()
+               b := make([]byte, x)
+               deb.r.Read(b)
+               fmt.Fprintf(os.Stderr, "%s{% x}=%q\n", indent, b, b)
+               return w + int(x)
        case tString:
-               b := make([]byte, dec.state.decodeUint())
-               dec.state.b.Read(b)
-               fmt.Printf("%q\n", b)
+               x, w := deb.readUint()
+               b := make([]byte, x)
+               deb.r.Read(b)
+               fmt.Fprintf(os.Stderr, "%s%q\n", indent, b)
+               return w + int(x)
        case tInterface:
-               b := make([]byte, dec.state.decodeUint())
-               dec.state.b.Read(b)
-               if len(b) == 0 {
-                       fmt.Printf("nil interface")
-               } else {
-                       fmt.Printf("interface value; type %q\n", b)
-                       dec.debugFromBuffer(indent, true)
-               }
+               return deb.interfaceValue(indent, n)
        default:
                fmt.Print("unknown\n")
        }
+       panic("unknown builtin")
+}
+
+
+// ArrayValue:
+//     uint(n) Value*n
+func (deb *debugger) arrayValue(indent tab, wire *wireType, n int) int {
+       elemId := wire.ArrayT.Elem
+       u, w := deb.readUint()
+       length := int(u)
+       for i := 0; i < length; i++ {
+               w += deb.fieldValue(indent, elemId, n-w)
+       }
+       if length != wire.ArrayT.Len {
+               fmt.Fprintf(os.Stderr, "%s(wrong length for array: %d should be %d)\n", indent, length, wire.ArrayT.Len)
+       }
+       return w
+}
+
+// MapValue:
+//     uint(n) (Value Value)*n  [n (key, value) pairs]
+func (deb *debugger) mapValue(indent tab, wire *wireType, n int) int {
+       keyId := wire.MapT.Key
+       elemId := wire.MapT.Elem
+       u, w := deb.readUint()
+       length := int(u)
+       for i := 0; i < length; i++ {
+               w += deb.fieldValue(indent+1, keyId, n-w)
+               w += deb.fieldValue(indent+1, elemId, n-w)
+       }
+       return w
+}
+
+// SliceValue:
+//     uint(n) (n Values)
+func (deb *debugger) sliceValue(indent tab, wire *wireType, n int) int {
+       elemId := wire.SliceT.Elem
+       u, w := deb.readUint()
+       length := int(u)
+       for i := 0; i < length; i++ {
+               w += deb.fieldValue(indent, elemId, n-w)
+       }
+       return w
 }
 
-func (dec *Decoder) debugStruct(indent int, id typeId, wire *wireType) {
-       tab(indent)
-       fmt.Printf("%s struct {\n", id.name())
+// StructValue:
+//     (int(fieldDelta) FieldValue)*
+func (deb *debugger) structValue(indent tab, id typeId, n int) int {
+       deb.dump(n, "Start of struct value of %q id=%d\n<<\n", id.name(), id)
+       fmt.Fprintf(os.Stderr, "%s%s struct {\n", indent, id.name())
+       wire, ok := deb.wireType[id]
+       if !ok {
+               errorf("type id %d not defined", id)
+       }
        strct := wire.StructT
-       state := newDecodeState(dec, dec.state.b)
-       state.fieldnum = -1
-       for dec.err == nil {
-               delta := int(state.decodeUint())
-               if delta < 0 {
-                       errorf("gob decode: corrupted data: negative delta")
-               }
+       fieldNum := -1
+       indent++
+       w := 0
+       for {
+               delta, wid := deb.readUint()
+               w += wid
+               n -= wid
                if delta == 0 { // struct terminator is zero delta fieldnum
                        break
                }
-               fieldNum := state.fieldnum + delta
+               fieldNum += int(delta)
                if fieldNum < 0 || fieldNum >= len(strct.Field) {
-                       errorf("field number out of range")
+                       deb.dump(n, "field number out of range: prevField=%d delta=%d", fieldNum-int(delta), delta)
                        break
                }
-               tab(indent)
-               fmt.Printf("%s(%d):\n", wire.StructT.Field[fieldNum].Name, fieldNum)
-               dec.printItem(indent+1, strct.Field[fieldNum].Id)
-               state.fieldnum = fieldNum
-       }
-       tab(indent)
-       fmt.Printf(" } // end %s struct\n", id.name())
-}
-
-func tab(indent int) {
-       for i, w := 0, 0; i < indent; i += w {
-               w = 10
-               if i+w > indent {
-                       w = indent - i
-               }
-               fmt.Print("\t\t\t\t\t\t\t\t\t\t"[:w])
+               fmt.Fprintf(os.Stderr, "%sfield %d:\t%s\n", indent, fieldNum, wire.StructT.Field[fieldNum].Name)
+               wid = deb.fieldValue(indent+1, strct.Field[fieldNum].Id, n)
+               w += wid
+               n -= wid
        }
+       indent--
+       fmt.Fprintf(os.Stderr, "%s} // end %s struct\n", indent, id.name())
+       deb.dump(n, ">> End of struct value of type %d %q", id, id.name())
+       return w
 }
index 2db75215c197881d8d6a9b3044b4dc708139e63a..73a926961212686eee141baee2f465ddf2af56c5 100644 (file)
@@ -49,14 +49,15 @@ func overflow(name string) os.ErrorString {
 
 // decodeUintReader reads an encoded unsigned integer from an io.Reader.
 // Used only by the Decoder to read the message length.
-func decodeUintReader(r io.Reader, buf []byte) (x uint64, err os.Error) {
-       _, err = r.Read(buf[0:1])
+func decodeUintReader(r io.Reader, buf []byte) (x uint64, width int, err os.Error) {
+       width = 1
+       _, err = r.Read(buf[0:width])
        if err != nil {
                return
        }
        b := buf[0]
        if b <= 0x7f {
-               return uint64(b), nil
+               return uint64(b), width, nil
        }
        nb := -int(int8(b))
        if nb > uint64Size {
@@ -75,6 +76,7 @@ func decodeUintReader(r io.Reader, buf []byte) (x uint64, err os.Error) {
        for i := 0; i < n; i++ {
                x <<= 8
                x |= uint64(buf[i])
+               width++
        }
        return
 }
index 664001a4b21e0bf7ba4ada7e9390a955e1d26b2d..10c72c37f586a9e46edd8ff12e306e15d77cd4ba 100644 (file)
@@ -58,7 +58,7 @@ func (dec *Decoder) recvType(id typeId) {
        dec.wireType[id] = wire
 
        // Load the next parcel.
-       dec.recv()
+       dec.recvMessage()
 }
 
 // Decode reads the next value from the connection and stores
@@ -76,23 +76,28 @@ func (dec *Decoder) Decode(e interface{}) os.Error {
        return dec.DecodeValue(value)
 }
 
-// recv reads the next count-delimited item from the input. It is the converse
-// of Encoder.send.
-func (dec *Decoder) recv() {
+// recvMessage reads the next count-delimited item from the input. It is the converse
+// of Encoder.writeMessage.
+func (dec *Decoder) recvMessage() {
        // Read a count.
        var nbytes uint64
-       nbytes, dec.err = decodeUintReader(dec.r, dec.countBuf[0:])
+       nbytes, _, dec.err = decodeUintReader(dec.r, dec.countBuf[0:])
        if dec.err != nil {
                return
        }
+       dec.readMessage(int(nbytes), dec.r)
+}
+
+// readMessage reads the next nbytes bytes from the input.
+func (dec *Decoder) readMessage(nbytes int, r io.Reader) {
        // Allocate the buffer.
-       if nbytes > uint64(len(dec.buf)) {
+       if nbytes > len(dec.buf) {
                dec.buf = make([]byte, nbytes+1000)
        }
        dec.byteBuffer = bytes.NewBuffer(dec.buf[0:nbytes])
 
        // Read the data
-       _, dec.err = io.ReadFull(dec.r, dec.buf[0:nbytes])
+       _, dec.err = io.ReadFull(r, dec.buf[0:nbytes])
        if dec.err != nil {
                if dec.err == os.EOF {
                        dec.err = io.ErrUnexpectedEOF
@@ -103,7 +108,7 @@ func (dec *Decoder) recv() {
 
 // decodeValueFromBuffer grabs the next value from the input. The Decoder's
 // buffer already contains data.  If the next item in the buffer is a type
-// descriptor, it may be necessary to reload the buffer, but recvType does that.
+// descriptor, it will be necessary to reload the buffer; recvType does that.
 func (dec *Decoder) decodeValueFromBuffer(value reflect.Value, ignoreInterfaceValue, countPresent bool) {
        for dec.state.b.Len() > 0 {
                // Receive a type id.
@@ -150,7 +155,7 @@ func (dec *Decoder) DecodeValue(value reflect.Value) os.Error {
        defer dec.mutex.Unlock()
 
        dec.err = nil
-       dec.recv()
+       dec.recvMessage()
        if dec.err != nil {
                return dec.err
        }
index 31253f16d096d4fedbedd79b0b9ec8fa12cc8880..b5ef7ef08cc65b7d0c522d6193f9a1f0d5484bb4 100644 (file)
@@ -219,6 +219,54 @@ be predefined or be defined before the value in the stream.
 */
 package gob
 
+/*
+Grammar:
+
+Tokens starting with a lower case letter are terminals; int(n)
+and uint(n) represent the signed/unsigned encodings of the value n.
+
+GobStream:
+       DelimitedMessage*
+DelimitedMessage:
+       uint(lengthOfMessage) Message
+Message:
+       TypeSequence TypedValue
+TypeSequence
+       (TypeDefinition DelimitedTypeDefinition*)?
+DelimitedTypeDefinition:
+       uint(lengthOfTypeDefinition) TypeDefinition
+TypedValue:
+       int(typeId) Value
+TypeDefinition:
+       int(-typeId) encodingOfWireType
+Value:
+       ConcreteValue | InterfaceValue
+ConcreteValue:
+       SingletonValue | StructValue
+SingletonValue:
+       int(0) FieldValue
+InterfaceValue:
+       NilInterfaceValue | NonNilInterfaceValue
+NilInterfaceValue:
+       uint(0)
+NonNilInterfaceValue:
+       ConcreteTypeName TypeSequence InterfaceContents
+ConcreteTypeName:
+       uint(lengthOfName) [already read=n] name
+InterfaceContents:
+       int(concreteTypeId) DelimitedValue
+DelimitedValue:
+       uint(length) Value
+ArrayValue:
+       uint(n) Value*n [n elements]
+MapValue:
+       uint(n) (Value Value)*n  [n (key, value) pairs]
+SliceValue:
+       uint(n) Value*n [n elements]
+StructValue:
+       (uint(fieldDelta) FieldValue)*
+*/
+
 /*
 For implementers and the curious, here is an encoded example.  Given
        type Point struct {x, y int}