You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucy.apache.org by ma...@apache.org on 2015/08/03 23:47:42 UTC
[3/7] lucy-clownfish git commit: Add GoToClownfish/ToGo conversion
routines.
Add GoToClownfish/ToGo conversion routines.
Project: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/commit/55d40ac6
Tree: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/tree/55d40ac6
Diff: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/diff/55d40ac6
Branch: refs/heads/master
Commit: 55d40ac67b7a076050c15e652d51f4a5dfc937a6
Parents: 56e4ab9
Author: Marvin Humphrey <ma...@rectangular.com>
Authored: Fri Jul 24 14:23:44 2015 -0700
Committer: Marvin Humphrey <ma...@rectangular.com>
Committed: Fri Jul 31 11:05:42 2015 -0700
----------------------------------------------------------------------
runtime/go/clownfish/clownfish.go | 383 +++++++++++++++++++++++++++-
runtime/go/clownfish/clownfish_test.go | 236 ++++++++++++++++-
2 files changed, 611 insertions(+), 8 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/55d40ac6/runtime/go/clownfish/clownfish.go
----------------------------------------------------------------------
diff --git a/runtime/go/clownfish/clownfish.go b/runtime/go/clownfish/clownfish.go
index 0fb7e32..21eb567 100644
--- a/runtime/go/clownfish/clownfish.go
+++ b/runtime/go/clownfish/clownfish.go
@@ -18,6 +18,8 @@ package clownfish
/*
+#include <limits.h>
+
#include "charmony.h"
#include "Clownfish/Obj.h"
@@ -26,7 +28,9 @@ package clownfish
#include "Clownfish/String.h"
#include "Clownfish/Blob.h"
#include "Clownfish/Hash.h"
+#include "Clownfish/HashIterator.h"
#include "Clownfish/Vector.h"
+#include "Clownfish/Num.h"
#include "Clownfish/Boolean.h"
#include "Clownfish/Util/Memory.h"
#include "Clownfish/Method.h"
@@ -63,6 +67,7 @@ import "C"
import "runtime"
import "unsafe"
import "fmt"
+import "math"
import "sync"
const (
@@ -139,7 +144,315 @@ func (o *ObjIMP) TOPTR() uintptr {
return o.ref
}
+func certifyCF(value interface{}, class *C.cfish_Class) {
+ cfObj, ok := value.(Obj)
+ if ok {
+ if C.cfish_Obj_is_a((*C.cfish_Obj)(unsafe.Pointer(cfObj.TOPTR())), class) {
+ return
+ }
+ }
+ className := StringToGo(unsafe.Pointer(C.CFISH_Class_Get_Name(class)))
+ panic(NewErr(fmt.Sprintf("Can't convert a %T to %s", value, className)))
+}
+
+// Convert a Go type into an incremented Clownfish object. If the supplied
+// object is a Clownfish object wrapped in a Go struct, extract the Clownfish
+// object and incref it before returning its address.
+func GoToClownfish(value interface{}, class unsafe.Pointer, nullable bool) unsafe.Pointer {
+ klass := (*C.cfish_Class)(class)
+
+ // Check for nil values.
+ if value == nil {
+ if nullable {
+ return nil
+ } else if class != nil {
+ className := StringToGo(unsafe.Pointer(C.CFISH_Class_Get_Name(klass)))
+ panic(NewErr("Cannot be nil, must be a valid " + className))
+ } else {
+ panic(NewErr("Cannot be nil"))
+ }
+ }
+
+ // Default to accepting any type.
+ if klass == nil {
+ klass = C.CFISH_OBJ
+ }
+
+ // Convert the value according to its type if possible.
+ var converted unsafe.Pointer
+ switch v := value.(type) {
+ case string:
+ if klass == C.CFISH_STRING || klass == C.CFISH_OBJ {
+ converted = GoToString(value)
+ }
+ case []byte:
+ if klass == C.CFISH_BLOB || klass == C.CFISH_OBJ {
+ converted = GoToBlob(value)
+ }
+ case int:
+ if klass == C.CFISH_INTEGER || klass == C.CFISH_OBJ {
+ converted = GoToInteger(value)
+ }
+ case uint:
+ if klass == C.CFISH_INTEGER || klass == C.CFISH_OBJ {
+ converted = GoToInteger(value)
+ }
+ case uintptr:
+ if klass == C.CFISH_INTEGER || klass == C.CFISH_OBJ {
+ converted = GoToInteger(value)
+ }
+ case int64:
+ if klass == C.CFISH_INTEGER || klass == C.CFISH_OBJ {
+ converted = GoToInteger(value)
+ }
+ case int32:
+ if klass == C.CFISH_INTEGER || klass == C.CFISH_OBJ {
+ converted = GoToInteger(value)
+ }
+ case int16:
+ if klass == C.CFISH_INTEGER || klass == C.CFISH_OBJ {
+ converted = GoToInteger(value)
+ }
+ case int8:
+ if klass == C.CFISH_INTEGER || klass == C.CFISH_OBJ {
+ converted = GoToInteger(value)
+ }
+ case uint64:
+ if klass == C.CFISH_INTEGER || klass == C.CFISH_OBJ {
+ converted = GoToInteger(value)
+ }
+ case uint32:
+ if klass == C.CFISH_INTEGER || klass == C.CFISH_OBJ {
+ converted = GoToInteger(value)
+ }
+ case uint16:
+ if klass == C.CFISH_INTEGER || klass == C.CFISH_OBJ {
+ converted = GoToInteger(value)
+ }
+ case uint8:
+ if klass == C.CFISH_INTEGER || klass == C.CFISH_OBJ {
+ converted = GoToInteger(value)
+ }
+ case float32:
+ if klass == C.CFISH_FLOAT || klass == C.CFISH_OBJ {
+ converted = GoToFloat(value)
+ }
+ case float64:
+ if klass == C.CFISH_FLOAT || klass == C.CFISH_OBJ {
+ converted = GoToFloat(value)
+ }
+ case []interface{}:
+ if klass == C.CFISH_VECTOR || klass == C.CFISH_OBJ {
+ converted = GoToVector(value)
+ }
+ case map[string]interface{}:
+ if klass == C.CFISH_HASH || klass == C.CFISH_OBJ {
+ converted = GoToHash(value)
+ }
+ case Obj:
+ converted = unsafe.Pointer(C.cfish_incref(unsafe.Pointer(v.TOPTR())))
+ }
+
+ // Confirm that we got what we were looking for and return.
+ if converted != nil {
+ if C.cfish_Obj_is_a((*C.cfish_Obj)(converted), klass) {
+ return unsafe.Pointer(C.cfish_incref(converted))
+ }
+ }
+
+ // Report a conversion error.
+ className := StringToGo(unsafe.Pointer(C.CFISH_Class_Get_Name(klass)))
+ panic(NewErr(fmt.Sprintf("Can't convert a %T to %s", value, className)))
+}
+
+func GoToString(value interface{}) unsafe.Pointer {
+ switch v := value.(type) {
+ case string:
+ size := len(v)
+ str := C.CString(v)
+ return unsafe.Pointer(C.cfish_Str_new_steal_utf8(str, C.size_t(size)))
+ case Obj:
+ certifyCF(v, C.CFISH_STRING)
+ return unsafe.Pointer(C.cfish_incref(unsafe.Pointer(v.TOPTR())))
+ default:
+ mess := fmt.Sprintf("Can't convert %T to clownfish.String", v)
+ panic(NewErr(mess))
+ }
+}
+
+func GoToBlob(value interface{}) unsafe.Pointer {
+ switch v := value.(type) {
+ case []byte:
+ size := C.size_t(len(v))
+ var buf *C.char = nil
+ if size > 0 {
+ buf = ((*C.char)(unsafe.Pointer(&v[0])))
+ }
+ return unsafe.Pointer(C.cfish_Blob_new(buf, size))
+ case Obj:
+ certifyCF(v, C.CFISH_BLOB)
+ return unsafe.Pointer(C.cfish_incref(unsafe.Pointer(v.TOPTR())))
+ default:
+ mess := fmt.Sprintf("Can't convert %T to clownfish.Blob", v)
+ panic(NewErr(mess))
+ }
+}
+
+func GoToInteger(value interface{}) unsafe.Pointer {
+ switch v := value.(type) {
+ case int:
+ return unsafe.Pointer(C.cfish_Int_new(C.int64_t(v)))
+ case uint:
+ if v > math.MaxInt64 {
+ mess := fmt.Sprintf("uint value too large: %v", v)
+ panic(NewErr(mess))
+ }
+ return unsafe.Pointer(C.cfish_Int_new(C.int64_t(v)))
+ case uintptr:
+ if v > math.MaxInt64 {
+ mess := fmt.Sprintf("uintptr value too large: %v", v)
+ panic(NewErr(mess))
+ }
+ return unsafe.Pointer(C.cfish_Int_new(C.int64_t(v)))
+ case uint64:
+ if v > math.MaxInt64 {
+ mess := fmt.Sprintf("uint64 value too large: %v", v)
+ panic(NewErr(mess))
+ }
+ return unsafe.Pointer(C.cfish_Int_new(C.int64_t(v)))
+ case uint32:
+ return unsafe.Pointer(C.cfish_Int_new(C.int64_t(v)))
+ case uint16:
+ return unsafe.Pointer(C.cfish_Int_new(C.int64_t(v)))
+ case uint8:
+ return unsafe.Pointer(C.cfish_Int_new(C.int64_t(v)))
+ case int64:
+ return unsafe.Pointer(C.cfish_Int_new(C.int64_t(v)))
+ case int32:
+ return unsafe.Pointer(C.cfish_Int_new(C.int64_t(v)))
+ case int16:
+ return unsafe.Pointer(C.cfish_Int_new(C.int64_t(v)))
+ case int8:
+ return unsafe.Pointer(C.cfish_Int_new(C.int64_t(v)))
+ case Obj:
+ certifyCF(v, C.CFISH_INTEGER)
+ return unsafe.Pointer(C.cfish_incref(unsafe.Pointer(v.TOPTR())))
+ default:
+ mess := fmt.Sprintf("Can't convert %T to clownfish.Integer", v)
+ panic(NewErr(mess))
+ }
+}
+
+func GoToFloat(value interface{}) unsafe.Pointer {
+ switch v := value.(type) {
+ case float32:
+ return unsafe.Pointer(C.cfish_Float_new(C.double(v)))
+ case float64:
+ return unsafe.Pointer(C.cfish_Float_new(C.double(v)))
+ case Obj:
+ certifyCF(v, C.CFISH_FLOAT)
+ return unsafe.Pointer(C.cfish_incref(unsafe.Pointer(v.TOPTR())))
+ default:
+ mess := fmt.Sprintf("Can't convert %T to clownfish.Float", v)
+ panic(NewErr(mess))
+ }
+}
+
+func GoToBoolean(value interface{}) unsafe.Pointer {
+ switch v := value.(type) {
+ case bool:
+ if v {
+ return unsafe.Pointer(C.cfish_incref(unsafe.Pointer(C.CFISH_TRUE)))
+ } else {
+ return unsafe.Pointer(C.cfish_incref(unsafe.Pointer(C.CFISH_FALSE)))
+ }
+ case Obj:
+ certifyCF(v, C.CFISH_BOOLEAN)
+ return unsafe.Pointer(C.cfish_incref(unsafe.Pointer(v.TOPTR())))
+ default:
+ mess := fmt.Sprintf("Can't convert %T to clownfish.Boolean", v)
+ panic(NewErr(mess))
+ }
+}
+
+func GoToVector(value interface{}) unsafe.Pointer {
+ switch v := value.(type) {
+ case []interface{}:
+ size := len(v)
+ vec := C.cfish_Vec_new(C.size_t(size))
+ for i := 0; i < size; i++ {
+ elem := GoToClownfish(v[i], nil, true)
+ C.CFISH_Vec_Store(vec, C.size_t(i), (*C.cfish_Obj)(elem))
+ }
+ return unsafe.Pointer(vec)
+ case Obj:
+ certifyCF(v, C.CFISH_VECTOR)
+ return unsafe.Pointer(C.cfish_incref(unsafe.Pointer(v.TOPTR())))
+ default:
+ mess := fmt.Sprintf("Can't convert %T to clownfish.Vector", v)
+ panic(NewErr(mess))
+ }
+}
+
+func GoToHash(value interface{}) unsafe.Pointer {
+ switch v := value.(type) {
+ case map[string]interface{}:
+ size := len(v)
+ hash := C.cfish_Hash_new(C.size_t(size))
+ for key, val := range v {
+ newVal := GoToClownfish(val, nil, true)
+ keySize := len(key)
+ keyStr := C.CString(key)
+ cfKey := C.cfish_Str_new_steal_utf8(keyStr, C.size_t(keySize))
+ defer C.cfish_dec_refcount(unsafe.Pointer(cfKey))
+ C.CFISH_Hash_Store(hash, cfKey, (*C.cfish_Obj)(newVal))
+ }
+ return unsafe.Pointer(hash)
+ case Obj:
+ certifyCF(v, C.CFISH_HASH)
+ return unsafe.Pointer(C.cfish_incref(unsafe.Pointer(v.TOPTR())))
+ default:
+ mess := fmt.Sprintf("Can't convert %T to clownfish.Hash", v)
+ panic(NewErr(mess))
+ }
+}
+
+func ToGo(ptr unsafe.Pointer) interface{} {
+ if ptr == nil {
+ return nil
+ }
+ class := C.cfish_Obj_get_class((*C.cfish_Obj)(ptr))
+ if class == C.CFISH_STRING {
+ return CFStringToGo(ptr)
+ } else if class == C.CFISH_BLOB {
+ return BlobToGo(ptr)
+ } else if class == C.CFISH_VECTOR {
+ return VectorToGo(ptr)
+ } else if class == C.CFISH_HASH {
+ return HashToGo(ptr)
+ } else if class == C.CFISH_BOOLEAN {
+ if ptr == unsafe.Pointer(C.CFISH_TRUE) {
+ return true
+ } else {
+ return false
+ }
+ } else if class == C.CFISH_INTEGER {
+ val := C.CFISH_Int_Get_Value((*C.cfish_Integer)(ptr))
+ return int64(val)
+ } else if class == C.CFISH_FLOAT {
+ val := C.CFISH_Float_Get_Value((*C.cfish_Float)(ptr))
+ return float64(val)
+ } else {
+ // Don't convert to a native Go type, but wrap in a Go struct.
+ return WRAPAny(ptr)
+ }
+}
+
func CFStringToGo(ptr unsafe.Pointer) string {
+ return StringToGo(ptr)
+}
+
+func StringToGo(ptr unsafe.Pointer) string {
cfString := (*C.cfish_String)(ptr)
if cfString == nil {
return ""
@@ -149,13 +462,77 @@ func CFStringToGo(ptr unsafe.Pointer) string {
defer C.cfish_dec_refcount(unsafe.Pointer(cfString))
}
data := C.CFISH_Str_Get_Ptr8(cfString)
- size := C.int(C.CFISH_Str_Get_Size(cfString))
- return C.GoStringN(data, size)
+ size := C.CFISH_Str_Get_Size(cfString)
+ if size > C.size_t(C.INT_MAX) {
+ panic(fmt.Sprintf("Overflow: %d > %d", size, C.INT_MAX))
+ }
+ return C.GoStringN(data, C.int(size))
+}
+
+func BlobToGo(ptr unsafe.Pointer) []byte {
+ blob := (*C.cfish_Blob)(ptr)
+ if blob == nil {
+ return nil
+ }
+ class := C.cfish_Obj_get_class((*C.cfish_Obj)(ptr))
+ if class != C.CFISH_BLOB {
+ mess := "Not a Blob: " + StringToGo(unsafe.Pointer(C.CFISH_Class_Get_Name(class)))
+ panic(NewErr(mess))
+ }
+ data := C.CFISH_Blob_Get_Buf(blob)
+ size := C.CFISH_Blob_Get_Size(blob)
+ if size > C.size_t(C.INT_MAX) {
+ panic(fmt.Sprintf("Overflow: %d > %d", size, C.INT_MAX))
+ }
+ return C.GoBytes(unsafe.Pointer(data), C.int(size))
+}
+
+func VectorToGo(ptr unsafe.Pointer) []interface{} {
+ vec := (*C.cfish_Vector)(ptr)
+ if vec == nil {
+ return nil
+ }
+ class := C.cfish_Obj_get_class((*C.cfish_Obj)(ptr))
+ if class != C.CFISH_VECTOR {
+ mess := "Not a Vector: " + StringToGo(unsafe.Pointer(C.CFISH_Class_Get_Name(class)))
+ panic(NewErr(mess))
+ }
+ size := C.CFISH_Vec_Get_Size(vec)
+ if size > C.size_t(maxInt) {
+ panic(fmt.Sprintf("Overflow: %d > %d", size, maxInt))
+ }
+ slice := make([]interface{}, int(size))
+ for i := 0; i < int(size); i++ {
+ slice[i] = ToGo(unsafe.Pointer(C.CFISH_Vec_Fetch(vec, C.size_t(i))))
+ }
+ return slice
+}
+
+func HashToGo(ptr unsafe.Pointer) map[string]interface{} {
+ hash := (*C.cfish_Hash)(ptr)
+ if hash == nil {
+ return nil
+ }
+ class := C.cfish_Obj_get_class((*C.cfish_Obj)(ptr))
+ if class != C.CFISH_HASH {
+ mess := "Not a Hash: " + StringToGo(unsafe.Pointer(C.CFISH_Class_Get_Name(class)))
+ panic(NewErr(mess))
+ }
+ size := C.CFISH_Hash_Get_Size(hash)
+ m := make(map[string]interface{}, int(size))
+ iter := C.cfish_HashIter_new(hash)
+ defer C.cfish_dec_refcount(unsafe.Pointer(iter))
+ for C.CFISH_HashIter_Next(iter) {
+ key := C.CFISH_HashIter_Get_Key(iter)
+ val := C.CFISH_HashIter_Get_Value(iter)
+ m[StringToGo(unsafe.Pointer(key))] = ToGo(unsafe.Pointer(val))
+ }
+ return m
}
func (e *ErrIMP) Error() string {
mess := C.CFISH_Err_Get_Mess((*C.cfish_Err)(unsafe.Pointer(e.ref)))
- return CFStringToGo(unsafe.Pointer(mess))
+ return StringToGo(unsafe.Pointer(mess))
}
//export GoCfish_PanicErr_internal
http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/55d40ac6/runtime/go/clownfish/clownfish_test.go
----------------------------------------------------------------------
diff --git a/runtime/go/clownfish/clownfish_test.go b/runtime/go/clownfish/clownfish_test.go
index 6103f24..6b31527 100644
--- a/runtime/go/clownfish/clownfish_test.go
+++ b/runtime/go/clownfish/clownfish_test.go
@@ -18,11 +18,237 @@ package clownfish
import "testing"
import "unsafe"
+import "reflect"
+import "math"
-func TestStuff(t *testing.T) {
- cfString := NewString("foo")
- goString := CFStringToGo(unsafe.Pointer(cfString.TOPTR()))
- if goString != "foo" {
- t.Error("Round-tripping strings failed")
+func deepCheck(t *testing.T, got, expected interface{}) {
+ if !reflect.DeepEqual(got, expected) {
+ t.Errorf("Expected %v got %v", expected, got)
}
}
+
+func TestStringToGo(t *testing.T) {
+ strings := []string{"foo", "", "z\u0000z"}
+ for _, val := range strings {
+ got := StringToGo(unsafe.Pointer(NewString(val).TOPTR()))
+ deepCheck(t, got, val)
+ }
+}
+
+func TestBlobToGo(t *testing.T) {
+ strings := []string{"foo", "", "z\u0000z"}
+ for _, str := range strings {
+ expected := []byte(str)
+ got := BlobToGo(unsafe.Pointer(NewBlob(expected).TOPTR()))
+ deepCheck(t, got, expected)
+ }
+}
+
+func TestIntegerToGo(t *testing.T) {
+ values := []int64{math.MaxInt64, math.MinInt64, 0, 1, -1}
+ for _, val := range values {
+ got := ToGo(unsafe.Pointer(NewInteger(val).TOPTR()))
+ deepCheck(t, got, val)
+ }
+}
+
+func TestFloatToGo(t *testing.T) {
+ values := []float64{math.MaxFloat64, math.SmallestNonzeroFloat64,
+ 0.0, -0.0, 0.5, -0.5, math.Inf(1), math.Inf(-1)}
+ for _, val := range values {
+ got := ToGo(unsafe.Pointer(NewFloat(val).TOPTR()))
+ deepCheck(t, got, val)
+ }
+ notNum := ToGo(unsafe.Pointer(NewFloat(math.NaN()).TOPTR()))
+ if !math.IsNaN(notNum.(float64)) {
+ t.Error("Didn't convert NaN cleanly")
+ }
+}
+
+func TestBoolToGo(t *testing.T) {
+ values := []bool{true, false}
+ for _, val := range values {
+ got := ToGo(unsafe.Pointer(NewBoolean(val).TOPTR()))
+ deepCheck(t, got, val)
+ }
+}
+
+func TestVectorToGo(t *testing.T) {
+ vec := NewVector(3)
+ vec.Push(NewString("foo"))
+ vec.Push(NewInteger(42))
+ //vec.Push(nil)
+ inner := NewVector(1)
+ inner.Push(NewString("bar"))
+ vec.Push(inner)
+ expected := []interface{}{
+ "foo",
+ int64(42),
+ //nil,
+ []interface{}{"bar"},
+ }
+ got := VectorToGo(unsafe.Pointer(vec.TOPTR()))
+ deepCheck(t, got, expected)
+}
+
+func TestHashToGo(t *testing.T) {
+ hash := NewHash(0)
+ hash.Store("str", NewString("foo"))
+ hash.Store("bool", NewBoolean(false))
+ hash.Store("float", NewFloat(-0.5))
+ hash.Store("int", NewInteger(42))
+ //hash.Store("nil", nil)
+ vec := NewVector(1)
+ vec.Push(NewString("bar"))
+ hash.Store("vector", vec)
+ got := HashToGo(unsafe.Pointer(hash.TOPTR()))
+
+ expected := map[string]interface{}{
+ "str": "foo",
+ "bool": false,
+ "float": -0.5,
+ "int": int64(42),
+ //"nil": nil,
+ "vector": []interface{}{"bar"},
+ }
+ deepCheck(t, got, expected)
+}
+
+func TestNilToGo(t *testing.T) {
+ got := ToGo(unsafe.Pointer(uintptr(0)))
+ if got != nil {
+ t.Errorf("Expected nil, got %v", got)
+ }
+}
+
+func TestGoToNil(t *testing.T) {
+ if GoToClownfish(nil, nil, true) != nil {
+ t.Error("Convert nullable nil successfully")
+ }
+}
+
+func TestGoToNilNotNullable(t *testing.T) {
+ defer func() { recover() }()
+ GoToClownfish(nil, nil, false) // should panic
+ t.Error("Non-nullable nil should trigger error") // should be unreachable
+}
+
+func TestGoToString(t *testing.T) {
+ strings := []string{"foo", "", "z\u0000z"}
+ for _, val := range strings {
+ got := WRAPAny(GoToString(val))
+ if _, ok := got.(String); !ok {
+ t.Errorf("Not a String, but a %T", got)
+ }
+ if ToGo(unsafe.Pointer(got.TOPTR())).(string) != val {
+ t.Error("Round trip failed")
+ }
+ }
+}
+
+func TestGoToBlob(t *testing.T) {
+ strings := []string{"foo", "", "z\u0000z"}
+ for _, str := range strings {
+ val := []byte(str)
+ got := WRAPAny(GoToBlob(val))
+ if _, ok := got.(Blob); !ok {
+ t.Errorf("Not a Blob, but a %T", got)
+ }
+ if !reflect.DeepEqual(ToGo(unsafe.Pointer(got.TOPTR())), val) {
+ t.Error("Round trip failed")
+ }
+ }
+}
+
+func checkIntConv(t *testing.T, got Obj) {
+ if _, ok := got.(Integer); !ok {
+ t.Errorf("Not an Integer, but a %T", got)
+ }
+}
+
+func TestGoToInteger(t *testing.T) {
+ checkIntConv(t, WRAPAny(GoToClownfish(int(42), nil, true)))
+ checkIntConv(t, WRAPAny(GoToClownfish(uint(42), nil, true)))
+ checkIntConv(t, WRAPAny(GoToClownfish(int64(42), nil, true)))
+ checkIntConv(t, WRAPAny(GoToClownfish(int32(42), nil, true)))
+ checkIntConv(t, WRAPAny(GoToClownfish(int16(42), nil, true)))
+ checkIntConv(t, WRAPAny(GoToClownfish(int8(42), nil, true)))
+ checkIntConv(t, WRAPAny(GoToClownfish(uint64(42), nil, true)))
+ checkIntConv(t, WRAPAny(GoToClownfish(uint32(42), nil, true)))
+ checkIntConv(t, WRAPAny(GoToClownfish(uint16(42), nil, true)))
+ checkIntConv(t, WRAPAny(GoToClownfish(uint8(42), nil, true)))
+ checkIntConv(t, WRAPAny(GoToClownfish(byte(42), nil, true)))
+ checkIntConv(t, WRAPAny(GoToClownfish(rune(42), nil, true)))
+}
+
+func TestGoToIntegerRangeError(t *testing.T) {
+ defer func() { recover() }()
+ GoToClownfish(uint64(math.MaxUint64), nil, true)
+ t.Error("Truncation didn't cause error")
+}
+
+func TestGoToFloat(t *testing.T) {
+ var got Obj
+
+ values := []float64{math.MaxFloat64, math.SmallestNonzeroFloat64,
+ 0.0, -0.0, 0.5, -0.5, math.Inf(1), math.Inf(-1)}
+ for _, val := range values {
+ got := WRAPAny(GoToFloat(val))
+ if _, ok := got.(Float); !ok {
+ t.Errorf("Not a Float, but a %T", got)
+ }
+ if !reflect.DeepEqual(ToGo(unsafe.Pointer(got.TOPTR())), val) {
+ t.Error("Round trip failed")
+ }
+ }
+
+ // NaN
+ got = WRAPAny(GoToFloat(math.NaN()))
+ if !math.IsNaN(ToGo(unsafe.Pointer(got.TOPTR())).(float64)) {
+ t.Error("Didn't convert NaN cleanly")
+ }
+
+ // float32
+ expected := float32(0.5)
+ got = WRAPAny(GoToClownfish(expected, nil, false))
+ deepCheck(t, got.(Float).GetValue(), float64(expected))
+}
+
+func TestGoToBoolean(t *testing.T) {
+ values := []bool{true, false}
+ for _, val := range values {
+ got := WRAPAny(GoToBoolean(val))
+ if _, ok := got.(Boolean); !ok {
+ t.Errorf("Not a Boolean, but a %T", got)
+ }
+ if !reflect.DeepEqual(ToGo(unsafe.Pointer(got.TOPTR())), val) {
+ t.Error("Round trip failed")
+ }
+ }
+}
+
+func TestGoToHash(t *testing.T) {
+ expected := map[string]interface{}{
+ "foo": int64(1),
+ "bar": []interface{}{},
+ }
+ got := WRAPAny(GoToHash(expected))
+ if _, ok := got.(Hash); !ok {
+ t.Errorf("Not a Hash, but a %T", got)
+ }
+ deepCheck(t, ToGo(unsafe.Pointer(got.TOPTR())), expected)
+}
+
+func TestGoToVector(t *testing.T) {
+ expected := []interface{}{
+ "foo",
+ "bar",
+ []interface{}{},
+ int64(-1),
+ }
+ got := WRAPAny(GoToVector(expected))
+ if _, ok := got.(Vector); !ok {
+ t.Errorf("Not a Vector, but a %T", got)
+ }
+ deepCheck(t, ToGo(unsafe.Pointer(got.TOPTR())), expected)
+}