main.go raw

   1  package main
   2  
   3  import (
   4  	"fmt"
   5  	"os"
   6  	"path/filepath"
   7  	"runtime"
   8  	"unsafe"
   9  
  10  	"github.com/ebitengine/purego"
  11  )
  12  
  13  const testMXH = `// mxh v1 abc123def456
  14  type Foo struct {
  15      X uint32
  16      Y [20]byte
  17  }
  18  func (Foo) EncodeTo(w io.Writer) error
  19  func (*Foo) DecodeFrom(r io.Reader) error
  20  type Bar struct {
  21      Z int64
  22  }
  23  func (Bar) EncodeTo(w io.Writer) error
  24  `
  25  
  26  func main() {
  27  	if len(os.Args) < 2 {
  28  		fmt.Fprintf(os.Stderr, "usage: %s <path-to-parsemxh.so>\n", os.Args[0])
  29  		os.Exit(1)
  30  	}
  31  	soPath := os.Args[1]
  32  	if !filepath.IsAbs(soPath) {
  33  		wd, _ := os.Getwd()
  34  		soPath = filepath.Join(wd, soPath)
  35  	}
  36  
  37  	lib, err := openLib(soPath)
  38  	if err != nil {
  39  		fmt.Fprintf(os.Stderr, "dlopen %s: %v\n", soPath, err)
  40  		os.Exit(1)
  41  	}
  42  
  43  	var mxhParse func(uintptr, int32) int32
  44  	purego.RegisterLibFunc(&mxhParse, lib, "moxie_mxh_parse")
  45  
  46  	var mxhHash func(int32, uintptr, int32) int32
  47  	purego.RegisterLibFunc(&mxhHash, lib, "moxie_mxh_hash")
  48  
  49  	var mxhTypeCount func(int32) int32
  50  	purego.RegisterLibFunc(&mxhTypeCount, lib, "moxie_mxh_type_count")
  51  
  52  	var mxhTypeName func(int32, int32, uintptr, int32) int32
  53  	purego.RegisterLibFunc(&mxhTypeName, lib, "moxie_mxh_type_name")
  54  
  55  	var mxhTypeFieldCount func(int32, int32) int32
  56  	purego.RegisterLibFunc(&mxhTypeFieldCount, lib, "moxie_mxh_type_field_count")
  57  
  58  	var mxhTypeFieldName func(int32, int32, int32, uintptr, int32) int32
  59  	purego.RegisterLibFunc(&mxhTypeFieldName, lib, "moxie_mxh_type_field_name")
  60  
  61  	var mxhTypeFieldType func(int32, int32, int32, uintptr, int32) int32
  62  	purego.RegisterLibFunc(&mxhTypeFieldType, lib, "moxie_mxh_type_field_type")
  63  
  64  	var mxhTypeHasEncode func(int32, int32) int32
  65  	purego.RegisterLibFunc(&mxhTypeHasEncode, lib, "moxie_mxh_type_has_encode")
  66  
  67  	var mxhTypeHasDecode func(int32, int32) int32
  68  	purego.RegisterLibFunc(&mxhTypeHasDecode, lib, "moxie_mxh_type_has_decode")
  69  
  70  	var mxhFree func(int32)
  71  	purego.RegisterLibFunc(&mxhFree, lib, "moxie_mxh_free")
  72  
  73  	// Parse the test data.
  74  	data := []byte(testMXH)
  75  	handle := mxhParse(uintptr(unsafe.Pointer(&data[0])), int32(len(data)))
  76  	if handle < 0 {
  77  		fmt.Fprintf(os.Stderr, "FAIL: moxie_mxh_parse returned %d\n", handle)
  78  		os.Exit(1)
  79  	}
  80  	fmt.Printf("PASS: moxie_mxh_parse returned handle %d\n", handle)
  81  
  82  	// Verify hash.
  83  	hash := getString(mxhHash, handle)
  84  	expect(hash, "abc123def456", "hash")
  85  
  86  	// Verify type count.
  87  	tc := mxhTypeCount(handle)
  88  	expectInt(tc, 2, "type_count")
  89  
  90  	// Verify Foo.
  91  	expectStr(mxhTypeName, handle, 0, "Foo", "type[0].name")
  92  	expectInt(mxhTypeFieldCount(handle, 0), 2, "Foo.field_count")
  93  	expectStrF(mxhTypeFieldName, handle, 0, 0, "X", "Foo.field[0].name")
  94  	expectStrF(mxhTypeFieldType, handle, 0, 0, "uint32", "Foo.field[0].type")
  95  	expectStrF(mxhTypeFieldName, handle, 0, 1, "Y", "Foo.field[1].name")
  96  	expectStrF(mxhTypeFieldType, handle, 0, 1, "[20]byte", "Foo.field[1].type")
  97  	expectInt(mxhTypeHasEncode(handle, 0), 1, "Foo.has_encode")
  98  	expectInt(mxhTypeHasDecode(handle, 0), 1, "Foo.has_decode")
  99  
 100  	// Verify Bar.
 101  	expectStr(mxhTypeName, handle, 1, "Bar", "type[1].name")
 102  	expectInt(mxhTypeFieldCount(handle, 1), 1, "Bar.field_count")
 103  	expectStrF(mxhTypeFieldName, handle, 1, 0, "Z", "Bar.field[0].name")
 104  	expectStrF(mxhTypeFieldType, handle, 1, 0, "int64", "Bar.field[0].type")
 105  	expectInt(mxhTypeHasEncode(handle, 1), 1, "Bar.has_encode")
 106  	expectInt(mxhTypeHasDecode(handle, 1), 0, "Bar.has_decode")
 107  
 108  	// Free the handle.
 109  	mxhFree(handle)
 110  
 111  	// Verify freed handle returns -1.
 112  	if mxhTypeCount(handle) != -1 {
 113  		fmt.Fprintf(os.Stderr, "FAIL: freed handle still accessible\n")
 114  		os.Exit(1)
 115  	}
 116  	fmt.Println("PASS: freed handle returns -1")
 117  
 118  	fmt.Println("\nAll parsemxh roundtrip tests passed.")
 119  }
 120  
 121  func getString(fn func(int32, uintptr, int32) int32, handle int32) string {
 122  	buf := make([]byte, 256)
 123  	n := fn(handle, uintptr(unsafe.Pointer(&buf[0])), int32(len(buf)))
 124  	if n < 0 {
 125  		return ""
 126  	}
 127  	return string(buf[:n])
 128  }
 129  
 130  func expect(got, want, label string) {
 131  	if got != want {
 132  		fmt.Fprintf(os.Stderr, "FAIL: %s = %q, want %q\n", label, got, want)
 133  		os.Exit(1)
 134  	}
 135  	fmt.Printf("PASS: %s = %q\n", label, got)
 136  }
 137  
 138  func expectInt(got, want int32, label string) {
 139  	if got != want {
 140  		fmt.Fprintf(os.Stderr, "FAIL: %s = %d, want %d\n", label, got, want)
 141  		os.Exit(1)
 142  	}
 143  	fmt.Printf("PASS: %s = %d\n", label, got)
 144  }
 145  
 146  func expectStr(fn func(int32, int32, uintptr, int32) int32, handle, idx int32, want, label string) {
 147  	buf := make([]byte, 256)
 148  	n := fn(handle, idx, uintptr(unsafe.Pointer(&buf[0])), int32(len(buf)))
 149  	got := ""
 150  	if n >= 0 {
 151  		got = string(buf[:n])
 152  	}
 153  	expect(got, want, label)
 154  }
 155  
 156  func expectStrF(fn func(int32, int32, int32, uintptr, int32) int32, handle, tidx, fidx int32, want, label string) {
 157  	buf := make([]byte, 256)
 158  	n := fn(handle, tidx, fidx, uintptr(unsafe.Pointer(&buf[0])), int32(len(buf)))
 159  	got := ""
 160  	if n >= 0 {
 161  		got = string(buf[:n])
 162  	}
 163  	expect(got, want, label)
 164  }
 165  
 166  func openLib(path string) (uintptr, error) {
 167  	switch runtime.GOOS {
 168  	case "linux", "darwin":
 169  		return purego.Dlopen(path, purego.RTLD_NOW|purego.RTLD_GLOBAL)
 170  	default:
 171  		return 0, fmt.Errorf("unsupported OS: %s", runtime.GOOS)
 172  	}
 173  }
 174