summaryrefslogtreecommitdiffstats
path: root/ffi.c
diff options
context:
space:
mode:
Diffstat (limited to 'ffi.c')
-rw-r--r--ffi.c117
1 files changed, 108 insertions, 9 deletions
diff --git a/ffi.c b/ffi.c
index 43ae753c..077751aa 100644
--- a/ffi.c
+++ b/ffi.c
@@ -97,6 +97,7 @@ struct txr_ffi_type {
val (*get)(struct txr_ffi_type *, mem_t *src, val self);
void (*in)(struct txr_ffi_type *, mem_t *src, val obj,
mem_t *rtvec[], val self);
+ void (*out)(struct txr_ffi_type *, int copy, val obj, mem_t *dest, val self);
mem_t *(*alloc)(struct txr_ffi_type *, val obj, val self);
void (*free)(void *);
};
@@ -852,6 +853,17 @@ static void ffi_ptr_in_put(struct txr_ffi_type *tft, val s, mem_t *dst,
}
}
+static void ffi_ptr_in_out(struct txr_ffi_type *tft, int copy, val s,
+ mem_t *dst, val self)
+{
+ val tgttype = tft->mtypes;
+ struct txr_ffi_type *tgtft = ffi_type_struct(tgttype);
+ if (tgtft->out != 0) {
+ mem_t *buf = *coerce(mem_t **, dst);
+ tgtft->out(tgtft, 0, s, buf, self);
+ }
+}
+
static void ffi_ptr_in_d_put(struct txr_ffi_type *tft, val s, mem_t *dst,
mem_t *rtvec[], val self)
{
@@ -894,6 +906,18 @@ static void ffi_ptr_out_put(struct txr_ffi_type *tft, val s, mem_t *dst,
}
}
+static void ffi_ptr_out_out(struct txr_ffi_type *tft, int copy, val s,
+ mem_t *dst, val self)
+{
+ val tgttype = tft->mtypes;
+ struct txr_ffi_type *tgtft = ffi_type_struct(tgttype);
+ mem_t *buf = *coerce(mem_t **, dst);
+ if (tgtft->out != 0)
+ tgtft->out(tgtft, 1, s, buf, self);
+ else
+ tgtft->put(tgtft, s, buf, 0, self);
+}
+
static val ffi_ptr_get(struct txr_ffi_type *tft, mem_t *src, val self)
{
val tgttype = tft->mtypes;
@@ -990,6 +1014,31 @@ static void ffi_struct_put(struct txr_ffi_type *tft, val strct, mem_t *dst,
}
}
+static void ffi_struct_out(struct txr_ffi_type *tft, int copy, val strct,
+ mem_t *dst, val self)
+{
+ val slots = tft->mnames;
+ val types = tft->mtypes;
+ ucnum offs = 0;
+
+ while (types) {
+ val slsym = pop(&slots);
+ val type = pop(&types);
+ struct txr_ffi_type *mtft = ffi_type_struct(type);
+ ucnum almask = mtft->align - 1;
+ offs = (offs + almask) & ~almask;
+ if (mtft->out != 0) {
+ if (slsym) {
+ val slval = slot(strct, slsym);
+ mtft->out(mtft, copy, slval, dst + offs, self);
+ } else {
+ memset(dst + offs, 0, mtft->size);
+ }
+ }
+ offs += mtft->size;
+ }
+}
+
static val ffi_struct_get(struct txr_ffi_type *tft, mem_t *src, val self)
{
val slots = tft->mnames;
@@ -1058,6 +1107,30 @@ static void ffi_array_put(struct txr_ffi_type *tft, val vec, mem_t *dst,
}
}
+static void ffi_array_out(struct txr_ffi_type *tft, int copy, val vec,
+ mem_t *dst, val self)
+{
+ val eltypes = tft->mtypes;
+ cnum nelem = tft->nelem, i;
+ int nt = tft->null_term;
+ ucnum offs = 0;
+
+ for (i = 0; i < nelem; i++) {
+ val eltype = pop(&eltypes);
+ struct txr_ffi_type *etft = ffi_type_struct(eltype);
+ cnum elsize = etft->size;
+ if (nt && i == nelem - 1) {
+ memset(dst + offs, 0, elsize);
+ break;
+ }
+ if (etft->out != 0) {
+ val elval = ref(vec, num_fast(i));
+ etft->out(etft, copy, elval, dst + offs, self);
+ }
+ offs += elsize;
+ }
+}
+
static val ffi_array_get(struct txr_ffi_type *tft, mem_t *src, val self)
{
val eltypes = tft->mtypes;
@@ -1141,6 +1214,8 @@ static val make_ffi_type_pointer(val syntax, val lisp_type,
mem_t *src, val self),
void (*in)(struct txr_ffi_type *, mem_t *src,
val obj, mem_t *rtvec[], val self),
+ void (*out)(struct txr_ffi_type *, int copy,
+ val obj, mem_t *dst, val self),
val tgtype)
{
struct txr_ffi_type *tft = coerce(struct txr_ffi_type *,
@@ -1159,6 +1234,7 @@ static val make_ffi_type_pointer(val syntax, val lisp_type,
tft->get = get;
tft->mtypes = tgtype;
tft->in = in;
+ tft->out = out;
tft->alloc = ffi_fixed_alloc;
tft->free = free;
@@ -1178,6 +1254,7 @@ static val make_ffi_type_struct(val syntax, val lisp_type,
val obj = cobj(coerce(mem_t *, tft), ffi_type_s, &ffi_type_struct_ops);
cnum total_size = 0;
cnum most_align = 0;
+ int need_out_handler = 0;
ft->type = FFI_TYPE_STRUCT;
ft->size = 0;
@@ -1206,10 +1283,14 @@ static val make_ffi_type_struct(val syntax, val lisp_type,
most_align = align;
total_size = (total_size + align - 1) / align * align + size;
+ need_out_handler = need_out_handler || mtft->out != 0;
}
elements[i] = 0;
+ if (need_out_handler)
+ tft->out = ffi_struct_out;
+
ft->elements = elements;
total_size = (total_size + most_align - 1) / most_align * most_align;
@@ -1254,6 +1335,8 @@ static val make_ffi_type_array(val syntax, val lisp_type,
if (i == 0) {
tft->size = etft->size * nelem;
tft->align = etft->align;
+ if (etft->out != 0)
+ tft->out = ffi_array_out;
}
}
@@ -1339,31 +1422,36 @@ val ffi_type_compile(val syntax)
return make_ffi_type_pointer(syntax, cptr_s, sizeof (mem_t *),
&ffi_type_pointer,
ffi_ptr_in_put, ffi_ptr_get,
- ffi_ptr_in_in, target_type);
+ ffi_ptr_in_in, ffi_ptr_in_out,
+ target_type);
} else if (sym == ptr_in_d_s) {
val target_type = ffi_type_compile(cadr(syntax));
return make_ffi_type_pointer(syntax, cptr_s, sizeof (mem_t *),
&ffi_type_pointer,
ffi_ptr_in_d_put, ffi_ptr_d_get,
- 0, target_type);
+ 0, ffi_ptr_in_out,
+ target_type);
} else if (sym == ptr_out_s) {
val target_type = ffi_type_compile(cadr(syntax));
return make_ffi_type_pointer(syntax, cptr_s, sizeof (mem_t *),
&ffi_type_pointer,
ffi_ptr_out_put, ffi_ptr_get,
- ffi_ptr_out_in, target_type);
+ ffi_ptr_out_in, ffi_ptr_out_out,
+ target_type);
} else if (sym == ptr_out_d_s) {
val target_type = ffi_type_compile(cadr(syntax));
return make_ffi_type_pointer(syntax, cptr_s, sizeof (mem_t *),
&ffi_type_pointer,
ffi_ptr_out_put, ffi_ptr_d_get,
- ffi_ptr_out_in, target_type);
+ ffi_ptr_out_in, ffi_ptr_out_out,
+ target_type);
} else if (sym == ptr_s) {
val target_type = ffi_type_compile(cadr(syntax));
return make_ffi_type_pointer(syntax, cptr_s, sizeof (mem_t *),
&ffi_type_pointer,
ffi_ptr_put, ffi_ptr_get,
- ffi_ptr_out_in, target_type);
+ ffi_ptr_out_in, ffi_ptr_out_out,
+ target_type);
} else if (sym == buf_s || sym == buf_d_s) {
cnum nelem = c_num(cadr(syntax));
val type = make_ffi_type_builtin(syntax, cptr_s, sizeof (mem_t *),
@@ -1684,21 +1772,32 @@ static void ffi_closure_dispatch(ffi_cif *cif, void *cret,
val rtype = tfcd->rettype;
struct txr_ffi_type *rtft = ffi_type_struct(rtype);
val retval = nil;
+ int out_pass_needed = 0;
args_decl(args, tfcl->nparam);
+ args_decl(args_cp, tfcl->nparam);
for (i = 0; i < nargs; i++) {
val type = pop(&types);
struct txr_ffi_type *mtft = ffi_type_struct(type);
val arg = mtft->get(mtft, convert(mem_t *, cargs[i]), self);
args_add(args, arg);
+ if (mtft->out != 0)
+ out_pass_needed = 1;
}
+ args_copy(args_cp, args);
+
retval = generic_funcall(tfcl->fun, args);
- /* rtvec is purposely null in the following call.
- * if the put placed any pointers into rtvec, it would
- * be wrong.
- */
+ if (out_pass_needed) {
+ for (types = tfcd->argtypes, i = 0; i < nargs; i++) {
+ val type = pop(&types);
+ val arg = args_at(args_cp, i);
+ struct txr_ffi_type *mtft = ffi_type_struct(type);
+ mtft->out(mtft, 0, arg, convert(mem_t *, cargs[i]), self);
+ }
+ }
+
rtft->put(rtft, retval, convert(mem_t *, cret), 0, self);
}