Files
query-orchestration/vendor/github.com/jupiterrider/ffi/ffi.go
T
Michael McGuinness fee71e7740 Merged in feature/postprocessing (pull request #114)
Feature/postprocessing

* tests

* passtest

* fixshorttests

* mosttests

* improvingbasedockerfile

* testspeeds

* testing

* host

* canparallel

* clean

* passfullsuite

* singlepagemax

* test

* findfeatures

* findstables

* tbls

* tablestoo

* tablestoo

* lateraltests

* tableloc

* cleanup

* inlinetable

* childids

* cleanup

* tests
2025-04-22 14:40:16 +00:00

183 lines
6.0 KiB
Go

//go:build (freebsd || linux || windows || darwin) && (amd64 || arm64)
package ffi
import (
"unsafe"
"github.com/ebitengine/purego"
)
var prepCif, prepCifVar, call uintptr
type Abi uint32
// Arg can be used as a return value for functions, which return integers smaller than 8 bytes.
//
// See [Call].
type Arg uint64
type Status uint32
const (
OK Status = iota
BadTypedef
BadAbi
BadArgType
)
func (s Status) String() string {
status := map[Status]string{OK: "OK", BadTypedef: "bad type definition", BadAbi: "bad ABI", BadArgType: "bad argument type"}
return status[s]
}
// These constants are used for the Type field of [Type].
const (
Void = iota
Int
Float
Double
Longdouble
Uint8
Sint8
Uint16
Sint16
Uint32
Sint32
Uint64
Sint64
Struct
Pointer
Complex
)
// Type is used to describe the structure of a data type.
//
// Example:
//
// typedef struct Point {
// int x;
// int y;
// } Point;
//
// typePoint := ffi.Type{Type: ffi.Struct, Elements: &[]*ffi.Type{&ffi.TypeSint32, &ffi.TypeSint32, nil}[0]}
//
// Primitive data types are already defined (e.g. [TypeDouble] for float64).
type Type struct {
Size uint64 // Initialize to 0 (automatically set by libffi as needed).
Alignment uint16 // Initialize to 0 (automatically set by libffi as needed).
Type uint16 // Use ffi.Struct for struct types.
Elements **Type // Pointer to the first element of a nil-terminated slice.
}
// Cif stands for "Call InterFace". It describes the signature of a function.
//
// Use [PrepCif] to initialize it.
type Cif struct {
Abi uint32
NArgs uint32
ArgTypes **Type
RType *Type
Bytes uint32
Flags uint32
}
// PrepCif initializes cif.
// - abi is the ABI to use. Normally [DefaultAbi] is what you want.
// - nArgs is the number of arguments. Use 0 if the function has none.
// - rType is the return type. Use [TypeVoid] if the function has none.
// - aTypes are the arguments. Leave empty or provide nil if the function has none.
//
// The returned status code will be [OK], if everything worked properly.
//
// Example:
//
// double cos(double x);
//
// var cif ffi.Cif
// status := ffi.PrepCif(&cif, ffi.DefaultAbi, 1, &ffi.TypeDouble, &ffi.TypeDouble)
// if status != ffi.OK {
// panic(status)
// }
func PrepCif(cif *Cif, abi Abi, nArgs uint32, rType *Type, aTypes ...*Type) Status {
if len(aTypes) > 0 {
ret, _, _ := purego.SyscallN(prepCif, uintptr(unsafe.Pointer(cif)), uintptr(abi), uintptr(nArgs), uintptr(unsafe.Pointer(rType)), uintptr(unsafe.Pointer(&aTypes[0])))
return Status(ret)
}
ret, _, _ := purego.SyscallN(prepCif, uintptr(unsafe.Pointer(cif)), uintptr(abi), uintptr(nArgs), uintptr(unsafe.Pointer(rType)))
return Status(ret)
}
// PrepCifVar initializes cif for a call to a variadic function.
//
// In general its operation is the same as for [PrepCif] except that:
// - nFixedArgs is the number of fixed arguments, prior to any variadic arguments. It must be greater than zero.
// - nTotalArgs is the total number of arguments, including variadic and fixed arguments. aTypes must have this many elements.
//
// This function will return [BadArgType] if any of the variable argument types is [TypeFloat].
// Same goes for integer types smaller than 4 bytes. See [issue 608].
//
// Note that, different cif's must be prepped for calls to the same function when different numbers of arguments are passed.
//
// Also note that a call to this function with nFixedArgs = nTotalArgs is NOT equivalent to a call to [PrepCif].
//
// Example:
//
// int printf(const char *restrict format, ...);
//
// var cif ffi.Cif
// status := ffi.PrepCifVar(&cif, ffi.DefaultAbi, 1, 2, &ffi.TypeSint32, &ffi.TypePointer, &ffi.TypeDouble)
// if status != ffi.OK {
// panic(status)
// }
//
// text, _ := unix.BytePtrFromString("Pi is %f\n")
// pi := math.Pi
// var nCharsPrinted int32
// ffi.Call(&cif, printf, unsafe.Pointer(&nCharsPrinted), unsafe.Pointer(&text), unsafe.Pointer(&pi))
//
// [issue 608]: https://github.com/libffi/libffi/issues/608
func PrepCifVar(cif *Cif, abi Abi, nFixedArgs, nTotalArgs uint32, rType *Type, aTypes ...*Type) Status {
const intSize = 4
// This check has been rebuild according to the original: https://github.com/libffi/libffi/blob/v3.4.6/src/prep_cif.c#L244
//
// Without rebuild, the type check wouldn't work for float,
// because libffi compares the pointer to ffi_type_float instead of value equality.
for i := nFixedArgs; i < nTotalArgs; i++ {
argType := *aTypes[i]
if argType == TypeFloat || ((argType.Type != Struct && argType.Type != Complex) && argType.Size < intSize) {
return BadArgType
}
}
if len(aTypes) > 0 {
ret, _, _ := purego.SyscallN(prepCifVar, uintptr(unsafe.Pointer(cif)), uintptr(abi), uintptr(nFixedArgs), uintptr(nTotalArgs), uintptr(unsafe.Pointer(rType)), uintptr(unsafe.Pointer(&aTypes[0])))
return Status(ret)
}
ret, _, _ := purego.SyscallN(prepCifVar, uintptr(unsafe.Pointer(cif)), uintptr(abi), uintptr(nFixedArgs), uintptr(nTotalArgs), uintptr(unsafe.Pointer(rType)))
return Status(ret)
}
// Call calls the function fn according to the description given in cif. cif must have already been prepared using [PrepCif].
// - fn is the address of the desired function. Use [purego.Dlsym] to get one.
// - rValue is a pointer to a variable that will hold the result of the function call. Provide nil if the function has no return value.
// You cannot use integer types smaller than 8 bytes here (float32 and structs are not affected). Use [Arg] instead and typecast afterwards.
// - aValues are pointers to the argument values. Leave empty or provide nil if the function takes none.
//
// Example:
//
// int ilogb(double x);
//
// var result ffi.Arg
// x := 1.0
// ffi.Call(&cif, ilogb, unsafe.Pointer(&result), unsafe.Pointer(&x))
// fmt.Printf("%d\n", int32(result))
func Call(cif *Cif, fn uintptr, rValue unsafe.Pointer, aValues ...unsafe.Pointer) {
if len(aValues) > 0 {
purego.SyscallN(call, uintptr(unsafe.Pointer(cif)), fn, uintptr(rValue), uintptr(unsafe.Pointer(&aValues[0])))
return
}
purego.SyscallN(call, uintptr(unsafe.Pointer(cif)), fn, uintptr(rValue))
}