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