blob: 1e8ec0b5db19c93de3964f16cc32765be9daab5e [file] [log] [blame]
// 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 transcoder
import (
"fmt"
"reflect"
"mojo/public/go/bindings"
"v.io/v23/vdl"
)
// FromMojo decodes the mojom-encoded data into valptr, which must be a pointer to
// the desired value. The datatype describes the type of the encoded data.
// Returns an error if the data cannot be decoded into valptr, based on the VDL
// value conversion rules.
// TODO(bprosnitz) Consider reimplementing this using mojom_type instead of vdl type
// so that we can take advantage of the struct offset in the mojom type.
func ValueFromMojo(valptr interface{}, data []byte, datatype *vdl.Type) error {
target, err := vdl.ReflectTarget(reflect.ValueOf(valptr))
if err != nil {
return err
}
return FromMojo(target, data, datatype)
}
func FromMojo(target vdl.Target, data []byte, datatype *vdl.Type) error {
mtv := &mojomToTargetTranscoder{modec: bindings.NewDecoder(data, nil)}
return mtv.transcodeValue(datatype, target, true, false)
}
type mojomToTargetTranscoder struct {
modec *bindings.Decoder
typeStack []*vdl.Type
}
func (mtv *mojomToTargetTranscoder) transcodeValue(vt *vdl.Type, target vdl.Target, isTopType, isNullable bool) error {
switch vt.Kind() {
case vdl.Bool:
value, err := mtv.modec.ReadBool()
if err != nil {
return err
}
return target.FromBool(value, vt)
case vdl.Int8:
value, err := mtv.modec.ReadInt8()
if err != nil {
return err
}
return target.FromInt(int64(value), vt)
case vdl.Int16:
value, err := mtv.modec.ReadInt16()
if err != nil {
return err
}
return target.FromInt(int64(value), vt)
case vdl.Int32:
value, err := mtv.modec.ReadInt32()
if err != nil {
return err
}
return target.FromInt(int64(value), vt)
case vdl.Int64:
value, err := mtv.modec.ReadInt64()
if err != nil {
return err
}
return target.FromInt(value, vt)
case vdl.Byte:
value, err := mtv.modec.ReadUint8()
if err != nil {
return err
}
return target.FromUint(uint64(value), vt)
case vdl.Uint16:
value, err := mtv.modec.ReadUint16()
if err != nil {
return err
}
return target.FromUint(uint64(value), vt)
case vdl.Uint32:
value, err := mtv.modec.ReadUint32()
if err != nil {
return err
}
return target.FromUint(uint64(value), vt)
case vdl.Uint64:
value, err := mtv.modec.ReadUint64()
if err != nil {
return err
}
return target.FromUint(value, vt)
case vdl.Float32:
value, err := mtv.modec.ReadFloat32()
if err != nil {
return err
}
return target.FromFloat(float64(value), vt)
case vdl.Float64:
value, err := mtv.modec.ReadFloat64()
if err != nil {
return err
}
return target.FromFloat(value, vt)
case vdl.String:
switch ptr, err := mtv.modec.ReadPointer(); {
case err != nil:
return err
case ptr == 0:
return fmt.Errorf("invalid null string pointer")
default:
value, err := mtv.modec.ReadString()
if err != nil {
return err
}
return target.FromString(value, vt)
}
return nil
case vdl.Enum:
index, err := mtv.modec.ReadInt32()
if err != nil {
return err
}
if int(index) >= vt.NumEnumLabel() || index < 0 {
return fmt.Errorf("enum label out of range")
}
target.FromEnumLabel(vt.EnumLabel(int(index)), vt)
return nil
case vdl.Array, vdl.List:
switch ptr, err := mtv.modec.ReadPointer(); {
case err != nil:
return err
case ptr == 0 && isNullable:
return target.FromNil(vdl.OptionalType(vt))
case ptr == 0 && !isNullable:
return fmt.Errorf("invalid null struct pointer")
}
if vt.IsBytes() {
str, err := mtv.modec.ReadString()
if err != nil {
return err
}
return target.FromBytes([]byte(str), vt)
} else {
elemBitSize := baseTypeSizeBits(vt.Elem())
numElems, err := mtv.modec.StartArray(elemBitSize)
if err != nil {
return err
}
listTarget, err := target.StartList(vt, int(numElems))
if err != nil {
return err
}
for i := 0; i < int(numElems); i++ {
elemTarget, err := listTarget.StartElem(i)
if err != nil {
return err
}
if err := mtv.transcodeValue(vt.Elem(), elemTarget, false, false); err != nil {
return err
}
if err := listTarget.FinishElem(elemTarget); err != nil {
return err
}
}
if err := target.FinishList(listTarget); err != nil {
return err
}
}
return mtv.modec.Finish()
case vdl.Set:
panic("unimplemented")
/*switch ptr, err := d.dec.ReadPointer(); {
case err != nil:
return err
case ptr == 0 && isNullable:
return target.FromNil(vdl.OptionalType(vt))
case ptr == 0 && !isNullable:
return fmt.Errorf("invalid null struct pointer")
}
keyBitSize := baseTypeSizeBits(vt.Key())
numKeys, err := d.dec.StartArray(keyBitSize)
if err != nil {
return err
}
setTarget, err := target.StartSet(vt, int(numKeys))
if err != nil {
return err
}
for i := 0; i < int(numKeys); i++ {
keyTarget, err := setTarget.StartKey()
if err != nil {
return err
}
if err := d.decodeValue(mt.Key, keyTarget, false, false); err != nil {
return err
}
if err := setTarget.FinishKey(keyTarget); err != nil {
return err
}
}
if err := target.FinishSet(setTarget); err != nil {
return err
}
return d.dec.Finish()*/
case vdl.Map:
switch ptr, err := mtv.modec.ReadPointer(); {
case err != nil:
return err
case ptr == 0 && isNullable:
return target.FromNil(vdl.OptionalType(vt))
case ptr == 0 && !isNullable:
return fmt.Errorf("invalid null struct pointer")
}
if err := mtv.modec.StartMap(); err != nil {
return err
}
var keys, values []*vdl.Value
keysTarget, err := vdl.ReflectTarget(reflect.ValueOf(&keys))
if err != nil {
return err
}
keysListType := vdl.ListType(vt.Key())
if err := mtv.transcodeValue(keysListType, keysTarget, false, false); err != nil {
return err
}
valuesTarget, err := vdl.ReflectTarget(reflect.ValueOf(&values))
if err != nil {
return err
}
valuesListType := vdl.ListType(vt.Elem())
if err := mtv.transcodeValue(valuesListType, valuesTarget, false, false); err != nil {
return err
}
if len(keys) != len(values) {
return fmt.Errorf("values don't match keys")
}
mapTarget, err := target.StartMap(vt, len(keys))
if err != nil {
return err
}
for i, key := range keys {
value := values[i]
keyTarget, err := mapTarget.StartKey()
if err != nil {
return err
}
if err := vdl.FromValue(keyTarget, key); err != nil {
return err
}
fieldTarget, err := mapTarget.FinishKeyStartField(keyTarget)
if err != nil {
return err
}
if err := vdl.FromValue(fieldTarget, value); err != nil {
return err
}
if err := mapTarget.FinishField(keyTarget, fieldTarget); err != nil {
return err
}
}
if err := target.FinishMap(mapTarget); err != nil {
return err
}
return mtv.modec.Finish()
case vdl.Struct:
// TODO(toddw): See the comment in encoder.mojomStructSize; we rely on the
// fields to be presented in the canonical mojom field ordering.
if !isTopType {
switch ptr, err := mtv.modec.ReadPointer(); {
case err != nil:
return err
case ptr == 0 && isNullable:
return target.FromNil(vdl.OptionalType(vt))
case ptr == 0 && !isNullable:
return fmt.Errorf("invalid null struct pointer")
}
}
_, err := mtv.modec.StartStruct()
if err != nil {
return err
}
targetFields, err := target.StartFields(vt)
if err != nil {
return err
}
for _, alloc := range computeStructLayout(vt) {
mfield := vt.Field(alloc.vdlStructIndex)
switch vkey, vfield, err := targetFields.StartField(mfield.Name); {
// TODO(toddw): Handle err == vdl.ErrFieldNoExist case?
case err != nil:
return err
default:
if err := mtv.transcodeValue(mfield.Type, vfield, false, false); err != nil {
return err
}
if err := targetFields.FinishField(vkey, vfield); err != nil {
return err
}
}
}
// TODO(toddw): Fill in fields that weren't decoded with their zero value.
if err := target.FinishFields(targetFields); err != nil {
return err
}
return mtv.modec.Finish()
case vdl.Union:
size, tag, err := mtv.modec.ReadUnionHeader()
if err != nil {
return err
}
if size == 0 {
mtv.modec.SkipUnionValue()
return target.FromNil(vdl.OptionalType(vt))
}
if int(tag) >= vt.NumField() {
return fmt.Errorf("union tag out of bounds")
}
fld := vt.Field(int(tag))
targetFields, err := target.StartFields(vt)
if err != nil {
return err
}
vKey, vField, err := targetFields.StartField(fld.Name)
if err != nil {
return err
}
if fld.Type.Kind() == vdl.Union {
switch ptr, err := mtv.modec.ReadPointer(); {
case err != nil:
return err
case ptr == 0 && isNullable:
return target.FromNil(vdl.OptionalType(fld.Type))
case ptr == 0 && !isNullable:
return fmt.Errorf("invalid null union pointer")
}
if err := mtv.modec.StartNestedUnion(); err != nil {
return err
}
}
if err := mtv.transcodeValue(fld.Type, vField, false, false); err != nil {
return err
}
if fld.Type.Kind() == vdl.Union {
if err := mtv.modec.Finish(); err != nil {
return err
}
}
if err := targetFields.FinishField(vKey, vField); err != nil {
return err
}
if err := target.FinishFields(targetFields); err != nil {
return err
}
mtv.modec.FinishReadingUnionValue()
return nil
case vdl.Optional:
return mtv.transcodeValue(vt.Elem(), target, false, true)
case vdl.Any:
panic("unimplemented")
//case vdl.TypeObject:
// panic("unimplemented")
default:
panic(fmt.Errorf("decodeValue unhandled vdl type: %v", vt))
}
}