The Go Programming Language

Source file src/pkg/gob/debug.go

     1	package gob
     2	
     3	// This file is not normally included in the gob package.  Used only for debugging the package itself.
     4	// Add debug.go to the files listed in the Makefile to add Debug to the gob package.
     5	// Except for reading uints, it is an implementation of a reader that is independent of
     6	// the one implemented by Decoder.
     7	
     8	import (
     9		"bytes"
    10		"fmt"
    11		"io"
    12		"os"
    13		"strings"
    14		"sync"
    15	)
    16	
    17	var dumpBytes = false // If true, print the remaining bytes in the input buffer at each item.
    18	
    19	// Init installs the debugging facility. If this file is not compiled in the
    20	// package, the tests in codec_test.go are no-ops.
    21	func init() {
    22		debugFunc = Debug
    23	}
    24	
    25	var (
    26		blanks = bytes.Repeat([]byte{' '}, 3*10)
    27		empty  = []byte(": <empty>\n")
    28		tabs   = strings.Repeat("\t", 100)
    29	)
    30	
    31	// tab indents itself when printed.
    32	type tab int
    33	
    34	func (t tab) String() string {
    35		n := int(t)
    36		if n > len(tabs) {
    37			n = len(tabs)
    38		}
    39		return tabs[0:n]
    40	}
    41	
    42	func (t tab) print() {
    43		fmt.Fprint(os.Stderr, t)
    44	}
    45	
    46	// A peekReader wraps an io.Reader, allowing one to peek ahead to see
    47	// what's coming without stealing the data from the client of the Reader.
    48	type peekReader struct {
    49		r    io.Reader
    50		data []byte // read-ahead data
    51	}
    52	
    53	// newPeekReader returns a peekReader that wraps r.
    54	func newPeekReader(r io.Reader) *peekReader {
    55		return &peekReader{r: r}
    56	}
    57	
    58	// Read is the usual method. It will first take data that has been read ahead.
    59	func (p *peekReader) Read(b []byte) (n int, err os.Error) {
    60		if len(p.data) == 0 {
    61			return p.r.Read(b)
    62		}
    63		// Satisfy what's possible from the read-ahead data.
    64		n = copy(b, p.data)
    65		// Move data down to beginning of slice, to avoid endless growth
    66		copy(p.data, p.data[n:])
    67		p.data = p.data[:len(p.data)-n]
    68		return
    69	}
    70	
    71	// peek returns as many bytes as possible from the unread
    72	// portion of the stream, up to the length of b.
    73	func (p *peekReader) peek(b []byte) (n int, err os.Error) {
    74		if len(p.data) > 0 {
    75			n = copy(b, p.data)
    76			if n == len(b) {
    77				return
    78			}
    79			b = b[n:]
    80		}
    81		if len(b) == 0 {
    82			return
    83		}
    84		m, e := io.ReadFull(p.r, b)
    85		if m > 0 {
    86			p.data = append(p.data, b[:m]...)
    87		}
    88		n += m
    89		if e == io.ErrUnexpectedEOF {
    90			// That means m > 0 but we reached EOF. If we got data
    91			// we won't complain about not being able to peek enough.
    92			if n > 0 {
    93				e = nil
    94			} else {
    95				e = os.EOF
    96			}
    97		}
    98		return n, e
    99	}
   100	
   101	type debugger struct {
   102		mutex          sync.Mutex
   103		remain         int  // the number of bytes known to remain in the input
   104		remainingKnown bool // the value of 'remain' is valid
   105		r              *peekReader
   106		wireType       map[typeId]*wireType
   107		tmp            []byte // scratch space for decoding uints.
   108	}
   109	
   110	// dump prints the next nBytes of the input.
   111	// It arranges to print the output aligned from call to
   112	// call, to make it easy to see what has been consumed.
   113	func (deb *debugger) dump(format string, args ...interface{}) {
   114		if !dumpBytes {
   115			return
   116		}
   117		fmt.Fprintf(os.Stderr, format+" ", args...)
   118		if !deb.remainingKnown {
   119			return
   120		}
   121		if deb.remain < 0 {
   122			fmt.Fprintf(os.Stderr, "remaining byte count is negative! %d\n", deb.remain)
   123			return
   124		}
   125		data := make([]byte, deb.remain)
   126		n, _ := deb.r.peek(data)
   127		if n == 0 {
   128			os.Stderr.Write(empty)
   129			return
   130		}
   131		b := new(bytes.Buffer)
   132		fmt.Fprintf(b, "[%d]{\n", deb.remain)
   133		// Blanks until first byte
   134		lineLength := 0
   135		if n := len(data); n%10 != 0 {
   136			lineLength = 10 - n%10
   137			fmt.Fprintf(b, "\t%s", blanks[:lineLength*3])
   138		}
   139		// 10 bytes per line
   140		for len(data) > 0 {
   141			if lineLength == 0 {
   142				fmt.Fprint(b, "\t")
   143			}
   144			m := 10 - lineLength
   145			lineLength = 0
   146			if m > len(data) {
   147				m = len(data)
   148			}
   149			fmt.Fprintf(b, "% x\n", data[:m])
   150			data = data[m:]
   151		}
   152		fmt.Fprint(b, "}\n")
   153		os.Stderr.Write(b.Bytes())
   154	}
   155	
   156	// Debug prints a human-readable representation of the gob data read from r.
   157	func Debug(r io.Reader) {
   158		err := debug(r)
   159		if err != nil {
   160			fmt.Fprintf(os.Stderr, "gob debug: %s\n", err)
   161		}
   162	}
   163	
   164	// debug implements Debug, but catches panics and returns
   165	// them as errors to be printed by Debug.
   166	func debug(r io.Reader) (err os.Error) {
   167		defer catchError(&err)
   168		fmt.Fprintln(os.Stderr, "Start of debugging")
   169		deb := &debugger{
   170			r:        newPeekReader(r),
   171			wireType: make(map[typeId]*wireType),
   172			tmp:      make([]byte, 16),
   173		}
   174		if b, ok := r.(*bytes.Buffer); ok {
   175			deb.remain = b.Len()
   176			deb.remainingKnown = true
   177		}
   178		deb.gobStream()
   179		return
   180	}
   181	
   182	// note that we've consumed some bytes
   183	func (deb *debugger) consumed(n int) {
   184		if deb.remainingKnown {
   185			deb.remain -= n
   186		}
   187	}
   188	
   189	// int64 decodes and returns the next integer, which must be present.
   190	// Don't call this if you could be at EOF.
   191	func (deb *debugger) int64() int64 {
   192		return toInt(deb.uint64())
   193	}
   194	
   195	// uint64 returns and decodes the next unsigned integer, which must be present.
   196	// Don't call this if you could be at EOF.
   197	// TODO: handle errors better.
   198	func (deb *debugger) uint64() uint64 {
   199		n, w, err := decodeUintReader(deb.r, deb.tmp)
   200		if err != nil {
   201			errorf("debug: read error: %s", err)
   202		}
   203		deb.consumed(w)
   204		return n
   205	}
   206	
   207	// GobStream:
   208	//	DelimitedMessage* (until EOF)
   209	func (deb *debugger) gobStream() {
   210		// Make sure we're single-threaded through here.
   211		deb.mutex.Lock()
   212		defer deb.mutex.Unlock()
   213	
   214		for deb.delimitedMessage(0) {
   215		}
   216	}
   217	
   218	// DelimitedMessage:
   219	//	uint(lengthOfMessage) Message
   220	func (deb *debugger) delimitedMessage(indent tab) bool {
   221		for {
   222			n := deb.loadBlock(true)
   223			if n < 0 {
   224				return false
   225			}
   226			deb.dump("Delimited message of length %d", n)
   227			deb.message(indent)
   228		}
   229		return true
   230	}
   231	
   232	// loadBlock preps us to read a message
   233	// of the length specified next in the input. It returns
   234	// the length of the block. The argument tells whether
   235	// an EOF is acceptable now.  If it is and one is found,
   236	// the return value is negative.
   237	func (deb *debugger) loadBlock(eofOK bool) int {
   238		n64, w, err := decodeUintReader(deb.r, deb.tmp) // deb.uint64 will error at EOF
   239		if err != nil {
   240			if eofOK && err == os.EOF {
   241				return -1
   242			}
   243			errorf("debug: unexpected error: %s", err)
   244		}
   245		deb.consumed(w)
   246		n := int(n64)
   247		if n < 0 {
   248			errorf("huge value for message length: %d", n64)
   249		}
   250		return int(n)
   251	}
   252	
   253	// Message:
   254	//	TypeSequence TypedValue
   255	// TypeSequence
   256	//	(TypeDefinition DelimitedTypeDefinition*)?
   257	// DelimitedTypeDefinition:
   258	//	uint(lengthOfTypeDefinition) TypeDefinition
   259	// TypedValue:
   260	//	int(typeId) Value
   261	func (deb *debugger) message(indent tab) bool {
   262		for {
   263			// Convert the uint64 to a signed integer typeId
   264			uid := deb.int64()
   265			id := typeId(uid)
   266			deb.dump("type id=%d", id)
   267			if id < 0 {
   268				deb.typeDefinition(indent, -id)
   269				n := deb.loadBlock(false)
   270				deb.dump("Message of length %d", n)
   271				continue
   272			} else {
   273				deb.value(indent, id)
   274				break
   275			}
   276		}
   277		return true
   278	}
   279	
   280	// Helper methods to make it easy to scan a type descriptor.
   281	
   282	// common returns the CommonType at the input point.
   283	func (deb *debugger) common() CommonType {
   284		fieldNum := -1
   285		name := ""
   286		id := typeId(0)
   287		for {
   288			delta := deb.delta(-1)
   289			if delta == 0 {
   290				break
   291			}
   292			fieldNum += delta
   293			switch fieldNum {
   294			case 0:
   295				name = deb.string()
   296			case 1:
   297				// Id typeId
   298				id = deb.typeId()
   299			default:
   300				errorf("corrupted CommonType")
   301			}
   302		}
   303		return CommonType{name, id}
   304	}
   305	
   306	// uint returns the unsigned int at the input point, as a uint (not uint64).
   307	func (deb *debugger) uint() uint {
   308		return uint(deb.uint64())
   309	}
   310	
   311	// int returns the signed int at the input point, as an int (not int64).
   312	func (deb *debugger) int() int {
   313		return int(deb.int64())
   314	}
   315	
   316	// typeId returns the type id at the input point.
   317	func (deb *debugger) typeId() typeId {
   318		return typeId(deb.int64())
   319	}
   320	
   321	// string returns the string at the input point.
   322	func (deb *debugger) string() string {
   323		x := int(deb.uint64())
   324		b := make([]byte, x)
   325		nb, _ := deb.r.Read(b)
   326		if nb != x {
   327			errorf("corrupted type")
   328		}
   329		deb.consumed(nb)
   330		return string(b)
   331	}
   332	
   333	// delta returns the field delta at the input point.  The expect argument,
   334	// if non-negative, identifies what the value should be.
   335	func (deb *debugger) delta(expect int) int {
   336		delta := int(deb.uint64())
   337		if delta < 0 || (expect >= 0 && delta != expect) {
   338			errorf("decode: corrupted type: delta %d expected %d", delta, expect)
   339		}
   340		return delta
   341	}
   342	
   343	// TypeDefinition:
   344	//	[int(-typeId) (already read)] encodingOfWireType
   345	func (deb *debugger) typeDefinition(indent tab, id typeId) {
   346		deb.dump("type definition for id %d", id)
   347		// Encoding is of a wireType. Decode the structure as usual
   348		fieldNum := -1
   349		wire := new(wireType)
   350		// A wireType defines a single field.
   351		delta := deb.delta(-1)
   352		fieldNum += delta
   353		switch fieldNum {
   354		case 0: // array type, one field of {{Common}, elem, length}
   355			// Field number 0 is CommonType
   356			deb.delta(1)
   357			com := deb.common()
   358			// Field number 1 is type Id of elem
   359			deb.delta(1)
   360			id := deb.typeId()
   361			// Field number 3 is length
   362			deb.delta(1)
   363			length := deb.int()
   364			wire.ArrayT = &arrayType{com, id, length}
   365	
   366		case 1: // slice type, one field of {{Common}, elem}
   367			// Field number 0 is CommonType
   368			deb.delta(1)
   369			com := deb.common()
   370			// Field number 1 is type Id of elem
   371			deb.delta(1)
   372			id := deb.typeId()
   373			wire.SliceT = &sliceType{com, id}
   374	
   375		case 2: // struct type, one field of {{Common}, []fieldType}
   376			// Field number 0 is CommonType
   377			deb.delta(1)
   378			com := deb.common()
   379			// Field number 1 is slice of FieldType
   380			deb.delta(1)
   381			numField := int(deb.uint())
   382			field := make([]*fieldType, numField)
   383			for i := 0; i < numField; i++ {
   384				field[i] = new(fieldType)
   385				deb.delta(1) // field 0 of fieldType: name
   386				field[i].Name = deb.string()
   387				deb.delta(1) // field 1 of fieldType: id
   388				field[i].Id = deb.typeId()
   389				deb.delta(0) // end of fieldType
   390			}
   391			wire.StructT = &structType{com, field}
   392	
   393		case 3: // map type, one field of {{Common}, key, elem}
   394			// Field number 0 is CommonType
   395			deb.delta(1)
   396			com := deb.common()
   397			// Field number 1 is type Id of key
   398			deb.delta(1)
   399			keyId := deb.typeId()
   400			// Field number 2 is type Id of elem
   401			deb.delta(1)
   402			elemId := deb.typeId()
   403			wire.MapT = &mapType{com, keyId, elemId}
   404		case 4: // GobEncoder type, one field of {{Common}}
   405			// Field number 0 is CommonType
   406			deb.delta(1)
   407			com := deb.common()
   408			wire.GobEncoderT = &gobEncoderType{com}
   409		default:
   410			errorf("bad field in type %d", fieldNum)
   411		}
   412		deb.printWireType(indent, wire)
   413		deb.delta(0) // end inner type (arrayType, etc.)
   414		deb.delta(0) // end wireType
   415		// Remember we've seen this type.
   416		deb.wireType[id] = wire
   417	}
   418	
   419	// Value:
   420	//	SingletonValue | StructValue
   421	func (deb *debugger) value(indent tab, id typeId) {
   422		wire, ok := deb.wireType[id]
   423		if ok && wire.StructT != nil {
   424			deb.structValue(indent, id)
   425		} else {
   426			deb.singletonValue(indent, id)
   427		}
   428	}
   429	
   430	// SingletonValue:
   431	//	uint(0) FieldValue
   432	func (deb *debugger) singletonValue(indent tab, id typeId) {
   433		deb.dump("Singleton value")
   434		// is it a builtin type?
   435		wire := deb.wireType[id]
   436		_, ok := builtinIdToType[id]
   437		if !ok && wire == nil {
   438			errorf("type id %d not defined", id)
   439		}
   440		m := deb.uint64()
   441		if m != 0 {
   442			errorf("expected zero; got %d", m)
   443		}
   444		deb.fieldValue(indent, id)
   445	}
   446	
   447	// InterfaceValue:
   448	//	NilInterfaceValue | NonNilInterfaceValue
   449	func (deb *debugger) interfaceValue(indent tab) {
   450		deb.dump("Start of interface value")
   451		if nameLen := deb.uint64(); nameLen == 0 {
   452			deb.nilInterfaceValue(indent)
   453		} else {
   454			deb.nonNilInterfaceValue(indent, int(nameLen))
   455		}
   456	}
   457	
   458	// NilInterfaceValue:
   459	//	uint(0) [already read]
   460	func (deb *debugger) nilInterfaceValue(indent tab) int {
   461		fmt.Fprintf(os.Stderr, "%snil interface\n", indent)
   462		return 0
   463	}
   464	
   465	// NonNilInterfaceValue:
   466	//	ConcreteTypeName TypeSequence InterfaceContents
   467	// ConcreteTypeName:
   468	//	uint(lengthOfName) [already read=n] name
   469	// InterfaceContents:
   470	//	int(concreteTypeId) DelimitedValue
   471	// DelimitedValue:
   472	//	uint(length) Value
   473	func (deb *debugger) nonNilInterfaceValue(indent tab, nameLen int) {
   474		// ConcreteTypeName
   475		b := make([]byte, nameLen)
   476		deb.r.Read(b) // TODO: CHECK THESE READS!!
   477		deb.consumed(nameLen)
   478		name := string(b)
   479	
   480		for {
   481			id := deb.typeId()
   482			if id < 0 {
   483				deb.typeDefinition(indent, -id)
   484				n := deb.loadBlock(false)
   485				deb.dump("Nested message of length %d", n)
   486			} else {
   487				// DelimitedValue
   488				x := deb.uint64() // in case we want to ignore the value; we don't.
   489				fmt.Fprintf(os.Stderr, "%sinterface value, type %q id=%d; valueLength %d\n", indent, name, id, x)
   490				deb.value(indent, id)
   491				break
   492			}
   493		}
   494	}
   495	
   496	// printCommonType prints a common type; used by printWireType.
   497	func (deb *debugger) printCommonType(indent tab, kind string, common *CommonType) {
   498		indent.print()
   499		fmt.Fprintf(os.Stderr, "%s %q id=%d\n", kind, common.Name, common.Id)
   500	}
   501	
   502	// printWireType prints the contents of a wireType.
   503	func (deb *debugger) printWireType(indent tab, wire *wireType) {
   504		fmt.Fprintf(os.Stderr, "%stype definition {\n", indent)
   505		indent++
   506		switch {
   507		case wire.ArrayT != nil:
   508			deb.printCommonType(indent, "array", &wire.ArrayT.CommonType)
   509			fmt.Fprintf(os.Stderr, "%slen %d\n", indent+1, wire.ArrayT.Len)
   510			fmt.Fprintf(os.Stderr, "%selemid %d\n", indent+1, wire.ArrayT.Elem)
   511		case wire.MapT != nil:
   512			deb.printCommonType(indent, "map", &wire.MapT.CommonType)
   513			fmt.Fprintf(os.Stderr, "%skey id=%d\n", indent+1, wire.MapT.Key)
   514			fmt.Fprintf(os.Stderr, "%selem id=%d\n", indent+1, wire.MapT.Elem)
   515		case wire.SliceT != nil:
   516			deb.printCommonType(indent, "slice", &wire.SliceT.CommonType)
   517			fmt.Fprintf(os.Stderr, "%selem id=%d\n", indent+1, wire.SliceT.Elem)
   518		case wire.StructT != nil:
   519			deb.printCommonType(indent, "struct", &wire.StructT.CommonType)
   520			for i, field := range wire.StructT.Field {
   521				fmt.Fprintf(os.Stderr, "%sfield %d:\t%s\tid=%d\n", indent+1, i, field.Name, field.Id)
   522			}
   523		case wire.GobEncoderT != nil:
   524			deb.printCommonType(indent, "GobEncoder", &wire.GobEncoderT.CommonType)
   525		}
   526		indent--
   527		fmt.Fprintf(os.Stderr, "%s}\n", indent)
   528	}
   529	
   530	// fieldValue prints a value of any type, such as a struct field.
   531	// FieldValue:
   532	//	builtinValue | ArrayValue | MapValue | SliceValue | StructValue | InterfaceValue
   533	func (deb *debugger) fieldValue(indent tab, id typeId) {
   534		_, ok := builtinIdToType[id]
   535		if ok {
   536			if id == tInterface {
   537				deb.interfaceValue(indent)
   538			} else {
   539				deb.printBuiltin(indent, id)
   540			}
   541			return
   542		}
   543		wire, ok := deb.wireType[id]
   544		if !ok {
   545			errorf("type id %d not defined", id)
   546		}
   547		switch {
   548		case wire.ArrayT != nil:
   549			deb.arrayValue(indent, wire)
   550		case wire.MapT != nil:
   551			deb.mapValue(indent, wire)
   552		case wire.SliceT != nil:
   553			deb.sliceValue(indent, wire)
   554		case wire.StructT != nil:
   555			deb.structValue(indent, id)
   556		case wire.GobEncoderT != nil:
   557			deb.gobEncoderValue(indent, id)
   558		default:
   559			panic("bad wire type for field")
   560		}
   561	}
   562	
   563	// printBuiltin prints a value not of a fundamental type, that is,
   564	// one whose type is known to gobs at bootstrap time.
   565	func (deb *debugger) printBuiltin(indent tab, id typeId) {
   566		switch id {
   567		case tBool:
   568			x := deb.int64()
   569			if x == 0 {
   570				fmt.Fprintf(os.Stderr, "%sfalse\n", indent)
   571			} else {
   572				fmt.Fprintf(os.Stderr, "%strue\n", indent)
   573			}
   574		case tInt:
   575			x := deb.int64()
   576			fmt.Fprintf(os.Stderr, "%s%d\n", indent, x)
   577		case tUint:
   578			x := deb.int64()
   579			fmt.Fprintf(os.Stderr, "%s%d\n", indent, x)
   580		case tFloat:
   581			x := deb.uint64()
   582			fmt.Fprintf(os.Stderr, "%s%g\n", indent, floatFromBits(x))
   583		case tComplex:
   584			r := deb.uint64()
   585			i := deb.uint64()
   586			fmt.Fprintf(os.Stderr, "%s%g+%gi\n", indent, floatFromBits(r), floatFromBits(i))
   587		case tBytes:
   588			x := int(deb.uint64())
   589			b := make([]byte, x)
   590			deb.r.Read(b)
   591			deb.consumed(x)
   592			fmt.Fprintf(os.Stderr, "%s{% x}=%q\n", indent, b, b)
   593		case tString:
   594			x := int(deb.uint64())
   595			b := make([]byte, x)
   596			deb.r.Read(b)
   597			deb.consumed(x)
   598			fmt.Fprintf(os.Stderr, "%s%q\n", indent, b)
   599		default:
   600			panic("unknown builtin")
   601		}
   602	}
   603	
   604	// ArrayValue:
   605	//	uint(n) FieldValue*n
   606	func (deb *debugger) arrayValue(indent tab, wire *wireType) {
   607		elemId := wire.ArrayT.Elem
   608		u := deb.uint64()
   609		length := int(u)
   610		for i := 0; i < length; i++ {
   611			deb.fieldValue(indent, elemId)
   612		}
   613		if length != wire.ArrayT.Len {
   614			fmt.Fprintf(os.Stderr, "%s(wrong length for array: %d should be %d)\n", indent, length, wire.ArrayT.Len)
   615		}
   616	}
   617	
   618	// MapValue:
   619	//	uint(n) (FieldValue FieldValue)*n  [n (key, value) pairs]
   620	func (deb *debugger) mapValue(indent tab, wire *wireType) {
   621		keyId := wire.MapT.Key
   622		elemId := wire.MapT.Elem
   623		u := deb.uint64()
   624		length := int(u)
   625		for i := 0; i < length; i++ {
   626			deb.fieldValue(indent+1, keyId)
   627			deb.fieldValue(indent+1, elemId)
   628		}
   629	}
   630	
   631	// SliceValue:
   632	//	uint(n) (n FieldValue)
   633	func (deb *debugger) sliceValue(indent tab, wire *wireType) {
   634		elemId := wire.SliceT.Elem
   635		u := deb.uint64()
   636		length := int(u)
   637		deb.dump("Start of slice of length %d", length)
   638	
   639		for i := 0; i < length; i++ {
   640			deb.fieldValue(indent, elemId)
   641		}
   642	}
   643	
   644	// StructValue:
   645	//	(uint(fieldDelta) FieldValue)*
   646	func (deb *debugger) structValue(indent tab, id typeId) {
   647		deb.dump("Start of struct value of %q id=%d\n<<\n", id.name(), id)
   648		fmt.Fprintf(os.Stderr, "%s%s struct {\n", indent, id.name())
   649		wire, ok := deb.wireType[id]
   650		if !ok {
   651			errorf("type id %d not defined", id)
   652		}
   653		strct := wire.StructT
   654		fieldNum := -1
   655		indent++
   656		for {
   657			delta := deb.uint64()
   658			if delta == 0 { // struct terminator is zero delta fieldnum
   659				break
   660			}
   661			fieldNum += int(delta)
   662			if fieldNum < 0 || fieldNum >= len(strct.Field) {
   663				deb.dump("field number out of range: prevField=%d delta=%d", fieldNum-int(delta), delta)
   664				break
   665			}
   666			fmt.Fprintf(os.Stderr, "%sfield %d:\t%s\n", indent, fieldNum, wire.StructT.Field[fieldNum].Name)
   667			deb.fieldValue(indent+1, strct.Field[fieldNum].Id)
   668		}
   669		indent--
   670		fmt.Fprintf(os.Stderr, "%s} // end %s struct\n", indent, id.name())
   671		deb.dump(">> End of struct value of type %d %q", id, id.name())
   672	}
   673	
   674	// GobEncoderValue:
   675	//	uint(n) byte*n
   676	func (deb *debugger) gobEncoderValue(indent tab, id typeId) {
   677		len := deb.uint64()
   678		deb.dump("GobEncoder value of %q id=%d, length %d\n", id.name(), id, len)
   679		fmt.Fprintf(os.Stderr, "%s%s (implements GobEncoder)\n", indent, id.name())
   680		data := make([]byte, len)
   681		_, err := deb.r.Read(data)
   682		if err != nil {
   683			errorf("gobEncoder data read: %s", err)
   684		}
   685		fmt.Fprintf(os.Stderr, "%s[% .2x]\n", indent+1, data)
   686	}

release.r60.3. Except as noted, this content is licensed under a Creative Commons Attribution 3.0 License.