| // Copyright 2009 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. | |
| // Delete the next line to include in the gob package. | |
| //go:build ignore | |
| // +build ignore | |
| package gob | |
| // This file is not normally included in the gob package. Used only for debugging the package itself. | |
| // Except for reading uints, it is an implementation of a reader that is independent of | |
| // the one implemented by Decoder. | |
| // To enable the Debug function, delete the +build ignore line above and do | |
| // go install | |
| import ( | |
| "bytes" | |
| "fmt" | |
| "io" | |
| "os" | |
| "strings" | |
| "sync" | |
| ) | |
| 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 tests in codec_test.go are no-ops. | |
| func init() { | |
| debugFunc = Debug | |
| } | |
| 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 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 error) { | |
| if len(p.data) > 0 { | |
| n = copy(b, p.data) | |
| if n == len(b) { | |
| return | |
| } | |
| 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 = io.EOF | |
| } | |
| } | |
| return n, e | |
| } | |
| 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 | |
| } | |
| fmt.Fprintf(os.Stderr, format+" ", args...) | |
| if !deb.remainingKnown { | |
| return | |
| } | |
| if deb.remain < 0 { | |
| fmt.Fprintf(os.Stderr, "remaining byte count is negative! %d\n", deb.remain) | |
| return | |
| } | |
| data := make([]byte, deb.remain) | |
| n, _ := deb.r.peek(data) | |
| if n == 0 { | |
| os.Stderr.Write(empty) | |
| return | |
| } | |
| 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. | |
| // It is a no-op unless debugging was enabled when the package was built. | |
| func Debug(r io.Reader) { | |
| err := debug(r) | |
| if err != nil { | |
| fmt.Fprintf(os.Stderr, "gob debug: %s\n", err) | |
| } | |
| } | |
| // debug implements Debug, but catches panics and returns | |
| // them as errors to be printed by Debug. | |
| func debug(r io.Reader) (err error) { | |
| defer catchError(&err) | |
| 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() | |
| return | |
| } | |
| // 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 | |
| } | |
| // GobStream: | |
| // DelimitedMessage* (until EOF) | |
| func (deb *debugger) gobStream() { | |
| // Make sure we're single-threaded through here. | |
| deb.mutex.Lock() | |
| defer deb.mutex.Unlock() | |
| for deb.delimitedMessage(0) { | |
| } | |
| } | |
| // 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 | |
| } | |
| // 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 == io.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. | |
| // 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 | |
| } | |
| fieldNum += delta | |
| switch fieldNum { | |
| case 0: | |
| name = deb.string() | |
| case 1: | |
| // Id typeId | |
| id = deb.typeId() | |
| default: | |
| errorf("corrupted CommonType, delta is %d fieldNum is %d", delta, fieldNum) | |
| } | |
| } | |
| return CommonType{name, id} | |
| } | |
| // uint returns the unsigned int at the input point, as a uint (not uint64). | |
| func (deb *debugger) uint() uint { | |
| return uint(deb.uint64()) | |
| } | |
| // 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") | |
| } | |
| deb.consumed(nb) | |
| return string(b) | |
| } | |
| // 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("decode: corrupted type: delta %d expected %d", delta, expect) | |
| } | |
| return delta | |
| } | |
| // 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() | |
| // Field number 2 is type Id of elem | |
| deb.delta(1) | |
| elemId := deb.typeId() | |
| wire.MapT = &mapType{com, keyId, elemId} | |
| case 4: // GobEncoder type, one field of {{Common}} | |
| // Field number 0 is CommonType | |
| deb.delta(1) | |
| com := deb.common() | |
| wire.GobEncoderT = &gobEncoderType{com} | |
| case 5: // BinaryMarshaler type, one field of {{Common}} | |
| // Field number 0 is CommonType | |
| deb.delta(1) | |
| com := deb.common() | |
| wire.BinaryMarshalerT = &gobEncoderType{com} | |
| case 6: // TextMarshaler type, one field of {{Common}} | |
| // Field number 0 is CommonType | |
| deb.delta(1) | |
| com := deb.common() | |
| wire.TextMarshalerT = &gobEncoderType{com} | |
| default: | |
| errorf("bad field in type %d", fieldNum) | |
| } | |
| 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 | |
| } | |
| // Value: | |
| // SingletonValue | StructValue | |
| func (deb *debugger) value(indent tab, id typeId) { | |
| wire, ok := deb.wireType[id] | |
| if ok && wire.StructT != nil { | |
| deb.structValue(indent, id) | |
| } else { | |
| deb.singletonValue(indent, id) | |
| } | |
| } | |
| // 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", id) | |
| } | |
| m := deb.uint64() | |
| if m != 0 { | |
| errorf("expected zero; got %d", m) | |
| } | |
| deb.fieldValue(indent, id) | |
| } | |
| // 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) | |
| } | |
| case wire.GobEncoderT != nil: | |
| deb.printCommonType(indent, "GobEncoder", &wire.GobEncoderT.CommonType) | |
| } | |
| 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 { | |
| if id == tInterface { | |
| deb.interfaceValue(indent) | |
| } else { | |
| deb.printBuiltin(indent, id) | |
| } | |
| return | |
| } | |
| wire, ok := deb.wireType[id] | |
| if !ok { | |
| errorf("type id %d not defined", id) | |
| } | |
| switch { | |
| case wire.ArrayT != nil: | |
| deb.arrayValue(indent, wire) | |
| case wire.MapT != nil: | |
| deb.mapValue(indent, wire) | |
| case wire.SliceT != nil: | |
| deb.sliceValue(indent, wire) | |
| case wire.StructT != nil: | |
| deb.structValue(indent, id) | |
| case wire.GobEncoderT != nil: | |
| deb.gobEncoderValue(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.uint64() | |
| fmt.Fprintf(os.Stderr, "%s%d\n", indent, x) | |
| case tFloat: | |
| x := deb.uint64() | |
| fmt.Fprintf(os.Stderr, "%s%g\n", indent, float64FromBits(x)) | |
| case tComplex: | |
| r := deb.uint64() | |
| i := deb.uint64() | |
| fmt.Fprintf(os.Stderr, "%s%g+%gi\n", indent, float64FromBits(r), float64FromBits(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") | |
| } | |
| } | |
| // ArrayValue: | |
| // uint(n) FieldValue*n | |
| func (deb *debugger) arrayValue(indent tab, wire *wireType) { | |
| elemId := wire.ArrayT.Elem | |
| u := deb.uint64() | |
| length := int(u) | |
| for i := 0; i < length; i++ { | |
| deb.fieldValue(indent, elemId) | |
| } | |
| if length != wire.ArrayT.Len { | |
| fmt.Fprintf(os.Stderr, "%s(wrong length for array: %d should be %d)\n", indent, length, wire.ArrayT.Len) | |
| } | |
| } | |
| // 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 | |
| u := deb.uint64() | |
| length := int(u) | |
| for i := 0; i < length; i++ { | |
| deb.fieldValue(indent+1, keyId) | |
| deb.fieldValue(indent+1, elemId) | |
| } | |
| } | |
| // SliceValue: | |
| // uint(n) (n FieldValue) | |
| func (deb *debugger) sliceValue(indent tab, wire *wireType) { | |
| elemId := wire.SliceT.Elem | |
| u := deb.uint64() | |
| length := int(u) | |
| deb.dump("Start of slice of length %d", length) | |
| for i := 0; i < length; i++ { | |
| deb.fieldValue(indent, elemId) | |
| } | |
| } | |
| // 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 | |
| fieldNum := -1 | |
| indent++ | |
| for { | |
| delta := deb.uint64() | |
| if delta == 0 { // struct terminator is zero delta fieldnum | |
| break | |
| } | |
| fieldNum += int(delta) | |
| if fieldNum < 0 || fieldNum >= len(strct.Field) { | |
| deb.dump("field number out of range: prevField=%d delta=%d", fieldNum-int(delta), delta) | |
| break | |
| } | |
| 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()) | |
| } | |
| // GobEncoderValue: | |
| // uint(n) byte*n | |
| func (deb *debugger) gobEncoderValue(indent tab, id typeId) { | |
| len := deb.uint64() | |
| deb.dump("GobEncoder value of %q id=%d, length %d\n", id.name(), id, len) | |
| fmt.Fprintf(os.Stderr, "%s%s (implements GobEncoder)\n", indent, id.name()) | |
| data := make([]byte, len) | |
| _, err := deb.r.Read(data) | |
| if err != nil { | |
| errorf("gobEncoder data read: %s", err) | |
| } | |
| fmt.Fprintf(os.Stderr, "%s[% .2x]\n", indent+1, data) | |
| } |