docs: note buffer reuse for allocation-free Into methods
- commit
- 390876105d824f784964ef893749ddd94a47cd66
- author
- brice · · 2026-10-01 18:51 UTC
- parent
- a0f4c49
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
2 files · +3 -2
M README.md +1 -1
| @@ -23,7 +23,7 @@ Pinned upstream headers and licenses are in `upstream/`; `upstream/METADATA` and | |||
| 23 | 23 | ||
| 24 | 24 | ## Allocation-free hot path | |
| 25 | 25 | ||
| 26 | - | `raw.Funcs` holds symbol addresses resolved once by `Available`; every generated method calls `purego.Syscall6` (or `Syscall15` for more than six arguments) directly, so no reflection or return boxing happens per call. Per-key calls — `State.KeySym`, `State.UTF8Into`, `State.UpdateMask`, `State.Layout`, `State.Mods`, `State.ModIndexActive`, `State.ModNameActive`, `Keymap.KeyRepeats`, `KeysymNameInto` and the `ComposeState` `Feed`/`Status`/`KeySym`/`UTF8Into`/`Reset` methods — perform 0 allocations (asserted by `TestHotPathAllocs`). The string-returning `UTF8` and `KeysymName` still allocate their result. | |
| 26 | + | `raw.Funcs` holds symbol addresses resolved once by `Available`; every generated method calls `purego.Syscall6` (or `Syscall15` for more than six arguments) directly, so no reflection or return boxing happens per call. Per-key calls — `State.KeySym`, `State.UTF8Into`, `State.UpdateMask`, `State.Layout`, `State.Mods`, `State.ModIndexActive`, `State.ModNameActive`, `Keymap.KeyRepeats`, `KeysymNameInto` and the `ComposeState` `Feed`/`Status`/`KeySym`/`UTF8Into`/`Reset` methods — perform 0 allocations (asserted by `TestHotPathAllocs`). The `*Into` methods stay allocation-free only with a long-lived `dst` reused across calls: a buffer passed to C escapes, so a fresh local array costs one allocation per call. The string-returning `UTF8` and `KeysymName` still allocate their result. | |
| 27 | 27 | ||
| 28 | 28 | ||
| 29 | 29 | ## v0.2.0: `raw` API change | |
| @@ -23,7 +23,7 @@ Pinned upstream headers and licenses are in `upstream/`; `upstream/METADATA` and | |||||
| 23 | 23 | ||||
| 24 | ## Allocation-free hot path | 24 | ## Allocation-free hot path | ||
| 25 | 25 | ||||
| 26 | - | `raw.Funcs` holds symbol addresses resolved once by `Available`; every generated method calls `purego.Syscall6` (or `Syscall15` for more than six arguments) directly, so no reflection or return boxing happens per call. Per-key calls — `State.KeySym`, `State.UTF8Into`, `State.UpdateMask`, `State.Layout`, `State.Mods`, `State.ModIndexActive`, `State.ModNameActive`, `Keymap.KeyRepeats`, `KeysymNameInto` and the `ComposeState` `Feed`/`Status`/`KeySym`/`UTF8Into`/`Reset` methods — perform 0 allocations (asserted by `TestHotPathAllocs`). The string-returning `UTF8` and `KeysymName` still allocate their result. | 26 | + | `raw.Funcs` holds symbol addresses resolved once by `Available`; every generated method calls `purego.Syscall6` (or `Syscall15` for more than six arguments) directly, so no reflection or return boxing happens per call. Per-key calls — `State.KeySym`, `State.UTF8Into`, `State.UpdateMask`, `State.Layout`, `State.Mods`, `State.ModIndexActive`, `State.ModNameActive`, `Keymap.KeyRepeats`, `KeysymNameInto` and the `ComposeState` `Feed`/`Status`/`KeySym`/`UTF8Into`/`Reset` methods — perform 0 allocations (asserted by `TestHotPathAllocs`). The `*Into` methods stay allocation-free only with a long-lived `dst` reused across calls: a buffer passed to C escapes, so a fresh local array costs one allocation per call. The string-returning `UTF8` and `KeysymName` still allocate their result. |
| 27 | 27 | ||||
| 28 | 28 | ||||
| 29 | ## v0.2.0: `raw` API change | 29 | ## v0.2.0: `raw` API change | ||
M internal/gen/gen.go +2 -1
| @@ -54,7 +54,8 @@ func constants(h string) string { | |||
| 54 | 54 | } | |
| 55 | 55 | ||
| 56 | 56 | // Functions describes the bound ABI; each name is checked against the pinned headers. | |
| 57 | - | // Pointers are uintptr to avoid GC passing Go pointers to native code unexpectedly. | |
| 57 | + | // Go memory is passed as unsafe.Pointer and converted inside the Syscall call | |
| 58 | + | // expression; native handles (contexts, keymaps, states) are uintptr. | |
| 58 | 59 | var Functions = []struct{ Name, Signature string }{ | |
| 59 | 60 | {"xkb_context_new", "func(uint32) uintptr"}, {"xkb_context_ref", "func(uintptr) uintptr"}, {"xkb_context_unref", "func(uintptr)"}, | |
| 60 | 61 | {"xkb_keymap_new_from_names", "func(uintptr, unsafe.Pointer, uint32) uintptr"}, {"xkb_keymap_new_from_string", "func(uintptr, unsafe.Pointer, uint32, uint32) uintptr"}, {"xkb_keymap_new_from_buffer", "func(uintptr, unsafe.Pointer, uintptr, uint32, uint32) uintptr"}, | |
| @@ -54,7 +54,8 @@ func constants(h string) string { | |||||
| 54 | } | 54 | } | ||
| 55 | 55 | ||||
| 56 | // Functions describes the bound ABI; each name is checked against the pinned headers. | 56 | // Functions describes the bound ABI; each name is checked against the pinned headers. | ||
| 57 | - | // Pointers are uintptr to avoid GC passing Go pointers to native code unexpectedly. | 57 | + | // Go memory is passed as unsafe.Pointer and converted inside the Syscall call |
| 58 | + | // expression; native handles (contexts, keymaps, states) are uintptr. | |||
| 58 | var Functions = []struct{ Name, Signature string }{ | 59 | var Functions = []struct{ Name, Signature string }{ | ||
| 59 | {"xkb_context_new", "func(uint32) uintptr"}, {"xkb_context_ref", "func(uintptr) uintptr"}, {"xkb_context_unref", "func(uintptr)"}, | 60 | {"xkb_context_new", "func(uint32) uintptr"}, {"xkb_context_ref", "func(uintptr) uintptr"}, {"xkb_context_unref", "func(uintptr)"}, | ||
| 60 | {"xkb_keymap_new_from_names", "func(uintptr, unsafe.Pointer, uint32) uintptr"}, {"xkb_keymap_new_from_string", "func(uintptr, unsafe.Pointer, uint32, uint32) uintptr"}, {"xkb_keymap_new_from_buffer", "func(uintptr, unsafe.Pointer, uintptr, uint32, uint32) uintptr"}, | 61 | {"xkb_keymap_new_from_names", "func(uintptr, unsafe.Pointer, uint32) uintptr"}, {"xkb_keymap_new_from_string", "func(uintptr, unsafe.Pointer, uint32, uint32) uintptr"}, {"xkb_keymap_new_from_buffer", "func(uintptr, unsafe.Pointer, uintptr, uint32, uint32) uintptr"}, | ||