v0.7.0-alpha.1 0789956

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 ThreeSmallFields struct {
123			x, y, z float32
124		}
125		var ThreeSmallFieldsFn func(ThreeSmallFields) float32
126		purego.RegisterLibFunc(&ThreeSmallFieldsFn, lib, "ThreeSmallFields")
127		if ret := ThreeSmallFieldsFn(ThreeSmallFields{1, 2, 7}); ret != expectedFloat {
128			t.Fatalf("ThreeSmallFields returned %f wanted %f", ret, expectedFloat)
129		}
130	}
131	{
132		type FloatAndInt struct {
133			x float32
134			y int32
135		}
136		var FloatAndIntFn func(FloatAndInt) float32
137		purego.RegisterLibFunc(&FloatAndIntFn, lib, "FloatAndInt")
138		if ret := FloatAndIntFn(FloatAndInt{3, 7}); ret != expectedFloat {
139			t.Fatalf("FloatAndIntFn returned %f wanted %f", ret, expectedFloat)
140		}
141	}
142	{
143		type DoubleStruct struct {
144			x float64
145		}
146		var DoubleStructFn func(DoubleStruct) float64
147		purego.RegisterLibFunc(&DoubleStructFn, lib, "DoubleStruct")
148		if ret := DoubleStructFn(DoubleStruct{10}); ret != expectedDouble {
149			t.Fatalf("DoubleStruct returned %f wanted %f", ret, expectedDouble)
150		}
151	}
152	{
153		type TwoDoubleStruct struct {
154			x, y float64
155		}
156		var TwoDoubleStructFn func(TwoDoubleStruct) float64
157		purego.RegisterLibFunc(&TwoDoubleStructFn, lib, "TwoDoubleStruct")
158		if ret := TwoDoubleStructFn(TwoDoubleStruct{3, 7}); ret != expectedDouble {
159			t.Fatalf("TwoDoubleStruct returned %f wanted %f", ret, expectedDouble)
160		}
161	}
162	{
163		type TwoDoubleTwoStruct struct {
164			x struct {
165				x, y float64
166			}
167		}
168		var TwoDoubleTwoStructFn func(TwoDoubleTwoStruct) float64
169		purego.RegisterLibFunc(&TwoDoubleTwoStructFn, lib, "TwoDoubleTwoStruct")
170		if ret := TwoDoubleTwoStructFn(TwoDoubleTwoStruct{x: struct{ x, y float64 }{x: 3, y: 7}}); ret != expectedDouble {
171			t.Fatalf("TwoDoubleTwoStruct returned %f wanted %f", ret, expectedDouble)
172		}
173	}
174	{
175		type ThreeDoubleStruct struct {
176			x, y, z float64
177		}
178		var ThreeDoubleStructFn func(ThreeDoubleStruct) float64
179		purego.RegisterLibFunc(&ThreeDoubleStructFn, lib, "ThreeDoubleStruct")
180		if ret := ThreeDoubleStructFn(ThreeDoubleStruct{1, 3, 6}); ret != expectedDouble {
181			t.Fatalf("ThreeDoubleStructFn returned %f wanted %f", ret, expectedDouble)
182		}
183	}
184	{
185		type LargeFloatStruct struct {
186			a, b, c, d, e, f float64
187		}
188		var LargeFloatStructFn func(LargeFloatStruct) float64
189		purego.RegisterLibFunc(&LargeFloatStructFn, lib, "LargeFloatStruct")
190		if ret := LargeFloatStructFn(LargeFloatStruct{1, 2, 3, 4, 5, -5}); ret != expectedDouble {
191			t.Fatalf("LargeFloatStructFn returned %f wanted %f", ret, expectedFloat)
192		}
193		var LargeFloatStructWithRegs func(a, b, c float64, s LargeFloatStruct) float64
194		purego.RegisterLibFunc(&LargeFloatStructWithRegs, lib, "LargeFloatStructWithRegs")
195		if ret := LargeFloatStructWithRegs(1, -1, 0, LargeFloatStruct{1, 2, 3, 4, 5, -5}); ret != expectedDouble {
196			t.Fatalf("LargeFloatStructWithRegs returned %f wanted %f", ret, expectedFloat)
197		}
198	}
199	{
200		type Rect struct {
201			x, y, w, h float64
202		}
203		var RectangleWithRegs func(a, b, c, d, e float64, rect Rect) float64
204		purego.RegisterLibFunc(&RectangleWithRegs, lib, "RectangleWithRegs")
205		if ret := RectangleWithRegs(1, 2, 3, 4, -2, Rect{1, 2, 3, -4}); ret != expectedDouble {
206			t.Fatalf("RectangleWithRegs returned %f wanted %f", ret, expectedDouble)
207		}
208		var RectangleSubtract func(rect Rect) float64
209		purego.RegisterLibFunc(&RectangleSubtract, lib, "RectangleSubtract")
210		if ret := RectangleSubtract(Rect{15, 5, 3, 7}); ret != expectedDouble {
211			t.Fatalf("RectangleSubtract returned %f wanted %f", ret, expectedDouble)
212		}
213		var Rectangle func(rect Rect) float64
214		purego.RegisterLibFunc(&Rectangle, lib, "Rectangle")
215		if ret := Rectangle(Rect{1, 2, 3, 4}); ret != expectedDouble {
216			t.Fatalf("Rectangle returned %f wanted %f", ret, expectedFloat)
217		}
218	}
219	{
220		type FloatArray struct {
221			a [2]float64
222		}
223		var FloatArrayFn func(rect FloatArray) float64
224		purego.RegisterLibFunc(&FloatArrayFn, lib, "FloatArray")
225		if ret := FloatArrayFn(FloatArray{a: [2]float64{3, 7}}); ret != expectedDouble {
226			t.Fatalf("FloatArray returned %f wanted %f", ret, expectedFloat)
227		}
228	}
229	{
230		type UnsignedChar4Bytes struct {
231			a, b, c, d byte
232		}
233		var UnsignedChar4BytesFn func(UnsignedChar4Bytes) uint32
234		purego.RegisterLibFunc(&UnsignedChar4BytesFn, lib, "UnsignedChar4Bytes")
235		if ret := UnsignedChar4BytesFn(UnsignedChar4Bytes{a: 0xDE, b: 0xAD, c: 0xBE, d: 0xEF}); ret != expectedUnsigned {
236			t.Fatalf("UnsignedChar4BytesFn returned %#x wanted %#x", ret, expectedUnsigned)
237		}
238	}
239	{
240		type UnsignedChar4BytesStruct struct {
241			x struct {
242				a byte
243			}
244			y struct {
245				b byte
246			}
247			z struct {
248				c byte
249			}
250			w struct {
251				d byte
252			}
253		}
254		var UnsignedChar4BytesStructFn func(UnsignedChar4BytesStruct) uint32
255		purego.RegisterLibFunc(&UnsignedChar4BytesStructFn, lib, "UnsignedChar4BytesStruct")
256		if ret := UnsignedChar4BytesStructFn(UnsignedChar4BytesStruct{
257			x: struct{ a byte }{a: 0xDE},
258			y: struct{ b byte }{b: 0xAD},
259			z: struct{ c byte }{c: 0xBE},
260			w: struct{ d byte }{d: 0xEF},
261		}); ret != expectedUnsigned {
262			t.Fatalf("UnsignedChar4BytesStructFn returned %#x wanted %#x", ret, expectedUnsigned)
263		}
264	}
265	{
266		type Short struct {
267			a, b, c, d uint16
268		}
269		var ShortFn func(Short) uint64
270		purego.RegisterLibFunc(&ShortFn, lib, "Short")
271		if ret := ShortFn(Short{a: 0xDEAD, b: 0xBEEF, c: 0xCAFE, d: 0xBABE}); ret != expectedLong {
272			t.Fatalf("ShortFn returned %#x wanted %#x", ret, expectedLong)
273		}
274	}
275	{
276		type Int struct {
277			a, b uint32
278		}
279		var IntFn func(Int) uint64
280		purego.RegisterLibFunc(&IntFn, lib, "Int")
281		if ret := IntFn(Int{a: 0xDEADBEEF, b: 0xCAFEBABE}); ret != expectedLong {
282			t.Fatalf("IntFn returned %#x wanted %#x", ret, expectedLong)
283		}
284	}
285	{
286		type Long struct {
287			a uint64
288		}
289		var LongFn func(Long) uint64
290		purego.RegisterLibFunc(&LongFn, lib, "Long")
291		if ret := LongFn(Long{a: 0xDEADBEEFCAFEBABE}); ret != expectedLong {
292			t.Fatalf("LongFn returned %#x wanted %#x", ret, expectedLong)
293		}
294	}
295	{
296		type Char8Bytes struct {
297			a, b, c, d, e, f, g, h int8
298		}
299		var Char8BytesFn func(Char8Bytes) int32
300		purego.RegisterLibFunc(&Char8BytesFn, lib, "Char8Bytes")
301		if ret := Char8BytesFn(Char8Bytes{a: -128, b: 127, c: 3, d: -88, e: -3, f: 34, g: -48, h: -20}); ret != expectedSigned {
302			t.Fatalf("Char8Bytes returned %d wanted %d", ret, expectedSigned)
303		}
304	}
305	{
306		type Odd struct {
307			a, b, c byte
308		}
309		var OddFn func(Odd) int32
310		purego.RegisterLibFunc(&OddFn, lib, "Odd")
311		if ret := OddFn(Odd{a: 12, b: 23, c: 46}); ret != expectedOdd {
312			t.Fatalf("OddFn returned %d wanted %d", ret, expectedOdd)
313		}
314	}
315	{
316		type Char2Short1 struct {
317			a, b byte
318			c    uint16
319		}
320		var Char2Short1s func(Char2Short1) int32
321		purego.RegisterLibFunc(&Char2Short1s, lib, "Char2Short1s")
322		if ret := Char2Short1s(Char2Short1{a: 12, b: 23, c: 46}); ret != expectedOdd {
323			t.Fatalf("Char2Short1s returned %d wanted %d", ret, expectedOdd)
324		}
325	}
326	{
327		type SignedChar2Short1 struct {
328			a, b int8
329			c    int16
330		}
331		var SignedChar2Short1Fn func(SignedChar2Short1) int32
332		purego.RegisterLibFunc(&SignedChar2Short1Fn, lib, "SignedChar2Short1")
333		if ret := SignedChar2Short1Fn(SignedChar2Short1{a: 100, b: -23, c: -200}); ret != expectedSigned {
334			t.Fatalf("SignedChar2Short1Fn returned %d wanted %d", ret, expectedSigned)
335		}
336	}
337	{
338		type Array4UnsignedChars struct {
339			a [4]uint8
340		}
341		var Array4UnsignedCharsFn func(chars Array4UnsignedChars) uint32
342		purego.RegisterLibFunc(&Array4UnsignedCharsFn, lib, "Array4UnsignedChars")
343		if ret := Array4UnsignedCharsFn(Array4UnsignedChars{a: [...]uint8{0xDE, 0xAD, 0xBE, 0xEF}}); ret != expectedUnsigned {
344			t.Fatalf("Array4UnsignedCharsFn returned %#x wanted %#x", ret, expectedUnsigned)
345		}
346	}
347	{
348		type Array3UnsignedChar struct {
349			a [3]uint8
350		}
351		var Array3UnsignedChars func(chars Array3UnsignedChar) uint32
352		purego.RegisterLibFunc(&Array3UnsignedChars, lib, "Array3UnsignedChars")
353		if ret := Array3UnsignedChars(Array3UnsignedChar{a: [...]uint8{0xDE, 0xAD, 0xBE}}); ret != expectedUnsigned {
354			t.Fatalf("Array4UnsignedCharsFn returned %#x wanted %#x", ret, expectedUnsigned)
355		}
356	}
357	{
358		type Array2UnsignedShort struct {
359			a [2]uint16
360		}
361		var Array2UnsignedShorts func(chars Array2UnsignedShort) uint32
362		purego.RegisterLibFunc(&Array2UnsignedShorts, lib, "Array2UnsignedShorts")
363		if ret := Array2UnsignedShorts(Array2UnsignedShort{a: [...]uint16{0xDEAD, 0xBEEF}}); ret != expectedUnsigned {
364			t.Fatalf("Array4UnsignedCharsFn returned %#x wanted %#x", ret, expectedUnsigned)
365		}
366	}
367	{
368		type Array4Chars struct {
369			a [4]int8
370		}
371		var Array4CharsFn func(chars Array4Chars) int32
372		purego.RegisterLibFunc(&Array4CharsFn, lib, "Array4Chars")
373		if ret := Array4CharsFn(Array4Chars{a: [...]int8{100, -127, 4, -100}}); ret != expectedSigned {
374			t.Fatalf("Array4CharsFn returned %#x wanted %#x", ret, expectedSigned)
375		}
376	}
377	{
378		type Array2Short struct {
379			a [2]int16
380		}
381		var Array2Shorts func(chars Array2Short) int32
382		purego.RegisterLibFunc(&Array2Shorts, lib, "Array2Shorts")
383		if ret := Array2Shorts(Array2Short{a: [...]int16{-333, 210}}); ret != expectedSigned {
384			t.Fatalf("Array4Shorts returned %#x wanted %#x", ret, expectedSigned)
385		}
386	}
387	{
388		type Array3Short struct {
389			a [3]int16
390		}
391		var Array3Shorts func(chars Array3Short) int32
392		purego.RegisterLibFunc(&Array3Shorts, lib, "Array3Shorts")
393		if ret := Array3Shorts(Array3Short{a: [...]int16{-333, 100, 110}}); ret != expectedSigned {
394			t.Fatalf("Array4Shorts returned %#x wanted %#x", ret, expectedSigned)
395		}
396	}
397	{
398		type BoolStruct struct {
399			b bool
400		}
401		var BoolStructFn func(BoolStruct) bool
402		purego.RegisterLibFunc(&BoolStructFn, lib, "BoolStruct")
403		if ret := BoolStructFn(BoolStruct{true}); ret != true {
404			t.Fatalf("BoolStructFn returned %v wanted %v", ret, true)
405		}
406		if ret := BoolStructFn(BoolStruct{false}); ret != false {
407			t.Fatalf("BoolStructFn returned %v wanted %v", ret, false)
408		}
409	}
410	{
411		type BoolFloat struct {
412			b bool
413			_ [3]byte // purego won't do padding for you so make sure it aligns properly with C struct
414			f float32
415		}
416		var BoolFloatFn func(BoolFloat) float32
417		purego.RegisterLibFunc(&BoolFloatFn, lib, "BoolFloat")
418		if ret := BoolFloatFn(BoolFloat{b: true, f: 10}); ret != expectedFloat {
419			t.Fatalf("BoolFloatFn returned %f wanted %f", ret, expectedFloat)
420		}
421		if ret := BoolFloatFn(BoolFloat{b: false, f: 10}); ret != -expectedFloat {
422			t.Fatalf("BoolFloatFn returned %f wanted %f", ret, -expectedFloat)
423		}
424	}
425	{
426		type point struct{ x, y float64 }
427		type size struct{ width, height float64 }
428		type Content struct {
429			point point
430			size  size
431		}
432		var InitWithContentRect func(*int, Content, int32, int32, bool) uint64
433		purego.RegisterLibFunc(&InitWithContentRect, lib, "InitWithContentRect")
434		if ret := InitWithContentRect(new(int),
435			// These numbers are created so that when added together and then divided by 11 it produces 0xdeadbeef
436			Content{point{x: 41_000_000_000, y: 95_000_000}, size{width: 214_000, height: 149}},
437			15, 4, true); ret != expectedUnsigned {
438			t.Fatalf("InitWithContentRect returned %d wanted %#x", ret, expectedUnsigned)
439		}
440	}
441}

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