3 ;; Copyright (C) 2014 <Dmitry Bogatov <KAction@gnu.org>>
5 ;; Author: <Dmitry Bogatov <KAction@gnu.org>>
7 ;; This program is free software; you can redistribute it and/or
8 ;; modify it under the terms of the GNU General Public License
9 ;; as published by the Free Software Foundation; either version 3
10 ;; of the License, or (at your option) any later version.
12 ;; This program is distributed in the hope that it will be useful,
13 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
14 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 ;; GNU General Public License for more details.
17 ;; You should have received a copy of the GNU General Public License
18 ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
22 ;; This module defines structure <ffi-object> describing scheme
23 ;; representation of C datatype. It contains of following fields:
25 ;; * type, see (system foreign)
26 ;; * encoder, that convert Scheme object to C datatype
27 ;; * decoder, that convert C datatype to Scheme object
28 ;; * size, for pointer types.
30 ;; Either encoder or decoder, but not both can be #f, this means
31 ;; than operation is not supported. If both are supported,
32 ;; decoder . encoder == id
33 ;; Reverse is not necessary correct, since decoding can omit some
34 ;; details of original object.
36 ;; This object is used by macros in (syntax ffi)
38 ;; Also, this module provides set of macros to define <ffi>s
39 ;; for common datatypes, encountered in C.
41 ;; The most generic form is `define-ffi-object`, that is little more,
42 ;; than keyword wrapper around <ffi-object> constructor. It have form
44 ;; (define-ffi-object object:
45 ;; #:decoder pointer->object
46 ;; #:encoder object->pointer
47 ;; #:type '* ;; Not needed, pointer is default)
49 ;; It is recommended, that if type is '*, to name decoders and encoders
50 ;; like in example, and if type is integral, use integral->object and
51 ;; object->integral naming. By convention name of <ffi> variable
54 ;; If type is '*, `define-ffi-object` also defines `?object:` variable,
55 ;; that in addition can convert #f <-> %null-pointer.
57 ;; Second provided macro is `define-ffi-enum`. It is called in form
58 ;; (define-ffi-enum enum/foo: (empty (half 75) full))
59 ;; which corresponds to following C declaration
60 ;; enum foo { empty, half = 75, full };
61 ;; Generated encoders/decoders converts symbols to/from int.
63 ;; Next, macro than corresponds to bitmasks, `define-ffi-mask`. Example:
64 ;; (define-ffi-mask mask/bar:
65 ;; ((nice #x1) (bad #x3))
68 ;; Next, dealing with structs. For example, in C
69 ;; struct foo_struct { int a; char *s; int i_do_not_care; };
70 ;; can be directly translated with `define-ffi-struct`
71 ;; (define-ffi-struct foo: ((int a) (?string: s) int))
73 ;; It defines srfi-9 struct <foo> with fields `a' and `b'. If not
74 ;; every field given name in `define-ffi-struct`, during decoding
75 ;; such fields are ignored, and during encoding are filled with
76 ;; specially-prepared garbage.
78 ;; Not very often, but we need to deal with function pointers. It
79 ;; can be nicely handled with
80 ;; (define-ffi-func func/make: ((string: name) (string: value) -> void))
82 ;; The most annoying part of C from ffi part of view are unions,
83 ;; since they cannot be parsed without context. Your best bet is
84 ;; define dummy struct of needed size in place of union, and count
89 (define-module (system ffi)
92 #:export (*: void: ?*: string: ?string:)
93 #:export (call-with-provided-memory)
94 #:export (define-extern)
96 #:export (define-ffi-enum)
97 #:export (define-ffi-func)
98 #:export (define-ffi-mask)
99 #:export (define-ffi-object)
100 #:export (define-ffi-struct)
102 #:export (extern-pointer)
103 #:export (extern-pointer-array)
104 #:export (make-ffi-enum)
105 #:export (make-ffi-func-decoder)
106 #:export (make-ffi-func-encoder)
107 #:export (make-ffi-make)
108 #:export (?pointer->string ?string->pointer)
109 #:export (make-ffi-struct)
113 #:export (with-provided-memory)
114 #:export (c-free c-malloc c-memmove c-strdup c-strlen))
116 (use-modules (ice-9 control))
117 (use-modules (ice-9 q))
118 (use-modules (oop goops))
119 (use-modules (ice-9 decorate))
120 (use-modules (ice-9 curried-definitions))
121 (use-modules (ice-9 match))
122 (use-modules (srfi srfi-9))
123 (use-modules (ice-9 optargs))
124 (use-modules (rnrs bytevectors))
125 (use-modules (srfi srfi-1))
126 (use-modules (syntax export))
127 (use-modules (syntax functional))
128 (use-modules (syntax implicit))
129 (use-modules (syntax record))
130 (use-modules (system foreign))
131 (use-modules (texinfo string-utils))
133 ;; Many primitive types have identity as encoder and/or decoder,
134 ;; but to make procedure names more meaningful, each one receive
135 ;; it's own instance of identity function.
136 (define (make-identity)
140 (define-class <ffi-object> ()
141 (ffi-decoder #:init-keyword #:decoder #:accessor .ffi-decoder
142 #:init-form (cute error "No decoder" <...>))
143 (ffi-encoder #:init-keyword #:encoder #:accessor .ffi-encoder
144 #:init-form (cute error "No encoder" <...>))
145 (ffi-type #:init-keyword #:type #:accessor .ffi-type
147 (ffi-clone #:init-keyword #:clone #:accessor .ffi-clone
148 #:init-form (make-identity))
149 (ffi-free #:init-keyword #:free #:accessor .ffi-free
150 #:init-form (make-identity)))
151 (define guard (make-guardian))
153 (define-record-type <unsafe>
156 (value unsafe-coerce))
158 (define-syntax-rule (promise-overload method-name)
159 (define-method (method-name (promise <promise>) . args)
160 (apply method-name (force promise) args)))
161 (promise-overload .ffi-decoder)
162 (promise-overload .ffi-encoder)
163 (promise-overload .ffi-type)
164 (promise-overload .ffi-clone)
165 (promise-overload .ffi-free)
172 (define ((use-ffi-accessor accessor) obj value)
173 ((accessor obj) value))
174 (define clone (use-ffi-accessor .ffi-clone))
175 (define free (use-ffi-accessor .ffi-free))
177 (define* (decode ffi-obj c-value #:key free-after?)
178 (define scm-value ((.ffi-decoder ffi-obj) c-value))
180 (free ffi-obj c-value))
183 (define* (encode ffi-obj scm-value #:key (clone-after? #t))
184 (if (unsafe? scm-value)
185 (unsafe-coerce scm-value)
186 (let ((c-value ((.ffi-encoder ffi-obj) scm-value)))
187 ;; If scm-value is #f it means that we are encoding either
188 ;; boolean or %null-pointer. In both cases cloning is not
189 ;; needed, in second it is a problem, since clone function
190 ;; expects valid pointer, not null one.
191 (if (and scm-value clone-after?)
192 (clone ffi-obj c-value)
195 (define-record-type* field (accessor ffi-object))
196 (define-class <ffi-struct> (<ffi-object>)
197 (struct-class #:init-keyword #:struct-class #:accessor .struct-class)
198 (ffi-objects #:init-keyword #:ffi-objects))
199 (promise-overload .struct-class)
200 (define-method (.ffi-objects (obj <ffi-struct>))
201 (map force* (slot-ref obj 'ffi-objects)))
203 (define-method (size-of (struct <ffi-struct>))
204 (sizeof (map .ffi-type (.ffi-objects struct))))
205 (define-method (size-of x)
208 (define-method (equal? x y)
209 ((@ (guile) equal?) x y))
211 (define-method (equal? (this <object>) (other <object>))
212 (let* ((class_ (class-of this))
213 (slots (map car (class-slots class_))))
214 (define (slot-values instance)
215 (map (cute slot-ref instance <>) slots))
216 (if (eq? class_ (class-of other))
217 (false-if-exception (every equal? (slot-values this) (slot-values other)))
220 (define-method (offset-of (struct <ffi-struct>) (field-name <symbol>))
221 (define field-names (map car (class-slots (.struct-class struct))))
222 (define ffi-objs (.ffi-objects struct))
223 (unless (member field-name field-names)
224 (error "Struct do not have such field:" struct field-name))
225 (let* ((fields-before# (list-index (cute eq? field-name <>) field-names)))
226 (if (zero? fields-before#)
228 (let* ((ffi-objects-before (take ffi-objs fields-before#))
229 (c-types-before (map .ffi-type ffi-objects-before)))
230 (sizeof c-types-before)))))
232 (define-method (offset-of (struct <ffi-struct>) (base <foreign>) (field-name <symbol>))
233 (make-pointer (+ (pointer-address base) (offset-of struct field-name))))
235 (define decorate:null-decoder
236 (make-decorator 'null-decoder ((f ptr))
237 (if (null-pointer? ptr)
241 (define decorate:null-clone
242 (make-decorator 'null-clone ((f ptr))
243 (if (null-pointer? ptr)
247 (define decorate:null-encoder
248 (make-decorator 'null-encoder ((f value))
253 (define (slot-modify obj slot fn)
254 (slot-set! obj slot (fn (slot-ref obj slot))))
256 (define (force-ffi-objects ffi-struct)
257 (slot-modify ffi-struct 'ffi-objects (cute map force* <>)))
259 (define (make-nullable obj)
260 (define new (shallow-clone obj))
261 (slot-modify new 'ffi-encoder decorate:null-encoder)
262 (slot-modify new 'ffi-decoder decorate:null-decoder)
263 (slot-modify new 'ffi-clone decorate:null-clone)
266 ;; A bit hacky way to get integer->foo functions on toplevel, allowing
267 ;; to avoid some macros and simplifying others.
268 (define (toplevel-define-accessors ffi-obj-name)
269 (define symbol (normalize-ffi-object-name ffi-obj-name))
270 (define int/encoder-name (symbol-append symbol '->integer))
271 (define int/decoder-name (symbol-append 'integer-> symbol))
272 (define pointer/encoder-name (symbol-append symbol '->pointer))
273 (define pointer/decoder-name (symbol-append 'pointer-> symbol))
274 (define pointer/free-name (symbol-append 'free- symbol '-pointer))
275 (define pointer/clone-name (symbol-append 'clone- symbol '-pointer))
276 (define ffi-object (module-ref (current-module) ffi-obj-name))
277 (define (current-module-define! symbol proc)
278 (module-define! (current-module) symbol proc)
279 (set-procedure-property! proc 'name symbol))
280 (define (toplevel-define-field symbol field)
281 (current-module-define! symbol (slot-ref ffi-object field)))
282 (if (eq? '* (slot-ref ffi-object 'ffi-type))
284 (toplevel-define-field pointer/decoder-name 'ffi-decoder)
285 (toplevel-define-field pointer/encoder-name 'ffi-encoder)
286 (toplevel-define-field pointer/free-name 'ffi-free)
287 (toplevel-define-field pointer/clone-name 'ffi-clone))
289 (toplevel-define-field int/decoder-name 'ffi-decoder)
290 (toplevel-define-field int/encoder-name 'ffi-encoder))))
292 (define (string-last str)
293 (string-ref str (1- (string-length str))))
295 (define (string-init str)
296 (substring str 0 (1- (string-length str))))
298 (define (normalize-ffi-object-name name)
299 ;; Just strip trailing colon, if any.
300 ;; There should be one, really.
301 (define name-str (symbol->string name))
302 (unless (eqv? (string-last name-str) #\:)
303 (error "Wrong ffi object name (should end with colon):" name))
304 (string->symbol (string-init name-str)))
306 (define (toplevel-maybe-define-nullable name)
307 (define object (module-ref (current-module) name))
308 (define nullable-name (symbol-append '? name))
309 (when (eq? '* (.ffi-type object))
310 (module-define! (current-module) nullable-name
311 (make-nullable object))
312 (toplevel-define-accessors nullable-name)))
314 (define-syntax-rule (define-ffi-object name kw ...)
316 (define name (make <ffi-object> kw ...))
317 (toplevel-define-accessors 'name)
318 (toplevel-maybe-define-nullable 'name)))
320 (define ((enum-lambda alist) key)
321 (or (assq-ref alist key)
322 (error "enum-lambda: key not found:" alist key)))
324 (define (regenerate-enum-values lst)
325 (let ((prev-value -1))
326 (define (regenerate x)
328 ((key value) (cons key value))
329 ((key . value) (cons key value))
330 (key (let ((value (1+ prev-value)))
331 (set! prev-value value)
333 (map-in-order regenerate lst)))
335 (define (reverse-cons pair)
336 (call-with-values (cute car+cdr pair) xcons))
338 (define* (make-ffi-enum alist #:key (type int))
339 (let ((full-form-alist (regenerate-enum-values alist)))
341 #:encoder (enum-lambda full-form-alist)
342 #:decoder (enum-lambda (map reverse-cons full-form-alist))
345 (define-syntax-rule (define-ffi-enum name (clause ...) kw ...)
347 (define name (make-ffi-enum '(clause ...) kw ...))
348 (toplevel-define-accessors 'name)))
350 (define* (make-ffi-mask alist #:key (type int))
351 (define mask-values (map cdr alist))
352 (define key->value (enum-lambda alist))
353 (define value->key (enum-lambda (map reverse-cons alist)))
354 (define (encoder symbols)
355 (apply logior (map key->value symbols)))
356 (define (decoder val)
357 (define (mask-present? mask)
358 (eqv? mask (logand mask val)))
359 (map value->key (filter mask-present? mask-values)))
365 (define-syntax-rule (define-ffi-mask name ((key value) ...) kw ...)
367 (define name (make-ffi-mask '((key . value) ...) kw ...))
368 (toplevel-define-accessors 'name)))
370 (define (call-with-provided-memory ffi-structs producer consumer)
371 (define (make-vector-for-ffi st)
372 (make-bytevector (slot-ref st 'struct-size)))
373 (let* ((vectors (map make-vector-for-ffi ffi-structs))
374 (pointers (map bytevector->pointer vectors))
375 (retvalue (apply producer pointers)))
376 (apply consumer retvalue (map decode ffi-structs pointers))))
378 (define-syntax with-provided-memory
380 ((_ ((ffi-obj name) ...) (result = memaction) exp exp* ...)
381 (call-with-provided-memory
383 (lambda (name ...) memaction)
384 (lambda (result name ...) exp exp* ...)))))
387 ;; function->pointer transformation
390 ;; Every arg-spec is cons pair (free-after? . ffi-object)
391 (define ((make-ffi-func-encoder ret-obj clone-retval? arg-specs) scm-fn)
392 (define (proxy . c-value-args)
393 (define (decode-and-maybe-free arg-spec c-value)
394 (decode (cdr arg-spec) c-value #:free-after? (car arg-spec)))
395 (define scm-value-args (map decode-and-maybe-free arg-specs c-value-args))
396 (define scm-ret-value (apply scm-fn scm-value-args))
397 (define c-ret-value (encode ret-obj scm-ret-value #:clone-after? clone-retval?))
399 (define (arg-spec-ffi-type arg-spec)
400 (.ffi-type (cdr arg-spec)))
401 (procedure->pointer (slot-ref ret-obj 'ffi-type)
403 (map arg-spec-ffi-type arg-specs)))
405 (define-syntax unify-encode-clause
407 ((_ (#:const ffi-obj _ ...)) (cons #f ffi-obj))
408 ((_ (#:alloc ffi-obj _ ...)) (cons #t ffi-obj))
409 ((_ (ffi-obj _ ...)) (cons #f ffi-obj))
410 ((_ ffi-obj) (cons #f ffi-obj))))
412 (define-syntax define-ffi-func
414 ((_ name (arg ... -> #:const ret-obj))
415 (define-ffi-object name
417 #:encoder (make-ffi-func-encoder
419 (list (unify-encode-clause arg) ...))))
420 ((_ name (arg ... -> #:alloc ret-obj))
421 (define-ffi-object name
423 #:encoder (make-ffi-func-encoder
425 (list (unify-encode-clause arg) ...))))
426 ((_ name (arg ... -> ret-obj))
427 (define-ffi-func name (arg ... -> #:const ret-obj)))))
430 ;; pointer->function transformation.
432 (define (make-raw-procedure ret-obj args-objs foreign-address)
433 (pointer->procedure (.ffi-type ret-obj) foreign-address
434 (map .ffi-type args-objs)))
436 (define (make-c-values arg-specs scm-args provided-args)
437 (define (xenq! el q) (enq! q el))
438 (define scm-args-q (fold xenq! (make-q) scm-args))
439 (define provided-args-q (fold xenq! (make-q) provided-args))
440 (define (process-arg-spec arg-spec)
442 (('fix ffi-obj value) value)
443 (('provide ffi-obj) (deq! provided-args-q))
444 (('const ffi-obj) (encode ffi-obj (deq! scm-args-q) #:clone-after? #f))
445 (('normal ffi-obj) (encode ffi-obj (deq! scm-args-q) #:clone-after? #t))))
446 (define c-values (map process-arg-spec arg-specs))
447 (unless (q-empty? scm-args-q)
448 (error "Wrong number of arguments (too much):" (car scm-args-q)))
451 ;; Every element of ARG-SPECS is expected to be a list, describing
452 ;; argument of function. Here is possibilities:
454 ;; (fix <ffi-object> <fixed-value>)
455 ;; (normal <ffi-object>)
456 ;; (const <ffi-object>)
457 ;; (provide <ffi-object)
458 (define ((make-ffi-func-decoder ret-obj free-retval? arg-specs) foreign-address)
459 (define arg-spec-ffi-object second)
460 (define (arg-spec-type-provide? arg-spec)
461 (eq? (car arg-spec) 'provide))
462 (define args-ffi-objs (map arg-spec-ffi-object arg-specs))
463 (define raw-procedure (make-raw-procedure ret-obj args-ffi-objs foreign-address))
464 (define provide-ffi-objs (filter arg-spec-type-provide? arg-specs))
466 (call-with-provided-memory
468 (lambda provided-pointers
469 (define c-values (make-c-values arg-specs scm-args provided-pointers))
470 (define c-ret-value (apply raw-procedure c-values))
471 (decode ret-obj c-ret-value #:free-after? free-retval?))
472 (lambda (scm-ret-value . provided-scm-values)
473 (if (null? provided-scm-values)
475 (cons scm-ret-value provided-scm-values))))))
479 (define (bool->integer b)
482 (define (integer->bool value)
485 (define-ffi-object void: #:decoder (const *unspecified*) #:type void)
486 (define-ffi-object *: #:decoder (make-identity) #:encoder (make-identity))
487 (define-ffi-object bool: #:decoder integer->bool #:encoder bool->integer #:type int)
489 (define (mirror-at-toplevel type-name)
490 (define type (module-ref (current-module) type-name))
491 (define varname (symbol-append type-name ':))
492 (module-define! (current-module) varname
493 (make <ffi-object> #:encoder (make-identity) #:decoder (make-identity) #:type type))
494 (module-export! (current-module) (list varname)))
496 (for-each mirror-at-toplevel
497 '(size_t int long ptrdiff_t int8 int16 int32 int64 uint8 uint16 uint32 uint64))
499 (define-syntax unify-decode-clause
501 ((_ (ffi-obj := value)) (list 'fix ffi-obj value))
502 ((_ (#:alloc ffi-obj _ ...)) (list 'normal ffi-obj))
503 ((_ (#:const ffi-obj _ ...)) (list 'const ffi-obj))
504 ((_ (ffi-obj _ ...)) (list 'normal ffi-obj))
505 ((_ ffi-obj) (list 'normal ffi-obj))
506 ((_ (#:provide ffi-obj _ ...)) (list 'provide ffi-obj))))
508 (define (scm-symbol->c-symbol symb)
509 (transform-string (symbol->string symb) #\- #\_))
511 (define* (dynamic-pointer* def-symbol #:key symbol (from (dynamic-link)))
512 (define c-symbol-str (or symbol (scm-symbol->c-symbol def-symbol)))
513 (dynamic-pointer c-symbol-str from))
515 (define (toplevel-define-ffi name ret-obj free-retval? arg-specs . kw)
516 (define (make-procedure* ret*)
517 (define make-decoder* (make-ffi-func-decoder ret* free-retval? arg-specs))
518 (define foreign-pointer (false-if-exception (apply dynamic-pointer* name kw)))
519 (when foreign-pointer
520 (make-decoder* foreign-pointer)))
521 (define (wrap-q-empty proc)
523 (catch 'q-empty (cute apply proc args)
524 (lambda args (error "Not enough arguments:" name )))))
526 (module-define! (current-module) name
527 (wrap-q-empty (make-procedure* ret-obj)))
528 (when (eq? '* (.ffi-type ret-obj))
529 (module-define! (current-module) (symbol-append name '*)
530 (wrap-q-empty (make-procedure* ?*:)))))
532 (define-syntax define-ffi
533 (syntax-rules (-> ::)
534 ((_ name (arg-clause ... -> free-retval? ret-obj) kw ...)
535 (toplevel-define-ffi 'name ret-obj free-retval?
536 (list (unify-decode-clause arg-clause) ...)
538 ((_ name (arg-clause ... -> #:const ret-obj) kw ...)
539 (define-ffi name (arg-clause ... -> #f ret-obj) kw ...))
540 ((_ name (arg-clause ... -> #:alloc ret-obj) kw ... )
541 (define-ffi name (arg-clause ... -> #t ret-obj) kw ...))
542 ((_ name (arg-clause ... -> ret-obj) kw ...)
543 (define-ffi name (arg-clause ... -> #f ret-obj) kw ...))
544 ((_ name :: #:const ret-obj kw ...)
545 (define-ffi (-> #f ret-obj) kw ...))
546 ((_ name :: #:alloc ret-obj kw ...)
547 (define-ffi (-> #t ret-obj) kw ...))))
549 (define-ffi c-free (*: -> void:) #:symbol "free")
550 (define-ffi c-memmove ((*: dest) (*: src) (size_t: n) -> *:) #:symbol "memmove")
551 (define-ffi c-strdup (*: -> *:) #:symbol "strdup")
552 (define-ffi c-malloc (size_t: -> *:) #:symbol "malloc")
554 (define-ffi-object string:
555 #:decoder pointer->string
556 #:encoder string->pointer
560 (define (string-like->pointer obj)
562 (let loop ((object obj))
564 (when (string? object)
566 (when (symbol? object)
567 (return (loop (symbol->string object))))
568 (when (keyword? object)
569 (return (loop (keyword->symbol object))))
570 (scm-error 'wrong-type-arg "string-like->pointer"
571 "Unsupported value ~A" (list obj))))))
573 (define (pointer->string-like ptr)
574 (string->symbol (pointer->string ptr)))
576 (export string-like:)
577 (define-ffi-object string-like:
578 #:decoder pointer->string-like
579 #:encoder string-like->pointer
583 (define (toplevel-define-scm-struct-class struct-name field-names)
584 (define class-name (symbol-append '<struct- struct-name '>))
585 (define (make-slot-spec field-name)
586 (list field-name #:init-keyword (symbol->keyword field-name)))
587 (define slot-specs (map make-slot-spec field-names))
588 (define class_ (make <class>
589 #:dsupers (list <object>)
592 (module-define! (current-module) class-name class_)
595 (define (make-ffi-struct name fields-alist)
596 (define ffi-objs (map cdr fields-alist))
597 (define field-names (map car fields-alist))
598 (define struct-class_ (toplevel-define-scm-struct-class name field-names))
599 (define c-types (delay (map .ffi-type ffi-objs)))
601 (define (parse-c-values foreign-pointer)
602 (parse-c-struct foreign-pointer (force c-types)))
603 (define (%encoder struct)
604 (define scm-values (map (cute slot-ref struct <>) field-names))
605 (define c-values (map (cute encode <> <> #:clone-after? #f) ffi-objs scm-values))
606 (make-c-struct (force c-types) c-values))
607 (define (%decoder foreign-pointer)
608 (define c-values (parse-c-values foreign-pointer))
609 (define scm-values (map decode ffi-objs c-values))
610 (define result-struct (make struct-class_))
611 (map (cute slot-set! result-struct <> <>) field-names scm-values)
613 (define (%clone foreign-pointer)
614 (define sizeof-struct (sizeof (force c-types)))
615 (define c-values (parse-c-values foreign-pointer))
616 (define cloned-c-values (map clone ffi-objs c-values))
617 (define new-memory (c-malloc sizeof-struct))
618 (when (null-pointer? new-memory)
619 (error "No memory:" sizeof-struct))
620 (c-memmove new-memory (make-c-struct (force c-types) c-values) sizeof-struct))
621 (define (%free foreign-pointer)
622 (define c-values (parse-c-values foreign-pointer))
623 (for-each free ffi-objs c-values)
624 (c-free foreign-pointer))
631 #:struct-class struct-class_
632 #:ffi-objects ffi-objs))
634 (define-syntax-rule (define-ffi-struct name ((ffi-obj field-name) ...))
636 (define name (make-ffi-struct (normalize-ffi-object-name 'name)
637 (list (cons 'field-name (delay ffi-obj)) ...)))
638 (toplevel-define-accessors 'name)
639 (toplevel-maybe-define-nullable 'name)))
642 (define (parse-c-type ptr type)
643 (car (parse-c-struct ptr (list type))))
644 (define (value-memory type value)
645 (make-c-struct (list type) (list value)))
647 (define ((extern ffi-obj) pointer)
648 (define objtype (.ffi-type ffi-obj))
649 (define objsize (size-of objtype))
651 (decode ffi-obj (parse-c-type pointer objtype)))
653 (c-memmove pointer (value-memory objtype (encode ffi-obj value)) objsize))
654 (make-procedure-with-setter get set))
656 (define* (extern-pointer ffi-obj #:key (free-on-set? #f) (clone-on-set? #t))
657 (unless (eq? (.ffi-type ffi-obj) '*)
658 (error "extern-pointer can only be used with pointer ffi-objects" ffi-obj))
660 (define pointer (dereference-pointer address))
662 (decode ffi-obj pointer))
665 (let ((new-pointer* (encode ffi-obj value)))
667 (clone ffi-obj new-pointer*)
670 (free ffi-obj pointer))
671 (c-memmove address (value-memory '* new-pointer) (sizeof '*)))
672 (make-procedure-with-setter get set)))
674 (define-syntax-rule (define-extern (ffi-obj name) kw ...)
675 (define name (false-if-exception
676 ((extern ffi-obj) (dynamic-pointer* 'name kw ...)))))
678 (define* (extern-pointer-array ffi-obj base-address array-size
679 #:key (free-on-set? #f) (clone-on-set? #t))
680 (define (void*-offset count)
681 (define offset (* count (sizeof '*)))
682 (make-pointer (+ (pointer-address base-address) offset)))
683 (define pointers (map void*-offset (iota array-size)))
684 (define pointer->accessor
685 (extern-pointer ffi-obj
686 #:free-on-set? free-on-set?
687 #:clone-on-set? clone-on-set?))
688 (define variables (list->vector (map pointer->accessor pointers)))
690 (vector-ref variables n)))