blob: b3cb7f70bbe68339c31f42cba8e5d6f5b30fed90 [file] [log] [blame]
package vom
// TODO(toddw): Add tests.
import (
"fmt"
"v.io/v23/vdl"
)
// encoderTypes maintains the mapping from type to type id, used by the encoder.
type encoderTypes struct {
typeToID map[*vdl.Type]typeID
nextID typeID
}
func newEncoderTypes() *encoderTypes {
return &encoderTypes{make(map[*vdl.Type]typeID), WireIDFirstUserType}
}
func (et *encoderTypes) LookupID(tt *vdl.Type) typeID {
if id := bootstrapTypeToID[tt]; id != 0 {
return id
}
return et.typeToID[tt]
}
func (et *encoderTypes) LookupOrAssignID(tt *vdl.Type) (typeID, bool) {
if id := et.LookupID(tt); id != 0 {
return id, false
}
// Assign a new id.
newID := et.nextID
et.nextID++
et.typeToID[tt] = newID
return newID, true
}
// decoderTypes maintains the mapping from type id to type, used by the decoder.
type decoderTypes struct {
idToWire map[typeID]wireType
idToType map[typeID]*vdl.Type
}
func newDecoderTypes() *decoderTypes {
return &decoderTypes{make(map[typeID]wireType), make(map[typeID]*vdl.Type)}
}
func (dt *decoderTypes) LookupOrBuildType(id typeID) (*vdl.Type, error) {
if tt := dt.lookupType(id); tt != nil {
return tt, nil
}
builder := new(vdl.TypeBuilder)
pending := make(map[typeID]vdl.PendingType)
result, err := dt.makeType(id, builder, pending)
if err != nil {
return nil, err
}
if !builder.Build() {
// TODO(toddw): Change TypeBuilder.Build() to directly return the error.
_, err := result.Built()
return nil, err
}
for id, pend := range pending {
tt, err := pend.Built()
if err != nil {
return nil, err
}
dt.idToType[id] = tt
}
built, err := result.Built()
if err != nil {
return nil, err
}
return built, nil
}
func (dt *decoderTypes) lookupType(id typeID) *vdl.Type {
if tt := bootstrapIDToType[id]; tt != nil {
return tt
}
return dt.idToType[id]
}
func (dt *decoderTypes) makeType(id typeID, builder *vdl.TypeBuilder, pending map[typeID]vdl.PendingType) (vdl.PendingType, error) {
wt := dt.idToWire[id]
if wt == nil {
return nil, fmt.Errorf("vom: unknown type ID %d", id)
}
// Make the type from its wireType representation. First remove it from
// dt.idToWire, and add it to pending, so that subsequent lookups will get the
// pending type. Eventually the built type will be added to dt.idToType.
delete(dt.idToWire, id)
if name := wt.(wireTypeGeneric).TypeName(); name != "" {
// Named types may be recursive, so we must create the named type first and
// add it to pending, before we make the base type. The base type may refer
// back to this named type, and will find it in pending.
namedType := builder.Named(name)
pending[id] = namedType
if wtNamed, ok := wt.(wireTypeNamedT); ok {
// This is a NamedType pointing at a base type.
baseType, err := dt.lookupOrMakeType(wtNamed.Value.Base, builder, pending)
if err != nil {
return nil, err
}
namedType.AssignBase(baseType)
return namedType, nil
}
// This isn't NamedType, but has a non-empty name.
baseType, err := dt.makeBaseType(wt, builder, pending)
if err != nil {
return nil, err
}
namedType.AssignBase(baseType)
return namedType, nil
}
// Unnamed types are made directly from their base type. It's fine to update
// pending after making the base type, since there's no way to create a
// recursive type based solely on unnamed vdl.
baseType, err := dt.makeBaseType(wt, builder, pending)
if err != nil {
return nil, err
}
pending[id] = baseType
return baseType, nil
}
func (dt *decoderTypes) makeBaseType(wt wireType, builder *vdl.TypeBuilder, pending map[typeID]vdl.PendingType) (vdl.PendingType, error) {
switch wt := wt.(type) {
case wireTypeNamedT:
return nil, fmt.Errorf("vom: NamedType has empty name: %v", wt)
case wireTypeEnumT:
enumType := builder.Enum()
for _, label := range wt.Value.Labels {
enumType.AppendLabel(label)
}
return enumType, nil
case wireTypeArrayT:
elemType, err := dt.lookupOrMakeType(wt.Value.Elem, builder, pending)
if err != nil {
return nil, err
}
return builder.Array().AssignElem(elemType).AssignLen(int(wt.Value.Len)), nil
case wireTypeListT:
elemType, err := dt.lookupOrMakeType(wt.Value.Elem, builder, pending)
if err != nil {
return nil, err
}
return builder.List().AssignElem(elemType), nil
case wireTypeSetT:
keyType, err := dt.lookupOrMakeType(wt.Value.Key, builder, pending)
if err != nil {
return nil, err
}
return builder.Set().AssignKey(keyType), nil
case wireTypeMapT:
keyType, err := dt.lookupOrMakeType(wt.Value.Key, builder, pending)
if err != nil {
return nil, err
}
elemType, err := dt.lookupOrMakeType(wt.Value.Elem, builder, pending)
if err != nil {
return nil, err
}
return builder.Map().AssignKey(keyType).AssignElem(elemType), nil
case wireTypeStructT:
structType := builder.Struct()
for _, field := range wt.Value.Fields {
fieldType, err := dt.lookupOrMakeType(field.Type, builder, pending)
if err != nil {
return nil, err
}
structType.AppendField(field.Name, fieldType)
}
return structType, nil
case wireTypeUnionT:
unionType := builder.Union()
for _, field := range wt.Value.Fields {
fieldType, err := dt.lookupOrMakeType(field.Type, builder, pending)
if err != nil {
return nil, err
}
unionType.AppendField(field.Name, fieldType)
}
return unionType, nil
case wireTypeOptionalT:
elemType, err := dt.lookupOrMakeType(wt.Value.Elem, builder, pending)
if err != nil {
return nil, err
}
return builder.Optional().AssignElem(elemType), nil
default:
return nil, fmt.Errorf("vom: unknown wire type definition %v", wt)
}
}
func (dt *decoderTypes) lookupOrMakeType(id typeID, builder *vdl.TypeBuilder, pending map[typeID]vdl.PendingType) (vdl.TypeOrPending, error) {
if tt := dt.lookupType(id); tt != nil {
return tt, nil
}
if p, ok := pending[id]; ok {
return p, nil
}
return dt.makeType(id, builder, pending)
}
func (dt *decoderTypes) AddWireType(id typeID, wt wireType) error {
if id < WireIDFirstUserType {
return fmt.Errorf("vom: type %q id %d invalid, the min user id is %d", wt, id, 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 := dt.idToWire[id]; dup {
return fmt.Errorf("vom: type %q id %d already defined as %q", wt, id, dup)
}
if _, dup := dt.idToType[id]; dup {
return fmt.Errorf("vom: type %q id %d already defined as %q", wt, id, dup)
}
dt.idToWire[id] = wt
return nil
}
func isWireTypeType(tt *vdl.Type) bool {
_, exist := bootstrapWireTypes[tt]
return exist
}
// TODO(toddw): Provide type management routines
// Bootstrap mappings between type, id and kind.
var (
bootstrapWireTypes map[*vdl.Type]struct{}
bootstrapIDToType map[typeID]*vdl.Type
bootstrapTypeToID map[*vdl.Type]typeID
bootstrapKindToID map[vdl.Kind]typeID
typeIDType = vdl.TypeOf(typeID(0))
wireTypeType = vdl.TypeOf((*wireType)(nil))
wireNamedType = vdl.TypeOf(wireNamed{})
wireEnumType = vdl.TypeOf(wireEnum{})
wireArrayType = vdl.TypeOf(wireArray{})
wireListType = vdl.TypeOf(wireList{})
wireSetType = vdl.TypeOf(wireSet{})
wireMapType = vdl.TypeOf(wireMap{})
wireFieldType = vdl.TypeOf(wireField{})
wireFieldListType = vdl.TypeOf([]wireField{})
wireStructType = vdl.TypeOf(wireStruct{})
wireUnionType = vdl.TypeOf(wireUnion{})
wireOptionalType = vdl.TypeOf(wireOptional{})
wireByteListType = vdl.TypeOf([]byte{})
wireStringListType = vdl.TypeOf([]string{})
)
func init() {
bootstrapWireTypes = make(map[*vdl.Type]struct{})
// The basic wire types for type definition.
for _, tt := range []*vdl.Type{
typeIDType,
wireTypeType,
wireFieldType,
wireFieldListType,
} {
bootstrapWireTypes[tt] = struct{}{}
}
// The extra wire types for each kind of type definition. The field indices
// in wireType should not be changed.
wtTypes := []*vdl.Type{
wireNamedType,
wireEnumType,
wireArrayType,
wireListType,
wireSetType,
wireMapType,
wireStructType,
wireUnionType,
wireOptionalType,
}
if len(wtTypes) != wireTypeType.NumField() {
panic("vom: wireType definition changed")
}
for ix, tt := range wtTypes {
if tt != wireTypeType.Field(ix).Type {
panic("vom: wireType definition changed")
}
bootstrapWireTypes[tt] = struct{}{}
}
bootstrapIDToType = make(map[typeID]*vdl.Type)
bootstrapTypeToID = make(map[*vdl.Type]typeID)
bootstrapKindToID = make(map[vdl.Kind]typeID)
// The basic bootstrap types can be converted between type, id and kind.
for id, tt := range map[typeID]*vdl.Type{
WireIDBool: vdl.BoolType,
WireIDByte: vdl.ByteType,
WireIDString: vdl.StringType,
WireIDUint16: vdl.Uint16Type,
WireIDUint32: vdl.Uint32Type,
WireIDUint64: vdl.Uint64Type,
WireIDInt16: vdl.Int16Type,
WireIDInt32: vdl.Int32Type,
WireIDInt64: vdl.Int64Type,
WireIDFloat32: vdl.Float32Type,
WireIDFloat64: vdl.Float64Type,
WireIDComplex64: vdl.Complex64Type,
WireIDComplex128: vdl.Complex128Type,
WireIDTypeObject: vdl.TypeObjectType,
WireIDAny: vdl.AnyType,
} {
bootstrapIDToType[id] = tt
bootstrapTypeToID[tt] = id
bootstrapKindToID[tt.Kind()] = id
}
// The extra bootstrap types can be converted between type and id.
for id, tt := range map[typeID]*vdl.Type{
WireIDByteList: wireByteListType,
WireIDStringList: wireStringListType,
} {
bootstrapIDToType[id] = tt
bootstrapTypeToID[tt] = id
}
}
// A generic interface for all wireType types.
type wireTypeGeneric interface {
TypeName() string
}
func (wt wireTypeNamedT) TypeName() string {
return wt.Value.Name
}
func (wt wireTypeEnumT) TypeName() string {
return wt.Value.Name
}
func (wt wireTypeArrayT) TypeName() string {
return wt.Value.Name
}
func (wt wireTypeListT) TypeName() string {
return wt.Value.Name
}
func (wt wireTypeSetT) TypeName() string {
return wt.Value.Name
}
func (wt wireTypeMapT) TypeName() string {
return wt.Value.Name
}
func (wt wireTypeStructT) TypeName() string {
return wt.Value.Name
}
func (wt wireTypeUnionT) TypeName() string {
return wt.Value.Name
}
func (wt wireTypeOptionalT) TypeName() string {
return wt.Value.Name
}