func doFinish(call rpc.ClientCall, numResults int) (C.SwiftByteArrayArray, error) { // Have all the results be decoded into *vdl.Value. resultPtrs := make([]interface{}, numResults) for i := 0; i < numResults; i++ { value := new(vdl.Value) resultPtrs[i] = &value } if err := call.Finish(resultPtrs...); err != nil { // Invocation error. return EmptySwiftByteArrayArray(), err } // VOM-encode the results. Note in the future we'll want a pathway where we can get the original VOM results // from finish so we don't end up wasting CPU & memory here. // Prepare the byte array array that can be accessed from Swift via C.malloc vomResultsMemory := C.malloc(C.size_t(numResults * int(C.sizeofSwiftByteArray))) // Make that malloc'd memory available as a slice to Go. vomResultsPtrsHdr := reflect.SliceHeader{ Data: uintptr(vomResultsMemory), Len: numResults, Cap: numResults, } vomResults := *(*[]C.SwiftByteArray)(unsafe.Pointer(&vomResultsPtrsHdr)) // Create the C Struct to return that encapsulates our byte array array var cVomResults C.SwiftByteArrayArray cVomResults.length = C._GoUint64(numResults) cVomResults.data = (*C.SwiftByteArray)(vomResultsMemory) // For each result, VOM encode into a byte array that we stick into the returned struct for i, resultPtr := range resultPtrs { // Remove the pointer from the result. Simply *resultPtr doesn't work // as resultPtr is of type interface{}. result := interface{}(sutil.DerefOrDie(resultPtr)) var vomResult []byte var err error if vomResult, err = vom.Encode(result); err != nil { return EmptySwiftByteArrayArray(), err } cVomResultCopy := C.malloc(C.size_t(len(vomResult))) C.memcpy(cVomResultCopy, unsafe.Pointer(&vomResult[0]), C.size_t(len(vomResult))) var cVomResult C.SwiftByteArray cVomResult.length = C._GoUint64(len(vomResult)) cVomResult.data = cVomResultCopy vomResults[i] = cVomResult } return cVomResults, nil }
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 }
//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. urlEncoded := C.GoString(base64UrlEncoded) maxLength := base64.URLEncoding.DecodedLen(len(urlEncoded)) bytesBacking := C.malloc(C.size_t(maxLength)) if bytesBacking == nil { vlog.Errorf("Unable allocate %v bytes", maxLength) return EmptySwiftByteArray() } var bytes []byte = (*[1 << 30]byte)(unsafe.Pointer(bytesBacking))[:maxLength:maxLength] n, err := base64.URLEncoding.Decode(bytes, []byte(urlEncoded)) if err != nil { vlog.Errorf("Unable to base64 decode string: %v\n", err) C.free(bytesBacking) return EmptySwiftByteArray() } var swiftArray C.SwiftByteArray swiftArray.length = C._GoUint64(n) swiftArray.data = bytesBacking return swiftArray }