| // Copyright 2018 The gVisor Authors. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package state |
| |
| import ( |
| "bytes" |
| "context" |
| "fmt" |
| "math" |
| "reflect" |
| |
| "gvisor.dev/gvisor/pkg/log" |
| "gvisor.dev/gvisor/pkg/state/wire" |
| ) |
| |
| // internalCallback is a interface called on object completion. |
| // |
| // There are two implementations: objectDecodeState & userCallback. |
| type internalCallback interface { |
| // source returns the dependent object. May be nil. |
| source() *objectDecodeState |
| |
| // callbackRun executes the callback. |
| callbackRun() |
| } |
| |
| // userCallback is an implementation of internalCallback. |
| type userCallback func() |
| |
| // source implements internalCallback.source. |
| func (userCallback) source() *objectDecodeState { |
| return nil |
| } |
| |
| // callbackRun implements internalCallback.callbackRun. |
| func (uc userCallback) callbackRun() { |
| uc() |
| } |
| |
| // objectDecodeState represents an object that may be in the process of being |
| // decoded. Specifically, it represents either a decoded object, or an an |
| // interest in a future object that will be decoded. When that interest is |
| // registered (via register), the storage for the object will be created, but |
| // it will not be decoded until the object is encountered in the stream. |
| type objectDecodeState struct { |
| // id is the id for this object. |
| id objectID |
| |
| // typ is the id for this typeID. This may be zero if this is not a |
| // type-registered structure. |
| typ typeID |
| |
| // obj is the object. This may or may not be valid yet, depending on |
| // whether complete returns true. However, regardless of whether the |
| // object is valid, obj contains a final storage location for the |
| // object. This is immutable. |
| // |
| // Note that this must be addressable (obj.Addr() must not panic). |
| // |
| // The obj passed to the decode methods below will equal this obj only |
| // in the case of decoding the top-level object. However, the passed |
| // obj may represent individual fields, elements of a slice, etc. that |
| // are effectively embedded within the reflect.Value below but with |
| // distinct types. |
| obj reflect.Value |
| |
| // blockedBy is the number of dependencies this object has. |
| blockedBy int |
| |
| // callbacksInline is inline storage for callbacks. |
| callbacksInline [2]internalCallback |
| |
| // callbacks is a set of callbacks to execute on load. |
| callbacks []internalCallback |
| |
| completeEntry |
| } |
| |
| // addCallback adds a callback to the objectDecodeState. |
| func (ods *objectDecodeState) addCallback(ic internalCallback) { |
| if ods.callbacks == nil { |
| ods.callbacks = ods.callbacksInline[:0] |
| } |
| ods.callbacks = append(ods.callbacks, ic) |
| } |
| |
| // findCycleFor returns when the given object is found in the blocking set. |
| func (ods *objectDecodeState) findCycleFor(target *objectDecodeState) []*objectDecodeState { |
| for _, ic := range ods.callbacks { |
| other := ic.source() |
| if other != nil && other == target { |
| return []*objectDecodeState{target} |
| } else if childList := other.findCycleFor(target); childList != nil { |
| return append(childList, other) |
| } |
| } |
| |
| // This should not occur. |
| Failf("no deadlock found?") |
| panic("unreachable") |
| } |
| |
| // findCycle finds a dependency cycle. |
| func (ods *objectDecodeState) findCycle() []*objectDecodeState { |
| return append(ods.findCycleFor(ods), ods) |
| } |
| |
| // source implements internalCallback.source. |
| func (ods *objectDecodeState) source() *objectDecodeState { |
| return ods |
| } |
| |
| // callbackRun implements internalCallback.callbackRun. |
| func (ods *objectDecodeState) callbackRun() { |
| ods.blockedBy-- |
| } |
| |
| // decodeState is a graph of objects in the process of being decoded. |
| // |
| // The decode process involves loading the breadth-first graph generated by |
| // encode. This graph is read in it's entirety, ensuring that all object |
| // storage is complete. |
| // |
| // As the graph is being serialized, a set of completion callbacks are |
| // executed. These completion callbacks should form a set of acyclic subgraphs |
| // over the original one. After decoding is complete, the objects are scanned |
| // to ensure that all callbacks are executed, otherwise the callback graph was |
| // not acyclic. |
| type decodeState struct { |
| // ctx is the decode context. |
| ctx context.Context |
| |
| // r is the input stream. |
| r wire.Reader |
| |
| // types is the type database. |
| types typeDecodeDatabase |
| |
| // objectByID is the set of objects in progress. |
| objectsByID []*objectDecodeState |
| |
| // deferred are objects that have been read, by no interest has been |
| // registered yet. These will be decoded once interest in registered. |
| deferred map[objectID]wire.Object |
| |
| // pending is the set of objects that are not yet complete. |
| pending completeList |
| |
| // stats tracks time data. |
| stats Stats |
| } |
| |
| // lookup looks up an object in decodeState or returns nil if no such object |
| // has been previously registered. |
| func (ds *decodeState) lookup(id objectID) *objectDecodeState { |
| if len(ds.objectsByID) < int(id) { |
| return nil |
| } |
| return ds.objectsByID[id-1] |
| } |
| |
| // checkComplete checks for completion. |
| func (ds *decodeState) checkComplete(ods *objectDecodeState) bool { |
| // Still blocked? |
| if ods.blockedBy > 0 { |
| return false |
| } |
| |
| // Track stats if relevant. |
| if ods.callbacks != nil && ods.typ != 0 { |
| ds.stats.start(ods.typ) |
| defer ds.stats.done() |
| } |
| |
| // Fire all callbacks. |
| for _, ic := range ods.callbacks { |
| ic.callbackRun() |
| } |
| |
| // Mark completed. |
| cbs := ods.callbacks |
| ods.callbacks = nil |
| ds.pending.Remove(ods) |
| |
| // Recursively check others. |
| for _, ic := range cbs { |
| if other := ic.source(); other != nil && other.blockedBy == 0 { |
| ds.checkComplete(other) |
| } |
| } |
| |
| return true // All set. |
| } |
| |
| // wait registers a dependency on an object. |
| // |
| // As a special case, we always allow _useable_ references back to the first |
| // decoding object because it may have fields that are already decoded. We also |
| // allow trivial self reference, since they can be handled internally. |
| func (ds *decodeState) wait(waiter *objectDecodeState, id objectID, callback func()) { |
| switch id { |
| case waiter.id: |
| // Trivial self reference. |
| fallthrough |
| case 1: |
| // Root object; see above. |
| if callback != nil { |
| callback() |
| } |
| return |
| } |
| |
| // Mark as blocked. |
| waiter.blockedBy++ |
| |
| // No nil can be returned here. |
| other := ds.lookup(id) |
| if callback != nil { |
| // Add the additional user callback. |
| other.addCallback(userCallback(callback)) |
| } |
| |
| // Mark waiter as unblocked. |
| other.addCallback(waiter) |
| } |
| |
| // waitObject notes a blocking relationship. |
| func (ds *decodeState) waitObject(ods *objectDecodeState, encoded wire.Object, callback func()) { |
| if rv, ok := encoded.(*wire.Ref); ok && rv.Root != 0 { |
| // Refs can encode pointers and maps. |
| ds.wait(ods, objectID(rv.Root), callback) |
| } else if sv, ok := encoded.(*wire.Slice); ok && sv.Ref.Root != 0 { |
| // See decodeObject; we need to wait for the array (if non-nil). |
| ds.wait(ods, objectID(sv.Ref.Root), callback) |
| } else if iv, ok := encoded.(*wire.Interface); ok { |
| // It's an interface (wait recurisvely). |
| ds.waitObject(ods, iv.Value, callback) |
| } else if callback != nil { |
| // Nothing to wait for: execute the callback immediately. |
| callback() |
| } |
| } |
| |
| // walkChild returns a child object from obj, given an accessor path. This is |
| // the decode-side equivalent to traverse in encode.go. |
| // |
| // For the purposes of this function, a child object is either a field within a |
| // struct or an array element, with one such indirection per element in |
| // path. The returned value may be an unexported field, so it may not be |
| // directly assignable. See decode_unsafe.go. |
| func walkChild(path []wire.Dot, obj reflect.Value) reflect.Value { |
| // See wire.Ref.Dots. The path here is specified in reverse order. |
| for i := len(path) - 1; i >= 0; i-- { |
| switch pc := path[i].(type) { |
| case *wire.FieldName: // Must be a pointer. |
| if obj.Kind() != reflect.Struct { |
| Failf("next component in child path is a field name, but the current object is not a struct. Path: %v, current obj: %#v", path, obj) |
| } |
| obj = obj.FieldByName(string(*pc)) |
| case wire.Index: // Embedded. |
| if obj.Kind() != reflect.Array { |
| Failf("next component in child path is an array index, but the current object is not an array. Path: %v, current obj: %#v", path, obj) |
| } |
| obj = obj.Index(int(pc)) |
| default: |
| panic("unreachable: switch should be exhaustive") |
| } |
| } |
| return obj |
| } |
| |
| // register registers a decode with a type. |
| // |
| // This type is only used to instantiate a new object if it has not been |
| // registered previously. This depends on the type provided if none is |
| // available in the object itself. |
| func (ds *decodeState) register(r *wire.Ref, typ reflect.Type) reflect.Value { |
| // Grow the objectsByID slice. |
| id := objectID(r.Root) |
| if len(ds.objectsByID) < int(id) { |
| ds.objectsByID = append(ds.objectsByID, make([]*objectDecodeState, int(id)-len(ds.objectsByID))...) |
| } |
| |
| // Does this object already exist? |
| ods := ds.objectsByID[id-1] |
| if ods != nil { |
| return walkChild(r.Dots, ods.obj) |
| } |
| |
| // Create the object. |
| if len(r.Dots) != 0 { |
| typ = ds.findType(r.Type) |
| } |
| v := reflect.New(typ) |
| ods = &objectDecodeState{ |
| id: id, |
| obj: v.Elem(), |
| } |
| ds.objectsByID[id-1] = ods |
| ds.pending.PushBack(ods) |
| |
| // Process any deferred objects & callbacks. |
| if encoded, ok := ds.deferred[id]; ok { |
| delete(ds.deferred, id) |
| ds.decodeObject(ods, ods.obj, encoded) |
| } |
| |
| return walkChild(r.Dots, ods.obj) |
| } |
| |
| // objectDecoder is for decoding structs. |
| type objectDecoder struct { |
| // ds is decodeState. |
| ds *decodeState |
| |
| // ods is current object being decoded. |
| ods *objectDecodeState |
| |
| // reconciledTypeEntry is the reconciled type information. |
| rte *reconciledTypeEntry |
| |
| // encoded is the encoded object state. |
| encoded *wire.Struct |
| } |
| |
| // load is helper for the public methods on Source. |
| func (od *objectDecoder) load(slot int, objPtr reflect.Value, wait bool, fn func()) { |
| // Note that we have reconciled the type and may remap the fields here |
| // to match what's expected by the decoder. The "slot" parameter here |
| // is in terms of the local type, where the fields in the encoded |
| // object are in terms of the wire object's type, which might be in a |
| // different order (but will have the same fields). |
| v := *od.encoded.Field(od.rte.FieldOrder[slot]) |
| od.ds.decodeObject(od.ods, objPtr.Elem(), v) |
| if wait { |
| // Mark this individual object a blocker. |
| od.ds.waitObject(od.ods, v, fn) |
| } |
| } |
| |
| // aterLoad implements Source.AfterLoad. |
| func (od *objectDecoder) afterLoad(fn func()) { |
| // Queue the local callback; this will execute when all of the above |
| // data dependencies have been cleared. |
| od.ods.addCallback(userCallback(fn)) |
| } |
| |
| // decodeStruct decodes a struct value. |
| func (ds *decodeState) decodeStruct(ods *objectDecodeState, obj reflect.Value, encoded *wire.Struct) { |
| if encoded.TypeID == 0 { |
| // Allow anonymous empty structs, but only if the encoded |
| // object also has no fields. |
| if encoded.Fields() == 0 && obj.NumField() == 0 { |
| return |
| } |
| |
| // Propagate an error. |
| Failf("empty struct on wire %#v has field mismatch with type %q", encoded, obj.Type().Name()) |
| } |
| |
| // Lookup the object type. |
| rte := ds.types.Lookup(typeID(encoded.TypeID), obj.Type()) |
| ods.typ = typeID(encoded.TypeID) |
| |
| // Invoke the loader. |
| od := objectDecoder{ |
| ds: ds, |
| ods: ods, |
| rte: rte, |
| encoded: encoded, |
| } |
| ds.stats.start(ods.typ) |
| defer ds.stats.done() |
| if sl, ok := obj.Addr().Interface().(SaverLoader); ok { |
| // Note: may be a registered empty struct which does not |
| // implement the saver/loader interfaces. |
| sl.StateLoad(Source{internal: od}) |
| } |
| } |
| |
| // decodeMap decodes a map value. |
| func (ds *decodeState) decodeMap(ods *objectDecodeState, obj reflect.Value, encoded *wire.Map) { |
| if obj.IsNil() { |
| // See pointerTo. |
| obj.Set(reflect.MakeMap(obj.Type())) |
| } |
| for i := 0; i < len(encoded.Keys); i++ { |
| // Decode the objects. |
| kv := reflect.New(obj.Type().Key()).Elem() |
| vv := reflect.New(obj.Type().Elem()).Elem() |
| ds.decodeObject(ods, kv, encoded.Keys[i]) |
| ds.decodeObject(ods, vv, encoded.Values[i]) |
| ds.waitObject(ods, encoded.Keys[i], nil) |
| ds.waitObject(ods, encoded.Values[i], nil) |
| |
| // Set in the map. |
| obj.SetMapIndex(kv, vv) |
| } |
| } |
| |
| // decodeArray decodes an array value. |
| func (ds *decodeState) decodeArray(ods *objectDecodeState, obj reflect.Value, encoded *wire.Array) { |
| if len(encoded.Contents) != obj.Len() { |
| Failf("mismatching array length expect=%d, actual=%d", obj.Len(), len(encoded.Contents)) |
| } |
| // Decode the contents into the array. |
| for i := 0; i < len(encoded.Contents); i++ { |
| ds.decodeObject(ods, obj.Index(i), encoded.Contents[i]) |
| ds.waitObject(ods, encoded.Contents[i], nil) |
| } |
| } |
| |
| // findType finds the type for the given wire.TypeSpecs. |
| func (ds *decodeState) findType(t wire.TypeSpec) reflect.Type { |
| switch x := t.(type) { |
| case wire.TypeID: |
| typ := ds.types.LookupType(typeID(x)) |
| rte := ds.types.Lookup(typeID(x), typ) |
| return rte.LocalType |
| case *wire.TypeSpecPointer: |
| return reflect.PtrTo(ds.findType(x.Type)) |
| case *wire.TypeSpecArray: |
| return reflect.ArrayOf(int(x.Count), ds.findType(x.Type)) |
| case *wire.TypeSpecSlice: |
| return reflect.SliceOf(ds.findType(x.Type)) |
| case *wire.TypeSpecMap: |
| return reflect.MapOf(ds.findType(x.Key), ds.findType(x.Value)) |
| default: |
| // Should not happen. |
| Failf("unknown type %#v", t) |
| } |
| panic("unreachable") |
| } |
| |
| // decodeInterface decodes an interface value. |
| func (ds *decodeState) decodeInterface(ods *objectDecodeState, obj reflect.Value, encoded *wire.Interface) { |
| if _, ok := encoded.Type.(wire.TypeSpecNil); ok { |
| // Special case; the nil object. Just decode directly, which |
| // will read nil from the wire (if encoded correctly). |
| ds.decodeObject(ods, obj, encoded.Value) |
| return |
| } |
| |
| // We now need to resolve the actual type. |
| typ := ds.findType(encoded.Type) |
| |
| // We need to imbue type information here, then we can proceed to |
| // decode normally. In order to avoid issues with setting value-types, |
| // we create a new non-interface version of this object. We will then |
| // set the interface object to be equal to whatever we decode. |
| origObj := obj |
| obj = reflect.New(typ).Elem() |
| defer origObj.Set(obj) |
| |
| // With the object now having sufficient type information to actually |
| // have Set called on it, we can proceed to decode the value. |
| ds.decodeObject(ods, obj, encoded.Value) |
| } |
| |
| // isFloatEq determines if x and y represent the same value. |
| func isFloatEq(x float64, y float64) bool { |
| switch { |
| case math.IsNaN(x): |
| return math.IsNaN(y) |
| case math.IsInf(x, 1): |
| return math.IsInf(y, 1) |
| case math.IsInf(x, -1): |
| return math.IsInf(y, -1) |
| default: |
| return x == y |
| } |
| } |
| |
| // isComplexEq determines if x and y represent the same value. |
| func isComplexEq(x complex128, y complex128) bool { |
| return isFloatEq(real(x), real(y)) && isFloatEq(imag(x), imag(y)) |
| } |
| |
| // decodeObject decodes a object value. |
| func (ds *decodeState) decodeObject(ods *objectDecodeState, obj reflect.Value, encoded wire.Object) { |
| switch x := encoded.(type) { |
| case wire.Nil: // Fast path: first. |
| // We leave obj alone here. That's because if obj represents an |
| // interface, it may have been imbued with type information in |
| // decodeInterface, and we don't want to destroy that. |
| case *wire.Ref: |
| // Nil pointers may be encoded in a "forceValue" context. For |
| // those we just leave it alone as the value will already be |
| // correct (nil). |
| if id := objectID(x.Root); id == 0 { |
| return |
| } |
| |
| // Note that if this is a map type, we go through a level of |
| // indirection to allow for map aliasing. |
| if obj.Kind() == reflect.Map { |
| v := ds.register(x, obj.Type()) |
| if v.IsNil() { |
| // Note that we don't want to clobber the map |
| // if has already been decoded by decodeMap. We |
| // just make it so that we have a consistent |
| // reference when that eventually does happen. |
| v.Set(reflect.MakeMap(v.Type())) |
| } |
| obj.Set(v) |
| return |
| } |
| |
| // Normal assignment: authoritative only if no dots. |
| v := ds.register(x, obj.Type().Elem()) |
| obj.Set(reflectValueRWAddr(v)) |
| case wire.Bool: |
| obj.SetBool(bool(x)) |
| case wire.Int: |
| obj.SetInt(int64(x)) |
| if obj.Int() != int64(x) { |
| Failf("signed integer truncated from %v to %v", int64(x), obj.Int()) |
| } |
| case wire.Uint: |
| obj.SetUint(uint64(x)) |
| if obj.Uint() != uint64(x) { |
| Failf("unsigned integer truncated from %v to %v", uint64(x), obj.Uint()) |
| } |
| case wire.Float32: |
| obj.SetFloat(float64(x)) |
| case wire.Float64: |
| obj.SetFloat(float64(x)) |
| if !isFloatEq(obj.Float(), float64(x)) { |
| Failf("floating point number truncated from %v to %v", float64(x), obj.Float()) |
| } |
| case *wire.Complex64: |
| obj.SetComplex(complex128(*x)) |
| case *wire.Complex128: |
| obj.SetComplex(complex128(*x)) |
| if !isComplexEq(obj.Complex(), complex128(*x)) { |
| Failf("complex number truncated from %v to %v", complex128(*x), obj.Complex()) |
| } |
| case *wire.String: |
| obj.SetString(string(*x)) |
| case *wire.Slice: |
| // See *wire.Ref above; same applies. |
| if id := objectID(x.Ref.Root); id == 0 { |
| return |
| } |
| // Note that it's fine to slice the array here and assume that |
| // contents will still be filled in later on. |
| typ := reflect.ArrayOf(int(x.Capacity), obj.Type().Elem()) // The object type. |
| v := ds.register(&x.Ref, typ) |
| obj.Set(reflectValueRWSlice3(v, 0, int(x.Length), int(x.Capacity))) |
| case *wire.Array: |
| ds.decodeArray(ods, obj, x) |
| case *wire.Struct: |
| ds.decodeStruct(ods, obj, x) |
| case *wire.Map: |
| ds.decodeMap(ods, obj, x) |
| case *wire.Interface: |
| ds.decodeInterface(ods, obj, x) |
| default: |
| // Shoud not happen, not propagated as an error. |
| Failf("unknown object %#v for %q", encoded, obj.Type().Name()) |
| } |
| } |
| |
| // Load deserializes the object graph rooted at obj. |
| // |
| // This function may panic and should be run in safely(). |
| func (ds *decodeState) Load(obj reflect.Value) { |
| ds.stats.init() |
| defer ds.stats.fini(func(id typeID) string { |
| return ds.types.LookupName(id) |
| }) |
| |
| // Create the root object. |
| rootOds := &objectDecodeState{ |
| id: 1, |
| obj: obj, |
| } |
| ds.objectsByID = append(ds.objectsByID, rootOds) |
| ds.pending.PushBack(rootOds) |
| |
| // Read the number of objects. |
| numObjects, object, err := ReadHeader(ds.r) |
| if err != nil { |
| Failf("header error: %w", err) |
| } |
| if !object { |
| Failf("object missing") |
| } |
| |
| // Decode all objects. |
| var ( |
| encoded wire.Object |
| ods *objectDecodeState |
| id objectID |
| tid = typeID(1) |
| ) |
| if err := safely(func() { |
| // Decode all objects in the stream. |
| // |
| // Note that the structure of this decoding loop should match the raw |
| // decoding loop in state/pretty/pretty.printer.printStream(). |
| for i := uint64(0); i < numObjects; { |
| // Unmarshal either a type object or object ID. |
| encoded = wire.Load(ds.r) |
| switch we := encoded.(type) { |
| case *wire.Type: |
| ds.types.Register(we) |
| tid++ |
| encoded = nil |
| continue |
| case wire.Uint: |
| id = objectID(we) |
| i++ |
| // Unmarshal and resolve the actual object. |
| encoded = wire.Load(ds.r) |
| ods = ds.lookup(id) |
| if ods != nil { |
| // Decode the object. |
| ds.decodeObject(ods, ods.obj, encoded) |
| } else { |
| // If an object hasn't had interest registered |
| // previously or isn't yet valid, we deferred |
| // decoding until interest is registered. |
| ds.deferred[id] = encoded |
| } |
| // For error handling. |
| ods = nil |
| encoded = nil |
| default: |
| Failf("wanted type or object ID, got %#v", encoded) |
| } |
| } |
| }); err != nil { |
| // Include as much information as we can, taking into account |
| // the possible state transitions above. |
| if ods != nil { |
| Failf("error decoding object ID %d (%T) from %#v: %w", id, ods.obj.Interface(), encoded, err) |
| } else if encoded != nil { |
| Failf("error decoding from %#v: %w", encoded, err) |
| } else { |
| Failf("general decoding error: %w", err) |
| } |
| } |
| |
| // Check if we have any deferred objects. |
| numDeferred := 0 |
| for id, encoded := range ds.deferred { |
| numDeferred++ |
| if s, ok := encoded.(*wire.Struct); ok && s.TypeID != 0 { |
| typ := ds.types.LookupType(typeID(s.TypeID)) |
| log.Warningf("unused deferred object: ID %d, type %v", id, typ) |
| } else { |
| log.Warningf("unused deferred object: ID %d, %#v", id, encoded) |
| } |
| } |
| if numDeferred != 0 { |
| Failf("still had %d deferred objects", numDeferred) |
| } |
| |
| // Scan and fire all callbacks. We iterate over the list of incomplete |
| // objects until all have been finished. We stop iterating if no |
| // objects become complete (there is a dependency cycle). |
| // |
| // Note that we iterate backwards here, because there will be a strong |
| // tendendcy for blocking relationships to go from earlier objects to |
| // later (deeper) objects in the graph. This will reduce the number of |
| // iterations required to finish all objects. |
| if err := safely(func() { |
| for ds.pending.Back() != nil { |
| thisCycle := false |
| for ods = ds.pending.Back(); ods != nil; { |
| if ds.checkComplete(ods) { |
| thisCycle = true |
| break |
| } |
| ods = ods.Prev() |
| } |
| if !thisCycle { |
| break |
| } |
| } |
| }); err != nil { |
| Failf("error executing callbacks for %#v: %w", ods.obj.Interface(), err) |
| } |
| |
| // Check if we have any remaining dependency cycles. If there are any |
| // objects left in the pending list, then it must be due to a cycle. |
| if ods := ds.pending.Front(); ods != nil { |
| // This must be the result of a dependency cycle. |
| cycle := ods.findCycle() |
| var buf bytes.Buffer |
| buf.WriteString("dependency cycle: {") |
| for i, cycleOS := range cycle { |
| if i > 0 { |
| buf.WriteString(" => ") |
| } |
| fmt.Fprintf(&buf, "%q", cycleOS.obj.Type()) |
| } |
| buf.WriteString("}") |
| Failf("incomplete graph: %s", string(buf.Bytes())) |
| } |
| } |
| |
| // ReadHeader reads an object header. |
| // |
| // Each object written to the statefile is prefixed with a header. See |
| // WriteHeader for more information; these functions are exported to allow |
| // non-state writes to the file to play nice with debugging tools. |
| func ReadHeader(r wire.Reader) (length uint64, object bool, err error) { |
| // Read the header. |
| err = safely(func() { |
| length = wire.LoadUint(r) |
| }) |
| if err != nil { |
| // On the header, pass raw I/O errors. |
| if sErr, ok := err.(*ErrState); ok { |
| return 0, false, sErr.Unwrap() |
| } |
| } |
| |
| // Decode whether the object is valid. |
| object = length&objectFlag != 0 |
| length &^= objectFlag |
| return |
| } |