github.com/jwijenbergh/purego@v0.0.0-20240126093400-70ff3a61df13/func_test.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // SPDX-FileCopyrightText: 2023 The Ebitengine Authors
     3  
     4  package purego_test
     5  
     6  import (
     7  	"fmt"
     8  	"runtime"
     9  	"testing"
    10  	"unsafe"
    11  
    12  	"github.com/jwijenbergh/purego"
    13  )
    14  
    15  // This is an internal OS-dependent function for getting the handle to a library
    16  //
    17  //go:linkname openLibrary openLibrary
    18  func openLibrary(name string) (uintptr, error)
    19  
    20  func getSystemLibrary() (string, error) {
    21  	switch runtime.GOOS {
    22  	case "darwin":
    23  		return "/usr/lib/libSystem.B.dylib", nil
    24  	case "linux":
    25  		return "libc.so.6", nil
    26  	case "freebsd":
    27  		return "libc.so.7", nil
    28  	case "windows":
    29  		return "ucrtbase.dll", nil
    30  	default:
    31  		return "", fmt.Errorf("GOOS=%s is not supported", runtime.GOOS)
    32  	}
    33  }
    34  
    35  func TestRegisterFunc(t *testing.T) {
    36  	library, err := getSystemLibrary()
    37  	if err != nil {
    38  		t.Fatalf("couldn't get system library: %s", err)
    39  	}
    40  	libc, err := openLibrary(library)
    41  	if err != nil {
    42  		t.Fatalf("failed to dlopen: %s", err)
    43  	}
    44  	var puts func(string)
    45  	purego.RegisterLibFunc(&puts, libc, "puts")
    46  	puts("Calling C from from Go without Cgo!")
    47  }
    48  
    49  func ExampleNewCallback() {
    50  	cb := purego.NewCallback(func(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 int) int {
    51  		fmt.Println(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15)
    52  		return a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + a10 + a11 + a12 + a13 + a14 + a15
    53  	})
    54  
    55  	var fn func(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 int) int
    56  	purego.RegisterFunc(&fn, cb)
    57  
    58  	ret := fn(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)
    59  	fmt.Println(ret)
    60  
    61  	// Output: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
    62  	// 120
    63  }
    64  
    65  func Test_qsort(t *testing.T) {
    66  	library, err := getSystemLibrary()
    67  	if err != nil {
    68  		t.Fatalf("couldn't get system library: %s", err)
    69  	}
    70  	libc, err := openLibrary(library)
    71  	if err != nil {
    72  		t.Fatalf("failed to dlopen: %s", err)
    73  	}
    74  
    75  	data := []int{88, 56, 100, 2, 25}
    76  	sorted := []int{2, 25, 56, 88, 100}
    77  	compare := func(a, b *int) int {
    78  		return *a - *b
    79  	}
    80  	var qsort func(data []int, nitms uintptr, size uintptr, compar func(a, b *int) int)
    81  	purego.RegisterLibFunc(&qsort, libc, "qsort")
    82  	qsort(data, uintptr(len(data)), unsafe.Sizeof(int(0)), compare)
    83  	for i := range data {
    84  		if data[i] != sorted[i] {
    85  			t.Errorf("got %d wanted %d at %d", data[i], sorted[i], i)
    86  		}
    87  	}
    88  }
    89  
    90  func TestRegisterFunc_Floats(t *testing.T) {
    91  	library, err := getSystemLibrary()
    92  	if err != nil {
    93  		t.Fatalf("couldn't get system library: %s", err)
    94  	}
    95  	libc, err := openLibrary(library)
    96  	if err != nil {
    97  		t.Fatalf("failed to dlopen: %s", err)
    98  	}
    99  	{
   100  		var strtof func(arg string) float32
   101  		purego.RegisterLibFunc(&strtof, libc, "strtof")
   102  		const (
   103  			arg = "2"
   104  		)
   105  		got := strtof(arg)
   106  		expected := float32(2)
   107  		if got != expected {
   108  			t.Errorf("strtof failed. got %f but wanted %f", got, expected)
   109  		}
   110  	}
   111  	{
   112  		var strtod func(arg string, ptr **byte) float64
   113  		purego.RegisterLibFunc(&strtod, libc, "strtod")
   114  		const (
   115  			arg = "1"
   116  		)
   117  		got := strtod(arg, nil)
   118  		expected := float64(1)
   119  		if got != expected {
   120  			t.Errorf("strtod failed. got %f but wanted %f", got, expected)
   121  		}
   122  	}
   123  }
   124  
   125  func TestRegisterLibFunc_Bool(t *testing.T) {
   126  	// this callback recreates the state where the return register
   127  	// contains other information but the least significant byte is false
   128  	cbFalse := purego.NewCallback(func() uintptr {
   129  		return 0x7F5948AE9A00
   130  	})
   131  	var runFalse func() bool
   132  	purego.RegisterFunc(&runFalse, cbFalse)
   133  	expected := false
   134  	if got := runFalse(); got != expected {
   135  		t.Errorf("runFalse failed. got %t but wanted %t", got, expected)
   136  	}
   137  }
   138  
   139  func TestCallbackFnPtrDedup(t *testing.T) {
   140  	imp := func() uintptr {
   141  		return 0
   142  	}
   143  
   144  	ref1 := purego.NewCallbackFnPtr(&imp)
   145  	ref2 := purego.NewCallbackFnPtr(&imp)
   146  
   147  	if ref1 != ref2 {
   148  		t.Errorf("deduplicate expected %d to equal %d", ref1, ref2)
   149  	}
   150  }