Skip to content

Commit

Permalink
Add Strs helper for Go 1.6 cgo pointer passing rules.
Browse files Browse the repository at this point in the history
This new helper creates C strings in the C heap using malloc, unlike
the normal Str helper that uses Go memory.

It's possible to pass it to cgo, for example, for gl.ShaderSource. This
is intended usage:

	csources, free := gl.Strs(source) // source string ends with "\x00"
	gl.ShaderSource(shader, 1, csources, nil)
	free()

Or for multiple parameters:

	csources, free := gl.Strs(sources...) // all strings end with "\x00"
	gl.ShaderSource(shader, len(sources), csources, nil)
	free()

It returns a free func that must be called once the string is no longer
needed to free memory.

Pair programmed implementation of Strs with @slimsag.

Fixes #31.

Generated using changes in go-gl/glow#69.
  • Loading branch information
dmitshur committed Feb 20, 2016
1 parent 3da5c09 commit a18221b
Show file tree
Hide file tree
Showing 13 changed files with 559 additions and 39 deletions.
46 changes: 43 additions & 3 deletions all-core/gl/conversions.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@
package gl

import (
"C"
"fmt"
"log"
"reflect"
"strings"
"unsafe"
)

// #include <stdlib.h>
import "C"

// Ptr takes a slice or pointer (to a singular scalar value or the first
// element of an array or slice) and returns its GL-compatible address.
func Ptr(data interface{}) unsafe.Pointer {
Expand Down Expand Up @@ -51,7 +52,7 @@ func PtrOffset(offset int) unsafe.Pointer {
// must ensure the string is not garbage collected.
func Str(str string) *uint8 {
if !strings.HasSuffix(str, "\x00") {
log.Fatalln("str argument missing null terminator:", str)
panic("str argument missing null terminator: " + str)
}
header := (*reflect.StringHeader)(unsafe.Pointer(&str))
return (*uint8)(unsafe.Pointer(header.Data))
Expand All @@ -62,3 +63,42 @@ func Str(str string) *uint8 {
func GoStr(cstr *uint8) string {
return C.GoString((*C.char)(unsafe.Pointer(cstr)))
}

// Strs takes a list of null-terminated Go strings and return's their C counterpart.
//
// The returned free function must be called once you are done using the strings in
// order to free the memory.
//
// If no strings are provided as a parameter, or if any string is not null-terminated,
// this function will panic.
func Strs(strs ...string) (cstrs **uint8, free func()) {
if len(strs) == 0 {
panic("Strs: expected at least 1 string")
}

// Allocate a contiguous array large enough to hold all the strings' contents.
n := 0
for i := range strs {
if !strings.HasSuffix(strs[i], "\x00") {
panic("Strs: str argument missing null terminator: " + strs[i])
}
n += len(strs[i])
}
data := C.malloc(C.size_t(n))

// Copy all the strings into data.
dataSlice := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
Data: uintptr(data),
Len: n,
Cap: n,
}))
css := make([]*uint8, len(strs)) // Populated with pointers to each string.
offset := 0
for i := range strs {
copy(dataSlice[offset:offset+len(strs[i])], strs[i][:]) // Copy strs[i] into proper data location.
css[i] = (*uint8)(unsafe.Pointer(&dataSlice[offset])) // Set a pointer to it.
offset += len(strs[i])
}

return (**uint8)(&css[0]), func() { C.free(data) }
}
46 changes: 43 additions & 3 deletions v2.1/gl/conversions.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@
package gl

import (
"C"
"fmt"
"log"
"reflect"
"strings"
"unsafe"
)

// #include <stdlib.h>
import "C"

// Ptr takes a slice or pointer (to a singular scalar value or the first
// element of an array or slice) and returns its GL-compatible address.
func Ptr(data interface{}) unsafe.Pointer {
Expand Down Expand Up @@ -51,7 +52,7 @@ func PtrOffset(offset int) unsafe.Pointer {
// must ensure the string is not garbage collected.
func Str(str string) *uint8 {
if !strings.HasSuffix(str, "\x00") {
log.Fatalln("str argument missing null terminator:", str)
panic("str argument missing null terminator: " + str)
}
header := (*reflect.StringHeader)(unsafe.Pointer(&str))
return (*uint8)(unsafe.Pointer(header.Data))
Expand All @@ -62,3 +63,42 @@ func Str(str string) *uint8 {
func GoStr(cstr *uint8) string {
return C.GoString((*C.char)(unsafe.Pointer(cstr)))
}

// Strs takes a list of null-terminated Go strings and return's their C counterpart.
//
// The returned free function must be called once you are done using the strings in
// order to free the memory.
//
// If no strings are provided as a parameter, or if any string is not null-terminated,
// this function will panic.
func Strs(strs ...string) (cstrs **uint8, free func()) {
if len(strs) == 0 {
panic("Strs: expected at least 1 string")
}

// Allocate a contiguous array large enough to hold all the strings' contents.
n := 0
for i := range strs {
if !strings.HasSuffix(strs[i], "\x00") {
panic("Strs: str argument missing null terminator: " + strs[i])
}
n += len(strs[i])
}
data := C.malloc(C.size_t(n))

// Copy all the strings into data.
dataSlice := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
Data: uintptr(data),
Len: n,
Cap: n,
}))
css := make([]*uint8, len(strs)) // Populated with pointers to each string.
offset := 0
for i := range strs {
copy(dataSlice[offset:offset+len(strs[i])], strs[i][:]) // Copy strs[i] into proper data location.
css[i] = (*uint8)(unsafe.Pointer(&dataSlice[offset])) // Set a pointer to it.
offset += len(strs[i])
}

return (**uint8)(&css[0]), func() { C.free(data) }
}
46 changes: 43 additions & 3 deletions v3.1/gles2/conversions.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@
package gles2

import (
"C"
"fmt"
"log"
"reflect"
"strings"
"unsafe"
)

// #include <stdlib.h>
import "C"

// Ptr takes a slice or pointer (to a singular scalar value or the first
// element of an array or slice) and returns its GL-compatible address.
func Ptr(data interface{}) unsafe.Pointer {
Expand Down Expand Up @@ -51,7 +52,7 @@ func PtrOffset(offset int) unsafe.Pointer {
// must ensure the string is not garbage collected.
func Str(str string) *uint8 {
if !strings.HasSuffix(str, "\x00") {
log.Fatalln("str argument missing null terminator:", str)
panic("str argument missing null terminator: " + str)
}
header := (*reflect.StringHeader)(unsafe.Pointer(&str))
return (*uint8)(unsafe.Pointer(header.Data))
Expand All @@ -62,3 +63,42 @@ func Str(str string) *uint8 {
func GoStr(cstr *uint8) string {
return C.GoString((*C.char)(unsafe.Pointer(cstr)))
}

// Strs takes a list of null-terminated Go strings and return's their C counterpart.
//
// The returned free function must be called once you are done using the strings in
// order to free the memory.
//
// If no strings are provided as a parameter, or if any string is not null-terminated,
// this function will panic.
func Strs(strs ...string) (cstrs **uint8, free func()) {
if len(strs) == 0 {
panic("Strs: expected at least 1 string")
}

// Allocate a contiguous array large enough to hold all the strings' contents.
n := 0
for i := range strs {
if !strings.HasSuffix(strs[i], "\x00") {
panic("Strs: str argument missing null terminator: " + strs[i])
}
n += len(strs[i])
}
data := C.malloc(C.size_t(n))

// Copy all the strings into data.
dataSlice := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
Data: uintptr(data),
Len: n,
Cap: n,
}))
css := make([]*uint8, len(strs)) // Populated with pointers to each string.
offset := 0
for i := range strs {
copy(dataSlice[offset:offset+len(strs[i])], strs[i][:]) // Copy strs[i] into proper data location.
css[i] = (*uint8)(unsafe.Pointer(&dataSlice[offset])) // Set a pointer to it.
offset += len(strs[i])
}

return (**uint8)(&css[0]), func() { C.free(data) }
}
46 changes: 43 additions & 3 deletions v3.2-compatibility/gl/conversions.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@
package gl

import (
"C"
"fmt"
"log"
"reflect"
"strings"
"unsafe"
)

// #include <stdlib.h>
import "C"

// Ptr takes a slice or pointer (to a singular scalar value or the first
// element of an array or slice) and returns its GL-compatible address.
func Ptr(data interface{}) unsafe.Pointer {
Expand Down Expand Up @@ -51,7 +52,7 @@ func PtrOffset(offset int) unsafe.Pointer {
// must ensure the string is not garbage collected.
func Str(str string) *uint8 {
if !strings.HasSuffix(str, "\x00") {
log.Fatalln("str argument missing null terminator:", str)
panic("str argument missing null terminator: " + str)
}
header := (*reflect.StringHeader)(unsafe.Pointer(&str))
return (*uint8)(unsafe.Pointer(header.Data))
Expand All @@ -62,3 +63,42 @@ func Str(str string) *uint8 {
func GoStr(cstr *uint8) string {
return C.GoString((*C.char)(unsafe.Pointer(cstr)))
}

// Strs takes a list of null-terminated Go strings and return's their C counterpart.
//
// The returned free function must be called once you are done using the strings in
// order to free the memory.
//
// If no strings are provided as a parameter, or if any string is not null-terminated,
// this function will panic.
func Strs(strs ...string) (cstrs **uint8, free func()) {
if len(strs) == 0 {
panic("Strs: expected at least 1 string")
}

// Allocate a contiguous array large enough to hold all the strings' contents.
n := 0
for i := range strs {
if !strings.HasSuffix(strs[i], "\x00") {
panic("Strs: str argument missing null terminator: " + strs[i])
}
n += len(strs[i])
}
data := C.malloc(C.size_t(n))

// Copy all the strings into data.
dataSlice := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
Data: uintptr(data),
Len: n,
Cap: n,
}))
css := make([]*uint8, len(strs)) // Populated with pointers to each string.
offset := 0
for i := range strs {
copy(dataSlice[offset:offset+len(strs[i])], strs[i][:]) // Copy strs[i] into proper data location.
css[i] = (*uint8)(unsafe.Pointer(&dataSlice[offset])) // Set a pointer to it.
offset += len(strs[i])
}

return (**uint8)(&css[0]), func() { C.free(data) }
}
46 changes: 43 additions & 3 deletions v3.2-core/gl/conversions.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@
package gl

import (
"C"
"fmt"
"log"
"reflect"
"strings"
"unsafe"
)

// #include <stdlib.h>
import "C"

// Ptr takes a slice or pointer (to a singular scalar value or the first
// element of an array or slice) and returns its GL-compatible address.
func Ptr(data interface{}) unsafe.Pointer {
Expand Down Expand Up @@ -51,7 +52,7 @@ func PtrOffset(offset int) unsafe.Pointer {
// must ensure the string is not garbage collected.
func Str(str string) *uint8 {
if !strings.HasSuffix(str, "\x00") {
log.Fatalln("str argument missing null terminator:", str)
panic("str argument missing null terminator: " + str)
}
header := (*reflect.StringHeader)(unsafe.Pointer(&str))
return (*uint8)(unsafe.Pointer(header.Data))
Expand All @@ -62,3 +63,42 @@ func Str(str string) *uint8 {
func GoStr(cstr *uint8) string {
return C.GoString((*C.char)(unsafe.Pointer(cstr)))
}

// Strs takes a list of null-terminated Go strings and return's their C counterpart.
//
// The returned free function must be called once you are done using the strings in
// order to free the memory.
//
// If no strings are provided as a parameter, or if any string is not null-terminated,
// this function will panic.
func Strs(strs ...string) (cstrs **uint8, free func()) {
if len(strs) == 0 {
panic("Strs: expected at least 1 string")
}

// Allocate a contiguous array large enough to hold all the strings' contents.
n := 0
for i := range strs {
if !strings.HasSuffix(strs[i], "\x00") {
panic("Strs: str argument missing null terminator: " + strs[i])
}
n += len(strs[i])
}
data := C.malloc(C.size_t(n))

// Copy all the strings into data.
dataSlice := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
Data: uintptr(data),
Len: n,
Cap: n,
}))
css := make([]*uint8, len(strs)) // Populated with pointers to each string.
offset := 0
for i := range strs {
copy(dataSlice[offset:offset+len(strs[i])], strs[i][:]) // Copy strs[i] into proper data location.
css[i] = (*uint8)(unsafe.Pointer(&dataSlice[offset])) // Set a pointer to it.
offset += len(strs[i])
}

return (**uint8)(&css[0]), func() { C.free(data) }
}
Loading

0 comments on commit a18221b

Please sign in to comment.