diff options
Diffstat (limited to 'src/pkg/gob/debug.go')
-rw-r--r-- | src/pkg/gob/debug.go | 752 |
1 files changed, 549 insertions, 203 deletions
diff --git a/src/pkg/gob/debug.go b/src/pkg/gob/debug.go index f3632a080..e4583901e 100644 --- a/src/pkg/gob/debug.go +++ b/src/pkg/gob/debug.go @@ -2,309 +2,655 @@ 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()) +type debugger struct { + mutex sync.Mutex + remain int // the number of bytes known to remain in the input + remainingKnown bool // the value of 'remain' is valid + r *peekReader + wireType map[typeId]*wireType + tmp []byte // scratch space for decoding uints. +} + +// 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(format string, args ...interface{}) { + if !dumpBytes { 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) + fmt.Fprintf(os.Stderr, format+" ", args...) + if !deb.remainingKnown { return } - // Allocate the buffer. - if nbytes > uint64(len(dec.buf)) { - dec.buf = make([]byte, nbytes+1000) + if deb.remain < 0 { + fmt.Fprintf(os.Stderr, "remaining byte count is negative! %d\n", deb.remain) + return } - 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 - } + data := make([]byte, deb.remain) + n, _ := deb.r.peek(data) + if n == 0 { + os.Stderr.Write(empty) return } - if dump { - fmt.Printf("received %d bytes:\n\t% x\n", nbytes, dec.byteBuffer.Bytes()) + b := new(bytes.Buffer) + fmt.Fprintf(b, "[%d]{\n", deb.remain) + // 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]) + } + // 10 bytes per line + for len(data) > 0 { + if lineLength == 0 { + fmt.Fprint(b, "\t") + } + m := 10 - lineLength + lineLength = 0 + if m > len(data) { + m = len(data) + } + fmt.Fprintf(b, "% x\n", data[:m]) + data = data[m:] + } + fmt.Fprint(b, "}\n") + os.Stderr.Write(b.Bytes()) +} + +// 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), + } + if b, ok := r.(*bytes.Buffer); ok { + deb.remain = b.Len() + deb.remainingKnown = true } + deb.gobStream() +} + +// note that we've consumed some bytes +func (deb *debugger) consumed(n int) { + if deb.remainingKnown { + deb.remain -= n + } +} + +// int64 decodes and returns the next integer, which must be present. +// Don't call this if you could be at EOF. +func (deb *debugger) int64() int64 { + return toInt(deb.uint64()) } +// uint64 returns and decodes the next unsigned integer, which must be present. +// Don't call this if you could be at EOF. +// TODO: handle errors better. +func (deb *debugger) uint64() uint64 { + n, w, err := decodeUintReader(deb.r, deb.tmp) + if err != nil { + errorf("debug: read error: %s", err) + } + deb.consumed(w) + return n +} -// debug is like Decode but just prints what it finds. It should be safe even for corrupted data. -func (dec *Decoder) debug() { +// 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("Delimited message of length %d", n) + deb.message(indent) + } + 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 - } +// 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, w, err := decodeUintReader(deb.r, deb.tmp) // deb.uint64 will error at EOF + if err != nil { + if eofOK && err == os.EOF { + return -1 + } + errorf("debug: unexpected error: %s", err) + } + deb.consumed(w) + n := int(n64) + if n < 0 { + errorf("huge value for message length: %d", n64) + } + return int(n) +} + +// Message: +// TypeSequence TypedValue +// TypeSequence +// (TypeDefinition DelimitedTypeDefinition*)? +// DelimitedTypeDefinition: +// uint(lengthOfTypeDefinition) TypeDefinition +// TypedValue: +// int(typeId) Value +func (deb *debugger) message(indent tab) bool { + for { + // Convert the uint64 to a signed integer typeId + uid := deb.int64() + id := typeId(uid) + deb.dump("type id=%d", id) + if id < 0 { + deb.typeDefinition(indent, -id) + n := deb.loadBlock(false) + deb.dump("Message of length %d", n) continue + } else { + deb.value(indent, id) + break } + } + return true +} + +// Helper methods to make it easy to scan a type descriptor. - // 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 +// common returns the CommonType at the input point. +func (deb *debugger) common() CommonType { + fieldNum := -1 + name := "" + id := typeId(0) + for { + delta := deb.delta(-1) + if delta == 0 { break } - if countPresent { - dec.state.decodeUint() + fieldNum += delta + switch fieldNum { + case 0: + name = deb.string() + case 1: + // Id typeId + id = deb.typeId() + default: + errorf("corrupted CommonType") } - dec.debugPrint(indent, id) - break } + return CommonType{name, id} } -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 - } +// uint returns the unsigned int at the input point, as a uint (not uint64). +func (deb *debugger) uint() uint { + return uint(deb.uint64()) +} - // Type: - wire := new(wireType) - dec.err = dec.decode(tWireType, reflect.NewValue(wire)) - if dec.err == nil { - printWireType(wire) +// int returns the signed int at the input point, as an int (not int64). +func (deb *debugger) int() int { + return int(deb.int64()) +} + +// typeId returns the type id at the input point. +func (deb *debugger) typeId() typeId { + return typeId(deb.int64()) +} + +// string returns the string at the input point. +func (deb *debugger) string() string { + x := int(deb.uint64()) + b := make([]byte, x) + nb, _ := deb.r.Read(b) + if nb != x { + errorf("corrupted type") } - // Remember we've seen this type. - dec.wireType[id] = wire + deb.consumed(nb) + return string(b) +} - // Load the next parcel. - dec.debugRecv() +// delta returns the field delta at the input point. The expect argument, +// if non-negative, identifies what the value should be. +func (deb *debugger) delta(expect int) int { + delta := int(deb.uint64()) + if delta < 0 || (expect >= 0 && delta != expect) { + errorf("gob decode: corrupted type: delta %d expected %d", delta, expect) + } + return delta } -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) +// TypeDefinition: +// [int(-typeId) (already read)] encodingOfWireType +func (deb *debugger) typeDefinition(indent tab, id typeId) { + deb.dump("type definition for id %d", id) + // Encoding is of a wireType. Decode the structure as usual + fieldNum := -1 + wire := new(wireType) + // A wireType defines a single field. + delta := deb.delta(-1) + fieldNum += delta + switch fieldNum { + case 0: // array type, one field of {{Common}, elem, length} + // Field number 0 is CommonType + deb.delta(1) + com := deb.common() + // Field number 1 is type Id of elem + deb.delta(1) + id := deb.typeId() + // Field number 3 is length + deb.delta(1) + length := deb.int() + wire.ArrayT = &arrayType{com, id, length} + + case 1: // slice type, one field of {{Common}, elem} + // Field number 0 is CommonType + deb.delta(1) + com := deb.common() + // Field number 1 is type Id of elem + deb.delta(1) + id := deb.typeId() + wire.SliceT = &sliceType{com, id} + + case 2: // struct type, one field of {{Common}, []fieldType} + // Field number 0 is CommonType + deb.delta(1) + com := deb.common() + // Field number 1 is slice of FieldType + deb.delta(1) + numField := int(deb.uint()) + field := make([]*fieldType, numField) + for i := 0; i < numField; i++ { + field[i] = new(fieldType) + deb.delta(1) // field 0 of fieldType: name + field[i].Name = deb.string() + deb.delta(1) // field 1 of fieldType: id + field[i].Id = deb.typeId() + deb.delta(0) // end of fieldType } + wire.StructT = &structType{com, field} + + case 3: // map type, one field of {{Common}, key, elem} + // Field number 0 is CommonType + deb.delta(1) + com := deb.common() + // Field number 1 is type Id of key + deb.delta(1) + keyId := deb.typeId() + wire.SliceT = &sliceType{com, id} + // Field number 2 is type Id of elem + deb.delta(1) + elemId := deb.typeId() + wire.MapT = &mapType{com, keyId, elemId} + default: + errorf("bad field in type %d", fieldNum) } - fmt.Printf("}\n") + deb.printWireType(indent, wire) + deb.delta(0) // end inner type (arrayType, etc.) + deb.delta(0) // end wireType + // Remember we've seen this type. + deb.wireType[id] = wire } -func printCommonType(kind string, common *CommonType) { - fmt.Printf("\t%s %q\n\tid: %d\n", kind, common.Name, common.Id) -} -func (dec *Decoder) debugPrint(indent int, id typeId) { - wire, ok := dec.wireType[id] +// Value: +// SingletonValue | StructValue +func (deb *debugger) value(indent tab, id typeId) { + wire, ok := deb.wireType[id] if ok && wire.StructT != nil { - dec.debugStruct(indent+1, id, wire) + deb.structValue(indent, id) } else { - dec.debugSingle(indent+1, id, wire) + deb.singletonValue(indent, id) } } -func (dec *Decoder) debugSingle(indent int, id typeId, wire *wireType) { +// SingletonValue: +// uint(0) FieldValue +func (deb *debugger) singletonValue(indent tab, id typeId) { + deb.dump("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 := deb.uint64() + if m != 0 { + errorf("expected zero; got %d", m) } - dec.state.decodeUint() - dec.printItem(indent, id) + deb.fieldValue(indent, id) } -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) { + deb.dump("Start of interface value") + if nameLen := deb.uint64(); nameLen == 0 { + deb.nilInterfaceValue(indent) + } else { + deb.nonNilInterfaceValue(indent, int(nameLen)) } +} + +// 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 int) { + // ConcreteTypeName + b := make([]byte, nameLen) + deb.r.Read(b) // TODO: CHECK THESE READS!! + deb.consumed(nameLen) + name := string(b) + + for { + id := deb.typeId() + if id < 0 { + deb.typeDefinition(indent, -id) + n := deb.loadBlock(false) + deb.dump("Nested message of length %d", n) + } else { + // DelimitedValue + x := deb.uint64() // in case we want to ignore the value; we don't. + fmt.Fprintf(os.Stderr, "%sinterface value, type %q id=%d; valueLength %d\n", indent, name, id, x) + deb.value(indent, id) + break + } + } +} + +// 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: + 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: + 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: + deb.printCommonType(indent, "slice", &wire.SliceT.CommonType) + fmt.Fprintf(os.Stderr, "%selem id=%d\n", indent+1, wire.SliceT.Elem) + case wire.StructT != nil: + 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) +} + +// fieldValue prints a value of any type, such as a struct field. +// FieldValue: +// builtinValue | ArrayValue | MapValue | SliceValue | StructValue | InterfaceValue +func (deb *debugger) fieldValue(indent tab, id typeId) { _, ok := builtinIdToType[id] if ok { - dec.printBuiltin(indent, id) + if id == tInterface { + deb.interfaceValue(indent) + } else { + deb.printBuiltin(indent, id) + } return } - wire, ok := dec.wireType[id] + wire, ok := deb.wireType[id] if !ok { - errorf("type id %d not defined\n", id) + errorf("type id %d not defined", id) } switch { case wire.ArrayT != nil: - dec.printArray(indent, wire) + deb.arrayValue(indent, wire) case wire.MapT != nil: - dec.printMap(indent, wire) + deb.mapValue(indent, wire) case wire.SliceT != nil: - dec.printSlice(indent, wire) + deb.sliceValue(indent, wire) case wire.StructT != nil: - dec.debugStruct(indent, id, wire) + deb.structValue(indent, id) + default: + panic("bad wire type for field") + } +} + +// printBuiltin prints a value not of a fundamental type, that is, +// one whose type is known to gobs at bootstrap time. +func (deb *debugger) printBuiltin(indent tab, id typeId) { + switch id { + case tBool: + x := deb.int64() + if x == 0 { + fmt.Fprintf(os.Stderr, "%sfalse\n", indent) + } else { + fmt.Fprintf(os.Stderr, "%strue\n", indent) + } + case tInt: + x := deb.int64() + fmt.Fprintf(os.Stderr, "%s%d\n", indent, x) + case tUint: + x := deb.int64() + fmt.Fprintf(os.Stderr, "%s%d\n", indent, x) + case tFloat: + x := deb.uint64() + fmt.Fprintf(os.Stderr, "%s%g\n", indent, floatFromBits(x)) + case tComplex: + r := deb.uint64() + i := deb.uint64() + fmt.Fprintf(os.Stderr, "%s%g+%gi\n", indent, floatFromBits(r), floatFromBits(i)) + case tBytes: + x := int(deb.uint64()) + b := make([]byte, x) + deb.r.Read(b) + deb.consumed(x) + fmt.Fprintf(os.Stderr, "%s{% x}=%q\n", indent, b, b) + case tString: + x := int(deb.uint64()) + b := make([]byte, x) + deb.r.Read(b) + deb.consumed(x) + fmt.Fprintf(os.Stderr, "%s%q\n", indent, b) + default: + panic("unknown builtin") } } -func (dec *Decoder) printArray(indent int, wire *wireType) { + +// ArrayValue: +// uint(n) FieldValue*n +func (deb *debugger) arrayValue(indent tab, wire *wireType) { elemId := wire.ArrayT.Elem - n := int(dec.state.decodeUint()) - for i := 0; i < n && dec.err == nil; i++ { - dec.printItem(indent, elemId) + u := deb.uint64() + length := int(u) + for i := 0; i < length; i++ { + deb.fieldValue(indent, elemId) } - if n != wire.ArrayT.Len { - tab(indent) - fmt.Printf("(wrong length for array: %d should be %d)\n", n, wire.ArrayT.Len) + if length != wire.ArrayT.Len { + fmt.Fprintf(os.Stderr, "%s(wrong length for array: %d should be %d)\n", indent, length, wire.ArrayT.Len) } } -func (dec *Decoder) printMap(indent int, wire *wireType) { +// MapValue: +// uint(n) (FieldValue FieldValue)*n [n (key, value) pairs] +func (deb *debugger) mapValue(indent tab, 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) + u := deb.uint64() + length := int(u) + for i := 0; i < length; i++ { + deb.fieldValue(indent+1, keyId) + deb.fieldValue(indent+1, elemId) } } -func (dec *Decoder) printSlice(indent int, wire *wireType) { +// SliceValue: +// uint(n) (n FieldValue) +func (deb *debugger) sliceValue(indent tab, wire *wireType) { elemId := wire.SliceT.Elem - n := int(dec.state.decodeUint()) - for i := 0; i < n && dec.err == nil; i++ { - dec.printItem(indent, elemId) - } -} + u := deb.uint64() + length := int(u) + deb.dump("Start of slice of length %d", length) -func (dec *Decoder) printBuiltin(indent int, id typeId) { - tab(indent) - switch id { - case tBool: - if dec.state.decodeInt() == 0 { - fmt.Printf("false\n") - } else { - fmt.Printf("true\n") - } - case tInt: - fmt.Printf("%d\n", dec.state.decodeInt()) - case tUint: - fmt.Printf("%d\n", dec.state.decodeUint()) - case tFloat: - fmt.Printf("%g\n", floatFromBits(dec.state.decodeUint())) - case tBytes: - b := make([]byte, dec.state.decodeUint()) - dec.state.b.Read(b) - fmt.Printf("% x\n", b) - case tString: - b := make([]byte, dec.state.decodeUint()) - dec.state.b.Read(b) - fmt.Printf("%q\n", b) - 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) - } - default: - fmt.Print("unknown\n") + for i := 0; i < length; i++ { + deb.fieldValue(indent, elemId) } } -func (dec *Decoder) debugStruct(indent int, id typeId, wire *wireType) { - tab(indent) - fmt.Printf("%s struct {\n", id.name()) +// StructValue: +// (uint(fieldDelta) FieldValue)* +func (deb *debugger) structValue(indent tab, id typeId) { + deb.dump("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++ + for { + delta := deb.uint64() 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("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) + deb.fieldValue(indent+1, strct.Field[fieldNum].Id) } + indent-- + fmt.Fprintf(os.Stderr, "%s} // end %s struct\n", indent, id.name()) + deb.dump(">> End of struct value of type %d %q", id, id.name()) } |