mirror of https://github.com/ceph/go-ceph
cutil: add a new internal package for c+go utility functions
Now we have sufficient boilerplate in our code for interacting with various types and ceph calls with similar needs we establish a new internal package, "cutil" (C utilities). Note that many of the return types are wrapped. This is due to the limits placed on us by cgo. Despite the irritating limitations Go places on "exporting" C types it still ought to help in the long run for patterns that are very common or patterns that are subtle and we want to write specific tests for. Signed-off-by: John Mulligan <jmulligan@redhat.com>
This commit is contained in:
parent
d4079e3949
commit
e40744fdf6
|
@ -0,0 +1,62 @@
|
|||
package cutil
|
||||
|
||||
/*
|
||||
#include <stdlib.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// CommandInput can be used to manage the input to ceph's *_command functions.
|
||||
type CommandInput struct {
|
||||
cmd []*C.char
|
||||
inbuf []byte
|
||||
}
|
||||
|
||||
// NewCommandInput creates C-level pointers from go byte buffers suitable
|
||||
// for passing off to ceph's *_command functions.
|
||||
func NewCommandInput(cmd [][]byte, inputBuffer []byte) *CommandInput {
|
||||
ci := &CommandInput{
|
||||
cmd: make([]*C.char, len(cmd)),
|
||||
inbuf: inputBuffer,
|
||||
}
|
||||
for i := range cmd {
|
||||
ci.cmd[i] = C.CString(string(cmd[i]))
|
||||
}
|
||||
return ci
|
||||
}
|
||||
|
||||
// Free any C memory managed by this CommandInput.
|
||||
func (ci *CommandInput) Free() {
|
||||
for i := range ci.cmd {
|
||||
C.free(unsafe.Pointer(ci.cmd[i]))
|
||||
}
|
||||
ci.cmd = nil
|
||||
}
|
||||
|
||||
// Cmd returns an unsafe wrapper around an array of C-strings.
|
||||
func (ci *CommandInput) Cmd() CharPtrPtr {
|
||||
ptr := &ci.cmd[0]
|
||||
return CharPtrPtr(ptr)
|
||||
}
|
||||
|
||||
// CmdLen returns the length of the array of C-strings returned by
|
||||
// Cmd.
|
||||
func (ci *CommandInput) CmdLen() SizeT {
|
||||
return SizeT(len(ci.cmd))
|
||||
}
|
||||
|
||||
// InBuf returns an unsafe wrapper to a C char*.
|
||||
func (ci *CommandInput) InBuf() CharPtr {
|
||||
if len(ci.inbuf) == 0 {
|
||||
return nil
|
||||
}
|
||||
return CharPtr(&ci.inbuf[0])
|
||||
}
|
||||
|
||||
// InBufLen returns the length of the buffer returned by InBuf.
|
||||
func (ci *CommandInput) InBufLen() SizeT {
|
||||
return SizeT(len(ci.inbuf))
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
package cutil
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCommandInput(t *testing.T) {
|
||||
t.Run("newAndFree", func(t *testing.T) {
|
||||
ci := NewCommandInput(
|
||||
[][]byte{[]byte("foobar")},
|
||||
nil)
|
||||
ci.Free()
|
||||
})
|
||||
t.Run("cmd", func(t *testing.T) {
|
||||
ci := NewCommandInput(
|
||||
[][]byte{[]byte("foobar")},
|
||||
nil)
|
||||
defer ci.Free()
|
||||
assert.Len(t, ci.cmd, 1)
|
||||
assert.EqualValues(t, 1, ci.CmdLen())
|
||||
assert.NotNil(t, ci.Cmd())
|
||||
})
|
||||
t.Run("cmd2", func(t *testing.T) {
|
||||
ci := NewCommandInput(
|
||||
[][]byte{[]byte("foobar"), []byte("snarf")},
|
||||
nil)
|
||||
defer ci.Free()
|
||||
assert.Len(t, ci.cmd, 2)
|
||||
assert.EqualValues(t, 2, ci.CmdLen())
|
||||
assert.NotNil(t, ci.Cmd())
|
||||
})
|
||||
t.Run("noInBuf", func(t *testing.T) {
|
||||
ci := NewCommandInput(
|
||||
[][]byte{[]byte("foobar")},
|
||||
nil)
|
||||
defer ci.Free()
|
||||
assert.EqualValues(t, 0, ci.InBufLen())
|
||||
assert.Equal(t, CharPtr(nil), ci.InBuf())
|
||||
})
|
||||
t.Run("hasInBuf", func(t *testing.T) {
|
||||
ci := NewCommandInput(
|
||||
[][]byte{[]byte("foobar")},
|
||||
[]byte("original oregano"))
|
||||
defer ci.Free()
|
||||
assert.EqualValues(t, 16, ci.InBufLen())
|
||||
assert.NotEqual(t, CharPtr(nil), ci.InBuf())
|
||||
})
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
package cutil
|
||||
|
||||
/*
|
||||
#include <stdlib.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// CommandOutput can be used to manage the outputs of ceph's *_command
|
||||
// functions.
|
||||
type CommandOutput struct {
|
||||
outBuf *C.char
|
||||
outBufLen C.size_t
|
||||
outs *C.char
|
||||
outsLen C.size_t
|
||||
}
|
||||
|
||||
// NewCommandOutput returns an empty CommandOutput. The pointers that
|
||||
// a CommandOutput provides can be used to get the results of ceph's
|
||||
// *_command functions.
|
||||
func NewCommandOutput() *CommandOutput {
|
||||
return &CommandOutput{}
|
||||
}
|
||||
|
||||
// Free any C memory tracked by this object.
|
||||
func (co *CommandOutput) Free() {
|
||||
if co.outBuf != nil {
|
||||
C.free(unsafe.Pointer(co.outBuf))
|
||||
}
|
||||
if co.outs != nil {
|
||||
C.free(unsafe.Pointer(co.outs))
|
||||
}
|
||||
}
|
||||
|
||||
// OutBuf returns an unsafe wrapper around a pointer to a `char*`.
|
||||
func (co *CommandOutput) OutBuf() CharPtrPtr {
|
||||
return CharPtrPtr(&co.outBuf)
|
||||
}
|
||||
|
||||
// OutBufLen returns an unsafe wrapper around a pointer to a size_t.
|
||||
func (co *CommandOutput) OutBufLen() SizeTPtr {
|
||||
return SizeTPtr(&co.outBufLen)
|
||||
}
|
||||
|
||||
// Outs returns an unsafe wrapper around a pointer to a `char*`.
|
||||
func (co *CommandOutput) Outs() CharPtrPtr {
|
||||
return CharPtrPtr(&co.outs)
|
||||
}
|
||||
|
||||
// OutsLen returns an unsafe wrapper around a pointer to a size_t.
|
||||
func (co *CommandOutput) OutsLen() SizeTPtr {
|
||||
return SizeTPtr(&co.outsLen)
|
||||
}
|
||||
|
||||
// GoValues returns native go values converted from the internal C-language
|
||||
// values tracked by this object.
|
||||
func (co *CommandOutput) GoValues() (buf []byte, status string) {
|
||||
if co.outBufLen > 0 {
|
||||
buf = C.GoBytes(unsafe.Pointer(co.outBuf), C.int(co.outBufLen))
|
||||
}
|
||||
if co.outsLen > 0 {
|
||||
status = C.GoStringN(co.outs, C.int(co.outsLen))
|
||||
}
|
||||
return buf, status
|
||||
}
|
||||
|
||||
// testSetString is only used by the unit tests for this file.
|
||||
// It is located here due to the restriction on having import "C" in
|
||||
// go test files. :-(
|
||||
// It mimics a C function that takes a pointer to a
|
||||
// string and length and allocates memory and sets the pointers
|
||||
// to the new string and its length.
|
||||
func testSetString(strp CharPtrPtr, lenp SizeTPtr, s string) {
|
||||
sp := (**C.char)(strp)
|
||||
lp := (*C.size_t)(lenp)
|
||||
*sp = C.CString(s)
|
||||
*lp = C.size_t(len(s))
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package cutil
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCommandOutput(t *testing.T) {
|
||||
t.Run("newAndFree", func(t *testing.T) {
|
||||
co := NewCommandOutput()
|
||||
assert.NotNil(t, co)
|
||||
co.Free()
|
||||
})
|
||||
t.Run("setValues", func(t *testing.T) {
|
||||
co := NewCommandOutput()
|
||||
assert.NotNil(t, co)
|
||||
defer co.Free()
|
||||
testSetString(co.OutBuf(), co.OutBufLen(), "i got style")
|
||||
testSetString(co.Outs(), co.OutsLen(), "i got rhythm")
|
||||
b, s := co.GoValues()
|
||||
assert.EqualValues(t, []byte("i got style"), b)
|
||||
assert.EqualValues(t, "i got rhythm", s)
|
||||
})
|
||||
t.Run("setOnlyOutBuf", func(t *testing.T) {
|
||||
co := NewCommandOutput()
|
||||
assert.NotNil(t, co)
|
||||
defer co.Free()
|
||||
testSetString(co.OutBuf(), co.OutBufLen(), "i got style")
|
||||
b, s := co.GoValues()
|
||||
assert.EqualValues(t, []byte("i got style"), b)
|
||||
assert.EqualValues(t, "", s)
|
||||
})
|
||||
t.Run("setOnlyOuts", func(t *testing.T) {
|
||||
co := NewCommandOutput()
|
||||
assert.NotNil(t, co)
|
||||
defer co.Free()
|
||||
testSetString(co.Outs(), co.OutsLen(), "i got rhythm")
|
||||
b, s := co.GoValues()
|
||||
assert.Nil(t, b)
|
||||
assert.EqualValues(t, "i got rhythm", s)
|
||||
})
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package cutil
|
||||
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Basic types from C that we can make "public" without too much fuss.
|
||||
|
||||
// SizeT wraps size_t from C.
|
||||
type SizeT C.size_t
|
||||
|
||||
// This section contains a bunch of types that are basically just
|
||||
// unsafe.Pointer but have specific types to help "self document" what the
|
||||
// underlying pointer is really meant to represent.
|
||||
|
||||
// CharPtrPtr is an unsafe pointer wrapping C's `char**`.
|
||||
type CharPtrPtr unsafe.Pointer
|
||||
|
||||
// CharPtr is an unsafe pointer wrapping C's `char*`.
|
||||
type CharPtr unsafe.Pointer
|
||||
|
||||
// SizeTPtr is an unsafe pointer wrapping C's `size_t*`.
|
||||
type SizeTPtr unsafe.Pointer
|
Loading…
Reference in New Issue