Wiki
Download
Manual
Eggs
API
Tests
Bugs
show
edit
history
You can edit this page using
wiki syntax
for markup.
Article contents:
[[tags: egg]] '''DRAFT. This egg has not been published yet and its API will change. If you want to beta test, provide feedback, and help shape the final API, join {{##jiffi}} on Libera.Chat or email the author.''' == Jiffi High-level helpers for writing foreign function interface (FFI) bindings to C libraries. If you prefer to automatically generate FFI bindings for simple C/C++ code, try the [[/egg/bind|{{bind}} egg]] instead. <table role="presentation"><tr><td style="padding-top: 0.6rem; padding-right: 0.4rem;"> <strong><img alt="Hint" src="https://upload.wikimedia.org/wikipedia/commons/e/e7/Dialog-information_on.svg" width=32 /></strong> </td><td style="vertical-align: middle;"> '''If you are new to Jiffi,''' the best place to start is the [[https://gitlab.com/jcroisant/jiffi/-/blob/main/docs/getting-started.md|'''Getting Started with Jiffi''']] guide. The wiki page you are reading now contains detailed reference documentation for every feature in Jiffi, which is a lot of information! After reading the Getting Started guide to gain a basic understanding, come back to this page to look up details as needed. </td></tr></table> ; Project / Source Code: [[https://gitlab.com/jcroisant/jiffi]] ; Issue Tracker: [[https://gitlab.com/jcroisant/jiffi/issues]] ; Author: [[/users/john-croisant|John Croisant]] ; License: [[https://gitlab.com/jcroisant/jiffi/blob/main/LICENSE.txt|BSD]] '''Table of Contents''' ([[#about|skip]]) [[toc:]] == About Jiffi is designed specifically for writing FFI bindings by hand, rather than automatic generation. Its design goals are: # '''Usability:''' Bindings should be easy to use. They should follow Scheme idioms, and not require the user to understand low-level details about C memory management, pointers, etc. # '''Safety:''' Bindings should, by default, protect users against type errors and memory errors at the C level. Writing unsafe code should require a deliberate choice. # '''Maintainability:''' The binding code itself should be easy for its authors to understand, extend, and debug. It should be clear, explicit, predictable, and greppable. # '''Flexibility:''' Jiffi should be flexible enough able to handle the most common 80% of FFI use cases, and give binding authors a high degree of control over their API style. # '''Performance:''' Bindings should be as efficient as possible without sacrificing the other goals. Jiffi is especially well-suited for writing FFI bindings for C libraries that are so large that it is difficult for a human to maintain bindings using CHICKEN's [[/man/5/Module (chicken foreign)|FFI primitives]], and so syntactically complex that the [[/egg/bind|{{bind}} egg]] cannot parse it and generate bindings automatically. Jiffi evolved from the FFI helpers that were used to write the [[/egg/sdl2|{{sdl2}} egg]]. Jiffi is also good for beginners who are not familiar with the intricacies of CHICKEN's FFI features, or who are not very comfortable with C concepts like pointers and memory management, which can cause crashes and security vulnerabilities if not done correctly. Jiffi helps you create easy-to-use, safe, and efficient FFI bindings, even if you are not a C expert. All of Jiffi's features are built using the FFI primitives provided by CHICKEN, so they are cross-compatible. The documentation for each macro describes which primitives it uses, so you can understand what is happening behind the scenes. In most cases you can use one feature of Jiffi without using the other features, although some are closely coupled out of necessity. == Installation Jiffi has not yet been published, so you must clone or download the git repository, then run {{chicken-install}} within the directory. Jiffi requires CHICKEN 4.13 or higher. It is compatible with both CHICKEN 4 and CHICKEN 5. If you define any [[#armor-thread-safety|thread-safe armors]], you should add [[/eggref/5/srfi-18|{{srfi-18}}]] to your dependencies. (This is not necessary for CHICKEN 4, which has SRFI-18 built in.) == Example A thorough example can be found in the {{tests}} directory of the source code: * [[https://gitlab.com/jcroisant/jiffi/blob/main/tests/libfoo.scm|{{libfoo.scm}}]]: example bindings to a made-up C library * [[https://gitlab.com/jcroisant/jiffi/blob/main/tests/libfoo.h|{{libfoo.h}}]]: the made-up C library Snippets of this example are included in this wiki documentation. == Functions and Macros === {{define-binding}} <macro>(define-binding ...)</macro> Defines a Scheme procedure which calls a C function or C macro. This is syntactic sugar for {{define}} and [[/man/5/Module (chicken foreign)#foreign-lambda|{{foreign-lambda}}]]. Usage: <enscript> (define-binding (SCHEME-NAME FUNC_NAME) ; or just SCHEME-NAME safe: SAFE ; optional return: RETURN-TYPE ; optional args: ((ARG-TYPE ARG_NAME) ...)) ; optional </enscript> {{SCHEME-NAME}} is the procedure name to define in Scheme. {{FUNC_NAME}} is a non-quoted symbol or string containing the name of the C function or C macro. If {{SCHEME-NAME}} and {{FUNC_NAME}} are the same, you can write {{SCHEME-NAME}} instead of {{(SCHEME-NAME FUNC_NAME)}}. If {{SAFE}} is {{#t}}, the binding will use [[/man/5/Module (chicken foreign)#foreign-safe-lambda|{{foreign-safe-lambda}}]]. You should do this if you want the C function to be able to [[/man/5/Module (chicken foreign)#callbacks|call back into Scheme]]. If {{SAFE}} is {{#f}} or the {{safe:}} clause is omitted, the binding will use {{foreign-lambda}}. {{RETURN-TYPE}} is a [[/man/5/Foreign type specifiers|foreign type specifier]] describing the return value of the function. Each {{ARG-TYPE}} is a foreign type specifier describing that argument's type. Each {{ARG_NAME}} is a non-quoted symbol describing the argument, usually the argument name in C. This is not actually used in the macro, but it makes it easier for you to see that the binding has the correct number and order of arguments, so mistakes are less likely. If the {{return:}} clause is omitted, the return type is {{void}}. If the {{args:}} clause is omitted, the procedure accepts no arguments. Examples: <enscript> ;; C function and macro definitions (foreign-declare " FOO_Event* FOO_CreateKeyEvent(FOO_KeyCode code, FOO_KeyMod mods) { FOO_Event* ev = malloc(sizeof(FOO_Event)); ev->type = FOO_EVENT_TYPE_KEY; ev->key.code = code; ev->key.mods = mods; return ev; } #define FOO_ADD1(x) ((x) + 1) ") ;; Binding to a C function, with scheme-style name, and custom foreign ;; types (see examples in the "Armor wrapper" and "Armor unwrapper" ;; sections) to automatically convert return value and args. (define-binding (create-key-event FOO_CreateKeyEvent) return: FOO_KeyEvent* args: ((FOO_KeyCode code) (FOO_KeyMod mods))) ;; Binding to a C macro (define-binding FOO_ADD1 return: int args: ((int x))) (define ev (create-key-event 'a '(lctrl))) (key-event-code ev) ; ⇒ 'a (key-event-mods ev) ; ⇒ '(lctrl) (FOO_ADD1 4) ; ⇒ 5 </enscript> === {{define-binding** (2)}} <macro>(define-binding** ...)</macro> Like [[#define-binding|{{define-binding}}]], except that it is based on [[#foreign-lambda|{{foreign-lambda**}}]], so you can specify a custom body of C code, or construct the function body at macro expansion time. Usage: <enscript> (define-binding** SCHEME-NAME safe: SAFE ; optional return: RETURN-TYPE ; optional args: ((ARG-TYPE ARG_NAME) ...) ; optional BODY-CLAUSE ...) </enscript> {{SCHEME-NAME}} is the procedure name to define in Scheme. If {{SAFE}} is {{#t}}, the binding will use {{foreign-safe-lambda**}}. You should do this if you want the C function to be able to [[/man/5/Module (chicken foreign)#callbacks|call back into Scheme]]. If {{SAFE}} is {{#f}} or the {{safe:}} clause is omitted, the binding will use {{foreign-lambda**}}. If the {{return:}} clause is omitted, the return type is {{void}}. If the {{args:}} clause is omitted, the function accepts no arguments. Each {{ARG_NAME}} must be a non-quoted symbol that is a valid C variable name. You can then use {{ARG_NAME}} within the function body. Each {{BODY-CLAUSE}} becomes a separate line of the function body. It can either be a string, or a list of the form {{(FORMAT-STRING FORMAT-ARG ...)}}, which are arguments passed to [[/man/5/Module (chicken format)#sprintf|{{sprintf}}]] to construct the string. See [[#foreign-lambda|{{foreign-lambda**}}]] for more information. For obscure technical reasons, you must use {{C_return(...)}} instead of the normal {{return}} statement to return a value from C. Example: <enscript> ;; Create a binding to arbitrary C code. (define-binding** squaref return: float args: ((float x)) "C_return(x * x);") (squaref 8.0) ; ⇒ 64.0 ;; Expands into a define-binding** that gets the value of a struct ;; field. This is similar to how struct-getter is implemented. (define-syntax define-struct-getter (syntax-rules () ((define-struct-getter GETTER STRUCT_NAME FIELD-NAME FIELD-TYPE) (define-binding** GETTER return: FIELD-TYPE args: (((c-pointer STRUCT_NAME) obj)) ;; These could easily be one line, but they are split to ;; demonstrate that you can have multiple body clauses: "C_return(" ("obj->~A" 'FIELD-NAME) ");")))) (define-struct-getter event-type "FOO_Event" "type" int) ;; Expands to: ;; ;; (define-binding** event-type ;; return: int ;; args: (((c-pointer "FOO_Event") obj)) ;; "C_return(" ;; ("obj->~A" '"type") ;; ");"))));; ;; ;; Which produces the C function body: ;; ;; C_return( ;; obj->type ;; ); </enscript> === {{foreign-lambda**}} <macro>(foreign-lambda** ...)</macro> <macro>(foreign-safe-lambda** ...)</macro> Like [[/man/5/Module (chicken foreign)#foreign-lambda|{{foreign-lambda*}}]] and [[/man/5/Module (chicken foreign)#foreign-safe-lambda|{{foreign-safe-lambda*}}]], except the function body strings can be constructed dynamically at macro expansion time using [[/man/5/Module (chicken format)#sprintf|{{sprintf}}]]. These are building blocks for higher-level macros that generate function bodies based on the macro's arguments. Usage: <enscript> (foreign-lambda** ; or foreign-safe-lambda** RETURN-TYPE ((ARG-TYPE ARG_NAME) ...) BODY-CLAUSE ...) </enscript> {{RETURN-TYPE}} is a [[/man/5/Foreign type specifiers|foreign type specifier]] describing the return value. Each {{ARG-TYPE}} is a foreign type specifier describing the argument. Each {{ARG_NAME}} is a non-quoted symbol which is used as the variable name within the function body. It must be a valid C variable name. Each {{BODY-CLAUSE}} becomes a separate line of the function body. It can either be a string, or a list of the form {{(FORMAT-STRING FORMAT-ARG ...)}}, which are arguments passed to {{sprintf}} to construct the string. Because the body string is constructed at macro expansion time, each {{FORMAT-ARG}} must be an expression that can be evaluated at macro expansion time in the default environment, such as a quoted symbol, a string, a number, or a call to a built-in procedure. For obscure technical reasons, you must use {{C_return(...)}} instead of the normal {{return}} statement to return a value from C. Examples: <enscript> (define return-foo (foreign-lambda** c-string () ; no arguments "C_return(\"foo\");")) (return-foo) ; ⇒ "foo" ;; Silly example that expands into a foreign-lambda** that performs a ;; simple integer math operation described by the macro caller. ;; X and Y are evaluated at macro-expansion time. (define-syntax c-math (syntax-rules () ((math-fn X OPERATOR Y) (foreign-lambda** int () ; no arguments ("C_return(~A ~A ~A);" X 'OPERATOR Y))))) ;; Body becomes "C_return(40 + 2);" (define return-42 (c-math (* 8 5) + (/ 4 2))) (return-42) ; ⇒ 42 </enscript> === {{define-callback}} <macro>(define-callback ...)</macro> Create a C function using Scheme code, that can be called from C code or used as a C [[/man/5/Module (chicken foreign)#callbacks|callback]]. See the [[https://gitlab.com/jcroisant/jiffi/blob/main/docs/callbacks.md|Callbacks in Jiffi]] guide. Usage: <enscript> (define-callback (SCHEME-NAME C_NAME) ; or SCHEME-NAME quals: "QUALIFIER ..." ; optional return: RETURN-TYPE ; optional args: ((ARG-TYPE ARG-NAME) ...) ; optional BODY ...) </enscript> {{SCHEME-NAME}} is a variable to define in Scheme. It will hold a pointer to the C function that this macro creates. It can be passed to a C function that expects a callback function pointer, for example. {{C_NAME}} is a non-quoted symbol or string specifying the name of the function to define in C. It must be a valid C function name. In addition to being defined as a C function, {{C_NAME}} is also defined as a Scheme procedure. If you don't care what {{C_NAME}} to define, you can write {{SCHEME-NAME}} instead of {{(SCHEME-NAME C_NAME)}}. A valid {{C_NAME}} will be automatically generated. It will be unpredictable to help avoid name collisions. {{"QUALIFIER ..."}} is an optional string containing C type qualifiers for the function, such as {{"__stdcall const"}}. If the {{quals:}} clause is omitted, no qualifiers are used. {{RETURN-TYPE}} is a [[/man/5/Foreign type specifiers|foreign type specifier]] describing the return value. If the {{return:}} clause is omitted, the return type is {{void}}. Each {{ARG-TYPE}} is a foreign type specifier describing the argument. Each {{ARG-NAME}} is a non-quoted symbol which is used as the variable name within the body. If the {{args:}} clause is omitted, the function accepts no arguments. Each {{BODY}} is a Scheme expression. The value of the final {{BODY}} expression must the match the return type. This macro uses {{define-external}} to define the C function, and [[/man/5/Module (chicken foreign)#location|{{location}}]] to get the C function pointer. == GC Roots In CHICKEN, Scheme values often move to new locations in memory when the garbage collector (GC) runs. So, you cannot simply store a pointer to the Scheme value inside a C struct, for example. The Scheme value may soon move to a new location, while the C pointer will still point to the old location. You can work around this by using a GC root, which is an object that holds a reference to a Scheme value that will always be valid, even after the garbage collector runs. You can store a pointer to the GC root in the C struct, then later use that pointer to retrieve the original Scheme value. Besides storing values in structs, GC roots are also useful when using a Scheme procedure as a callback to a C function that invokes the callback repeatedly (such as an iterator) or at a later time (such as an event handler). If the GC runs, the callback procedure itself might be moved to new location, so the pointer will be invalid the next time the C function tries to call it. Instead, you can often use a GC root in combination with [[#define-callback|{{define-callback}}]]. See the [[https://gitlab.com/jcroisant/jiffi/blob/main/docs/callbacks.md|Callbacks in Jiffi]] guide. GC roots can be created and used in C code with [[/man/5/Embedding#chicken_new_gc_root|{{CHICKEN_new_gc_root}} and related functions]]. Jiffi provides bindings and helper procedures to make it easy to use GC roots in Scheme code as well. === {{gc-root-ref}} <procedure>(gc-root-ref root) → value</procedure> Look up and return the Scheme value for the given GC root. {{root}} must be a pointer to a GC root, such as one created with {{call-with-gc-root}} or {{make-gc-root}}. This is a binding to [[/man/5/Embedding#chicken_gc_root_ref|{{CHICKEN_gc_root_ref}}]]. === {{call-with-gc-root}} <procedure>(call-with-gc-root value proc) → result of proc</procedure> Creates a [[#gc-roots|GC root]] for the given Scheme {{value}}, and calls {{proc}} with the GC roots as arguments. After {{proc}} returns, deletes the GC root and returns the result of {{proc}}. {{proc}} must be a procedure which accepts one argument, a pointer to the GC root which can be passed to {{gc-root-ref}} to get {{value}}. The GC root pointer will become invalid after {{proc}} returns. <table role="presentation"><tr><td style="padding-top: 0.6rem; padding-right: 0.4rem;"> <strong><img alt="Warning" src="https://upload.wikimedia.org/wikipedia/commons/6/6e/Dialog-warning.svg" width=32 /></strong> </td><td style="vertical-align: middle;"> If {{proc}} does not return normally (e.g. if an exception occurs, or if you invoke a continuation), the GC root will not be deleted, which will cause a memory leak. Use {{gc-root-delete!}} within {{proc}} to manually clean up the GC roots before raising an exception or calling a continuation. </td></tr></table> Example: <enscript> (let ((a (list 1 2 3))) (call-with-gc-root a (lambda (a-root) (assert (eq? a (gc-root-ref a-root))) 'ok!))) ;; ⇒ ok! </enscript> This is a convenience procedure based on {{make-gc-root}} and {{gc-root-delete!}}. === {{make-gc-root}} <procedure>(make-gc-root value) → pointer</procedure> Creates a GC root for the given Scheme value, and returns a pointer to the GC root. The pointer can be passed to {{gc-root-ref}} to get a reference to {{value}} again. This procedure uses [[/man/5/Embedding#chicken_new_gc_root|{{CHICKEN_new_gc_root}}]] and {{CHICKEN_gc_root_set}}. === {{gc-root-delete!}} <procedure>(gc-root-delete! root)</procedure> Delete the given GC root. You should delete GC roots when you are done using them, to avoid memory leaks. Do not pass a GC root pointer to {{gc-root-ref}} after it has been deleted. {{root}} must be a pointer to a GC root, such as one created by {{call-with-gc-root}} or {{make-gc-root}}. This is a binding to [[/man/5/Embedding#chicken_delete_gc_root|{{CHICKEN_delete_gc_root}}]]. == Constants and Enums === {{define-foreign-values}} <macro>(define-foreign-values ...)</macro> Defines (and optionally exports) variables with the values of C constants or expressions. Each value can be the name of a {{const}}, {{enum}}, or {{#define}}; or it can be a string containing a C expression which will be evaluated at run time. If you want to define procedures to convert integer constants to symbols, and vice versa, use [[#define-enum-group|{{define-enum-group}}]] instead. Usage: <enscript> (define-foreign-values export: EXPORT ; optional type: FOREIGN-TYPE (VAR EXPRESSION) ; or just VAR ...) </enscript> If {{EXPORT}} is {{#t}}, every {{VAR}} will be automatically exported from the current module, so you don't need to write a long {{(export ...)}} form with all the variables. If {{EXPORT}} is {{#f}} or the keyword clause is omitted, they are not automatically exported. {{FOREIGN-TYPE}} is a [[/man/5/Foreign type specifiers|foreign type specifier]] that describes all the foreign values. Each {{VAR}} is a variable name to define in Scheme. Each {{EXPRESSION}} is a non-quoted symbol or string containing the name of a foreign constant in C, or a string containing a C expression. If {{VAR}} and {{EXPRESSION}} are the same, you can write {{VAR}} instead of {{(VAR EXPRESSION)}}. This must be a variable name (non-quoted symbol), not a string. Examples: <enscript> ;; C enum definitions (foreign-declare " #define FOO_BAR \"Bar!\" #define FOO_BUZZ \"Buzz!\" const float FOO_PHI = 2.718281828459045; const float FOO_PI = 3.141592653589793; typedef enum { FOO_BLEND_NONE = 0, FOO_BLEND_ADD = 1, FOO_BLEND_SUB = 2, FOO_BLEND_MUL = 4 } FOO_BlendMode; #define FOO_ADD1(x) ((x) + 1) ") ;; #define (define-foreign-values export: #t type: c-string FOO_BAR FOO_BUZZ) ;; const (define-foreign-values export: #t type: float (φ FOO_PHI) (π FOO_PI)) ;; enum (define-foreign-values export: #t type: int FOO_BLEND_NONE FOO_BLEND_ADD FOO_BLEND_SUB FOO_BLEND_MUL) ;; C expression (define-foreign-values export: #t type: double (four "1.5 + 2.5") (five "FOO_ADD1(4)") (sqrt2 "sqrt(2.0)")) FOO_BAR ; ⇒ "Bar!" φ ; ⇒ 2.71828174591064 FOO_BLEND_SUB ; ⇒ 2 four ; ⇒ 4.0 five ; ⇒ 5.0 sqrt2 ; ⇒ 1.4142135623731 </enscript> {{define-foreign-values}} is based on [[/man/5/Module (chicken foreign)#foreign-value|{{foreign-value}}]]. It is basically equivalent to: <enscript> (export VAR ...) (define VAR (foreign-value EXPRESSION FOREIGN-TYPE)) ... </enscript> === {{define-enum-group}} <macro>(define-enum-group ...)</macro> Defines procedures to convert integer constants to symbols, and vice versa, and optionally defines a variable for each integer constant. Also generates type declarations for the converter procedures. If you only want to define variables, and don't need symbol conversion procedures, see [[#define-foreign-values|{{define-foreign-values}}]]. Usage: <enscript> (define-enum-group type: FOREIGN-TYPE vars: VARS-MODE ; optional, default: define symbol->int: SYMBOL->INT ; optional allow-ints: ALLOW-INTS ; optional int->symbol: INT->SYMBOL ; optional ;; Optional `requires:' clause for enum requirements ((requires: SCHEME-FEATURE-CLAUSE "C_PREPROCESSOR_EXPR") ;; If VAR and CONSTANT are the same, you can write ;; VAR instead of (VAR CONSTANT) (SYMBOL (VAR CONSTANT) FLAG ...) ...) ...) </enscript> {{FOREIGN-TYPE}} is a [[/man/5/Foreign type specifiers#integers|foreign integer type specifier]] that will be used for all the constants. The type specifier {{integer}} is a safe choice, but if you know all the constants are in the range -1,073,741,824 to 1,073,741,823 inclusive, you can safely use {{int}} which may be more efficient. {{VARS-MODE}} specifies whether variables for the enums should be defined and/or exported from the current module, using [[#define-foreign-values|{{define-foreign-values}}]]. If {{VARS-MODE}} is {{define}} or the clause is omitted, each {{VAR}} will be defined but not automatically exported, equivalent to {{(define-foreign-values export: #f ...)}}. If {{VARS-MODE}} is {{export}}, every {{VAR}} will be defined and also automatically exported, equivalent to {{(define-foreign-values export: #t ...)}}. If {{VARS-MODE}} is {{#f}}, variables will not be defined or exported. This allows you to define the enum converter procedures without defining any variables. {{SYMBOL->INT}} and {{INT->SYMBOL}} are procedure names to define as [[#enum-converters|enum converters]]. If any is {{#f}} or its keyword clause is omitted, that procedure will not be defined. If {{ALLOW-INTS}} is {{#t}}, {{SYMBOL->INT}} will also accept integers, not only symbols. Integers are returned immediately, without any checks. If {{ALLOW-INTS}} is {{#f}} or the keyword clause is omitted, then if {{SYMBOL->INT}} is called with an integer, it will invoke its {{not-found-callback}} (if there is one) or signal an exception. The optional {{(requires: SCHEME-FEATURE-CLAUSE "C_PREPROCESSOR_EXPR")}} clause is used to specify [[#enum-requirements|enum requirements]]. If the {{(requires: ...)}} clause is omitted, the enums in the list will always be included in the group. Each {{(SYMBOL (VAR CONSTANT) FLAG ...)}} or {{(SYMBOL VAR FLAG ...)}} defines the relationship between a symbol and an integer constant. Each {{SYMBOL}} is a non-quoted symbol. It will be a valid argument to {{SYMBOL->INT}}. And it will be a possible return value from {{INT->SYMBOL}}, unless it is marked with the {{alias}} flag. Each {{VAR}} is a variable name to define in Scheme, with the value of {{CONSTANT}}. Each {{CONSTANT}} is a non-quoted symbol or string containing the name of a C integer constant, such as a global {{const}}, {{#define}}, or {{enum}} value. (It is not necessary for all the {{CONSTANT}}s to be from the same {{enum}} statement.) Or, it can be a string containing an integer constant expression, which must be a valid {{case}} label in a C {{switch}} statement, such as {{"1 + 1"}} or {{"FOO - 1"}}. The value of each constant or expression must be unique within this enum group, unless it is marked with the {{alias}} flag. If {{VAR}} and {{CONSTANT}} are the same, you can write {{VAR}} instead of {{(VAR CONSTANT)}}. This must be a variable name (non-quoted symbol), not a string. {{FLAG}}s are optional non-quoted symbols marking this symbol/constant relationship as special in some way. The following flag is currently recognized (other flags are ignored): ; {{alias}} : This constant has the same integer value as another constant in this group. This constant will be omitted from {{INT->SYMBOL}} to avoid a C compiler error about duplicate case values. Basic example: <enscript> ;; C enum definitions (foreign-declare " typedef enum { FOO_BLEND_NONE = 0, FOO_BLEND_ADD = 1, FOO_BLEND_SUB = 2, FOO_BLEND_MUL = 4 } FOO_BlendMode; ") (define-enum-group type: int vars: export symbol->int: blend-mode->int int->symbol: int->blend-mode ((none BLEND_NONE) (add BLEND_ADD) (sub BLEND_SUB) (mul BLEND_MUL))) (blend-mode->int 'sub) ; ⇒ 2 (int->blend-mode BLEND_SUB) ; ⇒ 'sub (blend-mode->int 'zzz) ; Error: unrecognized enum symbol: zzz (int->blend-mode 42) ; Error: unrecognized enum value: 42 ;; Using not-found-callbacks: (blend-mode->int 'zzz (lambda (sym) -1)) ; ⇒ -1 (int->blend-mode 42 identity) ; ⇒ 42 </enscript> Advanced example (renaming, aliases, requirements): <enscript> ;; C enum definitions (foreign-declare " #define FOO_VERSION 110 typedef enum { POWER_EMPTY = 0, POWER_NONE = 0, /* alias */ POWER_LOW = 1, POWER_HIGH, } PowerLevel; ") ;; Or compile with flag: -D foo-1.1+ (begin-for-syntax (register-feature! 'foo-1.1+)) (define-enum-group type: int vars: export symbol->int: power-level->int allow-ints: #t int->symbol: int->power-level ;; These enums are always included. ((empty (power/empty FOO_POWER_EMPTY)) (none (power/none FOO_POWER_NONE) alias) (low (power/low FOO_POWER_LOW)) (high (power/high FOO_POWER_HIGH))) ;; Feature ID foo-1.1+ is registered above, so these requirements ;; are satisfied, so this enum will be included. ((requires: foo-1.1+ "FOO_VERSION >= 110") (wired (power/wired FOO_POWER_WIRED))) ;; These requirements are not satisfied, so this will be omitted. ((requires: foo-1.2+ "FOO_VERSION >= 120") (charge (power/charge FOO_POWER_CHARGE)))) ;; `FOO_POWER_LOW' was renamed to `power/low' power/low ; => 1 FOO_POWER_LOW ; Error: unbound variable: FOO_POWER_LOW ;; power/none is an alias, so it is omitted from int->power-level. ;; power/empty has the same integer value, so 'empty is returned. (int->power-level power/none) ; ⇒ 'empty ;; `(requires: foo-1.2+ "FOO_VERSION >= 120")` was not satisfied. power/charge ; Error: unbound variable: power/charge (power-level->int 'charge) ; Error: unrecognized enum symbol: charge ;; `allow-ints: #t` so power-level->int accepts integer args. (power-level->int 42) ; ⇒ 42 </enscript> {{define-enum-group}} is based on [[/man/5/Module (chicken foreign)#foreign-value|{{foreign-value}}]] and [[/man/5/Module (chicken foreign)#foreign-primitive|{{foreign-primitive}}]]. Converting from a symbol to an integer uses a Scheme {{case}} form. Converting from an integer to a symbol uses a C {{switch}} statement. ==== Enum converters The enum converter procedures defined with [[#define-enum-group|{{define-enum-group}}]] have the following interfaces: <enscript> (SYMBOL->INT input #!optional not-found-callback) → integer (INT->SYMBOL input #!optional not-found-callback) → symbol </enscript> {{not-found-callback}} is an optional procedure of one argument. If a converter does not recognize the {{input}}, the converter returns the result of calling {{(not-found-callback input)}}. The callback should either return a value of the correct type (integer or symbol), or else signal an exception. If {{not-found-callback}} is {{#f}} or omitted, the converter will signal an exception if the input is unrecognized. If you passed {{allow-ints: #t}} to {{define-enum-group}}, then {{SYMBOL->INT}} will accept either a symbol or an integer. If it is passed an integer, it returns that integer immediately. It does not check whether the integer is one of the constants in this group. This allows users to provide the integer constant directly, for optimization or to handle special cases. But, it also allows users to pass invalid enum values, which might possibly cause the software to malfunction. You must decide whether flexibility or safety is more important for this enum group. ==== Enum requirements In some situations, certain enum constants are not always defined. For example, maybe certain constants are defined only in the newest version of the C library, or defined only when the C library is compiled with an optional feature, or defined only when compiled for a certain operating system or hardware architecture. If you try to include constants that are not defined, your software will have a C compiler error like "{{error: ‘FOO’ undeclared}}" or "{{error: use of undeclared identifier 'FOO}}". You can solve this by using a {{(requires:)}} clause to specify the conditions when certain constants should be included in the enum group: <enscript> (define-enum-group ;; (keywords args omitted for brevity) ((requires: SCHEME-FEATURE-CLAUSE "C_PREPROCESSOR_EXPR") (SYMBOL (VAR CONSTANT) FLAG ...) ...) ...) </enscript> You can pass multiple lists of constants, and each list can have different requirements. The constants in each list will be included in the enum group if its requirements are satisfied at compile time. If a list of constants does not have a {{(requires:)}} clause, those constants will always be included in the group. Each {{SCHEME-FEATURE-CLAUSE}} is a [[/man/5/Module (chicken platform)#features|feature identifier]] or other clause that is allowed in [[/man/5/Extensions to the standard#cond-expand|{{cond-expand}}]], in the code {{(cond-expand (SCHEME-FEATURE-CLAUSE ...))}}. Each {{"C_PREPROCESSOR_EXPR"}} is a string containing an C preprocessor expression that is allowed in the C code: <enscript highlight="c"> #if C_PREPROCESSOR_EXPR ... #endif </enscript> {{SCHEME-FEATURE-CLAUSE}} and {{"C_PREPROCESSOR_EXPR"}} should both have the same meaning. In other words, they should both pass when the enums are defined, and they should both fail when the enums are not defined. There should never be a situation where only one passes. === {{define-enum-packer}} <macro>(define-enum-packer ...)</macro> Defines a procedure that will "pack" a list of enums (or a single enum) into a single integer, aka a bitfield. The packer converts each enum into an integer, then combines them using [[/man/5/Module (chicken bitwise)#binary-integer-operations|{{bitwise-ior}}]]. This supplements [[#define-enum-group|{{define-enum-group}}]] for cases where the enums are bitmasks or flags. See also [[#define-enum-unpacker|{{define-enum-unpacker}}]] to go in the opposite direction, converting an integer into a list of symbols. Usage: <enscript> (define-enum-packer PACKER (SYMBOL->INT) allow-ints: ALLOW-INTS) ; optional </enscript> {{PACKER}} is the procedure name to define as the [[#enum-packer|enum packer]]. {{SYMBOL->INT}} is an existing procedure that converts a symbol into an integer value, such as a procedure defined with [[#define-enum-group|{{define-enum-group}}]]. It must accept a second argument, which will be the {{not-found-callback}} procedure passed to the packer, or {{#f}} if no callback was passed to the packer. If {{ALLOW-INTS}} is {{#t}}, the packer will accept integers as well as symbols. If the packer encounters an integer, either in the list of enums or as a single argument, the integer will be used without trying to convert it. This allows users to pack the bitfield manually, for optimization or special cases. If {{ALLOW-INTS}} is {{#f}} or the keyword clause is omitted, then the packer will always try to convert the enums using {{SYMBOL->INT}} regardless of type. Example: <enscript> (foreign-declare " typedef enum { FOO_KMOD_NONE = 0b0000, FOO_KMOD_LCTRL = 0b0001, FOO_KMOD_RCTRL = 0b0010, FOO_KMOD_CTRL = 0b0011 } FOO_KeyMod; ") (define-enum-group type: int symbol->int: keymod->int int->symbol: int->keymod ((none FOO_KMOD_NONE) (lctrl FOO_KMOD_LCTRL) (rctrl FOO_KMOD_RCTRL) (ctrl FOO_KMOD_CTRL))) (define-enum-packer pack-keymods (keymod->int) allow-ints: #t) (pack-keymods '(lctrl)) ; ⇒ 1 (pack-keymods 'lctrl) ; ⇒ 1 (pack-keymods '(rctrl lctrl)) ; ⇒ 3 (pack-keymods '(lctrl 6)) ; ⇒ 7 (pack-keymods '()) ; ⇒ 0 (pack-keymods 42) ; ⇒ 42 (pack-keymods '(lctrl foo)) ; error! (pack-keymods '(lctrl foo) (lambda (sym) 16)) ; ⇒ 17 </enscript> ==== Enum packer An enum packer procedure defined with [[#define-enum-packer|{{define-enum-packer}}]] has the following interface: <enscript> (PACKER enums #!optional not-found-callback) → integer </enscript> {{enums}} can be a list of symbols, or a single symbol. If the packer was defined with {{allow-ints: #t}}, the list can also contain integers, and {{enums}} can also be a single integer; the integer(s) will be used as-is without passing them to {{SYMBOL->INT}}. {{not-found-callback}} is an optional procedure that will be passed as the second argument to {{SYMBOL->INT}}. Assuming {{SYMBOL->INT}} was defined with [[#define-enum-group|{{define-enum-group}}]], the callback will be invoked if an enum is not recognized. The callback should either return an integer or signal an exception. === {{define-enum-unpacker}} <macro>(define-enum-unpacker ...)</macro> Defines a procedure that will "unpack" an integer (a bitfield) into a list of enum symbols. This supplements [[#define-enum-group|{{define-enum-group}}]] in cases where the enums are bitmasks or flags. See also [[#define-enum-packer|{{define-enum-packer}}]] to go in the opposite direction, converting a list of symbols into an integer. Usage: <enscript> (define-enum-unpacker UNPACKER (INT->SYMBOL) masks: BITMASKS) </enscript> {{UNPACKER}} is the procedure name to define as the [[#enum-unpacker|enum unpacker]]. {{INT->SYMBOL}} is an existing procedure that converts an integer value (bitmask) into a symbol, such as a procedure defined with [[#define-enum-group|{{define-enum-group}}]]. {{BITMASKS}} is an expression that evaluates to a list of all the integer bitmasks that should be recognized. The expression will be evaluated once, and each bitmask will be converted using {{INT->SYMBOL}} once, at the time {{UNPACKER}} is defined. <table role="presentation"><tr><td style="padding-top: 0.6rem; padding-right: 0.4rem;"> <strong><img alt="Hint" src="https://upload.wikimedia.org/wikipedia/commons/e/e7/Dialog-information_on.svg" width=32 /></strong> </td><td style="vertical-align: middle;"> Bitmasks that have a value of 0 (zero) will always match, so it is not useful to include them in the {{BITMASKS}} list. </td></tr></table> Example: <enscript> (foreign-declare " typedef enum { FOO_KMOD_NONE = 0b0000, FOO_KMOD_LCTRL = 0b0001, FOO_KMOD_RCTRL = 0b0010, FOO_KMOD_CTRL = 0b0011 } FOO_KeyMod; ") (define-enum-group type: int symbol->int: keymod->int int->symbol: int->keymod ((none FOO_KMOD_NONE) (lctrl FOO_KMOD_LCTRL) (rctrl FOO_KMOD_RCTRL) (ctrl FOO_KMOD_CTRL))) (define-enum-unpacker unpack-keymods (int->keymod) masks: (list FOO_KMOD_LCTRL FOO_KMOD_RCTRL FOO_KMOD_CTRL)) (unpack-keymods FOO_KMOD_NONE) ; ⇒ '() (unpack-keymods FOO_KMOD_LCTRL) ; ⇒ '(lctrl) (unpack-keymods FOO_KMOD_CTRL) ; ⇒ '(lctrl rctrl ctrl) </enscript> ==== Enum unpacker An enum unpacker procedure defined with [[#define-enum-unpacker|{{define-enum-unpacker}}]] has the following interface: <enscript> (UNPACKER bitfield) → list of symbols </enscript> An unpacker compares {{bitfield}} (an integer) to each bitmask in the {{BITMASKS}} list. If a bitmask matches (see below), the result of {{(INT->SYMBOL bitmask)}} is included in the list of symbols returned. The list of symbols will be in the same relative order as {{BITMASKS}}. Each bitmask matches only if ''every'' true bit in the bitmask is also true in the bitfield. Expressed in Scheme: {{(= bitmask (bitwise-and bitfield bitmask))}}. Expressed in C: {{bitmask == (bitfield & bitmask)}}. == Armor Armor types are [[/man/5/Module (chicken base)#record-structures|record types]] that are used to wrap bare data, such as pointers, [[/man/5/Module (chicken locative)|locatives]], or [[/man/5/Module (chicken blob)|blobs]]. Using armor provides protection against certain kinds of bugs, crashes, and security vulnerabilities. Armor also makes debugging easier, and provides extra functionality. But, it requires more memory and CPU time than using bare data, so you may want to use bare data in performance-critical situations. See "[[https://gitlab.com/jcroisant/jiffi/blob/main/docs/getting-started.md#why-should-i-use-armor|Why should I use armor?]]" in the Getting Started guide for more information. Armor types can be used to wrap C structs/unions, or arrays of C structs/unions. You can define an armor type for each struct, union, and array type in the C library, then use the macros in the "[[#structs-and-unions|Structs and Unions]]" or "[[#arrays-of-structs-and-unions|Arrays of Structs and Unions]]" sections to define additional procedures specific to those use cases. === {{define-armor-type}} <macro>(define-armor-type ...)</macro> Defines a [[/man/5/Module (chicken base)#record-structures|record type]] that can wrap a C struct/union or an array of structs/unions, and defines some basic procedures related to the record type. Also generates [[/man/5/Types|type declarations]] for those procedures, except for extra slot getters and setters (if there are any). Usage: <enscript> (define-armor-type ARMOR-NAME pred: PRED wrap: WRAP unwrap: UNWRAP children: CHILDREN ; optional, default #t locking: LOCK-EXPR ; optional ;; Extra slots are optional (SLOT SLOT-GETTER SLOT-SETTER) ; SLOT-SETTER is optional ...) </enscript> {{ARMOR-NAME}} is the record type name to define, using [[/man/5/Module (chicken base)#define-record-type|define-record-type]]. {{PRED}} is the procedure name to define as the type predicate, which returns {{#t}} if passed an instance of this armor type, or {{#f}} if passed anything else. {{WRAP}} is the procedure name to define as the [[#armor-wrapper|armor wrapper]], which returns a new armor instance that wraps some bare data. {{UNWRAP}} is the procedure name to define as the [[#armor-unwrapper|armor unwrapper]], which returns a pointer or locative to the bare data, or {{#f}} if it is [[#armor-null|null]]. {{CHILDREN}} is a boolean that specifies the default value of whether instances of this armor type track their children, so that the children can be nullified when the instance is freed or nullified. See [[#armor-tracks-children|{{armor-tracks-children?}}]]. {{LOCK-EXPR}} can be either [[#make-armor-lock|{{(make-armor-lock)}}]] to make instances of this armor type thread safe by default, or {{#f}} to make the type not thread-safe by default. See "[[https://gitlab.com/jcroisant/jiffi/blob/main/docs/getting-started.md#when-should-i-enable-armor-thread-safety|When should I enable armor thread safety?]]" in the Getting Started guide for more information. You can also use [[#armor-lock-set|{{armor-lock-set!}}]] to enable or disable thread safety for a single armor instance. Each {{SLOT}} is an optional extra slot in the record type, which you can use for your own purposes, for example to hold metadata. An extra slot can hold any Scheme object. <table role="presentation"><tr><td style="padding-top: 0.6rem; padding-right: 0.4rem;"> <strong><img alt="Hint" src="https://upload.wikimedia.org/wikipedia/commons/e/e7/Dialog-information_on.svg" width=32 /></strong> </td><td style="vertical-align: middle;"> If you are defining armor for a C array, and you want to use [[#define-array-allocators|{{define-array-allocators}}]], it is recommended that [[#array-armor-length-slot|the first extra slot is used to hold the array length]]. The slot name does not matter, only that it is the first. </td></tr></table> Each {{SLOT-GETTER}} and {{SLOT-SETTER}} are the procedure names to define as the slot getter and setter. If {{SLOT-SETTER}} is omitted, no setter procedure will be defined, and that slot will be read-only (it can only be set when the armor is created with {{WRAP}}). If {{SLOT-SETTER}} is the form {{(setter SLOT-GETTER)}}, no setter procedure will be defined, but that slot will be settable using {{(set! (SLOT-GETTER armor) value)}}. If you want to have a setter procedure and also make the getter settable with {{set!}}, use {{(set! (setter SLOT-GETTER) SLOT-SETTER)}} after defining the armor type (see example). If you define any extra slots, you may also want to provide {{SLOT-DEFAULT}}s if you use [[#define-struct-allocators|{{define-struct-allocators}}]] or [[#define-array-allocators|{{define-array-allocators}}]]. <table role="presentation"><tr><td style="padding-top: 0.6rem; padding-right: 0.4rem;"> <strong><img alt="Warning" src="https://upload.wikimedia.org/wikipedia/commons/6/6e/Dialog-warning.svg" width=32 /></strong> </td><td style="vertical-align: middle;"> The record type will also contain one or more private slots which are used internally by Jiffi. The number and order of slots may change. Only use the slot getters and setters to access the slots. Do not rely on the slot layout staying the same! </td></tr></table> Examples: <enscript> ;; Armor for a C struct/union (define-armor-type event pred: event? wrap: wrap-event unwrap: unwrap-event (extra1 event-extra1 (setter event-extra1)) (extra2 event-extra2 event-extra2-set!)) ;; Also make extra2 settable with (set! (event-extra2 armor) value) (set! (setter event-extra2) event-extra2-set!) ;; Armor for a C array (define-armor-type event-array pred: event-array? wrap: wrap-event-array unwrap: unwrap-event-array locking: (make-armor-lock) (length event-array-length) ; first extra slot holds the length (extra1 event-array-extra1 (setter event-array-extra1))) </enscript> ==== Armor wrapper An armor wrapper procedure defined with [[#define-armor-type|{{define-armor-type}}]] has one of the following interfaces, depending on whether extra slots are defined: <enscript> (WRAP data) → fresh armor instance (WRAP data #!optional (SLOT (void)) ...) → fresh armor instance </enscript> A wrapper procedure creates an armor instance that wraps a pointer, locative, blob, or {{#f}} (which CHICKEN treats as a null pointer). A wrapper procedure is also suitable to pass as {{RETCONVERT}} to [[/man/5/Module (chicken foreign)#define-foreign-type|{{define-foreign-type}}]]. Each {{SLOT}} is an extra slot name passed to {{define-armor-type}}. If no extra slots were defined, the wrapper does not accept any optional arguments. Examples: <enscript> ;; Manually convert arguments and wrap the returned pointer: (define-binding FOO_CreateKeyEvent return: (nonnull-c-pointer "FOO_Event") args: ((int code) (int mods))) (define (create-key-event code mods) (wrap-key-event (FOO_CreateKeyEvent (keycode->int key) (pack-keymods mods)))) ;; Or define-foreign-type to do it automatically: (define-foreign-type FOO_KeyEvent* (nonnull-c-pointer "FOO_Event") unwrap-key-event wrap-key-event) (define-foreign-type FOO_KeyCode int keycode->int int->keycode) (define-foreign-type FOO_KeyMods int pack-keymods unpack-keymods) (define-binding (create-key-event FOO_CreateKeyEvent) return: FOO_KeyEvent* args: ((FOO_KeyCode code) (FOO_KeyMods mods))) </enscript> ==== Armor unwrapper An armor unwrapper procedure defined with [[#define-armor-type|{{define-armor-type}}]] has the following interface: <enscript> (UNWRAP armor #!optional name) → pointer, locative, or #f </enscript> Returns a pointer or locative to the bare data inside {{armor}}. If {{armor}} is null, returns {{#f}} (which CHICKEN treats as a null pointer). The return value can be passed to a function binding that accepts a pointer argument. An unwrapper procedure is also suitable to pass as {{ARGCONVERT}} to [[/man/5/Module (chicken foreign)#define-foreign-type|{{define-foreign-type}}]]. {{armor}} must be an instance of the correct armor type, or a pointer, locative, blob, or {{#f}}. The reason the unwrapper is generic in this way, is to allow users to pass either an armor or bare data to function bindings. {{name}} is the procedure name to show in error messages if the unwrapper is passed the wrong type. For example, it can be a symbol with the name of the procedure that is calling the unwrapper, to make it easier for users to debug their code. If {{name}} is omitted or {{#f}}, the error message will not include a procedure name. Returns a pointer if {{armor}} wraps a pointer. If {{armor}}'s pointer is [[/man/5/Module (chicken memory)#tagged-pointers|tagged]], the returned pointer will have the same tag. Returns a locative if {{armor}} wraps a locative or a blob. Returns {{#f}} if {{armor}} wraps {{#f}}. Examples: <enscript> ;; Manually unwrap argument and convert the return value: (define-binding FOO_BlackboxPower return: int args: ((c-pointer bb))) (define (blackbox-power bb) (int->power-level (FOO_BlackboxPower (unwrap-blackbox bb 'blackbox-power)))) ;; Or define-foreign-type to do it automatically: (define-foreign-type FOO_PowerLevel int power-level->int int->power-level) ;; FOO_Blackbox is an opaque pointer type. (define-foreign-type FOO_Blackbox c-pointer unwrap-blackbox wrap-blackbox) (define-binding (blackbox-power FOO_BlackboxPower) return: FOO_PowerLevel args: ((FOO_Blackbox bb))) </enscript> === {{armor-printer}} <macro>(armor-printer ...) → procedure</macro> Expands into an anonymous procedure which can be used to print an armor instance as a string, to make debugging easier. On CHICKEN 5, you can pass the anonymous procedure to [[/man/5/Module (chicken base)#set-record-printer|{{set-record-printer!}}]]. If you want to support both CHICKEN 4 and CHICKEN 5, it is simpler to use [[#define-armor-printer|{{define-armor-printer}}]] instead of {{armor-printer}}. Usage: <enscript> (armor-printer ARMOR-NAME show-address: SHOW-ADDRESS ; optional (LABEL GETTER) ; optional ...) </enscript> Record printers are used to output the record instance as a string, for example in REPL results, error messages, or when included in strings. The main purpose of an armor record printer is to provide information to make debugging easier. Printers created with this macro use the following style: #<ARMOR-NAME address LABEL: value ...> If the armor is [[#armor-null|null]], it is printed like: #<ARMOR-NAME NULL> {{ARMOR-NAME}} is the record type name that was passed to [[#define-armor-type|{{define-armor-type}}]]. If {{SHOW-ADDRESS}} is {{#t}}, the armor data's [[#armor-address|memory address]] is shown in the output, in the format {{0x12345678}}. If {{SHOW-ADDRESS}} is {{#f}} or the keyword clause is omitted, the address is not shown. (This option does not affect whether {{NULL}} is shown when the armor is null. There is currently no option to disable that.) {{LABEL}} can be a non-quoted symbol or string containing the label to show for a field value. For example, it might be the name of a struct field. Or, if {{LABEL}} is {{#f}}, the field value will be printed with no label. {{GETTER}} is an existing procedure that returns a value when passed the record instance. For example, it could be a field getter defined with [[#define-struct-accessors|{{define-struct-accessors}}]] or [[#struct-getter|{{struct-getter}}]]. Example: <enscript> ;; CHICKEN 5 only (set-record-printer! key-event (armor-printer key-event show-address: #f (#f key-event-code) (mods key-event-mods))) (define ev (create-key-event 'a '(lctrl))) (printf "~A" ev) ; #<key-event a mods: (lctrl)> (free-key-event! ev) (printf "~A" ev) ; #<key-event NULL> </enscript> ==== {{define-armor-printer}} <macro>(define-armor-printer ...)</macro> Creates a record printer procedure using [[#armor-printer|{{armor-printer}}]], and sets it as the record printer for the specified record type. If you do not need to support CHICKEN 4, you may prefer to use {{armor-printer}} instead. Usage: <enscript> (define-armor-printer ARMOR-NAME show-address: SHOW-ADDRESS ; optional (LABEL GETTER) ; optional ...) </enscript> All arguments have the same meaning as with {{armor-printer}}. On CHICKEN 4, this is based on [[/man/4/Non-standard macros and special forms#define-record-printer|{{define-record-printer}}]]. On CHICKEN 5, it is based on [[/man/5/Module (chicken base)#set-record-printer|{{set-record-printer!}}]] Example: <enscript> (define-armor-printer key-event show-address: #f (#f key-event-code) (mods key-event-mods)) (define ev (create-key-event 'a '(lctrl))) (printf "~A" ev) ; #<key-event a mods: (lctrl)> (free-key-event! ev) (printf "~A" ev) ; #<key-event NULL> </enscript> === Generic armor operations These procedures and macros work with instances of any type defined with [[#define-armor-type|{{define-armor-type}}]]. ==== {{armor?}} <procedure>(armor? obj) → boolean</procedure> Returns {{#t}} if {{obj}} is an instance of any type defined with [[#define-armor-type|{{define-armor-type}}]], or {{#f}} if {{obj}} is anything else. ==== {{armor-address}} <procedure>(armor-address obj) → integer</procedure> Returns the memory address of the bare data that {{obj}} refers to. {{obj}} can be an instance of a type defined with [[#define-armor-type|{{define-armor-type}}]], or a pointer, [[/man/5/Module (chicken locative)|locative]], [[/man/5/Module (chicken blob)|blob]], or {{#f}} (which is treated as a null pointer, address 0). <table role="presentation"><tr><td style="padding-top: 0.6rem; padding-right: 0.4rem;"> <strong><img alt="Warning" src="https://upload.wikimedia.org/wikipedia/commons/6/6e/Dialog-warning.svg" width=32 /></strong> </td><td style="vertical-align: middle;"> The memory address of locatives, blobs, and armors wrapping locatives or blobs, may change during garbage collection! If you need the memory address of those things to never change, you can use [[/eggref/5/object-evict|{{object-evict}}]] to evict the blob or the locative's object before wrapping it in armor. </td></tr></table> ==== {{armor-eq?}} <procedure>(armor-eq? obj1 obj2) → boolean</procedure> Returns {{#t}} if both objects refer to the same memory address. Same as {{(= (armor-address obj1) (armor-address obj2))}}. Each object can be an instance of any type defined with [[#define-armor-type|{{define-armor-type}}]], or a pointer, [[/man/5/Module (chicken locative)|locative]], [[/man/5/Module (chicken blob)|blob]], or {{#f}} (which is treated as a null pointer, address 0). ==== {{armor-null?}} <procedure>(armor-null? obj) → boolean</procedure> Returns {{#t}} if {{obj}} refers to memory address 0. Equivalent to {{(zero? (armor-address obj))}}. {{obj}} can be an instance of a type defined with [[#define-armor-type|{{define-armor-type}}]], or a pointer, [[/man/5/Module (chicken locative)|locative]], [[/man/5/Module (chicken blob)|blob]], or {{#f}} (which is treated as a null pointer, address 0). ==== {{nullify-armor!}} <procedure>(nullify-armor! armor) → armor</procedure> Sets {{armor}}'s data to {{#f}} (which is treated as a null pointer) and returns {{armor}}. {{armor}} must be an instance of any type defined with [[#define-armor-type|{{define-armor-type}}]]. This procedure also recursively nullifies {{armor}}'s children (if it has any), unless {{armor}} does not [[#armor-tracks-children|track its children]]. [[#struct-freer|Struct freers]] and [[#array-freer|array freers]] will automatically call this procedure for you. You should call this procedure manually to nullify an armor when its data becomes unusable for other reasons, for example when calling a destructor C function. <enscript> (define-binding FOO_DestroyBlackbox args: ((c-pointer bb))) (define (destroy-blackbox! bb) ;; FOO_DestroyBlackbox frees bb's data, but if bb is an armor it ;; will still wrap a pointer to the old address, which could cause ;; a memory access violation if bb is used again later. ;; Unwrap bb to get its pointer before it is nullified. (let ((ptr (unwrap-blackbox bb))) ;; Nullify bb if it is an armor (rather than bare data) (when (armor? bb) (nullify-armor! bb)) ;; Pass the original pointer. (FOO_DestroyBlackbox ptr))) </enscript> ==== Armor parents and children Jiffi supports creating parent-child relationships between armors. This helps ensure proper cleanup and prevent certain kinds of memory-related bugs. See "[[https://gitlab.com/jcroisant/jiffi/blob/main/docs/getting-started.md#when-should-an-armor-be-a-child-of-another-armor|When should an armor be a child of another armor?]]" in the Getting Started guide for more information. [[#define-array-accessors|Array accessors]] that return an armor automatically create a parent-child relationship. For other cases, you can create the relationship manually with [[#armor-parent-set|{{armor-parent-set!}}]]. If you are using multiple SFRI-18 threads, you may need to enable [[#armor-thread-safety|thread safety]] on some parent armors. ===== {{armor-parent}} <procedure>(armor-parent armor) → armor or #f</procedure> If {{armor}} is the child of another armor, returns the parent armor. Otherwise, returns {{#f}}. {{armor}} must be an instance of any type defined with [[#define-armor-type|{{define-armor-type}}]]. An armor is the "child" of another armor if it wraps part of the parent armor's bare data. For example, an armor returned by an [[#define-array-accessors|array accessor]] is a child of the array armor, because it wraps part of the array's bare data. See also [[#armor-parent-set|{{armor-parent-set!}}]]. ===== {{armor-parent-set!}} <procedure>(armor-parent-set! child parent) → child</procedure> Performs internal bookkeeping to create a parent-child relationship between {{child}} and {{parent}}. Modifies both {{child}} and {{parent}}. Returns {{child}}. {{child}} and {{parent}} must each be an instance of any type defined with [[#define-armor-type|{{define-armor-type}}]]. They can be the same type or different types. <table role="presentation"><tr><td style="padding-top: 0.6rem; padding-right: 0.4rem;"> <strong><img alt="Warning" src="https://upload.wikimedia.org/wikipedia/commons/6/6e/Dialog-warning.svg" width=32 /></strong> </td><td style="vertical-align: middle;"> Calling this procedure from multiple threads at the same time with the same parent may cause the software to crash later or have security vulnerabilities, unless [[#armor-thread-safety|thread safety]] is enabled on the parent armor. </td></tr></table> You do not need to call this procedure for armors returned by [[#define-array-accessors|array accessors]], because they call this procedure automatically. You only need to call this procedure manually if you use an [[#armor-wrapper|armor wrapper]] to wrap part of the parent's bare data. For example, if you have an array of unions, you might want to wrap array items in different armor types depending on what data type they are. See the example below for how to do this using an [[#array-item-pointer-getter|array item pointer getter]]. Creating the parent-child relationship helps ensure proper cleanup and prevent certain kinds of memory-related bugs. See "[[https://gitlab.com/jcroisant/jiffi/blob/main/docs/getting-started.md#when-should-an-armor-be-a-child-of-another-armor|When should an armor be a child of another armor?]]" in the Getting Started guide for more information. Example: <enscript> ;; Return an event from the array, wrapped in either a key-event or ;; sensor-event instance, depending on what type of event it is. (define (event-array-ref/typed array i) ;; Use the array item pointer getter to get a bare pointer/locative. (let* ((ptr (event-array-ref* array i)) ;; Wrap ptr in the correct armor type. (event (case (event-type ptr) ((key) (wrap-key-event ptr)) ((sensor) (wrap-sensor-event ptr))))) ;; If array is wrapped in armor, set event's parent and return ;; event. Otherwise, just return event. (if (armor? array) (armor-parent-set! event array) event))) </enscript> ===== {{armor-tracks-children?}} <procedure>(armor-tracks-children? armor) → boolean</procedure> <setter>(armor-tracks-children-set! armor enabled)</setter> <setter>(set! (armor-tracks-children? armor) enabled)</setter> Armor instances can optionally perform bookkeeping to keep track of their children (see [[#armor-parent|{{armor-parent}}]]) so that the children can be nullified when the armor instance is freed or nullified. This helps protect against use-after-free bugs. See "[[https://gitlab.com/jcroisant/jiffi/blob/main/docs/getting-started.md#when-should-an-armor-be-a-child-of-another-armor|When should an armor be a child of another armor?]]" in the Getting Started guide for more information. {{armor-tracks-children?}} returns {{#t}} if {{armor}} performs bookkeeping to track its children, or {{#f}} if it does not. You can use {{armor-tracks-children-set!}} to enable or disable this behavior for {{armor}}. If you disable bookkeeping when {{armor}} already has children, it will forget about those children. <table role="presentation"><tr><td style="padding-top: 0.6rem; padding-right: 0.4rem;"> <strong><img alt="Hint" src="https://upload.wikimedia.org/wikipedia/commons/e/e7/Dialog-information_on.svg" width=32 /></strong> </td><td style="vertical-align: middle;"> You can pass the {{children:}} keyword argument to [[#define-armor-type|{{define-armor-type}}]] to enable or disable bookkeeping by default for all instances of that armor type. </td></tr></table> Disabling bookkeeping can improve performance for armors that will have many children, but you must be careful not to use the children after {{armor}} has been freed. If {{armor}} will never have children, there is no reason to disable bookkeeping. ==== Armor thread safety The procedures and macros in this section can be used to make armors safe to use from multiple SRFI-18 threads at the same time. See "[[https://gitlab.com/jcroisant/jiffi/blob/main/docs/getting-started.md#when-should-i-enable-armor-thread-safety|When should I enable armor thread safety?]]" in the Getting Started guide for more information. ===== {{make-armor-lock}} <macro>(make-armor-lock) → new armor lock</macro> Creates an armor lock which uses a [[/eggref/5/srfi-18|SRFI-18]] mutex to make an armor [[#armor-thread-safety|thread-safe]]. You must import {{srfi-18}} in the module that calls this macro. You can use [[#armor-lock-set|{{armor-lock-set!}}]] to assign the lock to a single armor instance. You can pass {{locking: (make-armor-lock)}} to [[#define-armor-type|{{define-armor-type}}]], to create and assign a new lock to every instance of the armor type automatically. <table role="presentation"><tr><td style="padding-top: 0.6rem; padding-right: 0.4rem;"> <strong><img alt="Warning" src="https://upload.wikimedia.org/wikipedia/commons/6/6e/Dialog-warning.svg" width=32 /></strong> </td><td style="vertical-align: middle;"> The implementation details of armor locks are unspecified, and may change in future versions of Jiffi. You should not do anything with an armor lock except assign it to an armor as described above. </td></tr></table> ===== {{armor-has-lock?}} <procedure>(armor-has-lock? armor) → boolean</procedure> Returns {{#t}} if {{armor}} has a lock to make it [[#armor-thread-safety|thread-safe]], or {{#f}} if it does not. See [[#armor-lock-set|{{armor-lock-set!}}]]. ===== {{armor-lock-set!}} <setter>(armor-lock-set! armor lock)</setter> Set {{armor}}'s lock to {{lock}}, which must either be an armor lock created with [[#make-armor-lock|{{make-armor-lock}}]] (to make the armor [[#armor-thread-safety|thread-safe]]), or {{#f}} (to make the armor not thread-safe). == Structs and Unions The macros in this section allow you to create, read, and modify C structs and C unions. Even though the macro names only say "struct", they work with C unions too. These macros are designed to be used in combination with an [[#armor|armor type]]. But, it is possible to use them with your own custom record type, if you provide a [[#armor-wrapper|wrapper procedure]] or [[#armor-wrapper|unwrapper procedure]] with the correct interface. Also, many of the procedures defined by these macros work with bare data, so it is not strictly necessary to use a record type at all. === {{define-struct-allocators}} <macro>(define-struct-allocators ...)</macro> Defines various procedures to allocate or free memory for a struct/union type defined with [[#define-armor-type|{{define-armor-type}}]]. You can omit any procedures that you don't need. Also generates [[/man/5/Types|type declarations]] for the procedures that are defined. See "[[https://gitlab.com/jcroisant/jiffi/blob/main/docs/getting-started.md#which-allocator-should-i-use|Which allocator should I use?]]" in the Getting Started guide. Usage: <enscript> (define-struct-allocators (ARMOR-NAME "STRUCT_NAME" PRED WRAP) free: FREE ; optional alloc: ALLOC ; optional alloc/blob: ALLOC/BLOB ; optional make: MAKE ; optional make/af: MAKE/AUTOFREE ; optional make/blob: MAKE/BLOB ; optional defaults: (SLOT-DEFAULT ...) ; optional </enscript> {{ARMOR-NAME}} is the name of the record type that is returned by {{WRAP}}, such as the record name that was passed to {{define-armor-type}}. {{"STRUCT_NAME"}} is a string containing the name of the C struct/union, exactly as it appears in C. {{PRED}} is an existing type predicate procedure for the armor type, such as a procedure defined with {{define-armor-type}}. {{WRAP}} is an existing procedures that wraps bare data in an armor instance, such as an [[#armor-wrapper|armor wrapper procedure]] defined with {{define-armor-type}}. {{FREE}} is the procedure name to define as a [[#struct-freer|struct freer]]. If {{FREE}} is {{#f}} or the keyword clause is omitted, the procedure will not be defined. {{ALLOC}} and {{ALLOC/BLOB}} are the procedure names to define as [[#struct-memory-allocators|struct memory allocators]]. If any is {{#f}} or its keyword clause is omitted, that procedure will not be defined. {{MAKE}}, {{MAKE/AUTOFREE}}, and {{MAKE/BLOB}} are the procedure names to define as [[#struct-makers|struct makers]]. If any is {{#f}} or its keyword clause is omitted, that procedure will not be defined. Each {{SLOT-DEFAULT}} is the default value of each additional {{SLOT}} defined with {{define-armor-type}}. {{MAKE}}, {{MAKE/AUTOFREE}}, and {{MAKE/BLOB}} will pass the {{SLOT-DEFAULT}}s as extra arguments to {{WRAP}}. Example: <enscript> (define-struct-allocators (event "FOO_Event" event? wrap-event) free: free-event! alloc: alloc-event alloc/blob: alloc-event/blob make: make-event make/af: make-event/autofree make/blob: make-event/blob defaults: ("e1" 'e2)) ; defaults for `extra1' and `extra2' </enscript> ==== Struct freer A struct freer procedure defined with [[#define-struct-allocators|{{define-struct-allocators}}]] has the following interface: <enscript> (FREE struct) → struct </enscript> Frees {{struct}}'s bare data (if appropriate), and [[#nullify-armor|nullifies]] {{struct}} (and possibly its children). Returns {{struct}}. {{struct}} must be an instance of the armor type that this freer was defined for. If {{struct}} wraps a non-null pointer, the pointer is freed using [[/man/5/Module (chicken memory)#free|{{free}}]]. If {{struct}} wraps a blob or locative, this procedure only nullifies {{struct}}, which may cause the blob or locative to be garbage collected if there are no other references to it. If {{struct}} is already null, this procedure has no effect, so it is safe to call on a struct that has already been freed, or that will be automatically freed later. But, if {{struct}} has a [[#armor-parent|parent]], this procedure only nullifies {{struct}}. It does not free the bare data, because the bare data is owned by the parent. ==== Struct memory allocators Struct memory allocator procedures defined with [[#define-struct-allocators|{{define-struct-allocators}}]] have the following interfaces: <enscript> (ALLOC) → tagged pointer to allocated memory (ALLOC/BLOB) → new blob </enscript> Allocates memory of the correct size to hold an instance of the C struct/union. The memory is initialized to zero. {{ALLOC}} allocates a region of static memory using [[/man/5/Module (chicken memory)#allocate|{{allocate}}]], [[/man/5/Module (chicken memory)#tagged-pointers|tags the pointer]] with {{'ARMOR-NAME}}, and returns the tagged pointer. To avoid memory leaks, you must free the pointer with [[/man/5/Module (chicken memory)#free|{{free}}]] when you are done with it. {{ALLOC/BLOB}} creates a blob using [[/man/5/Module (chicken blob)#make-blob|{{make-blob}}]]. The memory will be automatically freed when the blob is garbage collected. See "[[https://gitlab.com/jcroisant/jiffi/blob/main/docs/getting-started.md#warning-about-bare-data|Warning about bare data]]" in the Getting Started guide. [[#struct-makers|Struct makers]] are much safer, although a little less efficient. ==== Struct makers Struct maker procedures defined with [[#define-struct-allocators|{{define-struct-allocators}}]] have the following interfaces: <enscript> (MAKE) → new armor (MAKE/AUTOFREE) → new armor (MAKE/BLOB) → new armor </enscript> Allocates memory of the correct size to hold an instance of the C struct/union, then wraps it in an armor instance using {{WRAP}}. The memory is initialized to zero. The {{SLOT-DEFAULT}}s (if any) will be passed as extra arguments to {{WRAP}}. {{MAKE}} allocates a region of static memory using [[/man/5/Module (chicken memory)#allocate|{{allocate}}]], [[/man/5/Module (chicken memory)#tagged-pointers|tags the pointer]] with {{'ARMOR-NAME}}, then wraps the tagged pointer. To avoid memory leaks, you must manually free the record instance with this type's [[#struct-freer|freer]] when you are done with it. {{MAKE/AUTOFREE}} is like {{MAKE}}, except it also sets the struct's [[/man/5/Module (chicken gc)#set-finalizer|finalizer]] to automatically free the memory when the armor instance is garbage collected. {{MAKE/BLOB}} creates a blob using [[/man/5/Module (chicken blob)#make-blob|{{make-blob}}]], then wraps it. The blob will be garbage collected when the record instance is garbage collected, if there are no other references to the blob. === {{struct-copier}} <macro>(struct-copier ...) → procedure</macro> Expands to an anonymous procedure that performs a shallow copy of the memory from the source struct/union, overwriting the destination struct/union. Returns the destination struct/union after copying. Usage: <enscript> (struct-copier ("STRUCT_NAME" UNWRAP) name: NAME) ; optional </enscript> {{"STRUCT_NAME"}} is a string containing the name of the C struct/union, exactly as it appears in C. {{UNWRAP}} is an existing [[#armor-unwrapper|armor unwrapper]] procedure, such as a procedure defined with [[#define-armor-type|{{define-armor-type}}]]. {{NAME}} is an expression that evaluates to the procedure name (usually a symbol) to show in error messages if either struct argument cannot be unwrapped. If {{NAME}} is omitted or {{#f}}, error messages will not include a procedure name. The procedure will behave similar to this pseudo-code: <enscript> (lambda (src dest) (copy-bytes! (UNWRAP src NAME) (UNWRAP dest NAME) (foreign-type-size STRUCT_NAME)) dest) </enscript> <table role="presentation"><tr><td style="padding-top: 0.6rem; padding-right: 0.4rem;"> <strong><img alt="Hint" src="https://upload.wikimedia.org/wikipedia/commons/e/e7/Dialog-information_on.svg" width=32 /></strong> </td><td style="vertical-align: middle;"> The procedure does not copy the values of extra slots you may have defined with {{define-armor-type}}. </td></tr></table> <table role="presentation"><tr><td style="padding-top: 0.6rem; padding-right: 0.4rem;"> <strong><img alt="Warning" src="https://upload.wikimedia.org/wikipedia/commons/6/6e/Dialog-warning.svg" width=32 /></strong> </td><td style="vertical-align: middle;"> The procedure only performs a shallow copy of the C struct/union, byte-for-byte. This is safe for C structs/unions that only hold direct values like numbers or fixed-size arrays. But, if the C struct/union has a field that holds a pointer, that field of both the source and destination structs will hold a pointer to the same address, which can cause bugs if you are not careful. </td></tr></table> {{src}} and {{dest}} can each be an armor, pointer, locative, or blob. It is okay to be different, for example if {{src}} is a blob and {{dest}} is a pointer. See "[[https://gitlab.com/jcroisant/jiffi/blob/main/docs/getting-started.md#warning-about-bare-data|Warning about bare data]]" in the Getting Started guide. The procedure signals an exception if either {{src}} or {{dest}} are [[#armor-null|null]]. Example: <enscript> ;; Destructive copier (define copy-event! (struct-copier ("FOO_Event" unwrap-event) name: 'copy-event!)) ;; Non-destructive copier based on destructive copier (define (copy-event ev) (copy-event! ev (make-event/blob))) ;; Or using struct-copier directly inside a procedure (define (copy-event ev) ((struct-copier ("FOO_Event" unwrap-event) name: 'copy-event) ev (make-event/blob))) </enscript> === {{define-struct-accessors}} <macro>(define-struct-accessors ...)</macro> Defines getters and/or setters for one or more fields of a C struct/union. Also generates [[/man/5/Types|type declarations]] for the getters and setters. Usage: <enscript> (define-struct-accessors (ARMOR-NAME "STRUCT_NAME" PRED UNWRAP) ("FIELD_NAME" type: (SCHEME-TYPE FOREIGN-TYPE) ; or `type: FOREIGN-TYPE` getter: GETTER ; optional g-conv: G-CONV ; optional setter: SETTER ; optional s-conv: S-CONV) ; optional ...) </enscript> {{ARMOR-NAME}} is the name of the record type that is unwrapped by {{UNWRAP}}, such as the record name that was passed to [[#define-armor-type|{{define-armor-type}}]]. {{"STRUCT_NAME"}} is a string containing the name of the C struct/union, exactly as it appears in C. {{PRED}} is an existing type predicate for the armor type, such as a procedure defined with {{define-armor-type}}. {{UNWRAP}} is an existing [[#armor-unwrapper|armor unwrapper]] procedure, such as a procedure defined with {{define-armor-type}}. {{"FIELD_NAME"}} is a string containing the name of the field, exactly as it appears in C. You can access nested data by using {{"."}} or {{"->"}} in the field name. For example, {{"foo.bar"}} would access the {{foo}} field of the struct/union, then the {{bar}} field of {{foo}}. If the {{foo}} field holds a pointer, you would write {{"foo->bar"}} instead. You can define multiple accessors with the same {{"FIELD_NAME"}}, as long as the {{GETTER}} and {{SETTER}} names are different. For example, you might want to define a getter that returns the field as an integer, and another getter that uses an [[#enum-converters|enum converter]] as {{G-CONV}} to convert the field into a symbol. {{SCHEME-TYPE}} is a [[/man/5/Types|Scheme type specifier]], which is used as the return type of the getter (after conversion) and as the argument type of the setter (before conversion). {{FOREIGN-TYPE}} is a [[/man/5/Foreign type specifiers|foreign type specifier]] for the struct field. If {{SCHEME-TYPE}} and {{FOREIGN-TYPE}} are the same, you can write {{type: FOREIGN-TYPE}}. {{GETTER}} and {{SETTER}} are procedure names to define as the getter and setter procedures. Alternatively, if {{SETTER}} is the form {{(setter GETTER)}}, the field will be settable using {{(set! (GETTER struct) value)}}. The procedures are created using [[#struct-getter|{{struct-getter}}]] and [[#struct-setter|{{struct-setter}}]]. You can omit either keyword clause if you don't need a getter or setter. {{G-CONV}} is an existing procedure used by {{GETTER}} to convert the field value before returning it. For example, this could be a {{int->symbol}} [[#enum-converters|enum converter]] defined with [[#define-enum-group|{{define-enum-group}}]], an [[#enum-unpacker|enum unpacker]] defined with [[#define-enum-unpacker|{{define-enum-unpacker}}]], or an [[#armor-wrapper|armor wrapper]] defined with [[#define-armor-type|{{define-armor-type}}]]. The procedure must accept one argument, the value to convert. If {{G-CONV}} is {{#f}} or the keyword clause is omitted, the getter will not perform any conversion. {{S-CONV}} is an existing procedure used by {{SETTER}} to validate and/or convert the argument before setting the field value. For example, this could be a {{symbol->int}} enum converter defined with {{define-enum-group}}, an [[#enum-packer|enum packer]] defined with {{define-enum-packer}}, or an [[#armor-unwrapper|armor unwrapper]] defined with {{define-armor-type}}. You can perform validation by signaling an exception if the argument is invalid. The procedure must accept one argument, the value to convert. If {{S-CONV}} is {{#f}} or the keyword clause is omitted, the setter will not perform any conversion. Example: <enscript> ;; C struct and union definitions (foreign-declare " typedef struct { int type; FOO_KeyCode code; FOO_KeyMod mods; } FOO_KeyEvent; /* Union of two structs. */ typedef union { int type; FOO_KeyEvent key; FOO_SensorEvent sensor; } FOO_Event; ") (define-struct-accessors (key-event "FOO_Event" key-event? unwrap-key-event) ;; Access the "key" union member, then the "code" struct field ("key.code" type: (fixnum int) getter: key-event-code-int setter: key-event-code-int-set!) ;; With enum converters ("key.code" type: (symbol int) getter: key-event-code g-conv: int->keycode setter: key-event-code-set! s-conv: keycode->int)) ;; Also make code settable with (set! (key-event-code struct) value) (set! (setter key-event-code) key-event-code-set!) </enscript> ==== {{struct-getter}} <macro>(struct-getter ...) → procedure</macro> Expands to an anonymous procedure that gets the value of a struct/union field. Usage: <enscript> (struct-getter (ARMOR-NAME "STRUCT_NAME" PRED UNWRAP) FIELD-NAME type: FOREIGN-TYPE conv: G-CONV ; optional name: NAME) ; optional </enscript> {{NAME}} is an expression that evaluates to the procedure name (usually a symbol) to show in error messages if the struct cannot be unwrapped. If {{NAME}} is omitted or {{#f}}, error messages will not include a procedure name. See [[#define-struct-accessors|{{define-struct-accessors}}]] for the meaning of the other macro arguments. The procedure will behave similar to this pseudo-code: <enscript> (lambda (struct) (let ((ptr (UNWRAP struct NAME))) (if G-CONV (G-CONV (get-field ptr FIELD-NAME)) (get-field ptr FIELD-NAME)))) </enscript> Example: <enscript> (define event-type-getter (struct-getter (event "FOO_Event" event? unwrap-event) "type" type: int conv: int->event-type name: 'event-type-getter)) </enscript> ==== {{struct-setter}} <macro>(struct-setter ...) → procedure</macro> Expands to an anonymous procedure that sets a struct field value. Usage: <enscript> (struct-setter (ARMOR-NAME "STRUCT_NAME" PRED UNWRAP) FIELD-NAME type: FOREIGN-TYPE conv: S-CONV ; optional name: NAME) ; optional </enscript> {{NAME}} is an expression that evaluates to the procedure name (usually a symbol) to show in error messages if the struct cannot be unwrapped. If {{NAME}} is omitted or {{#f}}, error messages will not include a procedure name. See [[#define-struct-accessors|{{define-struct-accessors}}]] for the meaning of the other macro arguments. The procedure will behave similar to this pseudo-code: <enscript> (lambda (struct value) (let ((ptr (UNWRAP struct NAME))) (if S-CONV (set-field ptr FIELD-NAME (S-CONV value)) (set-field ptr FIELD-NAME value)))) </enscript> Example: <enscript> (set! (setter key-event-code) (struct-setter (key-event "FOO_Event" key-event? unwrap-key-event) "key.code" type: int conv: keycode->int name: 'key-event-code)) </enscript> == Arrays of Structs and Unions The macros in this section allow you to create, read, and modify C arrays of structs or unions. These macros are designed to be used in combination with an [[#armor|armor type]]. But, it is possible to use the allocators and some of the array accessors with your own custom record type, if you provide a [[#armor-wrapper|wrapper procedure]] or [[#armor-wrapper|unwrapper procedure]] with the correct interface. Also, some of the allocators work with bare data, so it is not strictly necessary to use a record type at all. === {{define-array-allocators}} <macro>(define-array-allocators ...)</macro> Defines various procedures to manage memory for an [[#define-armor-type|armor type]] that will hold a C array of structs or unions. You can omit any procedures that you don't need. Also generates [[/man/5/Types|type declarations]] for the procedures that are defined. See "[[https://gitlab.com/jcroisant/jiffi/blob/main/docs/getting-started.md#which-allocator-should-i-use|Which allocator should I use?]]" in the Getting Started guide. Usage: <enscript> (define-array-allocators (ARMOR-NAME "STRUCT_NAME" PRED WRAP) free: FREE ; optional alloc: ALLOC ; optional alloc/blob: ALLOC/BLOB ; optional make: MAKE ; optional make/af: MAKE/AUTOFREE ; optional make/blob: MAKE/BLOB ; optional defaults: (SLOT-DEFAULT ...) ; optional </enscript> This is very similar to [[#define-struct-allocators|{{define-struct-allocators}}]], except that the "alloc" and "make" procedures take a {{length}} argument for the number of items in the array. {{"STRUCT_NAME"}} is a string containing the name of the C struct/union (not the array type), exactly as it appears in C. For example, for an array of {{FOO_Event}} structs, just write {{"FOO_Event"}}. {{FREE}} will be defined as an [[#array-freer|array freer]], {{ALLOC}} and {{ALLOC/BLOB}} will be defined as [[#array-memory-allocators|array memory allocators]], and {{MAKE}}, {{MAKE/AUTOFREE}}, and {{MAKE/BLOB}} will be defined as [[#array-makers|array makers]]. The {{SLOT-DEFAULT}}s are the defaults for the '''slots after the first'''. They should not include a default for first slot, which is recommended to hold [[#array-armor-length-slot|the array length]]. The "make" procedures will call {{(WRAP data length SLOT-DEFAULT ...)}}. Example: <enscript> (define-array-allocators (event "FOO_Event" event? wrap-event) free: free-event! alloc: alloc-event alloc/blob: alloc-event/blob make: make-event make/af: make-event/autofree make/blob: make-event/blob defaults: ("e1" 'e2)) ; defaults for `extra1' and `extra2' </enscript> ==== Array armor length slot If you use [[#define-armor-type|{{define-armor-type}}]] to define armor that wraps a C array of structs or unions, it is recommended that the first extra slot be reserved for holding the array length. This is because "make" procedures defined with [[#define-array-allocators|{{define-array-allocators}}]] will pass the array length as the second argument to {{WRAP}}. If the first extra slot is the length slot, you don't have to do anything special to make that work. If for some reason you cannot use the first extra slot to hold the length, you will need to create an "adapter" procedure to pass to {{define-array-allocators}} instead of {{WRAP}}. For example, if length is the third extra slot: <enscript> (define-armor-type thing-array pred: thing-array? wrap: wrap-thing-array unwrap: unwrap-thing-array ;; length is not the first extra slot (slot1 thing-array-slot1 (setter thing-array-slot1)) (slot2 thing-array-slot2 (setter thing-array-slot2)) (length thing-array-length)) ;; Adapter procedure that reorders the slot arguments (define (thing-array-adapter data #!optional length slot1 slot2) (wrap-thing-array data slot1 slot2 length)) (define-array-allocators ;; Pass thing-array-adapter instead of wrap-thing-array (thing-array "Thing" thing-array? thing-array-adapter) ... defaults: ("slot1 default" "slot2 default")) </enscript> ==== Array freer An array freer procedure defined with [[#define-array-allocators|{{define-array-allocators}}]] has the following interface: <enscript> (FREE array) → array </enscript> Array freers behave the same as [[#struct-freer|struct freers]]. ==== Array memory allocators Array memory allocator procedures defined with [[#define-array-allocators|{{define-array-allocators}}]] have the following interfaces: <enscript> (ALLOC length) → tagged pointer to allocated memory (ALLOC/BLOB length) → new blob </enscript> Allocates memory of the correct size for a C array containing {{length}} instances of the C struct/union. The memory is initialized to zero. See "[[https://gitlab.com/jcroisant/jiffi/blob/main/docs/getting-started.md#warning-about-bare-data|Warning about bare data]]" in the Getting Started guide. [[#array-makers|Array makers]] are much safer, although a little less efficient. {{ALLOC}} allocates a region of static memory using [[/man/5/Module (chicken memory)#allocate|{{allocate}}]], [[/man/5/Module (chicken memory)#tagged-pointers|tags the pointer]] with {{'ARMOR-NAME}}, and returns the tagged pointer. To avoid memory leaks, you must free the pointer with [[/man/5/Module (chicken memory)#free|{{free}}]] when you are done with it. {{ALLOC/BLOB}} creates a blob using [[/man/5/Module (chicken blob)#make-blob|{{make-blob}}]]. The memory will be automatically freed when the blob is garbage collected. ==== Array makers Array maker procedures defined with [[#define-array-allocators|{{define-array-allocators}}]] have the following interfaces: <enscript> (MAKE length) → new array record (MAKE/AUTOFREE length) → new array record (MAKE/BLOB length) → new array record </enscript> Allocates memory of the correct size for a C array containing {{length}} instances of the struct/union, then wraps it in an armor instance using {{WRAP}}. The memory is initialized to zero. The {{SLOT-DEFAULTS}} (if any) will be passed as extra arguments to {{WRAP}}. {{MAKE}} allocates a region of static memory using [[/man/5/Module (chicken memory)#allocate|{{allocate}}]], [[/man/5/Module (chicken memory)#tagged-pointers|tags the pointer]] with {{'ARMOR-NAME}}, then wraps the tagged pointer. To avoid memory leaks, you must manually free the record instance with this type's [[#array-freer|freer]] when you are done with it. {{MAKE/AUTOFREE}} is like {{MAKE}}, except it also sets the array's [[/man/5/Module (chicken gc)#set-finalizer|finalizer]] to automatically free the array memory when the record is garbage collected. {{MAKE/BLOB}} creates a blob using [[/man/5/Module (chicken blob)#make-blob|{{make-blob}}]], then wraps it. The blob will be garbage collected when the record instance is garbage collected, if there are no other references to the blob. === {{define-array-accessors}} <macro>(define-array-accessors ...)</macro> Defines various procedures to get or set items of a C array of structs/unions. You can omit any procedures that you don't need. Also generates [[/man/5/Types|type declarations]] for the defined procedures. Usage: <enscript> (define-array-accessors (ARMOR-NAME "STRUCT_NAME" PRED UNWRAP LENGTH) (ITEM-ARMOR-NAME ITEM-PRED ITEM-WRAP ITEM-UNWRAP) ref: REF ; optional set: SET ; optional map: MAP ; optional for-each: FOR-EACH ; optional ref*: REF* ; optional map*: MAP* ; optional for-each*: FOR-EACH*) ; optional </enscript> {{ARMOR-NAME}} is the record type name for the array, such as the record name passed to [[#define-armor-type|{{define-armor-type}}]]. {{"STRUCT_NAME"}} is a string containing the name of the C struct/union (not the array type), exactly as it appears in C. For example, for an array of {{FOO_Event}} structs, just write {{"FOO_Event"}}. {{PRED}} is an existing type predicate for the array record type, such as a procedure defined with {{define-armor-type}}. {{UNWRAP}} is an existing [[#armor-unwrapper|armor unwrapper]] procedure, such as a procedure defined with {{define-armor-type}}. {{LENGTH}} is an existing procedure that returns the array length, such as an extra slot getter defined with {{define-armor-type}}. {{ITEM-ARMOR-NAME}} is the record type name that was passed to [[#define-armor-type|{{define-armor-type}}]] for the item type (not the array type). {{ITEM-PRED}}, {{ITEM-WRAP}}, and {{ITEM-UNWRAP}} are existing procedures for the item type (not the array type), such as the procedures defined with {{define-armor-type}}. {{ITEM-WRAP}} must return an instance of a type defined with {{define-armor-type}}. {{REF}} is the procedure name to define as an [[#array-item-getter|array item getter]], which returns an armor instance wrapping the bare data of an item in the array. If {{REF}} is {{#f}} or the keyword clause is omitted, the procedure will not be defined. {{SET}} is the procedure name to define as an [[#array-item-setter|array item setter]], which overwrites the memory for an item in the array. If {{SET}} is {{#f}} or the keyword clause is omitted, the procedure will not be defined and {{REF}} will not be settable. If {{SET}} is the form {{(setter REF)}}, no setter procedure will be defined, but the array will be settable using {{(set! (REF array i) struct)}}. If you want to have a setter procedure and also make the array settable with {{set!}}, use {{(set! (setter REF) SET)}} after defining the array accessors. {{MAP}} is the procedure name to define as an [[#array-item-mapper|array item mapper]], which repeatedly calls a procedure with each item in one or more arrays, and returns a list of the results. If {{MAP}} is {{#f}} or the keyword clause is omitted, the procedure will not be defined. {{FOR-EACH}} is the procedure name to define as an [[#array-item-iterator|array item iterator]], which repeatedly calls a procedure with each item in one or more arrays, in a guaranteed order, but does not return anything. If {{FOR-EACH}} is {{#f}} or the keyword clause is omitted, the procedure will not be defined. {{REF*}} is the procedure name to define as an [[#array-item-pointer-getter|array item pointer getter]], which returns a pointer or locative to an item in an array. If {{REF*}} is {{#f}} or the keyword clause is omitted, the procedure will not be defined. {{MAP*}} is the procedure name to define as an [[#array-item-pointer-mapper|array item pointer mapper]], which repeatedly calls a procedure with a pointer or locative to each item in one or more arrays, and returns a list of the results. If {{MAP*}} is {{#f}} or the keyword clause is omitted, the procedure will not be defined. {{FOR-EACH*}} is the procedure name to define as an [[#array-item-pointer-iterator|array item pointer iterator]], which repeatedly calls a procedure with a pointer or locative to each item in one or more arrays, in a guaranteed order, but does not return anything. If {{FOR-EACH*}} is {{#f}} or the keyword clause is omitted, the procedure will not be defined. Example: <enscript> (define-array-accessors (event-array "FOO_Event" event-array? unwrap-event-array event-array-length) (event event? wrap-event unwrap-event) ref: event-array-ref set: (setter event-array-ref) map: event-array-map for-each: event-array-for-each ref*: event-array-ref* map*: event-array-map* for-each*: event-array-for-each*) </enscript> ==== Array item getter An array item getter procedure defined with [[#define-array-accessors|{{define-array-accessors}}]] has the following interface: <enscript> (REF array i) → fresh armor instance </enscript> Returns an armor instance wrapping the item at index {{i}} of the array (starting at index 0). Signals an exception if {{i}} is out of bounds. {{array}} must be a non-null instance of the armor type that this procedure was defined for. The returned item's type is whatever {{ITEM-WRAP}} returns. It must be an instance of a type defined with [[#define-armor-type|{{define-armor-type}}]]. The item will refer to a location in the array's memory, so modifying the item will modify the array. A parent-child relationship will be automatically created between the array and the item, using [[#armor-parent-set|{{armor-parent-set!}}]]. The array will not be garbage collected while the item is using its memory. If you manually free the array with the [[#array-freer|array freer]], the item will be automatically nullified to prevent memory errors (unless you passed {{children: #f}} when defining the array's armor type). It is safe to free the item using a [[#struct-freer|struct freer]], but doing so will only call [[#nullify-armor|{{nullify-armor}}]] on the item armor, not actually free the memory. The memory is owned by the array, and will only be freed when the array is freed. <table role="presentation"><tr><td style="padding-top: 0.6rem; padding-right: 0.4rem;"> <strong><img alt="Warning" src="https://upload.wikimedia.org/wikipedia/commons/6/6e/Dialog-warning.svg" width=32 /></strong> </td><td style="vertical-align: middle;"> Calling this procedure from multiple threads at the same time on the same array may cause the software to crash later or have security vulnerabilities, unless you enable [[#armor-thread-safety|thread safety]] when defining the armor type. </td></tr></table> ==== Array item setter An array item setter procedure defined with [[#define-array-accessors|{{define-array-accessors}}]] has one of the following interfaces: <enscript> (SET array i item) (set! (REF array i) item) ; if SET is the form (setter REF) </enscript> Copies the memory from {{item}}, overwriting the memory at index {{i}} of the array (starting at index 0). Signals an exception if {{i}} is out of bounds. <table role="presentation"><tr><td style="padding-top: 0.6rem; padding-right: 0.4rem;"> <strong><img alt="Warning" src="https://upload.wikimedia.org/wikipedia/commons/6/6e/Dialog-warning.svg" width=32 /></strong> </td><td style="vertical-align: middle;"> Any existing armor instances, pointers, or locatives that refer to the array memory at index {{i}} will also be affected by the changes. </td></tr></table> {{array}} must be a non-null instance of the armor type that this procedure was defined for. {{item}} must be an armor instance, pointer, blob, or locative referring to the new data. It must be a valid argument to {{ITEM-UNWRAP}}. The memory from {{item}} is shallow-copied to overwrite the array at index {{i}}, as with [[#struct-copier|{{struct-copier}}]]. It is safe for {{item}} to be an item from {{array}}. It is safe for {{item}} itself to refer to the item at index {{i}} (in other words, it is safe to overwrite itself). See "[[https://gitlab.com/jcroisant/jiffi/blob/main/docs/getting-started.md#warning-about-bare-data|Warning about bare data]]" in the Getting Started guide. ==== Array item mapper An array item mapper procedure defined with [[#define-array-accessors|{{define-array-accessors}}]] has the following interface: <enscript> (MAP proc array_1 ... array_n) → list </enscript> Calls {{proc}} with each array index and the item(s) at that index of the array(s), and returns a list containing each result of {{proc}}, in order by array index. Unlike an [[#array-item-iterator|array item iterator]], the temporal order in which {{proc}} is applied to the items of the array(s) is unspecified, so you should not depend on side effects happening in order. {{proc}} must be a procedure with the interface {{(proc i item_1 ... item_n)}}, where {{i}} is the array index, and each {{item}} is the item from each {{array}} at index {{i}}. {{proc}} should return a single value. Each item will behave like it was returned from an [[#array-item-getter|array item getter]]. In particular, each item will refer to a location in the corresponding array's memory, so modifying the item will modify that array. Also, a parent-child relationship will be created between the array and the item. Unlike array item iterators, you can use the items passed to {{proc}} in any way you want, and keep references to them however long you want. Each {{array}} must be a non-null instance of the armor type that this mapper procedure was defined for. If the arrays are different lengths, mapping will end when the smallest array is exhausted. <table role="presentation"><tr><td style="padding-top: 0.6rem; padding-right: 0.4rem;"> <strong><img alt="Warning" src="https://upload.wikimedia.org/wikipedia/commons/6/6e/Dialog-warning.svg" width=32 /></strong> </td><td style="vertical-align: middle;"> Calling this procedure from multiple threads at the same time with the same array may cause the software to crash later or have security vulnerabilities, unless you enable [[#armor-thread-safety|thread safety]] when defining the array's armor type. </td></tr></table> ==== Array item iterator An array item iterator procedure defined with [[#define-array-accessors|{{define-array-accessors}}]] has the following interface: <enscript> (FOR-EACH proc array_1 ... array_n) </enscript> Calls {{proc}} with each array index and the item(s) at that index of the array(s). The iterator does not return anything. Unlike an [[#array-item-mapper|array item mapper]], the iterator is guaranteed to apply {{proc}} to the items of the array(s) in order from the first item(s) to the last, so you can depend on side effects happening in order. {{proc}} must be a procedure with the interface {{(proc i item_1 ... item_n)}}, where {{i}} is the array index, and each {{item}} is the item from each {{array}} at index {{i}}. The {{item}}s will be whatever type {{ITEM-WRAP}} returns. It must be an instance of a type defined with [[#define-armor-type|{{define-armor-type}}]]. Each item will refer to a location in the corresponding array's memory, so modifying the item will modify that array. <table role="presentation"><tr><td style="padding-top: 0.6rem; padding-right: 0.4rem;"> <strong><img alt="Warning" src="https://upload.wikimedia.org/wikipedia/commons/6/6e/Dialog-warning.svg" width=32 /></strong> </td><td style="vertical-align: middle;"> Unlike [[#array-item-mapper|array item mappers]], you must not use any item after {{proc}} finishes executing. For example, do not store the item in a variable outside of {{proc}} and then try to use it later (see example). This is because the iterator may re-use the same armor instance(s) each time {{proc}} is called, for efficiency. </td></tr></table> Each {{array}} must be a non-null instance of the armor type that this mapper procedure was defined for. If the arrays are different lengths, iteration will end when the smallest array is exhausted. Example: <enscript> (let ((array1 (make-event-array 20)) (array2 (make-event-array 3)) (results '()) (old-event #f)) (event-array-for-each (lambda (i event1 event2) ;; Good. You can modify the items within proc, and the changes ;; will be written to the array. (set! (event-type event1) 'key) (set! (event-type event2) 'sensor) ;; Good. You can copy data out of the items and keep it even ;; after proc ends. (set! results (append results (list i (event-type event1) (event-type event2)))) ;; Bad! Don't keep references to the armors after proc ends! (set! old-event event2)) array1 array2) ;; Iteration stopped when the smallest array was exhausted. (print results) ; (0 key sensor 1 key sensor 2 key sensor) ;; The event armors passed to proc are no longer valid! (print (event-type old-event))) ; error </enscript> ==== Array item pointer getter An array item pointer getter procedure defined with [[#define-array-accessors|{{define-array-accessors}}]] has the following interface: <enscript> (REF* array i) → tagged pointer or locative </enscript> Returns a pointer or locative referring to the memory of the item at index {{i}} of the array (starting at index 0). Signals an exception if {{i}} is out of bounds. {{array}} must be a non-null instance of the armor type that this procedure was defined for. If {{array}} wraps a pointer, this procedure returns a pointer [[/man/5/Module (chicken memory)#tagged-pointers|tagged]] with {{'ITEM-ARMOR-NAME}}; if {{array}} wraps a blob or locative, this procedure returns a locative. <table role="presentation"><tr><td style="padding-top: 0.6rem; padding-right: 0.4rem;"> <strong><img alt="Warning" src="https://upload.wikimedia.org/wikipedia/commons/6/6e/Dialog-warning.svg" width=32 /></strong> </td><td style="vertical-align: middle;"> The memory is owned by the array. You must not free the pointer or locative, and you must not use the pointer or locative after the array has been freed. Doing so can cause the software to crash or have security vulnerabilities. </td></tr></table> See "[[https://gitlab.com/jcroisant/jiffi/blob/main/docs/getting-started.md#warning-about-bare-data|Warning about bare data]]" in the Getting Started guide. [[#array-item-getter|Array item getters]] are much safer, although a little less efficient. ==== Array item pointer mapper An array item pointer mapper procedure defined with [[#define-array-accessors|{{define-array-accessors}}]] has the following interface: <enscript> (MAP* proc array_1 ... array_n) → list </enscript> Calls {{proc}} with each array index and a pointer or locative referring to the memory of the item(s) at that index of the array(s), and returns a list containing each result of {{proc}}, in order by array index. Unlike an [[#array-item-pointer-iterator|array item pointer iterator]], the temporal order in which {{proc}} is applied to the items of the array(s) is unspecified, so you should not depend on side effects happening in order. {{proc}} must be a procedure with the interface {{(proc i ptr_1 ... ptr_n)}}, where {{i}} is the array index, and each {{ptr}} is a pointer or locative referring to the item from each {{array}} at index {{i}}. {{proc}} should return a single value. Each {{array}} must be a non-null instance of the armor type that this mapper procedure was defined for. If the arrays are different lengths, mapping will end when the smallest array is exhausted. If an {{array}} wraps a pointer, {{proc}} will be passed a pointer [[/man/5/Module (chicken memory)#tagged-pointers|tagged]] with {{'ITEM-ARMOR-NAME}}; if an {{array}} wraps a blob or locative, {{proc}} will be passed a locative. <table role="presentation"><tr><td style="padding-top: 0.6rem; padding-right: 0.4rem;"> <strong><img alt="Warning" src="https://upload.wikimedia.org/wikipedia/commons/6/6e/Dialog-warning.svg" width=32 /></strong> </td><td style="vertical-align: middle;"> The memory is owned by the array. You must not free the pointer or locative, and you must not use the pointer or locative after the array has been freed. Doing so can cause the software to crash or have security vulnerabilities. </td></tr></table> See "[[https://gitlab.com/jcroisant/jiffi/blob/main/docs/getting-started.md#warning-about-bare-data|Warning about bare data]]" in the Getting Started guide. [[#array-item-mapper|Array item mappers]] are much safer, although a little less efficient. ==== Array item pointer iterator An array item pointer iterator procedure defined with [[#define-array-accessors|{{define-array-accessors}}]] has the following interface: <enscript> (FOR-EACH* proc array_1 ... array_n) </enscript> Calls {{proc}} with each array index and a pointer or locative referring to the memory of the item(s) at that index of the array(s). The iterator does not return anything. Unlike an [[#array-item-pointer-mapper|array item pointer mapper]], the iterator is guaranteed to call {{proc}} on the items of the array(s) in order from the first item(s) to the last, so you can depend on side effects happening in order. {{proc}} must be a procedure with the interface {{(proc i ptr_1 ... ptr_n)}}, where {{i}} is the array index, and each {{ptr}} is a pointer or locative referring to the item from each {{array}} at index {{i}}. Each {{array}} must be a non-null instance of the armor type that this mapper procedure was defined for. If the arrays are different lengths, iteration will end when the smallest array is exhausted. If an {{array}} wraps a pointer, {{proc}} will be passed a pointer [[/man/5/Module (chicken memory)#tagged-pointers|tagged]] with {{'ITEM-ARMOR-NAME}}; if an {{array}} wraps a blob or locative, {{proc}} will be passed a locative. <table role="presentation"><tr><td style="padding-top: 0.6rem; padding-right: 0.4rem;"> <strong><img alt="Warning" src="https://upload.wikimedia.org/wikipedia/commons/6/6e/Dialog-warning.svg" width=32 /></strong> </td><td style="vertical-align: middle;"> The memory is owned by the array. You must not free the pointer or locative, and you must not use the pointer or locative after the array has been freed. Doing so can cause the software to crash or have security vulnerabilities. </td></tr></table> See "[[https://gitlab.com/jcroisant/jiffi/blob/main/docs/getting-started.md#warning-about-bare-data|Warning about bare data]]" in the Getting Started guide. [[#array-item-iterator|Array item iterators]] are much safer, although a little less efficient. === {{define-array-copy!}} <macro>(define-array-copy! COPY! ...)</macro> Defines a [[#destructive-array-copier|destructive array copier]], and generates a [[/man/5/Types|type declaration]] for the procedure. Usage: <enscript> (define-array-copy! COPY! (ARMOR-NAME "STRUCT_NAME" PRED UNWRAP LENGTH)) </enscript> {{COPY!}} is the procedure name to define. {{ARMOR-NAME}} is the record type name for the array, such as the record name passed to [[#define-armor-type|{{define-armor-type}}]]. {{"STRUCT_NAME"}} is a string containing the name of the C struct/union, exactly as it appears in C. {{PRED}} is an existing type predicate for the array record type, such as a procedure defined with {{define-armor-type}}. {{UNWRAP}} is an existing [[#armor-unwrapper|armor unwrapper]] procedure, such as a procedure defined with [[#define-armor-type|{{define-armor-type}}]]. {{LENGTH}} is an existing procedure that returns the [[#array-armor-length-slot|array length]], such as an extra slot getter defined with {{define-armor-type}}. Example: <enscript> (define-array-copy! event-array-copy! (event-array "FOO_Event" event-array? unwrap-event-array event-array-length)) ;; Define a non-destructive version which creates a new array. (define (event-array-copy from #!optional (start 0) (end (event-array-length from))) (let ((new-array (make-event-array (- end start)))) (event-array-copy! new-array 0 from start end) new-array)) </enscript> ==== Destructive array copier A destructive array copier procedure defined with [[#define-array-copy!|{{define-array-copy!}}]] has the following interface: <enscript> (COPY! to at from #!optional start end) </enscript> Efficiently copies struct/union instances from one array to a different array, or from an array to itself. The return value is currently unspecified. <table role="presentation"><tr><td style="padding-top: 0.6rem; padding-right: 0.4rem;"> <strong><img alt="Hint" src="https://upload.wikimedia.org/wikipedia/commons/e/e7/Dialog-information_on.svg" width=32 /></strong> </td><td style="vertical-align: middle;"> The procedure does not copy the values of extra slots you may have defined with {{define-armor-type}}. </td></tr></table> <table role="presentation"><tr><td style="padding-top: 0.6rem; padding-right: 0.4rem;"> <strong><img alt="Warning" src="https://upload.wikimedia.org/wikipedia/commons/6/6e/Dialog-warning.svg" width=32 /></strong> </td><td style="vertical-align: middle;"> The procedure only performs a shallow copy of the C struct/unions, byte-for-byte. This is safe for C structs/unions that only hold direct values like numbers or fixed-size arrays. But, if the C struct/union has a field that holds a pointer, that field of both the source and destination structs will hold a pointer to the same address, which can cause bugs if you are not careful. </td></tr></table> {{to}} is the destination array to overwrite. It must be long enough to hold all the requested values. Specifically, its length must be greater than or equal to {{(+ at (- end start))}}. {{at}} is the first index of {{to}} to modify, i.e. {{from[start]}} will be copied to {{to[at]}}, {{from[start+1]}} will be copied to {{to[at+1]}}, etc. It must be greater than or equal to 0. {{from}} is the source array to copy values from. {{start}} is the first index of {{from}} to copy. It must be greater than or equal to 0, and less than {{end}}. It defaults to 0. {{end}} is the index of {{from}} to stop copying at, i.e. {{from[end-1]}} will be copied, but {{from[end]}} will not. It must be greater than {{start}}, and less than or equal to the length of {{from}}. It defaults to the length of {{from}}. {{to}} and {{from}} can be the same array, so you can copy from one part of an array to another part of the same array. The source and destination regions may overlap. The procedure has the same result as if the source data was copied into a temporary array, and then into the destination array. (In reality, no temporary array is needed, and this procedure does not allocate any memory.) {{to}} and {{from}} can each be an armor, pointer, locative, or blob. It is okay to be different, for example if {{to}} is a blob and {{from}} is a pointer. See "[[https://gitlab.com/jcroisant/jiffi/blob/main/docs/getting-started.md#warning-about-bare-data|Warning about bare data]]" in the Getting Started guide. In particular, you must be careful that the specified regions are in bounds for the arrays. The procedure signals an exception if either {{to}} or {{from}} are [[#armor-null|null]].
Description of your changes:
I would like to authenticate
Authentication
Username:
Password:
Spam control
What do you get when you subtract 5 from 8?