v0.6.0 32fe07e

struct_test.go RawHistoryFolder

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: 2024 The Ebitengine Authors
3
4//go:build darwin && (arm64 || amd64)
5
6package purego_test
7
8import (
9	"os"
10	"path/filepath"
11	"testing"
12
13	"github.com/ebitengine/purego"
14)
15
16func TestRegisterFunc_structArgs(t *testing.T) {
17	libFileName := filepath.Join(t.TempDir(), "structtest.so")
18	t.Logf("Build %v", libFileName)
19
20	if err := buildSharedLib("CC", libFileName, filepath.Join("testdata", "structtest", "struct_test.c")); err != nil {
21		t.Fatal(err)
22	}
23	defer os.Remove(libFileName)
24
25	lib, err := purego.Dlopen(libFileName, purego.RTLD_NOW|purego.RTLD_GLOBAL)
26	if err != nil {
27		t.Fatalf("Dlopen(%q) failed: %v", libFileName, err)
28	}
29
30	const (
31		expectedUnsigned         = 0xdeadbeef
32		expectedSigned           = -123
33		expectedOdd              = 12 + 23 + 46
34		expectedLong     uint64  = 0xdeadbeefcafebabe
35		expectedFloat    float32 = 10
36		expectedDouble   float64 = 10
37	)
38
39	{
40		type Empty struct{}
41		var NoStruct func(Empty) int64
42		purego.RegisterLibFunc(&NoStruct, lib, "NoStruct")
43		if ret := NoStruct(Empty{}); ret != expectedUnsigned {
44			t.Fatalf("NoStruct returned %#x wanted %#x", ret, expectedUnsigned)
45		}
46	}
47	{
48		type EmptyEmpty struct{}
49		var EmptyEmptyFn func(EmptyEmpty) int64
50		purego.RegisterLibFunc(&EmptyEmptyFn, lib, "EmptyEmpty")
51		if ret := EmptyEmptyFn(EmptyEmpty{}); ret != expectedUnsigned {
52			t.Fatalf("EmptyEmpty returned %#x wanted %#x", ret, expectedUnsigned)
53		}
54		var EmptyEmptyWithReg func(uint32, EmptyEmpty, uint32) int64
55		purego.RegisterLibFunc(&EmptyEmptyWithReg, lib, "EmptyEmptyWithReg")
56		if ret := EmptyEmptyWithReg(0xdead, EmptyEmpty{}, 0xbeef); ret != expectedUnsigned {
57			t.Fatalf("EmptyEmptyWithReg returned %#x wanted %#x", ret, expectedUnsigned)
58		}
59	}
60	{
61		type GreaterThan16Bytes struct {
62			x, y, z *int64
63		}
64		var x, y, z int64 = 0xEF, 0xBE00, 0xDEAD0000
65		var GreaterThan16BytesFn func(GreaterThan16Bytes) int64
66		purego.RegisterLibFunc(&GreaterThan16BytesFn, lib, "GreaterThan16Bytes")
67		if ret := GreaterThan16BytesFn(GreaterThan16Bytes{x: &x, y: &y, z: &z}); ret != expectedUnsigned {
68			t.Fatalf("GreaterThan16Bytes returned %#x wanted %#x", ret, expectedUnsigned)
69		}
70	}
71	{
72		type GreaterThan16BytesStruct struct {
73			a struct {
74				x, y, z *int64
75			}
76		}
77		var x, y, z int64 = 0xEF, 0xBE00, 0xDEAD0000
78		var GreaterThan16BytesStructFn func(GreaterThan16BytesStruct) int64
79		purego.RegisterLibFunc(&GreaterThan16BytesStructFn, lib, "GreaterThan16BytesStruct")
80		if ret := GreaterThan16BytesStructFn(GreaterThan16BytesStruct{a: struct{ x, y, z *int64 }{x: &x, y: &y, z: &z}}); ret != expectedUnsigned {
81			t.Fatalf("GreaterThan16BytesStructFn returned %#x wanted %#x", ret, expectedUnsigned)
82		}
83	}
84	{
85		type GreaterThan16Bytes struct {
86			x, y, z *int64
87		}
88		var x, y, z int64 = 0xEF, 0xBE00, 0xDEAD0000
89		var AfterRegisters func(a, b, c, d, e, f, g, h int, bytes GreaterThan16Bytes) int64
90		purego.RegisterLibFunc(&AfterRegisters, lib, "AfterRegisters")
91		if ret := AfterRegisters(0xD0000000, 0xE000000, 0xA00000, 0xD0000, 0xB000, 0xE00, 0xE0, 0xF, GreaterThan16Bytes{x: &x, y: &y, z: &z}); ret != expectedUnsigned {
92			t.Fatalf("AfterRegisters returned %#x wanted %#x", ret, expectedUnsigned)
93		}
94		var BeforeRegisters func(bytes GreaterThan16Bytes, a, b int64) uint64
95		z -= 0xFF
96		purego.RegisterLibFunc(&BeforeRegisters, lib, "BeforeRegisters")
97		if ret := BeforeRegisters(GreaterThan16Bytes{&x, &y, &z}, 0x0F, 0xF0); ret != expectedUnsigned {
98			t.Fatalf("BeforeRegisters returned %#x wanted %#x", ret, expectedUnsigned)
99		}
100	}
101	{
102		type IntLessThan16Bytes struct {
103			x, y int64
104		}
105		var IntLessThan16BytesFn func(bytes IntLessThan16Bytes) int64
106		purego.RegisterLibFunc(&IntLessThan16BytesFn, lib, "IntLessThan16Bytes")
107		if ret := IntLessThan16BytesFn(IntLessThan16Bytes{0xDEAD0000, 0xBEEF}); ret != expectedUnsigned {
108			t.Fatalf("IntLessThan16BytesFn returned %#x wanted %#x", ret, expectedUnsigned)
109		}
110	}
111	{
112		type FloatLessThan16Bytes struct {
113			x, y float32
114		}
115		var FloatLessThan16BytesFn func(FloatLessThan16Bytes) float32
116		purego.RegisterLibFunc(&FloatLessThan16BytesFn, lib, "FloatLessThan16Bytes")
117		if ret := FloatLessThan16BytesFn(FloatLessThan16Bytes{3, 7}); ret != expectedFloat {
118			t.Fatalf("FloatLessThan16Bytes returned %f wanted %f", ret, expectedFloat)
119		}
120	}
121	{
122		type FloatAndInt struct {
123			x float32
124			y int32
125		}
126		var FloatAndIntFn func(FloatAndInt) float32
127		purego.RegisterLibFunc(&FloatAndIntFn, lib, "FloatAndInt")
128		if ret := FloatAndIntFn(FloatAndInt{3, 7}); ret != expectedFloat {
129			t.Fatalf("FloatAndIntFn returned %f wanted %f", ret, expectedFloat)
130		}
131	}
132	{
133		type DoubleStruct struct {
134			x float64
135		}
136		var DoubleStructFn func(DoubleStruct) float64
137		purego.RegisterLibFunc(&DoubleStructFn, lib, "DoubleStruct")
138		if ret := DoubleStructFn(DoubleStruct{10}); ret != expectedDouble {
139			t.Fatalf("DoubleStruct returned %f wanted %f", ret, expectedDouble)
140		}
141	}
142	{
143		type TwoDoubleStruct struct {
144			x, y float64
145		}
146		var TwoDoubleStructFn func(TwoDoubleStruct) float64
147		purego.RegisterLibFunc(&TwoDoubleStructFn, lib, "TwoDoubleStruct")
148		if ret := TwoDoubleStructFn(TwoDoubleStruct{3, 7}); ret != expectedDouble {
149			t.Fatalf("TwoDoubleStruct returned %f wanted %f", ret, expectedDouble)
150		}
151	}
152	{
153		type TwoDoubleTwoStruct struct {
154			x struct {
155				x, y float64
156			}
157		}
158		var TwoDoubleTwoStructFn func(TwoDoubleTwoStruct) float64
159		purego.RegisterLibFunc(&TwoDoubleTwoStructFn, lib, "TwoDoubleTwoStruct")
160		if ret := TwoDoubleTwoStructFn(TwoDoubleTwoStruct{x: struct{ x, y float64 }{x: 3, y: 7}}); ret != expectedDouble {
161			t.Fatalf("TwoDoubleTwoStruct returned %f wanted %f", ret, expectedDouble)
162		}
163	}
164	{
165		type ThreeDoubleStruct struct {
166			x, y, z float64
167		}
168		var ThreeDoubleStructFn func(ThreeDoubleStruct) float64
169		purego.RegisterLibFunc(&ThreeDoubleStructFn, lib, "ThreeDoubleStruct")
170		if ret := ThreeDoubleStructFn(ThreeDoubleStruct{1, 3, 6}); ret != expectedDouble {
171			t.Fatalf("ThreeDoubleStructFn returned %f wanted %f", ret, expectedDouble)
172		}
173	}
174	{
175		type LargeFloatStruct struct {
176			a, b, c, d, e, f float64
177		}
178		var LargeFloatStructFn func(LargeFloatStruct) float64
179		purego.RegisterLibFunc(&LargeFloatStructFn, lib, "LargeFloatStruct")
180		if ret := LargeFloatStructFn(LargeFloatStruct{1, 2, 3, 4, 5, -5}); ret != expectedDouble {
181			t.Fatalf("LargeFloatStructFn returned %f wanted %f", ret, expectedFloat)
182		}
183		var LargeFloatStructWithRegs func(a, b, c float64, s LargeFloatStruct) float64
184		purego.RegisterLibFunc(&LargeFloatStructWithRegs, lib, "LargeFloatStructWithRegs")
185		if ret := LargeFloatStructWithRegs(1, -1, 0, LargeFloatStruct{1, 2, 3, 4, 5, -5}); ret != expectedDouble {
186			t.Fatalf("LargeFloatStructWithRegs returned %f wanted %f", ret, expectedFloat)
187		}
188	}
189	{
190		type Rect struct {
191			x, y, w, h float64
192		}
193		var RectangleWithRegs func(a, b, c, d, e float64, rect Rect) float64
194		purego.RegisterLibFunc(&RectangleWithRegs, lib, "RectangleWithRegs")
195		if ret := RectangleWithRegs(1, 2, 3, 4, -2, Rect{1, 2, 3, -4}); ret != expectedDouble {
196			t.Fatalf("RectangleWithRegs returned %f wanted %f", ret, expectedDouble)
197		}
198		var RectangleSubtract func(rect Rect) float64
199		purego.RegisterLibFunc(&RectangleSubtract, lib, "RectangleSubtract")
200		if ret := RectangleSubtract(Rect{15, 5, 3, 7}); ret != expectedDouble {
201			t.Fatalf("RectangleSubtract returned %f wanted %f", ret, expectedDouble)
202		}
203		var Rectangle func(rect Rect) float64
204		purego.RegisterLibFunc(&Rectangle, lib, "Rectangle")
205		if ret := Rectangle(Rect{1, 2, 3, 4}); ret != expectedDouble {
206			t.Fatalf("Rectangle returned %f wanted %f", ret, expectedFloat)
207		}
208	}
209	{
210		type FloatArray struct {
211			a [2]float64
212		}
213		var FloatArrayFn func(rect FloatArray) float64
214		purego.RegisterLibFunc(&FloatArrayFn, lib, "FloatArray")
215		if ret := FloatArrayFn(FloatArray{a: [2]float64{3, 7}}); ret != expectedDouble {
216			t.Fatalf("FloatArray returned %f wanted %f", ret, expectedFloat)
217		}
218	}
219	{
220		type UnsignedChar4Bytes struct {
221			a, b, c, d byte
222		}
223		var UnsignedChar4BytesFn func(UnsignedChar4Bytes) uint32
224		purego.RegisterLibFunc(&UnsignedChar4BytesFn, lib, "UnsignedChar4Bytes")
225		if ret := UnsignedChar4BytesFn(UnsignedChar4Bytes{a: 0xDE, b: 0xAD, c: 0xBE, d: 0xEF}); ret != expectedUnsigned {
226			t.Fatalf("UnsignedChar4BytesFn returned %#x wanted %#x", ret, expectedUnsigned)
227		}
228	}
229	{
230		type UnsignedChar4BytesStruct struct {
231			x struct {
232				a byte
233			}
234			y struct {
235				b byte
236			}
237			z struct {
238				c byte
239			}
240			w struct {
241				d byte
242			}
243		}
244		var UnsignedChar4BytesStructFn func(UnsignedChar4BytesStruct) uint32
245		purego.RegisterLibFunc(&UnsignedChar4BytesStructFn, lib, "UnsignedChar4BytesStruct")
246		if ret := UnsignedChar4BytesStructFn(UnsignedChar4BytesStruct{
247			x: struct{ a byte }{a: 0xDE},
248			y: struct{ b byte }{b: 0xAD},
249			z: struct{ c byte }{c: 0xBE},
250			w: struct{ d byte }{d: 0xEF},
251		}); ret != expectedUnsigned {
252			t.Fatalf("UnsignedChar4BytesStructFn returned %#x wanted %#x", ret, expectedUnsigned)
253		}
254	}
255	{
256		type Short struct {
257			a, b, c, d uint16
258		}
259		var ShortFn func(Short) uint64
260		purego.RegisterLibFunc(&ShortFn, lib, "Short")
261		if ret := ShortFn(Short{a: 0xDEAD, b: 0xBEEF, c: 0xCAFE, d: 0xBABE}); ret != expectedLong {
262			t.Fatalf("ShortFn returned %#x wanted %#x", ret, expectedLong)
263		}
264	}
265	{
266		type Int struct {
267			a, b uint32
268		}
269		var IntFn func(Int) uint64
270		purego.RegisterLibFunc(&IntFn, lib, "Int")
271		if ret := IntFn(Int{a: 0xDEADBEEF, b: 0xCAFEBABE}); ret != expectedLong {
272			t.Fatalf("IntFn returned %#x wanted %#x", ret, expectedLong)
273		}
274	}
275	{
276		type Long struct {
277			a uint64
278		}
279		var LongFn func(Long) uint64
280		purego.RegisterLibFunc(&LongFn, lib, "Long")
281		if ret := LongFn(Long{a: 0xDEADBEEFCAFEBABE}); ret != expectedLong {
282			t.Fatalf("LongFn returned %#x wanted %#x", ret, expectedLong)
283		}
284	}
285	{
286		type Char8Bytes struct {
287			a, b, c, d, e, f, g, h int8
288		}
289		var Char8BytesFn func(Char8Bytes) int32
290		purego.RegisterLibFunc(&Char8BytesFn, lib, "Char8Bytes")
291		if ret := Char8BytesFn(Char8Bytes{a: -128, b: 127, c: 3, d: -88, e: -3, f: 34, g: -48, h: -20}); ret != expectedSigned {
292			t.Fatalf("Char8Bytes returned %d wanted %d", ret, expectedSigned)
293		}
294	}
295	{
296		type Odd struct {
297			a, b, c byte
298		}
299		var OddFn func(Odd) int32
300		purego.RegisterLibFunc(&OddFn, lib, "Odd")
301		if ret := OddFn(Odd{a: 12, b: 23, c: 46}); ret != expectedOdd {
302			t.Fatalf("OddFn returned %d wanted %d", ret, expectedOdd)
303		}
304	}
305	{
306		type Char2Short1 struct {
307			a, b byte
308			c    uint16
309		}
310		var Char2Short1s func(Char2Short1) int32
311		purego.RegisterLibFunc(&Char2Short1s, lib, "Char2Short1s")
312		if ret := Char2Short1s(Char2Short1{a: 12, b: 23, c: 46}); ret != expectedOdd {
313			t.Fatalf("Char2Short1s returned %d wanted %d", ret, expectedOdd)
314		}
315	}
316	{
317		type SignedChar2Short1 struct {
318			a, b int8
319			c    int16
320		}
321		var SignedChar2Short1Fn func(SignedChar2Short1) int32
322		purego.RegisterLibFunc(&SignedChar2Short1Fn, lib, "SignedChar2Short1")
323		if ret := SignedChar2Short1Fn(SignedChar2Short1{a: 100, b: -23, c: -200}); ret != expectedSigned {
324			t.Fatalf("SignedChar2Short1Fn returned %d wanted %d", ret, expectedSigned)
325		}
326	}
327	{
328		type Array4UnsignedChars struct {
329			a [4]uint8
330		}
331		var Array4UnsignedCharsFn func(chars Array4UnsignedChars) uint32
332		purego.RegisterLibFunc(&Array4UnsignedCharsFn, lib, "Array4UnsignedChars")
333		if ret := Array4UnsignedCharsFn(Array4UnsignedChars{a: [...]uint8{0xDE, 0xAD, 0xBE, 0xEF}}); ret != expectedUnsigned {
334			t.Fatalf("Array4UnsignedCharsFn returned %#x wanted %#x", ret, expectedUnsigned)
335		}
336	}
337	{
338		type Array3UnsignedChar struct {
339			a [3]uint8
340		}
341		var Array3UnsignedChars func(chars Array3UnsignedChar) uint32
342		purego.RegisterLibFunc(&Array3UnsignedChars, lib, "Array3UnsignedChars")
343		if ret := Array3UnsignedChars(Array3UnsignedChar{a: [...]uint8{0xDE, 0xAD, 0xBE}}); ret != expectedUnsigned {
344			t.Fatalf("Array4UnsignedCharsFn returned %#x wanted %#x", ret, expectedUnsigned)
345		}
346	}
347	{
348		type Array2UnsignedShort struct {
349			a [2]uint16
350		}
351		var Array2UnsignedShorts func(chars Array2UnsignedShort) uint32
352		purego.RegisterLibFunc(&Array2UnsignedShorts, lib, "Array2UnsignedShorts")
353		if ret := Array2UnsignedShorts(Array2UnsignedShort{a: [...]uint16{0xDEAD, 0xBEEF}}); ret != expectedUnsigned {
354			t.Fatalf("Array4UnsignedCharsFn returned %#x wanted %#x", ret, expectedUnsigned)
355		}
356	}
357	{
358		type Array4Chars struct {
359			a [4]int8
360		}
361		var Array4CharsFn func(chars Array4Chars) int32
362		purego.RegisterLibFunc(&Array4CharsFn, lib, "Array4Chars")
363		if ret := Array4CharsFn(Array4Chars{a: [...]int8{100, -127, 4, -100}}); ret != expectedSigned {
364			t.Fatalf("Array4CharsFn returned %#x wanted %#x", ret, expectedSigned)
365		}
366	}
367	{
368		type Array2Short struct {
369			a [2]int16
370		}
371		var Array2Shorts func(chars Array2Short) int32
372		purego.RegisterLibFunc(&Array2Shorts, lib, "Array2Shorts")
373		if ret := Array2Shorts(Array2Short{a: [...]int16{-333, 210}}); ret != expectedSigned {
374			t.Fatalf("Array4Shorts returned %#x wanted %#x", ret, expectedSigned)
375		}
376	}
377	{
378		type Array3Short struct {
379			a [3]int16
380		}
381		var Array3Shorts func(chars Array3Short) int32
382		purego.RegisterLibFunc(&Array3Shorts, lib, "Array3Shorts")
383		if ret := Array3Shorts(Array3Short{a: [...]int16{-333, 100, 110}}); ret != expectedSigned {
384			t.Fatalf("Array4Shorts returned %#x wanted %#x", ret, expectedSigned)
385		}
386	}
387	{
388		type BoolStruct struct {
389			b bool
390		}
391		var BoolStructFn func(BoolStruct) bool
392		purego.RegisterLibFunc(&BoolStructFn, lib, "BoolStruct")
393		if ret := BoolStructFn(BoolStruct{true}); ret != true {
394			t.Fatalf("BoolStructFn returned %v wanted %v", ret, true)
395		}
396		if ret := BoolStructFn(BoolStruct{false}); ret != false {
397			t.Fatalf("BoolStructFn returned %v wanted %v", ret, false)
398		}
399	}
400	{
401		type BoolFloat struct {
402			b bool
403			_ [3]byte // purego won't do padding for you so make sure it aligns properly with C struct
404			f float32
405		}
406		var BoolFloatFn func(BoolFloat) float32
407		purego.RegisterLibFunc(&BoolFloatFn, lib, "BoolFloat")
408		if ret := BoolFloatFn(BoolFloat{b: true, f: 10}); ret != expectedFloat {
409			t.Fatalf("BoolFloatFn returned %f wanted %f", ret, expectedFloat)
410		}
411		if ret := BoolFloatFn(BoolFloat{b: false, f: 10}); ret != -expectedFloat {
412			t.Fatalf("BoolFloatFn returned %f wanted %f", ret, -expectedFloat)
413		}
414	}
415	{
416		type point struct{ x, y float64 }
417		type size struct{ width, height float64 }
418		type Content struct {
419			point point
420			size  size
421		}
422		var InitWithContentRect func(*int, Content, int32, int32, bool) uint64
423		purego.RegisterLibFunc(&InitWithContentRect, lib, "InitWithContentRect")
424		if ret := InitWithContentRect(new(int),
425			// These numbers are created so that when added together and then divided by 11 it produces 0xdeadbeef
426			Content{point{x: 41_000_000_000, y: 95_000_000}, size{width: 214_000, height: 149}},
427			15, 4, true); ret != expectedUnsigned {
428			t.Fatalf("InitWithContentRect returned %d wanted %#x", ret, expectedUnsigned)
429		}
430	}
431}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 102

↑↓ moveenter openesc close