detached ee17818

GIR: Extract `ByteSlice` and `GoStringSlice` from `purego` and use them instead of our custom conversion function when generating `[]string` property getters and setters

Signed-off-by: Felicitas Pojtinger <felicitas@pojtinger.com>
commit
ee17818d1377cc2ed124f5fa55d640e566566e8a
author
Felicitas Pojtinger · · 2025-10-31 05:57 UTC
committer
Jeroen Wijenbergh · · 2025-12-01 16:10 UTC
parent
86a6933
signature
unsigned
3 files · +55 -38
M internal/core/core.go +51 -0
@@ -158,6 +158,57 @@ func GetPaths(name string) []string {
158158 panic(fmt.Sprintf("Path for library: %s not found. Please set the path to this library shared object file manually with env variable: %s or PUREGOTK_LIB_FOLDER. Or make sure pkg-config is setup correctly", strings.ToLower(name), ev))
159159 }
160160
161+// hasSuffix tests whether the string s ends with suffix.
162+// This function was copied from purego
163+func hasSuffix(s, suffix string) bool {
164+ return len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix
165+}
166+
167+// ByteSlice creates a pointer to a byte slice of C strings
168+// This function was copied from purego
169+func ByteSlice(name []string) **byte {
170+ if name == nil {
171+ return nil
172+ }
173+ res := make([]*byte, len(name)+1)
174+ for i, v := range name {
175+ res[i] = CString(v)
176+ }
177+
178+ // the last element is NULL terminated for GTK
179+ res[len(name)] = nil
180+ return &res[0]
181+}
182+
183+// CString converts a go string to *byte that can be passed to C code.
184+// This function was copied from purego
185+func CString(name string) *byte {
186+ if hasSuffix(name, "\x00") {
187+ return &(*(*[]byte)(unsafe.Pointer(&name)))[0]
188+ }
189+ b := make([]byte, len(name)+1)
190+ copy(b, name)
191+ return &b[0]
192+}
193+
194+// GoStringSlice gets a string slice from a char** array
195+// This function was copied from purego
196+func GoStringSlice(c uintptr) []string {
197+ var ret []string
198+ for i := 0; ; i++ {
199+ ptrAddr := c + uintptr(i)*unsafe.Sizeof(uintptr(0))
200+ addr := *(*unsafe.Pointer)(unsafe.Pointer(&ptrAddr))
201+ // We take the address and then dereference it to trick go vet from creating a possible misuse of unsafe.Pointer
202+ ptr := *(*uintptr)(addr)
203+ if ptr == 0 {
204+ break
205+ }
206+ ret = append(ret, GoString(ptr))
207+ }
208+
209+ return ret
210+}
211+
161212 // GoString copies a char* to a Go string.
162213 // This function was copied from purego
163214 func GoString(c uintptr) string {
@@ -158,6 +158,57 @@ func GetPaths(name string) []string {
158 panic(fmt.Sprintf("Path for library: %s not found. Please set the path to this library shared object file manually with env variable: %s or PUREGOTK_LIB_FOLDER. Or make sure pkg-config is setup correctly", strings.ToLower(name), ev))158 panic(fmt.Sprintf("Path for library: %s not found. Please set the path to this library shared object file manually with env variable: %s or PUREGOTK_LIB_FOLDER. Or make sure pkg-config is setup correctly", strings.ToLower(name), ev))
159 }159 }
160 160
161+// hasSuffix tests whether the string s ends with suffix.
162+// This function was copied from purego
163+func hasSuffix(s, suffix string) bool {
164+ return len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix
165+}
166+
167+// ByteSlice creates a pointer to a byte slice of C strings
168+// This function was copied from purego
169+func ByteSlice(name []string) **byte {
170+ if name == nil {
171+ return nil
172+ }
173+ res := make([]*byte, len(name)+1)
174+ for i, v := range name {
175+ res[i] = CString(v)
176+ }
177+
178+ // the last element is NULL terminated for GTK
179+ res[len(name)] = nil
180+ return &res[0]
181+}
182+
183+// CString converts a go string to *byte that can be passed to C code.
184+// This function was copied from purego
185+func CString(name string) *byte {
186+ if hasSuffix(name, "\x00") {
187+ return &(*(*[]byte)(unsafe.Pointer(&name)))[0]
188+ }
189+ b := make([]byte, len(name)+1)
190+ copy(b, name)
191+ return &b[0]
192+}
193+
194+// GoStringSlice gets a string slice from a char** array
195+// This function was copied from purego
196+func GoStringSlice(c uintptr) []string {
197+ var ret []string
198+ for i := 0; ; i++ {
199+ ptrAddr := c + uintptr(i)*unsafe.Sizeof(uintptr(0))
200+ addr := *(*unsafe.Pointer)(unsafe.Pointer(&ptrAddr))
201+ // We take the address and then dereference it to trick go vet from creating a possible misuse of unsafe.Pointer
202+ ptr := *(*uintptr)(addr)
203+ if ptr == 0 {
204+ break
205+ }
206+ ret = append(ret, GoString(ptr))
207+ }
208+
209+ return ret
210+}
211+
161 // GoString copies a char* to a Go string.212 // GoString copies a char* to a Go string.
162 // This function was copied from purego213 // This function was copied from purego
163 func GoString(c uintptr) string {214 func GoString(c uintptr) string {
M internal/gir/util/util.go +2 -38
@@ -236,46 +236,10 @@ func PropertyVectorSet(notGLib bool) string {
236236 }
237237
238238 return prefix + `StrvGetType())
239-
240- cStrBytes := make([][]byte, len(value))
241- cStrings := make([]uintptr, len(value)+1)
242- for i, s := range value {
243- cStrBytes[i] = make([]byte, len(s)+1)
244- copy(cStrBytes[i], s)
245- cStrBytes[i][len(s)] = 0
246- cStrings[i] = uintptr(unsafe.Pointer(&cStrBytes[i][0]))
247- }
248- cStrings[len(value)] = 0
249-
250- v.SetBoxed(uintptr(unsafe.Pointer(&cStrings[0])))`
239+ v.SetBoxed(uintptr(unsafe.Pointer(core.ByteSlice(value))))`
251240 }
252241
253242 // PropertyVectorGet returns the code for getting a vector property value
254243 func PropertyVectorGet() string {
255- return `cStrvPtr := v.GetBoxed()
256- if cStrvPtr == 0 {
257- return nil
258- }
259-
260- var result []string
261- ptr := cStrvPtr
262- for {
263- strPtr := *(*uintptr)(unsafe.Pointer(ptr))
264- if strPtr == 0 {
265- break
266- }
267-
268- var length int
269- for i := 0; ; i++ {
270- if *(*byte)(unsafe.Pointer(strPtr + uintptr(i))) == 0 {
271- length = i
272- break
273- }
274- }
275-
276- bytes := unsafe.Slice((*byte)(unsafe.Pointer(strPtr)), length)
277- result = append(result, string(bytes))
278- ptr += unsafe.Sizeof(uintptr(0))
279- }
280- return result`
244+ return `return core.GoStringSlice(v.GetBoxed())`
281245 }
@@ -236,46 +236,10 @@ func PropertyVectorSet(notGLib bool) string {
236 }236 }
237 237
238 return prefix + `StrvGetType())238 return prefix + `StrvGetType())
239-239+ v.SetBoxed(uintptr(unsafe.Pointer(core.ByteSlice(value))))`
240- cStrBytes := make([][]byte, len(value))
241- cStrings := make([]uintptr, len(value)+1)
242- for i, s := range value {
243- cStrBytes[i] = make([]byte, len(s)+1)
244- copy(cStrBytes[i], s)
245- cStrBytes[i][len(s)] = 0
246- cStrings[i] = uintptr(unsafe.Pointer(&cStrBytes[i][0]))
247- }
248- cStrings[len(value)] = 0
249-
250- v.SetBoxed(uintptr(unsafe.Pointer(&cStrings[0])))`
251 }240 }
252 241
253 // PropertyVectorGet returns the code for getting a vector property value242 // PropertyVectorGet returns the code for getting a vector property value
254 func PropertyVectorGet() string {243 func PropertyVectorGet() string {
255- return `cStrvPtr := v.GetBoxed()244+ return `return core.GoStringSlice(v.GetBoxed())`
256- if cStrvPtr == 0 {
257- return nil
258- }
259-
260- var result []string
261- ptr := cStrvPtr
262- for {
263- strPtr := *(*uintptr)(unsafe.Pointer(ptr))
264- if strPtr == 0 {
265- break
266- }
267-
268- var length int
269- for i := 0; ; i++ {
270- if *(*byte)(unsafe.Pointer(strPtr + uintptr(i))) == 0 {
271- length = i
272- break
273- }
274- }
275-
276- bytes := unsafe.Slice((*byte)(unsafe.Pointer(strPtr)), length)
277- result = append(result, string(bytes))
278- ptr += unsafe.Sizeof(uintptr(0))
279- }
280- return result`
281 }245 }
M pkg/core/core.go +2 -0
@@ -4,6 +4,8 @@ import "github.com/jwijenbergh/puregotk/internal/core"
44
55 var (
66 GetPaths = core.GetPaths
7+ ByteSlice = core.ByteSlice
8+ GoStringSlice = core.GoStringSlice
79 GoString = core.GoString
810 SetPackageName = core.SetPackageName
911 SetSharedLibraries = core.SetSharedLibraries
@@ -4,6 +4,8 @@ import "github.com/jwijenbergh/puregotk/internal/core"
4 4
5 var (5 var (
6 GetPaths = core.GetPaths6 GetPaths = core.GetPaths
7+ ByteSlice = core.ByteSlice
8+ GoStringSlice = core.GoStringSlice
7 GoString = core.GoString9 GoString = core.GoString
8 SetPackageName = core.SetPackageName10 SetPackageName = core.SetPackageName
9 SetSharedLibraries = core.SetSharedLibraries11 SetSharedLibraries = core.SetSharedLibraries

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