TBR Initial push
Change-Id: I32d6201074d407620b86c15cedde87e186090c6a
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..84e64f6
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+.jiri
diff --git a/go/src/v.io/mojo/client/client.go b/go/src/v.io/mojo/client/client.go
new file mode 100644
index 0000000..e05fd7b
--- /dev/null
+++ b/go/src/v.io/mojo/client/client.go
@@ -0,0 +1,15 @@
+package v23
+
+import (
+ "mojo/public/go/application"
+ "mojo/public/go/bindings"
+
+ "mojo/public/interfaces/bindings/v23proxy"
+)
+
+func ConnectToRemoteService(ctx application.Context, r application.ServiceRequest, v23Name string) {
+ v23r, v23p := v23proxy.CreateMessagePipeForV23()
+ ctx.ConnectToApplication("mojo:v23proxy").ConnectToService(&v23r)
+ prox := v23proxy.NewV23Proxy(v23p, bindings.GetAsyncWaiter())
+ prox.SetupProxy(v23Name, r.Type(), r.Desc(), r.Name(), r.PassMessagePipe())
+}
diff --git a/go/src/v.io/mojo/proxy/fake_service.go b/go/src/v.io/mojo/proxy/fake_service.go
new file mode 100644
index 0000000..a289776
--- /dev/null
+++ b/go/src/v.io/mojo/proxy/fake_service.go
@@ -0,0 +1,306 @@
+package main
+
+import (
+ "fmt"
+ "log"
+ "strings"
+
+ "v.io/v23/context"
+ "v.io/v23/rpc"
+
+ "mojo/public/go/application"
+ "mojo/public/go/bindings"
+ "mojo/public/go/mojovdl"
+ "mojo/public/go/system"
+
+ "v.io/v23/vdl"
+ "v.io/v23/vdlroot/signature"
+
+ "mojo/public/interfaces/bindings/mojom_types"
+ "mojo/public/interfaces/bindings/service_describer"
+)
+
+// As long as fakeService meets the Invoker interface, it is allowed to pass as
+// a universal v23 service.
+// See the function objectToInvoker in v.io/x/ref/runtime/internal/rpc/server.go
+type fakeService struct {
+ appctx application.Context
+ suffix string
+ router *bindings.Router
+ ids bindings.Counter
+}
+
+// Prepare is used by the Fake Service to prepare the placeholders for the
+// input data.
+func (fs fakeService) Prepare(ctx *context.T, method string, numArgs int) (argptrs []interface{}, tags []*vdl.Value, _ error) {
+ inargs := make([]*vdl.Value, numArgs)
+ inptrs := make([]interface{}, len(inargs))
+ for i := range inargs {
+ inptrs[i] = &inargs[i]
+ }
+ return inptrs, nil, nil
+}
+
+// Wraps the interface request and the name of the requested mojo service.
+type v23ServiceRequest struct {
+ request bindings.InterfaceRequest
+ name string
+}
+
+func (v *v23ServiceRequest) Name() string {
+ return v.name
+}
+
+func (v *v23ServiceRequest) Type() mojom_types.MojomInterface {
+ panic("not supported")
+}
+
+func (v *v23ServiceRequest) Desc() map[string]mojom_types.UserDefinedType {
+ panic("not supported")
+}
+
+func (v *v23ServiceRequest) PassMessagePipe() system.MessagePipeHandle {
+ return v.request.PassMessagePipe()
+}
+
+// Invoke calls the mojom service based on the suffix and converts the mojom
+// results (a struct) to Vanadium results (a slice of *vdl.Value).
+// Note: The argptrs from Prepare are reused here. The vom bytes should have
+// been decoded into these argptrs, so there are actual values inside now.
+func (fs fakeService) Invoke(ctx *context.T, call rpc.StreamServerCall, method string, argptrs []interface{}) (results []interface{}, _ error) {
+ // fs.suffix consists of the mojo url and the application/interface name.
+ // The last part should be the name; everything else is the url.
+ parts := strings.Split(fs.suffix, "/")
+ mojourl := strings.Join(parts[:len(parts)-1], "/") // e.g., mojo:go_remote_echo_server. May be defined in a BUILD.gn file.
+ mojoname := parts[len(parts)-1] // e.g., mojo::examples::RemoteEcho. Defined from the interface + module.
+
+ // Create the generic message pipe. r is a bindings.InterfaceRequest, and
+ // p is a bindings.InterfacePointer.
+ r, p := bindings.CreateMessagePipeForMojoInterface()
+ v := v23ServiceRequest{
+ request: r,
+ name: mojoname,
+ } // v is an application.ServiceRequest with mojoname
+
+ // Connect to the mojourl.
+ fs.appctx.ConnectToApplication(mojourl).ConnectToService(&v)
+
+ // Then assign a new router the FakeService.
+ // This will never conflict because each FakeService is only invoked once.
+ fs.router = bindings.NewRouter(p.PassMessagePipe(), bindings.GetAsyncWaiter())
+ defer fs.Close_Proxy()
+
+ log.Printf("Fake Service Invoke (Remote Signature)")
+
+ // Vanadium relies on type information, so we will retrieve that first.
+ mojomInterface, desc, err := fs.callRemoteSignature(mojourl, mojoname)
+ if err != nil {
+ return nil, err
+ }
+
+ log.Printf("Fake Service Invoke Signature %v", mojomInterface)
+ log.Printf("Fake Service Invoke (Remote Method)")
+
+ // With the type information, we can make the method call to the remote interface.
+ methodResults, err := fs.callRemoteMethod(method, mojomInterface, desc, argptrs)
+ if err != nil {
+ return nil, err
+ }
+
+ log.Printf("Fake Service Invoke Results %v", methodResults)
+
+ // Convert methodResult to results.
+ results = make([]interface{}, len(methodResults))
+ for i := range methodResults {
+ results[i] = &methodResults[i]
+ }
+ return results, nil
+}
+
+func (fs fakeService) Close_Proxy() {
+ fs.router.Close()
+}
+
+// callRemoteSignature obtains type and header information from the remote
+// mojo service. Remote mojo interfaces all define a signature method.
+func (fs fakeService) callRemoteSignature(mojourl string, mojoname string) (mojomInterface mojom_types.MojomInterface, desc map[string]mojom_types.UserDefinedType, err error) {
+ /*log.Printf("callRemoteSignature: Prepare payload and header")
+
+ // Prepare the input for the mojo call.
+ // This consists of a payload and a header for the RemoteSignature.
+ payload := mojom_types.SignatureInput{}
+ header := bindings.MessageHeader{
+ Type: 0xffffffff, // Signature is always type 0xffffffff
+ Flags: bindings.MessageExpectsResponseFlag, // It always has a response.
+ RequestId: fs.ids.Count(),
+ }
+
+ log.Printf("callRemoteSignature: Encode payload and header")
+
+ var message *bindings.Message
+ if message, err = bindings.EncodeMessage(header, &payload); err != nil {
+ return response, fmt.Errorf("can't encode request: %v", err.Error())
+ }
+
+ log.Printf("callRemoteSignature => callRemoteGeneric")
+
+ outMessage, err := fs.callRemoteGeneric(message)
+ if err != nil {
+ return response, err
+ }
+
+ log.Printf("callRemoteSignature: Decode response")
+
+ if err = outMessage.DecodePayload(&response); err != nil {
+ return
+ }
+
+ return response, nil*/
+
+ // TODO(afandria): The service_describer mojom file defines the constant, but
+ // it is not actually present in the generated code:
+ // https://github.com/domokit/mojo/issues/469
+ // serviceDescriberInterfaceName := "_ServiceDescriber"
+
+ r, p := service_describer.CreateMessagePipeForServiceDescriber()
+ fs.appctx.ConnectToApplication(mojourl).ConnectToService(&r)
+ sDescriber := service_describer.NewServiceDescriberProxy(p, bindings.GetAsyncWaiter())
+ defer sDescriber.Close_Proxy()
+
+ r2, p2 := service_describer.CreateMessagePipeForServiceDescription()
+ err = sDescriber.DescribeService(mojoname, r2)
+ if err != nil {
+ return
+ }
+ sDescription := service_describer.NewServiceDescriptionProxy(p2, bindings.GetAsyncWaiter())
+ defer sDescription.Close_Proxy()
+
+ mojomInterface, err = sDescription.GetTopLevelInterface()
+ if err != nil {
+ return
+ }
+ descPtr, err := sDescription.GetAllTypeDefinitions()
+ if err != nil {
+ return
+ }
+ return mojomInterface, *descPtr, nil
+}
+
+// A helper function that sends a remote message that expects a response.
+func (fs fakeService) callRemoteGeneric(message *bindings.Message) (outMessage *bindings.Message, err error) {
+ log.Printf("callRemoteGeneric: Send message along the router")
+
+ readResult := <-fs.router.AcceptWithResponse(message)
+ if err = readResult.Error; err != nil {
+ return
+ }
+
+ log.Printf("callRemoteGeneric: Audit response message header flag")
+ // The message flag we receive back must be a bindings.MessageIsResponseFlag
+ if readResult.Message.Header.Flags != bindings.MessageIsResponseFlag {
+ err = &bindings.ValidationError{bindings.MessageHeaderInvalidFlags,
+ fmt.Sprintf("invalid message header flag: %v", readResult.Message.Header.Flags),
+ }
+ return
+ }
+
+ log.Printf("callRemoteGeneric: Audit response message header type")
+ // While the mojo service we called into will return a header whose
+ // type must match our outgoing one.
+ if got, want := readResult.Message.Header.Type, message.Header.Type; got != want {
+ err = &bindings.ValidationError{bindings.MessageHeaderUnknownMethod,
+ fmt.Sprintf("invalid method in response: expected %v, got %v", want, got),
+ }
+ return
+ }
+
+ return readResult.Message, nil
+}
+
+// callRemoteMethod calls the method remotely in a generic way.
+// Produces []*vdl.Value at the end for the invoker to return.
+func (fs fakeService) callRemoteMethod(method string, mi mojom_types.MojomInterface, desc map[string]mojom_types.UserDefinedType, argptrs []interface{}) ([]*vdl.Value, error) {
+ // We need to parse the signature result to get the method relevant info out.
+ found := false
+ var ordinal uint32
+ for ord, mm := range mi.Methods {
+ if *mm.DeclData.ShortName == method {
+ ordinal = ord
+ found = true
+ break
+ }
+ }
+
+ if !found {
+ return nil, fmt.Errorf("callRemoteMethod: method %s does not exist", method)
+ }
+
+ mm := mi.Methods[ordinal]
+
+ // A void function must have request id of 0, whereas one with response params
+ // should have a unique request id.
+ var rqId uint64
+ var flag uint32
+ if mm.ResponseParams != nil {
+ rqId = fs.ids.Count()
+ flag = bindings.MessageExpectsResponseFlag
+ } else {
+ flag = bindings.MessageNoFlag
+ }
+
+ header := bindings.MessageHeader{
+ Type: ordinal,
+ Flags: flag,
+ RequestId: rqId,
+ }
+
+ // Now produce the *bindings.Message that we will send to the other side.
+ inType := mojovdl.MojomStructToVDLType(mm.Parameters, desc)
+ message, err := encodeMessageFromVom(header, argptrs, inType)
+ if err != nil {
+ return nil, err
+ }
+
+ // Handle the 0 out-arg case first.
+ if mm.ResponseParams == nil {
+ if err = fs.router.Accept(message); err != nil {
+ return nil, err
+ }
+ return make([]*vdl.Value, 0), nil
+ }
+
+ // Otherwise, make a generic call with the message.
+ outMessage, err := fs.callRemoteGeneric(message)
+ if err != nil {
+ return nil, err
+ }
+
+ // Decode the *vdl.Value from the mojom bytes and mojom type.
+ outType := mojovdl.MojomStructToVDLType(*mm.ResponseParams, desc)
+ outVdlValue, err := mojovdl.DecodeValue(outMessage.Payload, outType)
+ if err != nil {
+ return nil, fmt.Errorf("mojovdl.DecodeValue failed: %v", err)
+ }
+
+ // Then split the *vdl.Value (struct) into []*vdl.Value
+ response := splitVdlValueByMojomType(outVdlValue, outType)
+ return response, nil
+}
+
+// The fake service has no signature.
+func (fs fakeService) Signature(ctx *context.T, call rpc.ServerCall) ([]signature.Interface, error) {
+ log.Printf("Fake Service Signature???")
+ return nil, nil
+}
+
+// The fake service knows nothing about method signatures.
+func (fs fakeService) MethodSignature(ctx *context.T, call rpc.ServerCall, method string) (signature.Method, error) {
+ log.Printf("Fake Service Method Signature???")
+ return signature.Method{}, nil
+}
+
+// The fake service will never need to glob.
+func (fs fakeService) Globber() *rpc.GlobState {
+ log.Printf("Fake Service Globber???")
+ return nil
+}
diff --git a/go/src/v.io/mojo/proxy/misc.go b/go/src/v.io/mojo/proxy/misc.go
new file mode 100644
index 0000000..d156b7f
--- /dev/null
+++ b/go/src/v.io/mojo/proxy/misc.go
@@ -0,0 +1,66 @@
+package main
+
+// Construct the proper *vdl.Value (as a struct) from the mojom type.
+import (
+ "fmt"
+
+ "mojo/public/go/bindings"
+ "mojo/public/go/mojovdl"
+
+ "v.io/v23/vdl"
+)
+
+// TODO(alexfandrianto): Since this function could panic, we should consider
+// handling that if it happens.
+func combineVdlValueByMojomType(values []*vdl.Value, t *vdl.Type) *vdl.Value {
+ outVdlValue := vdl.ZeroValue(t)
+ for i := 0; i < t.NumField(); i++ {
+ outVdlValue.StructField(i).Assign(values[i])
+ }
+ return outVdlValue
+}
+
+// Construct []*vdl.Value from a *vdl.Value (as a struct) via its mojom type.
+// TODO(alexfandrianto): Since this function could panic, we should consider
+// handling that if it happens.
+func splitVdlValueByMojomType(value *vdl.Value, t *vdl.Type) []*vdl.Value {
+ outVdlValues := make([]*vdl.Value, t.NumField())
+ for i := 0; i < t.NumField(); i++ {
+ outVdlValues[i] = value.StructField(i)
+ }
+ return outVdlValues
+}
+
+func encodeMessageFromVom(header bindings.MessageHeader, argptrs []interface{}, t *vdl.Type) (*bindings.Message, error) {
+ // Convert argptrs into their true form: []*vdl.Value
+ inargs := make([]*vdl.Value, len(argptrs))
+ for i := range argptrs {
+ inargs[i] = *argptrs[i].(**vdl.Value)
+ }
+
+ // Construct the proper *vdl.Value (as a struct) from the mojom type.
+ vdlValue := combineVdlValueByMojomType(inargs, t)
+
+ encoder := bindings.NewEncoder()
+ if err := header.Encode(encoder); err != nil {
+ return nil, err
+ }
+ if bytes, handles, err := encoder.Data(); err != nil {
+ return nil, err
+ } else {
+ // Encode here.
+ moreBytes, err := mojovdl.Encode(vdlValue)
+ if err != nil {
+ return nil, fmt.Errorf("mojovdl.Encode failed: %v", err)
+ }
+ // Append the encoded "payload" to the end of the slice.
+ bytes = append(bytes, moreBytes...)
+
+ return &bindings.Message{
+ Header: header,
+ Bytes: bytes,
+ Handles: handles,
+ Payload: moreBytes,
+ }, nil
+ }
+}
diff --git a/go/src/v.io/mojo/proxy/proxy.go b/go/src/v.io/mojo/proxy/proxy.go
new file mode 100644
index 0000000..26a9f64
--- /dev/null
+++ b/go/src/v.io/mojo/proxy/proxy.go
@@ -0,0 +1,269 @@
+package main
+
+import (
+ "flag"
+ "fmt"
+ "log"
+
+ "v.io/v23"
+ "v.io/v23/context"
+ "v.io/v23/options"
+ "v.io/v23/security"
+ "v.io/v23/vdl"
+
+ "mojo/public/go/application"
+ "mojo/public/go/bindings"
+ "mojo/public/go/mojovdl"
+ "mojo/public/go/system"
+ "mojo/public/interfaces/bindings/mojom_types"
+ "mojo/public/interfaces/bindings/v23proxy"
+
+ _ "v.io/x/ref/runtime/factories/static"
+)
+
+//#include "mojo/public/c/system/types.h"
+import "C"
+
+func init() {
+ flag.String("child-connection-id", "", "")
+ flag.String("mojo-platform-channel-handle", "", "")
+}
+
+type v23HeaderReceiver struct {
+ delegate *V23ServerDelegate
+ v23Name string
+ ifaceSig mojom_types.MojomInterface
+ desc map[string]mojom_types.UserDefinedType
+ serviceName string
+ handle system.MessagePipeHandle
+}
+
+func (r *v23HeaderReceiver) SetupProxy(v23Name string, ifaceSig mojom_types.MojomInterface, desc map[string]mojom_types.UserDefinedType, serviceName string, handle system.MessagePipeHandle) (err error) {
+ log.Printf("[server] In SetupProxy(%s, %v, %v, %s, %v)\n", v23Name, ifaceSig, desc, serviceName, handle)
+ r.v23Name = v23Name
+ r.ifaceSig = ifaceSig
+ r.desc = desc
+ r.serviceName = serviceName
+ r.handle = handle
+
+ go func() {
+ connector := bindings.NewConnector(r.handle, bindings.GetAsyncWaiter())
+
+ // Read generic calls in a loop
+ stub := &genericStub{
+ header: r,
+ ctx: r.delegate.ctx,
+ connector: connector,
+ }
+ bindingStub := bindings.NewStub(connector, stub)
+ for {
+ if err := bindingStub.ServeRequest(); err != nil {
+ connectionError, ok := err.(*bindings.ConnectionError)
+ if !ok || !connectionError.Closed() {
+ log.Println(err)
+ }
+ break
+ }
+ }
+ r.delegate.stubs = append(r.delegate.stubs, bindingStub)
+ }()
+ return nil
+}
+
+type byteCopyingPayload []byte
+
+func (bcp byteCopyingPayload) Encode(encoder *bindings.Encoder) error {
+ encoder.WriteRawBytes(bcp)
+ return nil
+}
+
+func (bcp byteCopyingPayload) Decode(decoder *bindings.Decoder) error {
+ panic("not supported")
+}
+
+type genericStub struct {
+ header *v23HeaderReceiver
+ ctx *context.T
+ connector *bindings.Connector
+}
+
+func (s *genericStub) Accept(message *bindings.Message) (err error) {
+ /*if int(message.Header.Type) >= len(s.header.ifaceSig.Methods) {
+ return fmt.Errorf("Method had index %d, but interface only has %d methods",
+ message.Header.Type, len(s.header.ifaceSig.Methods))
+ }*/
+ if _, ok := s.header.ifaceSig.Methods[message.Header.Type]; !ok {
+ return fmt.Errorf("Method had index %d, but interface only has %d methods",
+ message.Header.Type, len(s.header.ifaceSig.Methods))
+ }
+
+ methodSig := s.header.ifaceSig.Methods[message.Header.Type]
+ methodName := *methodSig.DeclData.ShortName
+ // Should we perform validation of flags like generated methods?
+ // Does this handle 0-arg methods?
+
+ messageBytes := message.Payload
+
+ response, err := s.Call(s.header.v23Name, methodName, messageBytes, methodSig.Parameters, methodSig.ResponseParams)
+ if err != nil {
+ return err
+ }
+ responseHeader := bindings.MessageHeader{
+ Type: message.Header.Type,
+ Flags: bindings.MessageIsResponseFlag,
+ RequestId: message.Header.RequestId,
+ }
+ responseMessage, err := bindings.EncodeMessage(responseHeader, byteCopyingPayload(response))
+ if err != nil {
+ return err
+ }
+ return s.connector.WriteMessage(responseMessage)
+}
+
+func (s *genericStub) Call(name, method string, value []byte, inParamsType mojom_types.MojomStruct, outParamsType *mojom_types.MojomStruct) ([]byte, error) {
+ log.Printf("server: %s.%s: %#v", name, method, inParamsType)
+
+ inVType := mojovdl.MojomStructToVDLType(inParamsType, s.header.desc)
+ var outVType *vdl.Type
+ if outParamsType != nil {
+ outVType = mojovdl.MojomStructToVDLType(*outParamsType, s.header.desc)
+ }
+
+ // Decode the vdl.Value from the mojom bytes and mojom type.
+ inVdlValue, err := mojovdl.DecodeValue(value, inVType)
+ if err != nil {
+ return nil, fmt.Errorf("mojovdl.DecodeValue failed: %v", err)
+ }
+
+ // inVdlValue is a struct, but we need to send []interface.
+ inargs := splitVdlValueByMojomType(inVdlValue, inVType)
+ inargsIfc := make([]interface{}, len(inargs))
+ for i := range inargs {
+ inargsIfc[i] = inargs[i]
+ }
+
+ // We know that the v23proxy (on the other side) will give us back a bunch of
+ // data in []interface{}. so we'll want to decode them into *vdl.Value.
+ log.Printf("%s %v\n", method, outParamsType)
+ outargs := make([]*vdl.Value, len(outParamsType.Fields))
+ outptrs := make([]interface{}, len(outargs))
+ for i := range outargs {
+ outptrs[i] = &outargs[i]
+ }
+
+ // Now, run the call without any authorization.
+ if err := v23.GetClient(s.ctx).Call(s.ctx, name, method, inargsIfc, outptrs, options.SkipServerEndpointAuthorization{}); err != nil {
+ return nil, err
+ }
+
+ // Now convert the []interface{} into a *vdl.Value (struct).
+ outVdlValue := combineVdlValueByMojomType(outargs, outVType)
+
+ // Finally, encode this *vdl.Value (struct) into mojom bytes and send the response.
+ result, err := mojovdl.Encode(outVdlValue)
+ if err != nil {
+ return nil, fmt.Errorf("mojovdl.Encode failed: %v", err)
+ }
+ return result, nil
+}
+
+type V23ServerDelegate struct {
+ ctx *context.T
+ stubs []*bindings.Stub
+ shutdown v23.Shutdown
+}
+
+func (delegate *V23ServerDelegate) Initialize(context application.Context) {
+ log.Printf("V23ServerDelegate.Initialize...")
+
+ // Start up v23 whenever a v23proxy is begun.
+ // This is done regardless of whether we are initializing this v23proxy for use
+ // as a client or as a server.
+ ctx, shutdown := v23.Init()
+ delegate.ctx = ctx
+ delegate.shutdown = shutdown
+
+ // TODO(alexfandrianto): Does Mojo stop us from creating too many v23proxy?
+ // Is it 1 per shell? Ideally, each device will only serve 1 of these v23proxy,
+ // but it is not problematic to have extra.
+ /*s := MakeServer(ctx)
+ err := s.ServeDispatcher("", &V23ProxyDispatcher{
+ appctx: context,
+ })*/
+ _, s, err := v23.WithNewDispatchingServer(ctx, "", &V23ProxyDispatcher{
+ appctx: context,
+ })
+ if err != nil {
+ log.Panic("Error serving service: ", err)
+ }
+
+ endpoints := s.Status().Endpoints
+ fmt.Printf("Listening at: /%v\n", endpoints[0])
+}
+
+/*func MakeServer(ctx *context.T) rpc.Server {
+ s, err := v23.NewServer(ctx)
+ if err != nil {
+ log.Panic("Failure creating server: ", err)
+ }
+
+ endpoints, err := s.Listen(v23.GetListenSpec(ctx))
+ if err != nil {
+ log.Panic("Error listening to service: ", err)
+ }
+ fmt.Printf("Listening at: /%v\n", endpoints[0])
+ return s
+}*/
+
+type V23ProxyDispatcher struct {
+ appctx application.Context
+}
+
+func (v23pd *V23ProxyDispatcher) Lookup(ctx *context.T, suffix string) (interface{}, security.Authorizer, error) {
+ log.Printf("Dispatcher: %s", suffix)
+ return fakeService{
+ appctx: v23pd.appctx,
+ suffix: suffix,
+ ids: bindings.NewCounter(),
+ }, security.AllowEveryone(), nil
+}
+
+func (delegate *V23ServerDelegate) Create(request v23proxy.V23_Request) {
+ headerReceiver := &v23HeaderReceiver{delegate: delegate}
+ v23Stub := v23proxy.NewV23Stub(request, headerReceiver, bindings.GetAsyncWaiter())
+ delegate.stubs = append(delegate.stubs, v23Stub)
+
+ go func() {
+ // Read header message
+ if err := v23Stub.ServeRequest(); err != nil {
+ connectionError, ok := err.(*bindings.ConnectionError)
+ if !ok || !connectionError.Closed() {
+ log.Println(err)
+ }
+ return
+ }
+ }()
+}
+
+func (delegate *V23ServerDelegate) AcceptConnection(connection *application.Connection) {
+ log.Printf("V23ServerDelegate.AcceptConnection...")
+ connection.ProvideServices(&v23proxy.V23_ServiceFactory{delegate})
+}
+
+func (delegate *V23ServerDelegate) Quit() {
+ log.Printf("V23ServerDelegate.Quit...")
+ for _, stub := range delegate.stubs {
+ stub.Close()
+ }
+
+ delegate.shutdown()
+}
+
+//export MojoMain
+func MojoMain(handle C.MojoHandle) C.MojoResult {
+ application.Run(&V23ServerDelegate{}, system.MojoHandle(handle))
+ return C.MOJO_RESULT_OK
+}
+
+func main() {
+}
diff --git a/go/src/v.io/mojo/transcoder/allocation_size.go b/go/src/v.io/mojo/transcoder/allocation_size.go
new file mode 100644
index 0000000..ff70e88
--- /dev/null
+++ b/go/src/v.io/mojo/transcoder/allocation_size.go
@@ -0,0 +1,37 @@
+package mojovdl
+
+import "v.io/v23/vdl"
+
+func neededStructAllocationSize(vt *vdl.Type) uint32 {
+ var totalBits uint32
+ for fi := 0; fi < vt.NumField(); fi++ {
+ field := vt.Field(fi)
+ totalBits += baseTypeSizeBits(field.Type)
+ }
+ return roundBitsTo64Alignment(totalBits)
+}
+
+func baseTypeSizeBits(vt *vdl.Type) uint32 {
+ switch vt.Kind() {
+ case vdl.Bool:
+ return 1
+ case vdl.Byte:
+ return 8
+ case vdl.Uint16, vdl.Int16:
+ return 16
+ case vdl.Uint32, vdl.Int32, vdl.Float32, vdl.Enum:
+ return 32
+ case vdl.Union:
+ return 128 // Header + value / pointer to inner union
+ default: // Either Uint64, Int64, Float64 or pointer.
+ return 64
+ }
+}
+
+// Round up to the nearest 8 byte length.
+func roundBitsTo64Alignment(numBits uint32) uint32 {
+ if numBits%64 == 0 {
+ return numBits / 8
+ }
+ return (numBits + (64 - numBits%64)) / 8
+}
diff --git a/go/src/v.io/mojo/transcoder/allocator.go b/go/src/v.io/mojo/transcoder/allocator.go
new file mode 100644
index 0000000..0f3be60
--- /dev/null
+++ b/go/src/v.io/mojo/transcoder/allocator.go
@@ -0,0 +1,75 @@
+package mojovdl
+
+import "encoding/binary"
+
+const HEADER_SIZE uint32 = 8
+
+// TODO(bprosnitz) maybe make an interface to support decode too
+type allocator struct {
+ // Buffer containing encoded data.
+ buf []byte
+
+ // Index of the first unclaimed byte in buf.
+ end uint32
+}
+
+func (a *allocator) makeRoom(size uint32) {
+ totalNeeded := a.end + size
+
+ allocationSize := uint32(1)
+ for allocationSize < totalNeeded {
+ allocationSize *= 2
+ }
+
+ if allocationSize != uint32(len(a.buf)) {
+ oldBuf := a.buf
+ a.buf = make([]byte, allocationSize)
+ copy(a.buf, oldBuf)
+ }
+}
+
+func (a *allocator) allocateBlock(size uint32, numElems uint32) (startIndex, endIndex uint32) {
+ size_with_header := size + HEADER_SIZE
+ size_with_header_rounded := size_with_header
+ if size_with_header%8 != 0 {
+ size_with_header_rounded = size_with_header + (8 - (size_with_header % 8))
+ }
+
+ a.makeRoom(size_with_header_rounded)
+ binary.LittleEndian.PutUint32(a.buf[a.end:a.end+4], size_with_header)
+ binary.LittleEndian.PutUint32(a.buf[a.end+4:a.end+8], numElems)
+
+ prevEnd := a.end
+ start := prevEnd + HEADER_SIZE
+ end := prevEnd + size_with_header
+ a.end = prevEnd + size_with_header_rounded
+ return start, end
+}
+
+func (a *allocator) Allocate(size uint32, numElems uint32) bytesRef {
+ begin, end := a.allocateBlock(size, numElems)
+ ref := bytesRef{
+ allocator: a,
+ startIndex: begin,
+ endIndex: end,
+ // zeros
+ }
+ return ref
+}
+
+func (a *allocator) AllocationFromPointer(absoluteIndex uint32) bytesRef {
+ headerPos := absoluteIndex - HEADER_SIZE
+ size := binary.LittleEndian.Uint32(a.buf[headerPos : headerPos+4])
+ return bytesRef{
+ allocator: a,
+ startIndex: absoluteIndex,
+ endIndex: absoluteIndex + size,
+ }
+}
+
+func (a *allocator) AllocatedBytes() []byte {
+ if a.buf == nil {
+ return []byte{}
+ }
+ return a.buf[:a.end]
+}
diff --git a/go/src/v.io/mojo/transcoder/decode.go b/go/src/v.io/mojo/transcoder/decode.go
new file mode 100644
index 0000000..b5b7142
--- /dev/null
+++ b/go/src/v.io/mojo/transcoder/decode.go
@@ -0,0 +1,370 @@
+package mojovdl
+
+import (
+ "fmt"
+ "reflect"
+
+ "mojo/public/go/bindings"
+
+ "v.io/v23/vdl"
+)
+
+// Decode 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.
+func Decode(data []byte, datatype *vdl.Type, valptr interface{}) error {
+ target, err := vdl.ReflectTarget(reflect.ValueOf(valptr))
+ if err != nil {
+ return err
+ }
+ d := &decoder{dec: bindings.NewDecoder(data, nil)}
+ return d.decodeValue(datatype, target, true, false)
+}
+
+// DecodeValue is like Decode, but decodes mojom-encoded data into a vdl.Value.
+func DecodeValue(data []byte, datatype *vdl.Type) (*vdl.Value, error) {
+ v := new(vdl.Value)
+ if err := Decode(data, datatype, &v); err != nil {
+ return nil, err
+ }
+ return v, nil
+}
+
+type decoder struct {
+ dec *bindings.Decoder
+ typeStack []*vdl.Type
+}
+
+func (d *decoder) decodeValue(vt *vdl.Type, target vdl.Target, isTopType, isNullable bool) error {
+ switch vt.Kind() {
+ case vdl.Bool:
+ value, err := d.dec.ReadBool()
+ if err != nil {
+ return err
+ }
+ return target.FromBool(value, vt)
+ case vdl.Int16:
+ value, err := d.dec.ReadInt16()
+ if err != nil {
+ return err
+ }
+ return target.FromInt(int64(value), vt)
+ case vdl.Int32:
+ value, err := d.dec.ReadInt32()
+ if err != nil {
+ return err
+ }
+ return target.FromInt(int64(value), vt)
+ case vdl.Int64:
+ value, err := d.dec.ReadInt64()
+ if err != nil {
+ return err
+ }
+ return target.FromInt(value, vt)
+ case vdl.Byte:
+ value, err := d.dec.ReadUint8()
+ if err != nil {
+ return err
+ }
+ return target.FromUint(uint64(value), vt)
+ case vdl.Uint16:
+ value, err := d.dec.ReadUint16()
+ if err != nil {
+ return err
+ }
+ return target.FromUint(uint64(value), vt)
+ case vdl.Uint32:
+ value, err := d.dec.ReadUint32()
+ if err != nil {
+ return err
+ }
+ return target.FromUint(uint64(value), vt)
+ case vdl.Uint64:
+ value, err := d.dec.ReadUint64()
+ if err != nil {
+ return err
+ }
+ return target.FromUint(value, vt)
+ case vdl.Float32:
+ value, err := d.dec.ReadFloat32()
+ if err != nil {
+ return err
+ }
+ return target.FromFloat(float64(value), vt)
+ case vdl.Float64:
+ value, err := d.dec.ReadFloat64()
+ if err != nil {
+ return err
+ }
+ return target.FromFloat(value, vt)
+ case vdl.String:
+ switch ptr, err := d.dec.ReadPointer(); {
+ case err != nil:
+ return err
+ case ptr == 0:
+ return fmt.Errorf("invalid null string pointer")
+ default:
+ value, err := d.dec.ReadString()
+ if err != nil {
+ return err
+ }
+ return target.FromString(value, vt)
+ }
+ return nil
+ case vdl.Enum:
+ index, err := d.dec.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.Complex64:
+ panic("unimplemented")
+ case vdl.Complex128:
+ panic("unimplemented")
+ case vdl.Array, vdl.List:
+ 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")
+ }
+
+ if vt.IsBytes() {
+ str, err := d.dec.ReadString()
+ if err != nil {
+ return err
+ }
+ return target.FromBytes([]byte(str), vt)
+ } else {
+ elemBitSize := baseTypeSizeBits(vt.Elem())
+ numElems, err := d.dec.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 := d.decodeValue(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 d.dec.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 := 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")
+ }
+ if err := d.dec.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 := d.decodeValue(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 := d.decodeValue(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 d.dec.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 := 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")
+ }
+ }
+ _, err := d.dec.StartStruct()
+ if err != nil {
+ return err
+ }
+ targetFields, err := target.StartFields(vt)
+ if err != nil {
+ return err
+ }
+ for i := 0; i < vt.NumField(); i++ {
+ mfield := vt.Field(i)
+ switch vkey, vfield, err := targetFields.StartField(mfield.Name); {
+ // TODO(toddw): Handle err == vdl.ErrFieldNoExist case?
+ case err != nil:
+ return err
+ default:
+ if err := d.decodeValue(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 d.dec.Finish()
+ case vdl.Union:
+ size, tag, err := d.dec.ReadUnionHeader()
+ if err != nil {
+ return err
+ }
+ if size == 0 {
+ d.dec.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 := d.dec.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 := d.dec.StartNestedUnion(); err != nil {
+ return err
+ }
+ }
+ if err := d.decodeValue(fld.Type, vField, false, false); err != nil {
+ return err
+ }
+ if fld.Type.Kind() == vdl.Union {
+ if err := d.dec.Finish(); err != nil {
+ return err
+ }
+ }
+ if err := targetFields.FinishField(vKey, vField); err != nil {
+ return err
+ }
+ if err := target.FinishFields(targetFields); err != nil {
+ return err
+ }
+ d.dec.FinishReadingUnionValue()
+ return nil
+ case vdl.Optional:
+ return d.decodeValue(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))
+ }
+}
diff --git a/go/src/v.io/mojo/transcoder/encode.go b/go/src/v.io/mojo/transcoder/encode.go
new file mode 100644
index 0000000..b778e0e
--- /dev/null
+++ b/go/src/v.io/mojo/transcoder/encode.go
@@ -0,0 +1,94 @@
+package mojovdl
+
+import (
+ "reflect"
+
+ "v.io/v23/vdl"
+)
+
+func Encode(value interface{}) ([]byte, error) {
+ enc := &encoder{
+ allocator: &allocator{},
+ }
+ err := vdl.FromReflect(enc, reflect.ValueOf(value))
+ return enc.Bytes(), err
+}
+
+type encoder struct {
+ allocator *allocator
+}
+
+func (e *encoder) Bytes() []byte {
+ return e.allocator.AllocatedBytes()
+}
+
+func (e *encoder) FromBool(src bool, tt *vdl.Type) error {
+ panic("cannot encode top level bool")
+}
+func (e *encoder) FromUint(src uint64, tt *vdl.Type) error {
+ panic("cannot encode top level uint")
+}
+func (e *encoder) FromInt(src int64, tt *vdl.Type) error {
+ panic("cannot encode top level int")
+}
+func (e *encoder) FromFloat(src float64, tt *vdl.Type) error {
+ panic("cannot encode top level float")
+}
+func (e *encoder) FromComplex(src complex128, tt *vdl.Type) error {
+ panic("cannot encode top level complex")
+}
+func (e *encoder) FromBytes(src []byte, tt *vdl.Type) error {
+ panic("cannot encode top level bytes")
+}
+func (e *encoder) FromString(src string, tt *vdl.Type) error {
+ panic("cannot encode top level string")
+}
+func (e *encoder) FromEnumLabel(src string, tt *vdl.Type) error {
+ panic("cannot encode top level enum")
+}
+func (e *encoder) FromTypeObject(src *vdl.Type) error {
+ panic("cannot encode top level type object")
+}
+func (e *encoder) FromNil(tt *vdl.Type) error {
+ panic("cannot encode top level nil")
+}
+
+func (e *encoder) StartList(tt *vdl.Type, len int) (vdl.ListTarget, error) {
+ panic("UNIMPLEMENTED")
+ return nil, nil
+}
+func (e *encoder) FinishList(x vdl.ListTarget) error {
+ return nil
+}
+func (e *encoder) StartSet(tt *vdl.Type, len int) (vdl.SetTarget, error) {
+ panic("UNIMPLEMENTED")
+}
+func (e *encoder) FinishSet(x vdl.SetTarget) error {
+ panic("UNIMPLEMENTED")
+
+}
+func (e *encoder) StartMap(tt *vdl.Type, len int) (vdl.MapTarget, error) {
+ panic("UNIMPLEMENTED")
+
+}
+func (e *encoder) FinishMap(x vdl.MapTarget) error {
+ panic("UNIMPLEMENTED")
+
+}
+func (e *encoder) StartFields(tt *vdl.Type) (vdl.FieldsTarget, error) {
+ if tt.Kind() == vdl.Union {
+ panic("not yet supported")
+ }
+ if tt.Kind() == vdl.Optional {
+ tt = tt.Elem()
+ }
+ block := e.allocator.Allocate(neededStructAllocationSize(tt), 0)
+ return fieldsTarget{
+ vdlType: tt,
+ block: block,
+ },
+ nil
+}
+func (e *encoder) FinishFields(x vdl.FieldsTarget) error {
+ return nil
+}
diff --git a/go/src/v.io/mojo/transcoder/ref.go b/go/src/v.io/mojo/transcoder/ref.go
new file mode 100644
index 0000000..9d53c75
--- /dev/null
+++ b/go/src/v.io/mojo/transcoder/ref.go
@@ -0,0 +1,35 @@
+package mojovdl
+
+type bytesRef struct {
+ allocator *allocator
+ startIndex, endIndex uint32
+}
+
+func (b bytesRef) Slice(low, high uint32) bytesRef {
+ return bytesRef{
+ allocator: b.allocator,
+ startIndex: b.startIndex + low,
+ endIndex: b.startIndex + high,
+ }
+}
+
+// SignedSlice allows going backwards in the slice (temporary hack to include header in slice for unions)
+func (b bytesRef) SignedSlice(low, high int) bytesRef {
+ return bytesRef{
+ allocator: b.allocator,
+ startIndex: uint32(int(b.startIndex) + low),
+ endIndex: uint32(int(b.startIndex) + high),
+ }
+}
+
+func (b bytesRef) Bytes() []byte {
+ return b.allocator.buf[b.startIndex:b.endIndex]
+}
+
+func (b bytesRef) AsPointer(fromRefPos bytesRef) uint32 {
+ offset := b.startIndex - fromRefPos.startIndex
+ if offset <= 0 {
+ panic("invalid non-positive offset for pointer")
+ }
+ return offset - HEADER_SIZE
+}
diff --git a/go/src/v.io/mojo/transcoder/struct_offset.go b/go/src/v.io/mojo/transcoder/struct_offset.go
new file mode 100644
index 0000000..317a45b
--- /dev/null
+++ b/go/src/v.io/mojo/transcoder/struct_offset.go
@@ -0,0 +1,204 @@
+package mojovdl
+
+import (
+ "fmt"
+
+ "v.io/v23/vdl"
+)
+
+func offsetInStruct(index int, structVdlType *vdl.Type) (byteOffset uint32, bitOffset uint8) {
+ if index >= structVdlType.NumField() {
+ panic(fmt.Sprintf("index %v out of bounds for type %v", index, structVdlType))
+ }
+
+ offsetComputation := &structOffsetComputation{}
+ for i := 0; i < index; i++ {
+ bits := baseTypeSizeBits(structVdlType.Field(i).Type)
+ switch bits {
+ case 1:
+ offsetComputation.next1()
+ case 8:
+ offsetComputation.next8()
+ case 16:
+ offsetComputation.next16()
+ case 32:
+ offsetComputation.next32()
+ case 64:
+ offsetComputation.next64()
+ case 128:
+ offsetComputation.next64()
+ offsetComputation.next64()
+ default:
+ panic("unknown bit size")
+ }
+ }
+
+ switch baseTypeSizeBits(structVdlType.Field(index).Type) {
+ case 1:
+ return offsetComputation.index1, offsetComputation.bitOffset1
+ case 8:
+ return offsetComputation.index8, 0
+ case 16:
+ return offsetComputation.index16, 0
+ case 32:
+ return offsetComputation.index32, 0
+ case 64, 128:
+ return offsetComputation.index64, 0
+
+ default:
+ panic("unknown bit size")
+ }
+}
+
+type structOffsetComputation struct {
+ index8, index16, index32, index64 uint32
+ index1 uint32
+ bitOffset1 uint8
+}
+
+func (sa *structOffsetComputation) next1() {
+ newBitOffset1 := (sa.bitOffset1 + 1) % 8
+ var nextIndex uint32
+ if sa.index1 != sa.index8 {
+ nextIndex = sa.index8
+ } else {
+ nextIndex = sa.index1 + 1
+ }
+ if sa.bitOffset1 == 0 {
+ // bit offset == 0 means fetch a new byte
+ if sa.index8 == sa.index1 {
+ sa.index8 = nextIndex
+ }
+ if sa.index16 == sa.index1 {
+ if nextIndex&0x1 == 0 {
+ sa.index16 = nextIndex
+ } else {
+ sa.index16 = nextIndex + 1
+ }
+ }
+ if sa.index32 == sa.index1 {
+ if nextIndex&0x3 == 0 {
+ sa.index32 = nextIndex
+ } else {
+ sa.index32 = nextIndex + (4 - (nextIndex & 0x3))
+ }
+ }
+ if sa.index64 == sa.index1 {
+ if nextIndex&0x7 == 0 {
+ sa.index64 = nextIndex
+ } else {
+ sa.index64 = nextIndex + (8 - (nextIndex & 0x7))
+ }
+ }
+ }
+
+ if sa.bitOffset1 == 7 {
+ sa.index1 = nextIndex
+ }
+ sa.bitOffset1 = newBitOffset1
+}
+func (sa *structOffsetComputation) next8() {
+ var newIndex8 uint32
+ if sa.index8 != sa.index16 {
+ newIndex8 = sa.index16
+ } else {
+ newIndex8 = sa.index8 + 1
+ }
+
+ if sa.index1 == sa.index8 && sa.bitOffset1 == 0 {
+ sa.index1 = newIndex8
+ }
+ if sa.index16 == sa.index8 {
+ if newIndex8&0x1 == 0 {
+ sa.index16 = newIndex8
+ } else {
+ sa.index16 = newIndex8 + 1
+ }
+ }
+ if sa.index32 == sa.index8 {
+ if newIndex8&0x3 == 0 {
+ sa.index32 = newIndex8
+ } else {
+ sa.index32 = newIndex8 + (4 - (newIndex8 & 0x3))
+ }
+ }
+ if sa.index64 == sa.index8 {
+ if newIndex8&0x7 == 0 {
+ sa.index64 = newIndex8
+ } else {
+ sa.index64 = newIndex8 + (8 - (newIndex8 & 0x7))
+ }
+ }
+ sa.index8 = newIndex8
+}
+func (sa *structOffsetComputation) next16() {
+ var newIndex16 uint32
+ if sa.index16 != sa.index32 {
+ newIndex16 = sa.index32
+ } else {
+ newIndex16 = sa.index16 + 2
+ }
+ if sa.index1 == sa.index16 && sa.bitOffset1 == 0 {
+ sa.index1 = newIndex16
+ }
+ if sa.index8 == sa.index16 {
+ sa.index8 = newIndex16
+ }
+ if sa.index32 == sa.index16 {
+ if newIndex16&0x3 == 0 {
+ sa.index32 = newIndex16
+ } else {
+ sa.index32 = newIndex16 + (4 - (newIndex16 & 0x3))
+ }
+ }
+ if sa.index64 == sa.index16 {
+ if newIndex16&0x7 == 0 {
+ sa.index64 = newIndex16
+ } else {
+ sa.index64 = newIndex16 + (8 - (newIndex16 & 0x7))
+ }
+ }
+ sa.index16 = newIndex16
+}
+func (sa *structOffsetComputation) next32() {
+ var newIndex32 uint32
+ if sa.index32 != sa.index64 {
+ newIndex32 = sa.index64
+ } else {
+ newIndex32 = sa.index32 + 4
+ }
+ if sa.index1 == sa.index32 && sa.bitOffset1 == 0 {
+ sa.index1 = newIndex32
+ }
+ if sa.index8 == sa.index32 {
+ sa.index8 = newIndex32
+ }
+ if sa.index16 == sa.index32 {
+ sa.index16 = newIndex32
+ }
+ if sa.index64 == sa.index32 {
+ if newIndex32&0x7 == 0 {
+ sa.index64 = newIndex32
+ } else {
+ sa.index64 = newIndex32 + (8 - (newIndex32 & 0x7))
+ }
+ }
+ sa.index32 = newIndex32
+}
+func (sa *structOffsetComputation) next64() {
+ newIndex64 := sa.index64 + 8
+
+ if sa.index1 == sa.index64 && sa.bitOffset1 == 0 {
+ sa.index1 = newIndex64
+ }
+ if sa.index8 == sa.index64 {
+ sa.index8 = newIndex64
+ }
+ if sa.index16 == sa.index64 {
+ sa.index16 = newIndex64
+ }
+ if sa.index32 == sa.index64 {
+ sa.index32 = newIndex64
+ }
+ sa.index64 = newIndex64
+}
diff --git a/go/src/v.io/mojo/transcoder/target.go b/go/src/v.io/mojo/transcoder/target.go
new file mode 100644
index 0000000..cfb26f5
--- /dev/null
+++ b/go/src/v.io/mojo/transcoder/target.go
@@ -0,0 +1,213 @@
+package mojovdl
+
+import (
+ "encoding/binary"
+ "fmt"
+ "math"
+
+ "v.io/v23/vdl"
+)
+
+type target struct {
+ currentBitOffset uint8
+ current bytesRef
+}
+
+func (t target) allocator() *allocator {
+ return t.current.allocator
+}
+
+func (t target) FromBool(src bool, tt *vdl.Type) error {
+ if src {
+ t.current.Bytes()[0] |= 1 << t.currentBitOffset
+ }
+ // shouldn't have to do anything for false
+ return nil
+}
+func (t target) FromUint(src uint64, tt *vdl.Type) error {
+ switch tt.Kind() {
+ case vdl.Byte:
+ t.current.Bytes()[0] = byte(src)
+ case vdl.Uint16:
+ binary.LittleEndian.PutUint16(t.current.Bytes(), uint16(src))
+ case vdl.Uint32:
+ binary.LittleEndian.PutUint32(t.current.Bytes(), uint32(src))
+ case vdl.Uint64:
+ binary.LittleEndian.PutUint64(t.current.Bytes(), src)
+ default:
+ return fmt.Errorf("invalid FromUint(%v, %v)", src, tt)
+ }
+ return nil
+}
+func (t target) FromInt(src int64, tt *vdl.Type) error {
+ switch tt.Kind() {
+ case vdl.Int16:
+ binary.LittleEndian.PutUint16(t.current.Bytes(), uint16(src))
+ case vdl.Int32:
+ binary.LittleEndian.PutUint32(t.current.Bytes(), uint32(src))
+ case vdl.Int64:
+ binary.LittleEndian.PutUint64(t.current.Bytes(), uint64(src))
+ default:
+ return fmt.Errorf("invalid FromInt(%v, %v)", src, tt)
+ }
+ return nil
+}
+func (t target) FromFloat(src float64, tt *vdl.Type) error {
+ switch tt.Kind() {
+ case vdl.Float32:
+ binary.LittleEndian.PutUint32(t.current.Bytes(), math.Float32bits(float32(src)))
+ case vdl.Float64:
+ binary.LittleEndian.PutUint64(t.current.Bytes(), math.Float64bits(src))
+ default:
+ return fmt.Errorf("invalid FromFloat(%v, %v)", src, tt)
+ }
+ return nil
+}
+func (t target) FromComplex(src complex128, tt *vdl.Type) error {
+ panic("UNIMPLEMENTED")
+}
+func (t target) writeBytes(src []byte) {
+ block := t.allocator().Allocate(uint32(len(src)), uint32(len(src)))
+ t.writePointer(block)
+ copy(block.Bytes(), src)
+}
+func (t target) FromBytes(src []byte, tt *vdl.Type) error {
+ t.writeBytes(src)
+ return nil
+}
+func (t target) FromString(src string, tt *vdl.Type) error {
+ t.writeBytes([]byte(src))
+ return nil
+}
+func (t target) FromEnumLabel(src string, tt *vdl.Type) error {
+ // enums in mojo are treated as an int32 on the wire (but have gaps in their values).
+ // This implementation assumes that we will use generated VDL values and not have gaps.
+ index := tt.EnumIndex(src)
+ binary.LittleEndian.PutUint32(t.current.Bytes(), uint32(index))
+ return nil
+}
+func (t target) FromTypeObject(src *vdl.Type) error {
+ panic("UNIMPLEMENTED")
+
+}
+func (t target) FromNil(tt *vdl.Type) error {
+ switch tt.Kind() {
+ case vdl.Optional:
+ elemType := tt.Elem()
+ switch elemType.Kind() {
+ case vdl.Union, vdl.Struct: // Array? String? Bytes? List? Set?
+ // Note: for union, this zeros 16 bytes, but for others it does just 8.
+ zeroBytes(t.current.Bytes())
+ default:
+ panic(fmt.Sprintf("Vdl type %v cannot be optional", tt))
+ }
+ case vdl.Any:
+ panic("Any rep not yet determined")
+ default:
+ panic("Type cannot be nil")
+ }
+ return nil
+}
+func zeroBytes(dat []byte) {
+ copy(dat, make([]byte, len(dat)))
+}
+
+func (t target) StartList(tt *vdl.Type, len int) (vdl.ListTarget, error) {
+ if tt.Kind() == vdl.Optional {
+ tt = tt.Elem()
+ }
+ bitsNeeded := baseTypeSizeBits(tt.Elem()) * uint32(len)
+ block := t.allocator().Allocate((bitsNeeded+7)/8, uint32(len))
+ t.writePointer(block)
+ if tt.Elem().Kind() == vdl.Bool {
+ return &bitListTarget{
+ block: block,
+ }, nil
+ } else {
+ return &listTarget{
+ incrementSize: baseTypeSizeBits(tt.Elem()) / 8,
+ block: block,
+ }, nil
+ }
+}
+func (t target) FinishList(x vdl.ListTarget) error {
+ return nil
+}
+
+// TODO(bprosnitz) This uses list, should we use map instead?
+func (t target) StartSet(tt *vdl.Type, len int) (vdl.SetTarget, error) {
+ if tt.Kind() == vdl.Optional {
+ tt = tt.Elem()
+ }
+ bitsNeeded := baseTypeSizeBits(tt.Key()) * uint32(len)
+ block := t.allocator().Allocate((bitsNeeded+7)/8, uint32(len))
+ t.writePointer(block)
+ if tt.Key().Kind() == vdl.Bool {
+ return &bitListTarget{
+ block: block,
+ }, nil
+ } else {
+ return &listTarget{
+ incrementSize: baseTypeSizeBits(tt.Key()) / 8,
+ block: block,
+ }, nil
+ }
+}
+func (t target) FinishSet(x vdl.SetTarget) error {
+ return nil
+}
+func (t target) StartMap(tt *vdl.Type, len int) (vdl.MapTarget, error) {
+ if tt.Kind() == vdl.Optional {
+ tt = tt.Elem()
+ }
+ pointerBlock := t.allocator().Allocate(16, 0)
+ t.writePointer(pointerBlock)
+
+ st := target{
+ current: pointerBlock.Slice(0, 8),
+ }
+ setTarget, err := st.StartSet(vdl.SetType(tt.Key()), len)
+ if err != nil {
+ return nil, err
+ }
+ lt := target{
+ current: pointerBlock.Slice(8, 16),
+ }
+ listTarget, err := lt.StartList(vdl.ListType(tt.Elem()), len)
+ if err != nil {
+ return nil, err
+ }
+ return &mapTarget{
+ setTarget: setTarget,
+ listTarget: listTarget,
+ }, nil
+}
+func (t target) FinishMap(x vdl.MapTarget) error {
+ return nil
+}
+func (t target) StartFields(tt *vdl.Type) (vdl.FieldsTarget, error) {
+ if tt.Kind() == vdl.Optional {
+ tt = tt.Elem()
+ }
+ if tt.Kind() == vdl.Union {
+ return unionFieldsTarget{
+ vdlType: tt,
+ block: t.current,
+ }, nil
+ }
+ block := t.allocator().Allocate(neededStructAllocationSize(tt), 0)
+ t.writePointer(block)
+ return fieldsTarget{
+ vdlType: tt,
+ block: block,
+ },
+ nil
+}
+func (t target) FinishFields(x vdl.FieldsTarget) error {
+ return nil
+}
+
+func (t target) writePointer(alloc bytesRef) {
+ offset := alloc.AsPointer(t.current)
+ binary.LittleEndian.PutUint64(t.current.Bytes(), uint64(offset))
+}
diff --git a/go/src/v.io/mojo/transcoder/type.go b/go/src/v.io/mojo/transcoder/type.go
new file mode 100644
index 0000000..3fc7c35
--- /dev/null
+++ b/go/src/v.io/mojo/transcoder/type.go
@@ -0,0 +1,265 @@
+package mojovdl
+
+import (
+ "fmt"
+
+ "mojo/public/interfaces/bindings/mojom_types"
+
+ "v.io/v23/vdl"
+)
+
+/*// Given a descriptor mapping, produce 2 maps.
+// The former maps from mojom identifiers to VDL Type.
+// The latter maps from VDL Type string (hash cons) to the mojom identifier.
+// These maps are used to interconvert more easily.
+func AnalyzeMojomDescriptors(mp map[string]mojom_types.UserDefinedType) map[string]*vdl.Type {
+ m2V := make(map[string]*vdl.Type)
+ for s, udt := range mp {
+ m2V[s] = mojomToVDLTypeUDT(udt, mp)
+ }
+ return m2V
+}*/
+
+// Convert the known type reference to a vdl type.
+// Panics if the type reference was not known.
+/*func TypeReferenceToVDLType(tr mojom_types.TypeReference, mp map[string]mojom_types.UserDefinedType) *vdl.Type {
+ if udt, ok := mp[tr.TypeKey]; ok {
+ return mojomToVDLTypeUDT(udt, mp)
+ }
+ panic("Type Key %s was not present in the mapping", tr.typeKey)
+}*/
+
+func mojomToVDLTypeUDT(udt mojom_types.UserDefinedType, mp map[string]mojom_types.UserDefinedType) (vt *vdl.Type) {
+ u := interface{}(udt)
+ switch u := u.(type) { // To do the type switch, udt has to be converted to interface{}.
+ case *mojom_types.UserDefinedTypeEnumType: // enum
+ me := u.Value
+
+ // TODO: Assumes that the maximum enum index is len(me.Values) - 1.
+ labels := make([]string, len(me.Values))
+ for _, ev := range me.Values { // per EnumValue...
+ // EnumValue contains a ConstantOccurrence
+ // ConstantOccurrence contains a ConstantValue
+ // ConstantValue is a union that contains an EnumConstantValue
+ ecv := ev.Value.Value.(*mojom_types.ConstantValueEnumValue).Value
+
+ // EnumConstantValue contains the EnumValueName and IntValue
+ labels[int(ecv.IntValue)] = *ecv.EnumValueName
+ }
+
+ vt = vdl.NamedType(*me.DeclData.ShortName, vdl.EnumType(labels...))
+ case *mojom_types.UserDefinedTypeStructType: // struct
+ ms := u.Value
+
+ vt = MojomStructToVDLType(ms, mp)
+ case *mojom_types.UserDefinedTypeUnionType: // union
+ mu := u.Value
+
+ vfields := make([]vdl.Field, len(mu.Fields))
+ for ix, mfield := range mu.Fields {
+ vfields[ix] = vdl.Field{
+ Name: *mfield.DeclData.ShortName,
+ Type: MojomToVDLType(mfield.Type, mp),
+ }
+ }
+ vt = vdl.NamedType(*mu.DeclData.ShortName, vdl.UnionType(vfields...))
+ case *mojom_types.UserDefinedTypeInterfaceType: // interface
+ panic("interfaces don't exist in vdl")
+ default: // unknown
+ panic(fmt.Errorf("user defined type %#v with unknown tag %d", udt, udt.Tag()))
+ }
+ return vt
+}
+
+func MojomStructToVDLType(ms mojom_types.MojomStruct, mp map[string]mojom_types.UserDefinedType) (vt *vdl.Type) {
+ vfields := make([]vdl.Field, len(ms.Fields))
+ for ix, mfield := range ms.Fields {
+ vfields[ix] = vdl.Field{
+ Name: *mfield.DeclData.ShortName,
+ Type: MojomToVDLType(mfield.FieldType, mp),
+ }
+ }
+ vt = vdl.NamedType(*ms.DeclData.ShortName, vdl.StructType(vfields...))
+ return vt
+}
+
+// Given a mojom Type and the descriptor mapping, produce the corresponding vdltype.
+func MojomToVDLType(mojomtype mojom_types.Type, mp map[string]mojom_types.UserDefinedType) (vt *vdl.Type) {
+ // TODO(alexfandrianto): Cyclic types?
+ mt := interface{}(mojomtype)
+ switch mt := interface{}(mt).(type) { // To do the type switch, mt has to be converted to interface{}.
+ case *mojom_types.TypeSimpleType: // TypeSimpleType
+ switch mt.Value {
+ case mojom_types.SimpleType_Bool:
+ vt = vdl.BoolType
+ case mojom_types.SimpleType_Double:
+ vt = vdl.BoolType
+ case mojom_types.SimpleType_Float:
+ vt = vdl.BoolType
+ case mojom_types.SimpleType_InT8:
+ panic("int8 doesn't exist in vdl")
+ case mojom_types.SimpleType_InT16:
+ vt = vdl.Int16Type
+ case mojom_types.SimpleType_InT32:
+ vt = vdl.Int32Type
+ case mojom_types.SimpleType_InT64:
+ vt = vdl.Int64Type
+ case mojom_types.SimpleType_UinT8:
+ vt = vdl.ByteType
+ case mojom_types.SimpleType_UinT16:
+ vt = vdl.Uint16Type
+ case mojom_types.SimpleType_UinT32:
+ vt = vdl.Uint32Type
+ case mojom_types.SimpleType_UinT64:
+ vt = vdl.Uint64Type
+ }
+ case *mojom_types.TypeStringType: // TypeStringType
+ st := mt.Value
+ if st.Nullable {
+ panic("nullable strings don't exist in vdl")
+ }
+ vt = vdl.StringType
+ case *mojom_types.TypeArrayType: // TypeArrayType
+ at := mt.Value
+ if at.Nullable {
+ panic("nullable arrays don't exist in vdl")
+ }
+ if at.FixedLength > 0 {
+ vt = vdl.ArrayType(int(at.FixedLength), MojomToVDLType(at.ElementType, mp))
+ } else {
+ vt = vdl.ListType(MojomToVDLType(at.ElementType, mp))
+ }
+ case *mojom_types.TypeMapType: // TypeMapType
+ // Note that mojom doesn't have sets.
+ m := mt.Value
+ if m.Nullable {
+ panic("nullable maps don't exist in vdl")
+ }
+ vt = vdl.MapType(MojomToVDLType(m.KeyType, mp), MojomToVDLType(m.ValueType, mp))
+ case *mojom_types.TypeHandleType: // TypeHandleType
+ panic("handles don't exist in vdl")
+ case *mojom_types.TypeTypeReference: // TypeTypeReference
+ tr := mt.Value
+ if tr.IsInterfaceRequest {
+ panic("interface requests don't exist in vdl")
+ }
+ udt := mp[*tr.TypeKey]
+ if udt.Tag() != 1 && tr.Nullable {
+ panic("nullable non-struct type reference cannot be represented in vdl")
+ }
+ vt = mojomToVDLTypeUDT(udt, mp)
+ default:
+ panic(fmt.Errorf("%#v has unknown tag %d", mojomtype, mojomtype.Tag()))
+ }
+
+ return vt
+}
+
+/*func V2M(vt *vdl.Type, v2M map[string]string) mojom_types.Type {
+ if m, ok := v2M[vt.String()]; ok {
+ return mojom_types.TypeTypeReference{
+ Value: mojom_types.TypeReference{
+ Nullable: nullable,
+ Identifier: m,
+ TypeKey: m,
+ },
+ }
+ }
+ panic("vdl type %#v was not present in the mapping", vt)
+}
+
+// From the vdltype and the reverse mapping of the descriptor (hashcons vdltype string => typekey),
+// produce the corresponding mojom Type.
+func VDLToMojomType(vt *vdl.Type, v2M map[string]string) mojom_types.Type {
+ return vdlToMojomTypeImpl(vt, v2M, false)
+}
+
+func vdlToMojomTypeImpl(vt *vdl.Type, v2M map[string]string, bool nullable) mojom_types.Type {
+ if m, ok := v2M[vt.String()]; ok {
+ return mojom_types.TypeTypeReference{
+ Value: mojom_types.TypeReference{
+ Nullable: nullable,
+ Identifier: m,
+ TypeKey: m,
+ },
+ }
+ }
+
+ fmt.Println("Missed the vdl to mojom map")
+ // In the unlikely case where v2M was insufficient, we have the remaining logic.
+
+ switch vt.Kind() {
+ case vdl.Bool:
+ return mojom_types.TypeSimpleType{Value: mojom_types.SimpleType_Bool}
+ case vdl.Byte:
+ return mojom_types.TypeSimpleType{Value: mojom_types.SimpleType_UinT8}
+ case vdl.Uint16:
+ return mojom_types.TypeSimpleType{Value: mojom_types.SimpleType_UinT16}
+ case vdl.Uint32:
+ return mojom_types.TypeSimpleType{Value: mojom_types.SimpleType_UinT32}
+ case vdl.Uint64:
+ return mojom_types.TypeSimpleType{Value: mojom_types.SimpleType_UinT64}
+ case vdl.Int16:
+ return mojom_types.TypeSimpleType{Value: mojom_types.SimpleType_InT16}
+ case vdl.Int32:
+ return mojom_types.TypeSimpleType{Value: mojom_types.SimpleType_InT32}
+ case vdl.Int64:
+ return mojom_types.TypeSimpleType{Value: mojom_types.SimpleType_InT64}
+ case vdl.Float32:
+ return mojom_types.TypeSimpleType{Value: mojom_types.SimpleType_Float}
+ case vdl.Float64:
+ return mojom_types.TypeSimpleType{Value: mojom_types.SimpleType_Double}
+ case vdl.Complex64:
+ panic("complex float doesn't exist in mojom")
+ case vdl.Complex128:
+ panic("complex double doesn't exist in mojom")
+ case vdl.String:
+ return mojom_types.TypeStringType{Value: mojom_types.StringType{}}
+ case vdl.Array:
+ elemType := VDLToMojomType(vt.Elem(), v2M)
+ return mojom_types.TypeArrayType{
+ Value: mojom_types.ArrayType{
+ FixedLength: int64(vt.Len()),
+ ElementType: elemType,
+ },
+ }
+ case vdl.List:
+ elemType := VDLToMojomType(vt.Elem(), v2M)
+ return mojom_types.TypeArrayType{
+ Value: mojom_types.ArrayType{
+ FixedLength: -1,
+ ElementType: elemType,
+ },
+ }
+ case vdl.Set:
+ panic("set doesn't exist in mojom")
+ case vdl.Map:
+ keyType := VDLToMojomType(vt.Key(), v2M)
+ elemType := VDLToMojomType(vt.Elem(), v2M)
+ return mojom_types.TypeMapType{
+ Value: mojom_types.MapType{
+ KeyType: &keyType,
+ ValueType: &elemType,
+ },
+ }
+ case vdl.Struct, vdl.Union, vdl.Enum:
+ mt := mojom_types.TypeTypeReference{
+ Value: mojom_types.TypeReference{
+ Nullable: nullable,
+ Identifier: v2M[vt.String()],
+ TypeKey: v2M[vt.String()],
+ },
+ }
+ return mt
+ case vdl.TypeObject:
+ panic("typeobject doesn't exist in mojom")
+ case vdl.Any:
+ panic("any doesn't exist in mojom")
+ case vdl.Optional:
+ // TODO(alexfandrianto): Unfortunately, without changing vdl, we can only
+ // manage optional (named) structs. This doesn't Nullify anything else.
+ return vdlToMojomTypeImpl(vt.Elem(), v2M, true)
+ }
+ panic(fmt.Errorf("%v can't be converted to MojomType", vt))
+}
+*/
diff --git a/go/src/v.io/mojo/transcoder/typed_targets.go b/go/src/v.io/mojo/transcoder/typed_targets.go
new file mode 100644
index 0000000..d09160e
--- /dev/null
+++ b/go/src/v.io/mojo/transcoder/typed_targets.go
@@ -0,0 +1,123 @@
+package mojovdl
+
+import (
+ "encoding/binary"
+
+ "v.io/v23/vdl"
+)
+
+// for struct
+type fieldsTarget struct {
+ vdlType *vdl.Type
+ block bytesRef
+}
+
+func (fe fieldsTarget) StartField(name string) (key, field vdl.Target, _ error) {
+ fieldType, fieldIndex := fe.vdlType.FieldByName(name)
+ byteOffset, bitOffset := offsetInStruct(fieldIndex, fe.vdlType)
+ numBits := baseTypeSizeBits(fieldType.Type)
+ refSize := (numBits + 7) / 8
+ newRef := fe.block.Slice(byteOffset, byteOffset+refSize)
+ return nil, target{
+ currentBitOffset: bitOffset,
+ current: newRef,
+ }, nil
+}
+func (fieldsTarget) FinishField(key, field vdl.Target) error {
+ return nil
+}
+
+type unionFieldsTarget struct {
+ vdlType *vdl.Type
+ block bytesRef
+}
+
+func (ufe unionFieldsTarget) StartField(name string) (key, field vdl.Target, _ error) {
+ fld, index := ufe.vdlType.FieldByName(name)
+ binary.LittleEndian.PutUint32(ufe.block.Bytes(), 16)
+ binary.LittleEndian.PutUint32(ufe.block.Bytes()[4:], uint32(index))
+
+ valueSlice := ufe.block.Slice(8, 16)
+ if fld.Type.Kind() == vdl.Union {
+ // nested union, create a pointer to the body
+ nestedUnionBlock := ufe.block.allocator.Allocate(8, 0)
+ offset := nestedUnionBlock.AsPointer(valueSlice)
+ binary.LittleEndian.PutUint64(valueSlice.Bytes(), uint64(offset))
+ return nil, target{
+ currentBitOffset: 0,
+ current: nestedUnionBlock.SignedSlice(-8, 8),
+ }, nil
+ }
+ return nil, target{
+ currentBitOffset: 0,
+ current: valueSlice,
+ }, nil
+}
+func (unionFieldsTarget) FinishField(key, field vdl.Target) error {
+ return nil
+}
+
+// doubles as set target
+type listTarget struct {
+ incrementSize uint32
+ block bytesRef
+ nextPosition uint32
+}
+
+func (lt *listTarget) StartElem(index int) (elem vdl.Target, _ error) {
+ // TODO(bprosnitz) Index is ignored -- we should probably remove this from Target.
+ sliceBlock := lt.block.Slice(lt.nextPosition, lt.nextPosition+lt.incrementSize)
+ lt.nextPosition += lt.incrementSize
+ return target{
+ current: sliceBlock,
+ }, nil
+}
+func (lt *listTarget) StartKey() (key vdl.Target, _ error) {
+ return lt.StartElem(0)
+}
+func (listTarget) FinishElem(elem vdl.Target) error {
+ return nil
+}
+func (listTarget) FinishKey(key vdl.Target) error {
+ return nil
+}
+
+type bitListTarget struct {
+ block bytesRef
+ nextBitPosition uint32
+}
+
+func (blt *bitListTarget) StartElem(index int) (elem vdl.Target, _ error) {
+ bitPos := blt.nextBitPosition
+ blt.nextBitPosition++
+ byteIndex := bitPos / 8
+ sliceBlock := blt.block.Slice(byteIndex, byteIndex+1)
+ return target{
+ currentBitOffset: uint8(bitPos % 8),
+ current: sliceBlock,
+ }, nil
+}
+func (blt *bitListTarget) StartKey() (key vdl.Target, _ error) {
+ return blt.StartElem(0)
+}
+func (bitListTarget) FinishElem(elem vdl.Target) error {
+ return nil
+}
+func (bitListTarget) FinishKey(key vdl.Target) error {
+ return nil
+}
+
+type mapTarget struct {
+ listTarget vdl.ListTarget
+ setTarget vdl.SetTarget
+}
+
+func (mt *mapTarget) StartKey() (key vdl.Target, _ error) {
+ return mt.setTarget.StartKey()
+}
+func (mt *mapTarget) FinishKeyStartField(key vdl.Target) (field vdl.Target, _ error) {
+ return mt.listTarget.StartElem(0)
+}
+func (mapTarget) FinishField(key, field vdl.Target) error {
+ return nil
+}