| // Copyright 2015 The Vanadium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package vom |
| |
| import ( |
| "io" |
| "sync" |
| |
| "v.io/v23/vdl" |
| "v.io/v23/verror" |
| ) |
| |
| var ( |
| errTypeInvalid = verror.Register(pkgPath+".errTypeInvalid", verror.NoRetry, "{1:}{2:} vom: type {3} id {4} invalid, the min user type id is {5}{:_}") |
| errAlreadyDefined = verror.Register(pkgPath+".errAlreadyDefined", verror.NoRetry, "{1:}{2:} vom: type {3} id {4} already defined as {5}{:_}") |
| errUnknownType = verror.Register(pkgPath+".errUnknownType", verror.NoRetry, "{1:}{2:} vom: unknown type id {3}{:_}") |
| errUnknownWireTypeDef = verror.Register(pkgPath+".errUnknownWireTypeDef", verror.NoRetry, "{1:}{2:} vom: unknown wire type definition {3}{:_}") |
| errStartNotCalled = verror.Register(pkgPath+".errStartNotCalled", verror.NoRetry, "{1:}{2:} vom: Start has not been called") |
| ) |
| |
| // TypeDecoder manages the receipt and unmarshalling of types from the other |
| // side of a connection. Start must be called to start decoding types, |
| // and Stop must be called to reclaim resources. |
| type TypeDecoder struct { |
| // The type encoder uses a 2-lock strategy for decoding. We use typeMu to lock |
| // type definitions, and use buildMu to allow only one worker to build types at |
| // a time. This is for simplifying the workflow and avoid unnecessary blocking |
| // for type lookups. |
| typeMu sync.RWMutex |
| idToType map[typeId]*vdl.Type // GUARDED_BY(typeMu) |
| |
| buildMu sync.Mutex |
| buildCond *sync.Cond |
| err error // GUARDED_BY(buildMu) |
| idToWire map[typeId]wireType // GUARDED_BY(buildMu) |
| dec *Decoder // GUARDED_BY(buildMu) |
| |
| processingControlMu sync.Mutex |
| goroutineRunning bool // GUARDED_BY(processingControlMu) |
| goroutineShouldStop bool // GUARDED_BY(processingControlMu) |
| } |
| |
| // NewTypeDecoder returns a new TypeDecoder that reads from the given reader. |
| // The TypeDecoder understands all wire type formats generated by the TypeEncoder. |
| func NewTypeDecoder(r io.Reader) *TypeDecoder { |
| return newTypeDecoderInternal(newDecbuf(r)) |
| } |
| |
| func newTypeDecoderInternal(buf *decbuf) *TypeDecoder { |
| td := &TypeDecoder{ |
| idToType: make(map[typeId]*vdl.Type), |
| idToWire: make(map[typeId]wireType), |
| dec: &Decoder{ |
| buf: buf, |
| typeDec: nil, |
| }, |
| } |
| td.buildCond = sync.NewCond(&td.buildMu) |
| return td |
| } |
| |
| func newDerivedTypeDecoderInternal(buf *decbuf, orig *TypeDecoder) *TypeDecoder { |
| td := &TypeDecoder{ |
| idToType: orig.idToType, |
| idToWire: orig.idToWire, |
| dec: &Decoder{ |
| buf: buf, |
| typeDec: nil, |
| }, |
| } |
| td.buildCond = sync.NewCond(&td.buildMu) |
| return td |
| } |
| |
| func (d *TypeDecoder) processLoop() { |
| var err error |
| for { |
| d.processingControlMu.Lock() |
| if d.goroutineShouldStop || err != nil { |
| d.goroutineShouldStop = false |
| d.goroutineRunning = false |
| d.processingControlMu.Unlock() |
| return |
| } |
| d.processingControlMu.Unlock() |
| // Note that we will block indefinitely if the underlying |
| // read blocks on the io.Reader. |
| err = d.readSingleType() |
| d.buildMu.Lock() |
| d.err = err |
| d.buildCond.Broadcast() |
| d.buildMu.Unlock() |
| // TODO(toddw): Reconsider d.err and d.buildCond strategy. |
| } |
| } |
| |
| // Start must be called to start decoding types. |
| func (d *TypeDecoder) Start() { |
| d.processingControlMu.Lock() |
| d.goroutineShouldStop = false |
| if !d.goroutineRunning { |
| d.goroutineRunning = true |
| go d.processLoop() |
| } |
| d.processingControlMu.Unlock() |
| } |
| |
| // Stop must be called after Start, to stop decoding types |
| // and reclaim resources. Once Stop is called, |
| // subsequent Decode calls on Decoders initialized with d |
| // will return errors. |
| func (d *TypeDecoder) Stop() { |
| d.processingControlMu.Lock() |
| d.goroutineShouldStop = true |
| d.processingControlMu.Unlock() |
| } |
| |
| // readSingleType reads a single wire type |
| func (d *TypeDecoder) readSingleType() error { |
| var wt wireType |
| curTypeID, err := d.dec.decodeWireType(&wt) |
| if err != nil { |
| return err |
| } |
| |
| // Add the wire type. |
| if err := d.addWireType(curTypeID, wt); err != nil { |
| return err |
| } |
| |
| if !d.dec.typeIncomplete { |
| if err := d.buildType(curTypeID); d.dec.buf.version >= Version81 && err != nil { |
| return err |
| } |
| } |
| |
| return nil |
| } |
| |
| // LookupType returns the type for tid. If the type is not yet available, |
| // this will wait until it arrives and is built. |
| func (d *TypeDecoder) lookupType(tid typeId) (*vdl.Type, error) { |
| if tt := d.lookupKnownType(tid); tt != nil { |
| return tt, nil |
| } |
| |
| d.buildMu.Lock() |
| defer d.buildMu.Unlock() |
| for { |
| if d.err != nil && d.err != io.EOF { |
| // Return any existing error immediately. Skip EOF because it |
| // may still be possible to lookup a type. |
| return nil, d.err |
| } |
| |
| if tt := d.lookupKnownType(tid); tt != nil { |
| return tt, nil |
| } |
| |
| if d.err != nil { |
| return nil, d.err |
| } |
| |
| d.processingControlMu.Lock() |
| running := d.goroutineRunning |
| d.processingControlMu.Unlock() |
| if !running { |
| return nil, verror.New(errStartNotCalled, nil) |
| } |
| |
| d.buildCond.Wait() |
| } |
| } |
| |
| // addWireType adds the wire type wt with the type id tid. |
| func (d *TypeDecoder) addWireType(tid typeId, wt wireType) error { |
| d.buildMu.Lock() |
| err := d.addWireTypeBuildLocked(tid, wt) |
| d.buildMu.Unlock() |
| return err |
| } |
| |
| func (d *TypeDecoder) addWireTypeBuildLocked(tid typeId, wt wireType) error { |
| if tid < WireIdFirstUserType { |
| return verror.New(errTypeInvalid, nil, wt, tid, WireIdFirstUserType) |
| } |
| // TODO(toddw): Allow duplicates according to some heuristic (e.g. only |
| // identical, or only if the later one is a "superset", etc). |
| if dup := d.lookupKnownType(tid); dup != nil { |
| return verror.New(errAlreadyDefined, nil, wt, tid, dup) |
| } |
| if dup := d.idToWire[tid]; dup != nil { |
| return verror.New(errAlreadyDefined, nil, wt, tid, dup) |
| } |
| d.idToWire[tid] = wt |
| return nil |
| } |
| |
| func (d *TypeDecoder) lookupKnownType(tid typeId) *vdl.Type { |
| if tt := bootstrapIdToType[tid]; tt != nil { |
| return tt |
| } |
| d.typeMu.RLock() |
| tt := d.idToType[tid] |
| d.typeMu.RUnlock() |
| return tt |
| } |
| |
| // buildType builds the type from the given wire type. |
| func (d *TypeDecoder) buildType(tid typeId) error { |
| builder := vdl.TypeBuilder{} |
| pending := make(map[typeId]vdl.PendingType) |
| _, err := d.makeType(tid, &builder, pending) |
| if err != nil { |
| return err |
| } |
| builder.Build() |
| types := make(map[typeId]*vdl.Type) |
| for tid, pt := range pending { |
| tt, err := pt.Built() |
| if err != nil { |
| return err |
| } |
| types[tid] = tt |
| } |
| // Add the types to idToType map. |
| d.typeMu.Lock() |
| for tid, tt := range types { |
| delete(d.idToWire, tid) |
| d.idToType[tid] = tt |
| } |
| d.typeMu.Unlock() |
| return nil |
| } |
| |
| // makeType makes the pending type from its wire type representation. |
| func (d *TypeDecoder) makeType(tid typeId, builder *vdl.TypeBuilder, pending map[typeId]vdl.PendingType) (vdl.PendingType, error) { |
| wt := d.idToWire[tid] |
| if wt == nil { |
| return nil, verror.New(errUnknownType, nil, tid) |
| } |
| // Make the type from its wireType representation. Both named and unnamed |
| // types may be recursive, so we must populate pending before subsequent |
| // recursive lookups. Eventually the built type will be added to dt.idToType. |
| if name := wt.(wireTypeGeneric).TypeName(); name != "" { |
| namedType := builder.Named(name) |
| pending[tid] = namedType |
| if wtNamed, ok := wt.(wireTypeNamedT); ok { |
| // This is a wireNamed pointing at a base type. |
| baseType, err := d.lookupOrMakeType(wtNamed.Value.Base, builder, pending) |
| if err != nil { |
| return nil, err |
| } |
| namedType.AssignBase(baseType) |
| return namedType, nil |
| } |
| // This isn't wireNamed, but has a non-empty name. |
| baseType, err := d.startBaseType(wt, builder) |
| if err != nil { |
| return nil, err |
| } |
| if err := d.finishBaseType(wt, baseType, builder, pending); err != nil { |
| return nil, err |
| } |
| namedType.AssignBase(baseType) |
| return namedType, nil |
| } |
| // We make unnamed types in two stages, to ensure that we populate pending |
| // before any recursive lookups. |
| unnamedType, err := d.startBaseType(wt, builder) |
| if err != nil { |
| return nil, err |
| } |
| pending[tid] = unnamedType |
| if err := d.finishBaseType(wt, unnamedType, builder, pending); err != nil { |
| return nil, err |
| } |
| return unnamedType, nil |
| } |
| |
| func (d *TypeDecoder) startBaseType(wt wireType, builder *vdl.TypeBuilder) (vdl.PendingType, error) { |
| switch wt := wt.(type) { |
| case wireTypeEnumT: |
| return builder.Enum(), nil |
| case wireTypeArrayT: |
| return builder.Array(), nil |
| case wireTypeListT: |
| return builder.List(), nil |
| case wireTypeSetT: |
| return builder.Set(), nil |
| case wireTypeMapT: |
| return builder.Map(), nil |
| case wireTypeStructT: |
| return builder.Struct(), nil |
| case wireTypeUnionT: |
| return builder.Union(), nil |
| case wireTypeOptionalT: |
| return builder.Optional(), nil |
| default: |
| return nil, verror.New(errUnknownWireTypeDef, nil, wt) |
| } |
| } |
| |
| func (d *TypeDecoder) finishBaseType(wt wireType, p vdl.PendingType, builder *vdl.TypeBuilder, pending map[typeId]vdl.PendingType) error { |
| switch wt := wt.(type) { |
| case wireTypeEnumT: |
| for _, label := range wt.Value.Labels { |
| p.(vdl.PendingEnum).AppendLabel(label) |
| } |
| case wireTypeArrayT: |
| elemType, err := d.lookupOrMakeType(wt.Value.Elem, builder, pending) |
| if err != nil { |
| return err |
| } |
| p.(vdl.PendingArray).AssignElem(elemType).AssignLen(int(wt.Value.Len)) |
| case wireTypeListT: |
| elemType, err := d.lookupOrMakeType(wt.Value.Elem, builder, pending) |
| if err != nil { |
| return err |
| } |
| p.(vdl.PendingList).AssignElem(elemType) |
| case wireTypeSetT: |
| keyType, err := d.lookupOrMakeType(wt.Value.Key, builder, pending) |
| if err != nil { |
| return err |
| } |
| p.(vdl.PendingSet).AssignKey(keyType) |
| case wireTypeMapT: |
| keyType, err := d.lookupOrMakeType(wt.Value.Key, builder, pending) |
| if err != nil { |
| return err |
| } |
| elemType, err := d.lookupOrMakeType(wt.Value.Elem, builder, pending) |
| if err != nil { |
| return err |
| } |
| p.(vdl.PendingMap).AssignKey(keyType).AssignElem(elemType) |
| case wireTypeStructT: |
| for _, field := range wt.Value.Fields { |
| fieldType, err := d.lookupOrMakeType(field.Type, builder, pending) |
| if err != nil { |
| return err |
| } |
| p.(vdl.PendingStruct).AppendField(field.Name, fieldType) |
| } |
| case wireTypeUnionT: |
| for _, field := range wt.Value.Fields { |
| fieldType, err := d.lookupOrMakeType(field.Type, builder, pending) |
| if err != nil { |
| return err |
| } |
| p.(vdl.PendingUnion).AppendField(field.Name, fieldType) |
| } |
| case wireTypeOptionalT: |
| elemType, err := d.lookupOrMakeType(wt.Value.Elem, builder, pending) |
| if err != nil { |
| return err |
| } |
| p.(vdl.PendingOptional).AssignElem(elemType) |
| } |
| return nil |
| } |
| |
| func (d *TypeDecoder) lookupOrMakeType(tid typeId, builder *vdl.TypeBuilder, pending map[typeId]vdl.PendingType) (vdl.TypeOrPending, error) { |
| if tt := d.lookupKnownType(tid); tt != nil { |
| return tt, nil |
| } |
| if p, ok := pending[tid]; ok { |
| return p, nil |
| } |
| return d.makeType(tid, builder, pending) |
| } |