Send output to an io.Writer
This commit is contained in:
parent
eb0121bff7
commit
2a880cef4c
13
cgo.c
Normal file
13
cgo.c
Normal file
@ -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);
|
||||||
|
}
|
8
cgo.h
Normal file
8
cgo.h
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
#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);
|
2
go.mod
2
go.mod
@ -1,3 +1,5 @@
|
|||||||
module git.codemonkeysoftware.net/b/go-ph7
|
module git.codemonkeysoftware.net/b/go-ph7
|
||||||
|
|
||||||
go 1.19
|
go 1.19
|
||||||
|
|
||||||
|
require github.com/mattn/go-pointer v0.0.1
|
||||||
|
2
go.sum
Normal file
2
go.sum
Normal file
@ -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=
|
61
ph7.go
61
ph7.go
@ -1,21 +1,16 @@
|
|||||||
package ph7
|
package ph7
|
||||||
|
|
||||||
/*
|
/*
|
||||||
#include <stdlib.h>
|
#include "cgo.h"
|
||||||
#include "ph7.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 "C"
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
pointer "github.com/mattn/go-pointer"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ResultCode int
|
type ResultCode int
|
||||||
@ -100,17 +95,17 @@ func (e Engine) ErrLog() (string, error) {
|
|||||||
func (e Engine) Compile(source []byte, phpOnly bool) (*VM, error) {
|
func (e Engine) Compile(source []byte, phpOnly bool) (*VM, error) {
|
||||||
csource := C.CBytes(source)
|
csource := C.CBytes(source)
|
||||||
defer C.free(csource)
|
defer C.free(csource)
|
||||||
vm := new(VM)
|
|
||||||
var flags C.int
|
var flags C.int
|
||||||
if phpOnly {
|
if phpOnly {
|
||||||
flags |= C.PH7_PHP_ONLY
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return vm, nil
|
return newVM(ptr), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e Engine) Close() error {
|
func (e Engine) Close() error {
|
||||||
@ -119,13 +114,40 @@ func (e Engine) Close() error {
|
|||||||
|
|
||||||
type VM struct {
|
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 {
|
func newVM(cvm *C.ph7_vm) *VM {
|
||||||
return nil
|
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 s *C.char
|
||||||
var n C.int
|
var n C.int
|
||||||
|
|
||||||
@ -137,10 +159,11 @@ func (vm VM) ExtractOutput() (string, error) {
|
|||||||
return C.GoStringN(s, n), nil
|
return C.GoStringN(s, n), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v VM) Exec() error {
|
func (vm *VM) Exec() error {
|
||||||
return newError(C.ph7_vm_exec(v.ptr, (*C.int)(nil)))
|
return newError(C.ph7_vm_exec(vm.ptr, (*C.int)(nil)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v VM) Close() error {
|
func (vm *VM) Close() error {
|
||||||
return newError(C.ph7_vm_release(v.ptr))
|
pointer.Unref(vm.pointerKey)
|
||||||
|
return newError(C.ph7_vm_release(vm.ptr))
|
||||||
}
|
}
|
||||||
|
36
ph7_test.go
36
ph7_test.go
@ -1,6 +1,8 @@
|
|||||||
package ph7_test
|
package ph7_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
ph7 "git.codemonkeysoftware.net/b/go-ph7"
|
ph7 "git.codemonkeysoftware.net/b/go-ph7"
|
||||||
@ -59,3 +61,37 @@ func TestCompileError(t *testing.T) {
|
|||||||
t.Fatalf("expected error message, got nothing")
|
t.Fatalf("expected error message, got nothing")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestOutputWriter(t *testing.T) {
|
||||||
|
const source = `<?php
|
||||||
|
for ($x = 0; $x <= 1000; $x++) {
|
||||||
|
echo $x;
|
||||||
|
}
|
||||||
|
?>`
|
||||||
|
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
|
||||||
|
Loading…
Reference in New Issue
Block a user