main 3a10209

syscall_stack_test.go RawHistoryFolder

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: 2026 The Ebitengine Authors
3
4//go:build !windows && !plan9
5
6package purego_test
7
8import (
9	"runtime"
10	"testing"
11	"unsafe"
12
13	"github.com/bnema/purego"
14	"github.com/bnema/purego/internal/load"
15)
16
17//go:noinline
18func growCallbackStack(depth int) {
19	var pad [4096]byte
20	pad[0] = byte(depth)
21	if depth > 0 {
22		growCallbackStack(depth - 1)
23	}
24	runtime.KeepAlive(pad)
25}
26
27func TestSyscall6CallbackStackRelocation(t *testing.T) {
28	library, err := getSystemLibrary()
29	if err != nil {
30		t.Fatal(err)
31	}
32	lib, err := load.OpenLibrary(library)
33	if err != nil {
34		t.Fatal(err)
35	}
36	defer load.CloseLibrary(lib)
37	search, err := load.OpenSymbol(lib, "bsearch")
38	if err != nil {
39		t.Fatal(err)
40	}
41	for _, fast := range []bool{false, true} {
42		name := "NewCallback"
43		if fast {
44			name = "NewCallbackInts"
45		}
46		t.Run(name, func(t *testing.T) {
47			calls := 0
48			compare := func() uintptr { calls++; growCallbackStack(128); return 1 }
49			var cb uintptr
50			if fast {
51				cb = purego.NewCallbackInts(func(*purego.CallbackArgs) uintptr { return compare() })
52			} else {
53				cb = purego.NewCallback(func(_ purego.CDecl, _, _ uintptr) uintptr { return compare() })
54			}
55			// A fresh goroutine starts with a small stack for each callback kind.
56			done := make(chan uintptr, 1)
57			go func() {
58				key, data := new(uintptr), new(uintptr)
59				r, _, _ := purego.Syscall6(search, uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(data)), 1, unsafe.Sizeof(*data), cb, 0)
60				done <- r
61			}()
62			if got := <-done; got != 0 {
63				t.Errorf("bsearch returned %#x, want NULL (key absent)", got)
64			}
65			if calls != 1 {
66				t.Errorf("comparator called %d times, want 1", calls)
67			}
68		})
69	}
70}
71
72func TestSyscall6Allocs(t *testing.T) {
73	library, err := getSystemLibrary()
74	if err != nil {
75		t.Fatal(err)
76	}
77	lib, err := load.OpenLibrary(library)
78	if err != nil {
79		t.Fatal(err)
80	}
81	defer load.CloseLibrary(lib)
82	fn, err := load.OpenSymbol(lib, "abs")
83	if err != nil {
84		t.Fatal(err)
85	}
86	if got := testing.AllocsPerRun(100, func() { purego.Syscall6(fn, 1, 0, 0, 0, 0, 0) }); got != 0 {
87		t.Fatalf("Syscall6 allocations = %v, want 0", got)
88	}
89}

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