Home > Net >  Is there a way to directly copy golang strings to a pre-allocated C char buffer
Is there a way to directly copy golang strings to a pre-allocated C char buffer

Time:10-02

I have a variable (but capped in combined size) number of GoStrings that need to be passed to C, and I want to do it as cheaply as possible. I'm going to perform this operation many times (so pre-allocating buffers that are reused can be considered zero cost).

My initial approach was to loop over the GoStrings, converting each to a CString and pushing it to C.

for _, str := range mystrings {
   cstr := C.CString(str)
   defer C.free(unsafe.Pointer(cstr))
   C.push_str(pushStrFn, cstr)
}

Of course, this is performing N heap allocations thanks to C.CString, as well as N CGo invocations - all of which are not cheap.

Next up was to build a single big string in Go using an allocated-at-the-beginning-of-time strings.Builder, and then pass it to C with some length information in a single CGo call. This is one CString invocation, and one CGo call - a substantial improvement.

builder.Reset()
for _, str := range mystrings {
   builder.WriteString(str)
}
C.push_strs(pushStrsFn, C.CString(builder.String()))

But this approach is still performing an unnecessary copy! Ideally I'd like to pre-allocate a big chunk of memory that I can pass to C, and just copy the strings directly to it without using a big GoString intermediary.

I'm able to pre-allocate a big array ahead of time, and iterate over the characters in the GoStrings, copying them over one at a time. This avoids the intermediate copy, but is substantially slower than a dedicated string-copying function (like that of the builder).

cCharArray := C.malloc(C.size_t(MAX_SIZE) * C.size_t(unsafe.Sizeof(uintptr(0))))
goCharArray := (*[1<<30 - 1]C.char)(cCharArray)
for _, str := range mystrings {
   for i, c := range str {
      goCharArray[offset i] = C.char(c)
   }
}
C.push_charArray(pushCharArrayFn, (*C.char)(cCharArray))

Is there a faster way to do this that I'm missing? Can I somehow feed a C buffer to strings.Builder, or use a string-copying function directly to the C buffer?

CodePudding user response:

Did you try out C.strncpy, or this wrapper over it ? https://github.com/chai2010/cgo/blob/master/char.go#L61 . If this is not faster you could try to cast the pointer to the Go string into a CString pointer using unsafe.

  • Related