diff --git a/cgo.c b/cgo.c new file mode 100644 index 0000000..d50841e --- /dev/null +++ b/cgo.c @@ -0,0 +1,13 @@ +#include "cgo.h" + +int engine_config_err_log(ph7 *pEngine, const char **pzPtr, int *pLen) { + return ph7_config(pEngine, PH7_CONFIG_ERR_LOG, pzPtr, pLen); +} + +int vm_extract_output(ph7_vm *pVm, const char **pzPtr, int *pLen) { + return ph7_vm_config(pVm, PH7_VM_CONFIG_EXTRACT_OUTPUT, pzPtr, pLen); +} + +int vm_set_output_callback(ph7_vm *pVm, void *pUserData) { + return ph7_vm_config(pVm, PH7_VM_CONFIG_OUTPUT, &write_vm_output, pUserData); +} diff --git a/cgo.h b/cgo.h new file mode 100644 index 0000000..cf529a7 --- /dev/null +++ b/cgo.h @@ -0,0 +1,8 @@ +#include +#include "ph7.h" + +int engine_config_err_log(ph7 *pEngine, const char **pzPtr, int *pLen); +int vm_extract_output(ph7_vm *pVm, const char **pzPtr, int *pLen); + +extern int write_vm_output(void*, unsigned int, void*); +int vm_set_output_callback(ph7_vm *pVm, void *pUserData); diff --git a/go.mod b/go.mod index ca422a0..3662698 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,5 @@ module git.codemonkeysoftware.net/b/go-ph7 go 1.19 + +require github.com/mattn/go-pointer v0.0.1 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..1fdefea --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/mattn/go-pointer v0.0.1 h1:n+XhsuGeVO6MEAp7xyEukFINEa+Quek5psIR/ylA6o0= +github.com/mattn/go-pointer v0.0.1/go.mod h1:2zXcozF6qYGgmsG+SeTZz3oAbFLdD3OWqnUbNvJZAlc= diff --git a/ph7.go b/ph7.go index c99a908..efea127 100644 --- a/ph7.go +++ b/ph7.go @@ -1,21 +1,16 @@ package ph7 /* -#include +#include "cgo.h" #include "ph7.h" - -int engine_config_err_log(ph7 *pEngine, const char **pzPtr, int *pLen) { - return ph7_config(pEngine, PH7_CONFIG_ERR_LOG, pzPtr, pLen); -} - -int vm_extract_output(ph7_vm *pVm, const char **pzPtr, int *pLen) { - return ph7_vm_config(pVm, PH7_VM_CONFIG_EXTRACT_OUTPUT, pzPtr, pLen); -} */ import "C" import ( "fmt" "io" + "unsafe" + + pointer "github.com/mattn/go-pointer" ) type ResultCode int @@ -100,17 +95,17 @@ func (e Engine) ErrLog() (string, error) { func (e Engine) Compile(source []byte, phpOnly bool) (*VM, error) { csource := C.CBytes(source) defer C.free(csource) - vm := new(VM) var flags C.int if phpOnly { flags |= C.PH7_PHP_ONLY } - err := newError(C.ph7_compile_v2(e.ptr, (*C.char)(csource), C.int(len(source)), &vm.ptr, flags)) + var ptr *C.ph7_vm + err := newError(C.ph7_compile_v2(e.ptr, (*C.char)(csource), C.int(len(source)), &ptr, flags)) if err != nil { return nil, err } - return vm, nil + return newVM(ptr), nil } func (e Engine) Close() error { @@ -118,14 +113,41 @@ func (e Engine) Close() error { } type VM struct { - ptr *C.ph7_vm + ptr *C.ph7_vm + pointerKey unsafe.Pointer + outputWriter io.Writer + // TODO clear outputWriteError when resetting VM + outputWriteErr error } -func (vm VM) SetOutputWriter(w io.Writer) error { - return nil +func newVM(cvm *C.ph7_vm) *VM { + vm := new(VM) + vm.ptr = cvm + vm.pointerKey = pointer.Save(vm) + return vm } -func (vm VM) ExtractOutput() (string, error) { +//export write_vm_output +func write_vm_output(buf unsafe.Pointer, bufLen C.uint, userData unsafe.Pointer) C.int { + vm := pointer.Restore(userData).(*VM) + gobuf := C.GoBytes(buf, C.int(bufLen)) + _, vm.outputWriteErr = vm.outputWriter.Write(gobuf) + if vm.outputWriteErr != nil { + return C.int(ResultCodeAbort) + } + return C.int(ResultCodeOK) +} + +func (vm *VM) SetOutputWriter(w io.Writer) error { + vm.outputWriter = w + return newError(C.vm_set_output_callback(vm.ptr, vm.pointerKey)) +} + +func (vm *VM) OutputWriteError() error { + return vm.outputWriteErr +} + +func (vm *VM) ExtractOutput() (string, error) { var s *C.char var n C.int @@ -137,10 +159,11 @@ func (vm VM) ExtractOutput() (string, error) { return C.GoStringN(s, n), nil } -func (v VM) Exec() error { - return newError(C.ph7_vm_exec(v.ptr, (*C.int)(nil))) +func (vm *VM) Exec() error { + return newError(C.ph7_vm_exec(vm.ptr, (*C.int)(nil))) } -func (v VM) Close() error { - return newError(C.ph7_vm_release(v.ptr)) +func (vm *VM) Close() error { + pointer.Unref(vm.pointerKey) + return newError(C.ph7_vm_release(vm.ptr)) } diff --git a/ph7_test.go b/ph7_test.go index a1fde66..a180d88 100644 --- a/ph7_test.go +++ b/ph7_test.go @@ -1,6 +1,8 @@ package ph7_test import ( + "bytes" + "fmt" "testing" ph7 "git.codemonkeysoftware.net/b/go-ph7" @@ -59,3 +61,37 @@ func TestCompileError(t *testing.T) { t.Fatalf("expected error message, got nothing") } } + +func TestOutputWriter(t *testing.T) { + const source = `` + var expected bytes.Buffer + for i := 0; i <= 1000; i++ { + _, err := fmt.Fprint(&expected, i) + mustSucceed(t, err) + } + + engine, err := ph7.NewEngine() + mustSucceed(t, err) + defer mustSucceedF(t, engine.Close) + + vm, err := engine.Compile([]byte(source), false) + mustSucceed(t, err) + defer vm.Close() + + var actual bytes.Buffer + err = vm.SetOutputWriter(&actual) + mustSucceed(t, err) + + mustSucceed(t, vm.Exec()) + + if !bytes.Equal(expected.Bytes(), actual.Bytes()) { + t.Fatalf("expected %s, got %s", expected.String(), actual.String()) + } +} + +// TODO Test failing writer, including vm.OutputWriteError +// TODO Test setting writer twice