a geicko-2 based round robin ranking system designed to test c++ battleship submissions
battleship.dunkirk.sh
1// Copyright (C) 2019 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
2//
3// Use of this source code is governed by an MIT-style
4// license that can be found in the LICENSE file.
5
6package sqlite3
7
8// You can't export a Go function to C and have definitions in the C
9// preamble in the same file, so we have to have callbackTrampoline in
10// its own file. Because we need a separate file anyway, the support
11// code for SQLite custom functions is in here.
12
13/*
14#ifndef USE_LIBSQLITE3
15#include "sqlite3-binding.h"
16#else
17#include <sqlite3.h>
18#endif
19#include <stdlib.h>
20
21void _sqlite3_result_text(sqlite3_context* ctx, const char* s);
22void _sqlite3_result_blob(sqlite3_context* ctx, const void* b, int l);
23*/
24import "C"
25
26import (
27 "errors"
28 "fmt"
29 "math"
30 "reflect"
31 "sync"
32 "unsafe"
33)
34
35//export callbackTrampoline
36func callbackTrampoline(ctx *C.sqlite3_context, argc int, argv **C.sqlite3_value) {
37 args := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.sqlite3_value)(nil))]*C.sqlite3_value)(unsafe.Pointer(argv))[:argc:argc]
38 fi := lookupHandle(C.sqlite3_user_data(ctx)).(*functionInfo)
39 fi.Call(ctx, args)
40}
41
42//export stepTrampoline
43func stepTrampoline(ctx *C.sqlite3_context, argc C.int, argv **C.sqlite3_value) {
44 args := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.sqlite3_value)(nil))]*C.sqlite3_value)(unsafe.Pointer(argv))[:int(argc):int(argc)]
45 ai := lookupHandle(C.sqlite3_user_data(ctx)).(*aggInfo)
46 ai.Step(ctx, args)
47}
48
49//export doneTrampoline
50func doneTrampoline(ctx *C.sqlite3_context) {
51 ai := lookupHandle(C.sqlite3_user_data(ctx)).(*aggInfo)
52 ai.Done(ctx)
53}
54
55//export compareTrampoline
56func compareTrampoline(handlePtr unsafe.Pointer, la C.int, a *C.char, lb C.int, b *C.char) C.int {
57 cmp := lookupHandle(handlePtr).(func(string, string) int)
58 return C.int(cmp(C.GoStringN(a, la), C.GoStringN(b, lb)))
59}
60
61//export commitHookTrampoline
62func commitHookTrampoline(handle unsafe.Pointer) int {
63 callback := lookupHandle(handle).(func() int)
64 return callback()
65}
66
67//export rollbackHookTrampoline
68func rollbackHookTrampoline(handle unsafe.Pointer) {
69 callback := lookupHandle(handle).(func())
70 callback()
71}
72
73//export updateHookTrampoline
74func updateHookTrampoline(handle unsafe.Pointer, op int, db *C.char, table *C.char, rowid int64) {
75 callback := lookupHandle(handle).(func(int, string, string, int64))
76 callback(op, C.GoString(db), C.GoString(table), rowid)
77}
78
79//export authorizerTrampoline
80func authorizerTrampoline(handle unsafe.Pointer, op int, arg1 *C.char, arg2 *C.char, arg3 *C.char) int {
81 callback := lookupHandle(handle).(func(int, string, string, string) int)
82 return callback(op, C.GoString(arg1), C.GoString(arg2), C.GoString(arg3))
83}
84
85//export preUpdateHookTrampoline
86func preUpdateHookTrampoline(handle unsafe.Pointer, dbHandle uintptr, op int, db *C.char, table *C.char, oldrowid int64, newrowid int64) {
87 hval := lookupHandleVal(handle)
88 data := SQLitePreUpdateData{
89 Conn: hval.db,
90 Op: op,
91 DatabaseName: C.GoString(db),
92 TableName: C.GoString(table),
93 OldRowID: oldrowid,
94 NewRowID: newrowid,
95 }
96 callback := hval.val.(func(SQLitePreUpdateData))
97 callback(data)
98}
99
100// Use handles to avoid passing Go pointers to C.
101type handleVal struct {
102 db *SQLiteConn
103 val any
104}
105
106var handleLock sync.Mutex
107var handleVals = make(map[unsafe.Pointer]handleVal)
108
109func newHandle(db *SQLiteConn, v any) unsafe.Pointer {
110 handleLock.Lock()
111 defer handleLock.Unlock()
112 val := handleVal{db: db, val: v}
113 var p unsafe.Pointer = C.malloc(C.size_t(1))
114 if p == nil {
115 panic("can't allocate 'cgo-pointer hack index pointer': ptr == nil")
116 }
117 handleVals[p] = val
118 return p
119}
120
121func lookupHandleVal(handle unsafe.Pointer) handleVal {
122 handleLock.Lock()
123 defer handleLock.Unlock()
124 return handleVals[handle]
125}
126
127func lookupHandle(handle unsafe.Pointer) any {
128 return lookupHandleVal(handle).val
129}
130
131func deleteHandles(db *SQLiteConn) {
132 handleLock.Lock()
133 defer handleLock.Unlock()
134 for handle, val := range handleVals {
135 if val.db == db {
136 delete(handleVals, handle)
137 C.free(handle)
138 }
139 }
140}
141
142// This is only here so that tests can refer to it.
143type callbackArgRaw C.sqlite3_value
144
145type callbackArgConverter func(*C.sqlite3_value) (reflect.Value, error)
146
147type callbackArgCast struct {
148 f callbackArgConverter
149 typ reflect.Type
150}
151
152func (c callbackArgCast) Run(v *C.sqlite3_value) (reflect.Value, error) {
153 val, err := c.f(v)
154 if err != nil {
155 return reflect.Value{}, err
156 }
157 if !val.Type().ConvertibleTo(c.typ) {
158 return reflect.Value{}, fmt.Errorf("cannot convert %s to %s", val.Type(), c.typ)
159 }
160 return val.Convert(c.typ), nil
161}
162
163func callbackArgInt64(v *C.sqlite3_value) (reflect.Value, error) {
164 if C.sqlite3_value_type(v) != C.SQLITE_INTEGER {
165 return reflect.Value{}, fmt.Errorf("argument must be an INTEGER")
166 }
167 return reflect.ValueOf(int64(C.sqlite3_value_int64(v))), nil
168}
169
170func callbackArgBool(v *C.sqlite3_value) (reflect.Value, error) {
171 if C.sqlite3_value_type(v) != C.SQLITE_INTEGER {
172 return reflect.Value{}, fmt.Errorf("argument must be an INTEGER")
173 }
174 i := int64(C.sqlite3_value_int64(v))
175 val := false
176 if i != 0 {
177 val = true
178 }
179 return reflect.ValueOf(val), nil
180}
181
182func callbackArgFloat64(v *C.sqlite3_value) (reflect.Value, error) {
183 if C.sqlite3_value_type(v) != C.SQLITE_FLOAT {
184 return reflect.Value{}, fmt.Errorf("argument must be a FLOAT")
185 }
186 return reflect.ValueOf(float64(C.sqlite3_value_double(v))), nil
187}
188
189func callbackArgBytes(v *C.sqlite3_value) (reflect.Value, error) {
190 switch C.sqlite3_value_type(v) {
191 case C.SQLITE_BLOB:
192 l := C.sqlite3_value_bytes(v)
193 p := C.sqlite3_value_blob(v)
194 return reflect.ValueOf(C.GoBytes(p, l)), nil
195 case C.SQLITE_TEXT:
196 l := C.sqlite3_value_bytes(v)
197 c := unsafe.Pointer(C.sqlite3_value_text(v))
198 return reflect.ValueOf(C.GoBytes(c, l)), nil
199 default:
200 return reflect.Value{}, fmt.Errorf("argument must be BLOB or TEXT")
201 }
202}
203
204func callbackArgString(v *C.sqlite3_value) (reflect.Value, error) {
205 switch C.sqlite3_value_type(v) {
206 case C.SQLITE_BLOB:
207 l := C.sqlite3_value_bytes(v)
208 p := (*C.char)(C.sqlite3_value_blob(v))
209 return reflect.ValueOf(C.GoStringN(p, l)), nil
210 case C.SQLITE_TEXT:
211 c := (*C.char)(unsafe.Pointer(C.sqlite3_value_text(v)))
212 return reflect.ValueOf(C.GoString(c)), nil
213 default:
214 return reflect.Value{}, fmt.Errorf("argument must be BLOB or TEXT")
215 }
216}
217
218func callbackArgGeneric(v *C.sqlite3_value) (reflect.Value, error) {
219 switch C.sqlite3_value_type(v) {
220 case C.SQLITE_INTEGER:
221 return callbackArgInt64(v)
222 case C.SQLITE_FLOAT:
223 return callbackArgFloat64(v)
224 case C.SQLITE_TEXT:
225 return callbackArgString(v)
226 case C.SQLITE_BLOB:
227 return callbackArgBytes(v)
228 case C.SQLITE_NULL:
229 // Interpret NULL as a nil byte slice.
230 var ret []byte
231 return reflect.ValueOf(ret), nil
232 default:
233 panic("unreachable")
234 }
235}
236
237func callbackArg(typ reflect.Type) (callbackArgConverter, error) {
238 switch typ.Kind() {
239 case reflect.Interface:
240 if typ.NumMethod() != 0 {
241 return nil, errors.New("the only supported interface type is any")
242 }
243 return callbackArgGeneric, nil
244 case reflect.Slice:
245 if typ.Elem().Kind() != reflect.Uint8 {
246 return nil, errors.New("the only supported slice type is []byte")
247 }
248 return callbackArgBytes, nil
249 case reflect.String:
250 return callbackArgString, nil
251 case reflect.Bool:
252 return callbackArgBool, nil
253 case reflect.Int64:
254 return callbackArgInt64, nil
255 case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Int, reflect.Uint:
256 c := callbackArgCast{callbackArgInt64, typ}
257 return c.Run, nil
258 case reflect.Float64:
259 return callbackArgFloat64, nil
260 case reflect.Float32:
261 c := callbackArgCast{callbackArgFloat64, typ}
262 return c.Run, nil
263 default:
264 return nil, fmt.Errorf("don't know how to convert to %s", typ)
265 }
266}
267
268func callbackConvertArgs(argv []*C.sqlite3_value, converters []callbackArgConverter, variadic callbackArgConverter) ([]reflect.Value, error) {
269 var args []reflect.Value
270
271 if len(argv) < len(converters) {
272 return nil, fmt.Errorf("function requires at least %d arguments", len(converters))
273 }
274
275 for i, arg := range argv[:len(converters)] {
276 v, err := converters[i](arg)
277 if err != nil {
278 return nil, err
279 }
280 args = append(args, v)
281 }
282
283 if variadic != nil {
284 for _, arg := range argv[len(converters):] {
285 v, err := variadic(arg)
286 if err != nil {
287 return nil, err
288 }
289 args = append(args, v)
290 }
291 }
292 return args, nil
293}
294
295type callbackRetConverter func(*C.sqlite3_context, reflect.Value) error
296
297func callbackRetInteger(ctx *C.sqlite3_context, v reflect.Value) error {
298 switch v.Type().Kind() {
299 case reflect.Int64:
300 case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Int, reflect.Uint:
301 v = v.Convert(reflect.TypeOf(int64(0)))
302 case reflect.Bool:
303 b := v.Interface().(bool)
304 if b {
305 v = reflect.ValueOf(int64(1))
306 } else {
307 v = reflect.ValueOf(int64(0))
308 }
309 default:
310 return fmt.Errorf("cannot convert %s to INTEGER", v.Type())
311 }
312
313 C.sqlite3_result_int64(ctx, C.sqlite3_int64(v.Interface().(int64)))
314 return nil
315}
316
317func callbackRetFloat(ctx *C.sqlite3_context, v reflect.Value) error {
318 switch v.Type().Kind() {
319 case reflect.Float64:
320 case reflect.Float32:
321 v = v.Convert(reflect.TypeOf(float64(0)))
322 default:
323 return fmt.Errorf("cannot convert %s to FLOAT", v.Type())
324 }
325
326 C.sqlite3_result_double(ctx, C.double(v.Interface().(float64)))
327 return nil
328}
329
330func callbackRetBlob(ctx *C.sqlite3_context, v reflect.Value) error {
331 if v.Type().Kind() != reflect.Slice || v.Type().Elem().Kind() != reflect.Uint8 {
332 return fmt.Errorf("cannot convert %s to BLOB", v.Type())
333 }
334 i := v.Interface()
335 if i == nil || len(i.([]byte)) == 0 {
336 C.sqlite3_result_null(ctx)
337 } else {
338 bs := i.([]byte)
339 C._sqlite3_result_blob(ctx, unsafe.Pointer(&bs[0]), C.int(len(bs)))
340 }
341 return nil
342}
343
344func callbackRetText(ctx *C.sqlite3_context, v reflect.Value) error {
345 if v.Type().Kind() != reflect.String {
346 return fmt.Errorf("cannot convert %s to TEXT", v.Type())
347 }
348 cstr := C.CString(v.Interface().(string))
349 C._sqlite3_result_text(ctx, cstr)
350 return nil
351}
352
353func callbackRetNil(ctx *C.sqlite3_context, v reflect.Value) error {
354 return nil
355}
356
357func callbackRetGeneric(ctx *C.sqlite3_context, v reflect.Value) error {
358 if v.IsNil() {
359 C.sqlite3_result_null(ctx)
360 return nil
361 }
362
363 cb, err := callbackRet(v.Elem().Type())
364 if err != nil {
365 return err
366 }
367
368 return cb(ctx, v.Elem())
369}
370
371func callbackRet(typ reflect.Type) (callbackRetConverter, error) {
372 switch typ.Kind() {
373 case reflect.Interface:
374 errorInterface := reflect.TypeOf((*error)(nil)).Elem()
375 if typ.Implements(errorInterface) {
376 return callbackRetNil, nil
377 }
378
379 if typ.NumMethod() == 0 {
380 return callbackRetGeneric, nil
381 }
382
383 fallthrough
384 case reflect.Slice:
385 if typ.Elem().Kind() != reflect.Uint8 {
386 return nil, errors.New("the only supported slice type is []byte")
387 }
388 return callbackRetBlob, nil
389 case reflect.String:
390 return callbackRetText, nil
391 case reflect.Bool, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Int, reflect.Uint:
392 return callbackRetInteger, nil
393 case reflect.Float32, reflect.Float64:
394 return callbackRetFloat, nil
395 default:
396 return nil, fmt.Errorf("don't know how to convert to %s", typ)
397 }
398}
399
400func callbackError(ctx *C.sqlite3_context, err error) {
401 cstr := C.CString(err.Error())
402 defer C.free(unsafe.Pointer(cstr))
403 C.sqlite3_result_error(ctx, cstr, C.int(-1))
404}
405
406// Test support code. Tests are not allowed to import "C", so we can't
407// declare any functions that use C.sqlite3_value.
408func callbackSyntheticForTests(v reflect.Value, err error) callbackArgConverter {
409 return func(*C.sqlite3_value) (reflect.Value, error) {
410 return v, err
411 }
412}