| // 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 |
| |
| 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 ...any) { |
| 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) |
| } |