summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKaz Kylheku <kaz@kylheku.com>2019-01-15 07:30:53 -0800
committerKaz Kylheku <kaz@kylheku.com>2019-01-15 07:30:53 -0800
commit4375946224455b2509d6ca27e48c2cd6d51a704e (patch)
tree075337981a301105ef8ce4f0ee01aa19e12030e9
parent8ea9cc713f52fd0a1357aa5cded97ceb234b669a (diff)
downloadtxr-4375946224455b2509d6ca27e48c2cd6d51a704e.tar.gz
txr-4375946224455b2509d6ca27e48c2cd6d51a704e.tar.bz2
txr-4375946224455b2509d6ca27e48c2cd6d51a704e.zip
ffi: arrays: be more forgiving of length mismatches.
* ffi.c (min): New macro. (ffi_array_put_common): Tolerate sequences which are shorter than the array. Use seq_info to classify the sequence and use separate code for the vector and list case, avoiding taking the length of the list. * txr.1: Documented.
-rw-r--r--ffi.c43
-rw-r--r--txr.14
2 files changed, 38 insertions, 9 deletions
diff --git a/ffi.c b/ffi.c
index b11f0d42..10e6946b 100644
--- a/ffi.c
+++ b/ffi.c
@@ -68,6 +68,8 @@
? (size) \
: sizeof (ffi_arg))
+#define min(a, b) ((a) < (b) ? (a) : (b))
+
#if HAVE_LITTLE_ENDIAN
#define ifbe(expr) (0)
#define ifbe2(expr1, expr2) (expr2)
@@ -2382,19 +2384,42 @@ static void ffi_array_put_common(struct txr_ffi_type *tft, val vec, mem_t *dst,
struct txr_ffi_type *etft = ffi_type_struct(eltype);
cnum elsize = etft->size;
int nt = tft->null_term;
- cnum i;
+ cnum i = 0;
ucnum offs = 0;
+ seq_info_t si = seq_info(vec);
- for (i = 0; i < nelem; i++) {
- if (nt && i == nelem - 1) {
- memset(dst + offs, 0, elsize);
- break;
- } else {
- val elval = ref(vec, num_fast(i));
- etft->put(etft, elval, dst + offs, self);
- offs += elsize;
+ switch (si.kind) {
+ case SEQ_NIL:
+ case SEQ_LISTLIKE:
+ {
+ val iter = si.obj;
+
+ for (; i < nelem - nt && !endp(iter); i++, iter = cdr(iter)) {
+ val elval = car(iter);
+ etft->put(etft, elval, dst + offs, self);
+ offs += elsize;
+ }
}
+ break;
+ case SEQ_VECLIKE:
+ {
+ val v = si.obj;
+ cnum lim = min(nelem - nt, c_num(length(si.obj)));
+
+ for (; i < lim; i++) {
+ val elval = ref(v, num_fast(i));
+ etft->put(etft, elval, dst + offs, self);
+ offs += elsize;
+ }
+ }
+ break;
+ default:
+ uw_throwf(error_s, lit("~a: ~s isn't convertible to a C array"), self,
+ vec, nao);
}
+
+ if (i < nelem)
+ memset(dst + offs, 0, elsize * (nelem - i));
}
static void ffi_array_put(struct txr_ffi_type *tft, val vec, mem_t *dst,
diff --git a/txr.1 b/txr.1
index f62cc5cf..b8f6f4a0 100644
--- a/txr.1
+++ b/txr.1
@@ -59140,6 +59140,10 @@ simply iterates over the Lisp sequence, and performs an element for
element conversion to
.metn type .
+If the sequence is shorter than the array, then the remaining elements
+are filled with zero bits. If the sequence is longer than the array, then the
+excess elements in the sequence are ignored.
+
Since Lisp arrays and C arrays do not share the same representation,
temporary buffers are automatically created and destroyed by FFI
to manage the conversion.