From a16e4b9a26e5b9a7550aec66bc4fee2f0fe4c6ed Mon Sep 17 00:00:00 2001 From: Virgil Date: Fri, 3 Apr 2026 19:12:25 +0000 Subject: [PATCH] feat(cgo): add RFC string conversion utilities Co-Authored-By: Virgil --- buffer_test.go | 38 +++++++++++++++++++++++++++++++++++++- string_conversion.go | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 string_conversion.go diff --git a/buffer_test.go b/buffer_test.go index d3aa4e4..df084cb 100644 --- a/buffer_test.go +++ b/buffer_test.go @@ -1,6 +1,9 @@ package cgo -import "testing" +import ( + "testing" + "unsafe" +) func TestBufferLifecycleAndCopy(t *testing.T) { t.Parallel() @@ -68,6 +71,39 @@ func TestBufferUseAfterFreePanics(t *testing.T) { }) } +func TestCStringRoundTrip(t *testing.T) { + t.Parallel() + + source := "hello" + cString := CString(source) + defer Free(unsafe.Pointer(cString)) + + converted := GoString(cString) + if converted != source { + t.Fatalf("expected %q, got %q", source, converted) + } +} + +func TestGoStringNilIsEmpty(t *testing.T) { + t.Parallel() + + got := GoString(nil) + if got != "" { + t.Fatalf("expected empty string, got %q", got) + } +} + +func TestFreeNilDoesNotPanic(t *testing.T) { + t.Parallel() + + defer func() { + if r := recover(); r != nil { + t.Fatalf("Free(nil) panicked: %v", r) + } + }() + Free(nil) +} + func assertPanics(t *testing.T, want string, fn func()) { t.Helper() diff --git a/string_conversion.go b/string_conversion.go new file mode 100644 index 0000000..d6761f8 --- /dev/null +++ b/string_conversion.go @@ -0,0 +1,39 @@ +package cgo + +/* +#include +*/ +import "C" + +import "unsafe" + +// GoString converts a null-terminated C string to a Go string. +// +// cStr := C.CString("example") +// result := cgo.GoString(cStr) +// cgo.Free(unsafe.Pointer(cStr)) +func GoString(cs *C.char) string { + if cs == nil { + return "" + } + return C.GoString(cs) +} + +// CString converts a Go string to a C string. +// +// cStr := cgo.CString("hello") +// defer cgo.Free(unsafe.Pointer(cStr)) +func CString(value string) *C.char { + return C.CString(value) +} + +// Free releases memory previously returned by CString. +// +// cStr := cgo.CString("hello") +// cgo.Free(unsafe.Pointer(cStr)) +func Free(ptr unsafe.Pointer) { + if ptr == nil { + return + } + C.free(ptr) +}