diff --git a/all-core/gl/conversions.go b/all-core/gl/conversions.go index 87dd9d5..4c56205 100644 --- a/all-core/gl/conversions.go +++ b/all-core/gl/conversions.go @@ -3,14 +3,15 @@ package gl import ( - "C" "fmt" - "log" "reflect" "strings" "unsafe" ) +// #include +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 { @@ -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)) @@ -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) } +} diff --git a/v2.1/gl/conversions.go b/v2.1/gl/conversions.go index 87dd9d5..4c56205 100644 --- a/v2.1/gl/conversions.go +++ b/v2.1/gl/conversions.go @@ -3,14 +3,15 @@ package gl import ( - "C" "fmt" - "log" "reflect" "strings" "unsafe" ) +// #include +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 { @@ -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)) @@ -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) } +} diff --git a/v3.1/gles2/conversions.go b/v3.1/gles2/conversions.go index c8405f4..d4062db 100644 --- a/v3.1/gles2/conversions.go +++ b/v3.1/gles2/conversions.go @@ -3,14 +3,15 @@ package gles2 import ( - "C" "fmt" - "log" "reflect" "strings" "unsafe" ) +// #include +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 { @@ -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)) @@ -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) } +} diff --git a/v3.2-compatibility/gl/conversions.go b/v3.2-compatibility/gl/conversions.go index 87dd9d5..4c56205 100644 --- a/v3.2-compatibility/gl/conversions.go +++ b/v3.2-compatibility/gl/conversions.go @@ -3,14 +3,15 @@ package gl import ( - "C" "fmt" - "log" "reflect" "strings" "unsafe" ) +// #include +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 { @@ -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)) @@ -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) } +} diff --git a/v3.2-core/gl/conversions.go b/v3.2-core/gl/conversions.go index 87dd9d5..4c56205 100644 --- a/v3.2-core/gl/conversions.go +++ b/v3.2-core/gl/conversions.go @@ -3,14 +3,15 @@ package gl import ( - "C" "fmt" - "log" "reflect" "strings" "unsafe" ) +// #include +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 { @@ -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)) @@ -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) } +} diff --git a/v3.3-compatibility/gl/conversions.go b/v3.3-compatibility/gl/conversions.go index 87dd9d5..4c56205 100644 --- a/v3.3-compatibility/gl/conversions.go +++ b/v3.3-compatibility/gl/conversions.go @@ -3,14 +3,15 @@ package gl import ( - "C" "fmt" - "log" "reflect" "strings" "unsafe" ) +// #include +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 { @@ -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)) @@ -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) } +} diff --git a/v3.3-core/gl/conversions.go b/v3.3-core/gl/conversions.go index 87dd9d5..4c56205 100644 --- a/v3.3-core/gl/conversions.go +++ b/v3.3-core/gl/conversions.go @@ -3,14 +3,15 @@ package gl import ( - "C" "fmt" - "log" "reflect" "strings" "unsafe" ) +// #include +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 { @@ -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)) @@ -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) } +} diff --git a/v4.1-compatibility/gl/conversions.go b/v4.1-compatibility/gl/conversions.go index 87dd9d5..4c56205 100644 --- a/v4.1-compatibility/gl/conversions.go +++ b/v4.1-compatibility/gl/conversions.go @@ -3,14 +3,15 @@ package gl import ( - "C" "fmt" - "log" "reflect" "strings" "unsafe" ) +// #include +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 { @@ -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)) @@ -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) } +} diff --git a/v4.1-core/gl/conversions.go b/v4.1-core/gl/conversions.go index 87dd9d5..4c56205 100644 --- a/v4.1-core/gl/conversions.go +++ b/v4.1-core/gl/conversions.go @@ -3,14 +3,15 @@ package gl import ( - "C" "fmt" - "log" "reflect" "strings" "unsafe" ) +// #include +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 { @@ -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)) @@ -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) } +} diff --git a/v4.4-compatibility/gl/conversions.go b/v4.4-compatibility/gl/conversions.go index 87dd9d5..4c56205 100644 --- a/v4.4-compatibility/gl/conversions.go +++ b/v4.4-compatibility/gl/conversions.go @@ -3,14 +3,15 @@ package gl import ( - "C" "fmt" - "log" "reflect" "strings" "unsafe" ) +// #include +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 { @@ -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)) @@ -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) } +} diff --git a/v4.4-core/gl/conversions.go b/v4.4-core/gl/conversions.go index 87dd9d5..4c56205 100644 --- a/v4.4-core/gl/conversions.go +++ b/v4.4-core/gl/conversions.go @@ -3,14 +3,15 @@ package gl import ( - "C" "fmt" - "log" "reflect" "strings" "unsafe" ) +// #include +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 { @@ -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)) @@ -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) } +} diff --git a/v4.5-compatibility/gl/conversions.go b/v4.5-compatibility/gl/conversions.go index 87dd9d5..4c56205 100644 --- a/v4.5-compatibility/gl/conversions.go +++ b/v4.5-compatibility/gl/conversions.go @@ -3,14 +3,15 @@ package gl import ( - "C" "fmt" - "log" "reflect" "strings" "unsafe" ) +// #include +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 { @@ -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)) @@ -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) } +} diff --git a/v4.5-core/gl/conversions.go b/v4.5-core/gl/conversions.go index 87dd9d5..4c56205 100644 --- a/v4.5-core/gl/conversions.go +++ b/v4.5-core/gl/conversions.go @@ -3,14 +3,15 @@ package gl import ( - "C" "fmt" - "log" "reflect" "strings" "unsafe" ) +// #include +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 { @@ -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)) @@ -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) } +}