blob: 712ec350b1ef31535106d1972c022cb2784264b4 [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.
// +build darwin,ios
package util
import (
"encoding/base64"
"math"
"time"
"unsafe"
"v.io/x/lib/vlog"
)
/*
#include <string.h> // memcpy
#include <stdlib.h>
#import "../types.h"
*/
import "C"
// Useful when needing to return SOMETHING for a given function that otherwise is throwing an error via the errPtr
func EmptySwiftByteArray() C.SwiftByteArray {
var empty C.SwiftByteArray
empty.length = 0
empty.data = nil
return empty
}
func GoBytesCopy(swiftByteArrayPtr unsafe.Pointer) []byte {
swiftArray := *(*C.SwiftByteArray)(swiftByteArrayPtr)
length := C.int(swiftArray.length)
return C.GoBytes(swiftArray.data, length)
}
func GoBytesNoCopy(swiftByteArrayPtr unsafe.Pointer) []byte {
// Taken from https://github.com/golang/go/wiki/cgo
// "To create a Go slice backed by a C array (without copying the original data),
// one needs to acquire this length at runtime and use a type conversion to a pointer
// to a very big array and then slice it to the length that you want"
swiftArray := *(*C.SwiftByteArray)(swiftByteArrayPtr)
length := int(swiftArray.length)
var bytes []byte = (*[1 << 30]byte)(unsafe.Pointer(swiftArray.data))[:length:length]
return bytes
}
// Utils to convert between Go times and durations and NSTimeInterval in Swift
func NSTimeInterval(t time.Time) C.double {
return C.double(t.UnixNano() / 1000000000.0)
}
func GoTime(t float64) time.Time {
seconds := math.Floor(t)
nsec := (t - seconds) * 1000000000.0
return time.Unix(int64(seconds), int64(nsec))
}
func GoDuration(d float64) time.Duration {
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.
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
}