swift: Discovery API for VanadiumCore
Wraps the high-level scan/advertise discovery API using exported CGO
functions. Uses JSON to marshal the advertising structs back and
forth since we don't yet have VOM.
Also fixes logging for VanadiumCore.
TODO: Support attachments.
Change-Id: I52766dddb7e4e490118db11e7e109b8073e82f9e
diff --git a/swift.go b/swift.go
index 2407db7..a6f1a06 100644
--- a/swift.go
+++ b/swift.go
@@ -49,7 +49,6 @@
sutil.ThrowSwiftError(nil, err, unsafe.Pointer(errOut))
return
}
-
vlog.Log.Configure(vlog.OverridePriorConfiguration(true), dir, toStderr, level, vmodule)
}
diff --git a/types.h b/types.h
index 622697d..e1b556e 100644
--- a/types.h
+++ b/types.h
@@ -19,6 +19,17 @@
typedef _GoHandle GoContextHandle;
typedef _GoHandle GoCancelableContextHandle;
typedef _GoHandle GoClientCallHandle;
+typedef _GoHandle GoDiscoveryHandle;
+
+typedef struct {
+ _GoUint64 length;
+ char *data;
+} SwiftCString;
+
+typedef struct {
+ _GoUint64 length;
+ SwiftCString *data;
+} SwiftCStringArray;
// Express byte arrays with lengths
typedef struct {
@@ -44,7 +55,12 @@
// Asynchronous callback function pointers, which use their own handle (AsyncCallbackIdentifier) to overcome
// Swift limitations on context-free closures (function pointers)
typedef int AsyncCallbackIdentifier;
-typedef void (*SwiftAsyncSuccessCallback)(AsyncCallbackIdentifier);
-typedef void (*SwiftAsyncSuccessHandleCallback)(AsyncCallbackIdentifier, _GoHandle);
-typedef void (*SwiftAsyncSuccessByteArrayArrayCallback)(AsyncCallbackIdentifier, SwiftByteArrayArray);
-typedef void (*SwiftAsyncFailureCallback)(AsyncCallbackIdentifier, SwiftVError);
+typedef void (*SwiftAsyncJsonCallback)(AsyncCallbackIdentifier asyncId, SwiftByteArray json);
+typedef void (*SwiftAsyncSuccessCallback)(AsyncCallbackIdentifier asyncId);
+typedef void (*SwiftAsyncSuccessHandleCallback)(AsyncCallbackIdentifier asyncId, _GoHandle handle);
+typedef void (*SwiftAsyncSuccessByteArrayArrayCallback)(AsyncCallbackIdentifier asyncId, SwiftByteArrayArray byteArrayArray);
+typedef void (*SwiftAsyncFailureCallback)(AsyncCallbackIdentifier asyncId, SwiftVError err);
+
+// Other callbacks
+typedef void (*SwiftAsyncVoidCallback)();
+
diff --git a/util/type.go b/util/type.go
index 63457c4..712ec35 100644
--- a/util/type.go
+++ b/util/type.go
@@ -16,6 +16,7 @@
)
/*
+#include <string.h> // memcpy
#include <stdlib.h>
#import "../types.h"
*/
@@ -61,6 +62,17 @@
return time.Duration(int64(d * 1e9))
}
+func GoString(swiftStringPtr unsafe.Pointer, freeWhenDone bool) string {
+ str := *(*C.SwiftCString)(swiftStringPtr)
+ if freeWhenDone && str.data != nil {
+ defer C.free(unsafe.Pointer(str.data))
+ }
+ if str.data == nil || str.length == 0 {
+ return ""
+ }
+ return C.GoStringN(str.data, C.int(str.length))
+}
+
//export swift_io_v_swift_impl_util_type_nativeBase64UrlDecode
func swift_io_v_swift_impl_util_type_nativeBase64UrlDecode(base64UrlEncoded *C.char) C.SwiftByteArray {
// Decode the base64 url encoded string to bytes in a way that prevents extra copies along the CGO boundary.
diff --git a/v23/discovery/swift.go b/v23/discovery/swift.go
new file mode 100644
index 0000000..0a6f65a
--- /dev/null
+++ b/v23/discovery/swift.go
@@ -0,0 +1,106 @@
+// 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.
+
+// +build darwin
+
+package discovery
+
+import (
+ "encoding/json"
+ "unsafe"
+
+ "v.io/v23"
+ "v.io/v23/discovery"
+ "v.io/v23/security"
+ sutil "v.io/x/swift/util"
+ scontext "v.io/x/swift/v23/context"
+)
+
+/*
+#include <stdlib.h>
+#import "../../types.h"
+
+static void CallAdvertisingCallback(SwiftAsyncSuccessCallback callback, AsyncCallbackIdentifier asyncId) {
+ callback(asyncId);
+}
+static void CallScanCallback(SwiftAsyncJsonCallback callback, AsyncCallbackIdentifier asyncId, SwiftByteArray data) {
+ callback(asyncId, data);
+}
+*/
+import "C"
+
+//export swift_io_v_v23_discovery_new
+func swift_io_v_v23_discovery_new(ctxHandle C.GoContextHandle, errOut *C.SwiftVError) C.GoDiscoveryHandle {
+ ctx := scontext.GoContext(uint64(ctxHandle))
+ d, err := v23.NewDiscovery(ctx)
+ if err != nil {
+ sutil.ThrowSwiftError(ctx, err, unsafe.Pointer(errOut))
+ return C.GoDiscoveryHandle(0)
+ }
+ return C.GoDiscoveryHandle(sutil.GoNewRef(&d))
+}
+
+//export swift_io_v_v23_discovery_finalize
+func swift_io_v_v23_discovery_finalize(discoveryHandle C.GoDiscoveryHandle) {
+ sutil.GoUnref(uint64(discoveryHandle))
+}
+
+// Exports the discovery advertise API to CGO using JSON to marshal ads
+//export swift_io_v_v23_discovery_advertise
+func swift_io_v_v23_discovery_advertise(ctxHandle C.GoContextHandle, discoveryHandle C.GoDiscoveryHandle, adJson C.SwiftByteArray, visibilityArray C.SwiftCStringArray, asyncId C.AsyncCallbackIdentifier, doneCallback C.SwiftAsyncSuccessCallback, errOut *C.SwiftVError) bool {
+ ctx := scontext.GoContext(uint64(ctxHandle))
+ d := GoDiscoveryT(uint64(discoveryHandle))
+ ad := discovery.Advertisement{}
+ if err := json.Unmarshal(sutil.GoBytesNoCopy(unsafe.Pointer(&adJson)), &ad); err != nil {
+ sutil.ThrowSwiftError(ctx, err, unsafe.Pointer(errOut))
+ return false
+ }
+ var visibility []security.BlessingPattern
+ for _, v := range visibilityArray.toStrings() {
+ visibility = append(visibility, security.BlessingPattern(v))
+ }
+ doneChan, err := d.Advertise(ctx, &ad, visibility)
+ if err != nil {
+ sutil.ThrowSwiftError(ctx, err, unsafe.Pointer(errOut))
+ return false
+ }
+ go func() {
+ <-doneChan
+ C.CallAdvertisingCallback(doneCallback, asyncId)
+ }()
+ return true
+}
+
+// Exports the discovery scan API to CGO using JSON to marshal ads
+//export swift_io_v_v23_discovery_scan
+func swift_io_v_v23_discovery_scan(ctxHandle C.GoContextHandle, discoveryHandle C.GoDiscoveryHandle, query C.SwiftCString, asyncId C.AsyncCallbackIdentifier, callbackBlock C.SwiftAsyncJsonCallback, errOut *C.SwiftVError) bool {
+ ctx := scontext.GoContext(uint64(ctxHandle))
+ d := GoDiscoveryT(uint64(discoveryHandle))
+ goQuery := sutil.GoString(unsafe.Pointer(&query), false)
+ ch, err := d.Scan(ctx, goQuery)
+ if err != nil {
+ sutil.ThrowSwiftError(ctx, err, unsafe.Pointer(errOut))
+ return false
+ }
+ go func() {
+ for update := range ch {
+ data := struct {
+ IsLost bool
+ Ad discovery.Advertisement
+ }{
+ IsLost: update.IsLost(),
+ Ad: update.Advertisement(),
+ }
+ b, err := json.Marshal(data)
+ if err != nil {
+ ctx.Fatal("Unable to JSON serialize discovery update: ", err)
+ }
+ ba := swiftBytesCopy(b)
+ C.CallScanCallback(callbackBlock, asyncId, ba)
+ C.free(ba.data)
+ }
+ C.CallScanCallback(callbackBlock, asyncId, emptySwiftByteArray())
+ }()
+ return true
+}
diff --git a/v23/discovery/util.go b/v23/discovery/util.go
new file mode 100644
index 0000000..ce7470e
--- /dev/null
+++ b/v23/discovery/util.go
@@ -0,0 +1,57 @@
+package discovery
+
+import (
+ "fmt"
+ "unsafe"
+
+ "v.io/v23/discovery"
+ "v.io/x/swift/util"
+)
+
+/*
+#include <string.h> // memcpy
+#include <stdlib.h>
+#import "../../types.h"
+*/
+import "C"
+
+// Converts a native pointer from Swift to a Go Discovery.T. Panics if it can't.
+func GoDiscoveryT(discoveryHandle uint64) discovery.T {
+ valptr := util.GoGetRef(discoveryHandle)
+ if d, ok := valptr.(*discovery.T); ok {
+ return *d
+ } else {
+ panic(fmt.Sprintf("Couldn't get discovery.T from handle with id %d", discoveryHandle))
+ }
+}
+
+func swiftBytesCopy(data []byte) C.SwiftByteArray {
+ var a C.SwiftByteArray
+ a.length = C._GoUint64(len(data))
+ a.data = C.malloc(C.size_t(len(data)))
+ if a.data == nil {
+ panic(fmt.Errorf("Unable to allocate %d bytes", a.length))
+ }
+ C.memmove(a.data, unsafe.Pointer(&data[0]), C.size_t(len(data)))
+ return a
+}
+
+func emptySwiftByteArray() C.SwiftByteArray {
+ var empty C.SwiftByteArray
+ empty.length = 0
+ empty.data = nil
+ return empty
+}
+
+func (s *C.SwiftCString) toString() string {
+ return C.GoStringN(s.data, C.int(s.length))
+}
+
+func (a *C.SwiftCStringArray) toStrings() []string {
+ var cstrs = (*[1 << 30]C.SwiftCString)(unsafe.Pointer(a.data))[:int(a.length):int(a.length)]
+ var ret []string
+ for _, cstr := range cstrs {
+ ret = append(ret, cstr.toString())
+ }
+ return ret
+}
\ No newline at end of file
diff --git a/v23/swift.go b/v23/swift.go
index 201603f..60cbc76 100644
--- a/v23/swift.go
+++ b/v23/swift.go
@@ -8,6 +8,7 @@
import (
_ "v.io/x/swift/v23/context"
+ _ "v.io/x/swift/v23/discovery"
_ "v.io/x/swift/v23/security"
)