module: c-ffi-implementation Author: Peter Benson Copyright: Original Code is Copyright (c) 1995-2004 Functional Objects, Inc. All rights reserved. License: Functional Objects Library Public License Version 1.0 Dual-license: GNU Lesser General Public License Warranty: Distributed WITHOUT WARRANTY OF ANY KIND /// top level exported /// notice that import-map is not needed since the default import for /// any pointer is itself define open abstract simple-c-mapped-subtype (, ) export-map type-union(, ), export-function: export-c-string; pointer-type ; end; /// the instantiation of define sealed concrete c-subtype () end; /// make a . Notice size and fill keywords accepted as /// any collection. // TODO: CORRECTNESS: What are the real restrictions? define inline function check-c-string-size-options (class :: , size, element-count) => (size :: false-or(), element-count :: ) case size => if (element-count & size + 1 ~== element-count) error("The element-count: %=, size: %= options to make on " "%= are inconsistent.", element-count, size, class); end; if (size < 0) error("The size: %= option to make on %= is less than the " "minimum size 0.", size, class); end; values(size, size + 1); element-count => values(#f, element-count); otherwise => values(0, 1); end; end function; define method make (class == , #rest other-keys, #key size = #f, element-count = #f, fill = $not-given, address = #f) => (o :: ); if (address) apply(next-method, class, other-keys); else let (size, element-count) = check-c-string-size-options(class, size, element-count); // Extra-bytes and that stuff is handled in the default method, we just // arrange to default element-count to 1 + size to account for the null // terminator. let result = apply(next-method, class, element-count: element-count, other-keys); if (size) let size :: = size; let raw-pointer :: = primitive-unwrap-c-pointer(result); if (fill) // check type and fill in default. let fill :: = if (given?(fill)) fill else ' ' end; let raw-fill :: = integer-as-raw(as(, fill)); for (i :: from 0 below size) primitive-c-unsigned-char-at-setter (raw-fill, raw-pointer, integer-as-raw(i), integer-as-raw(0)); end; end; // Null terminate. primitive-c-unsigned-char-at-setter (integer-as-raw(0), raw-pointer, integer-as-raw(size), integer-as-raw(0)); end; result end; end; define method make (class == , #rest other-keys, #key size = #f, element-count = #f, fill = $not-given, address = #f) => (o :: ) apply(make, , other-keys); end method; /// A constructor for static C strings. // TODO: CORRECTNESS: Make a macro? define function C-string-constant (string :: ) => (value :: ) make(, address: primitive-wrap-machine-word (primitive-cast-pointer-as-raw (primitive-string-as-raw(string)))) end function; /* // For some reason this doesn't get called with the right stuff. /// This initialize method that accepts a fill: keyword can be inherited. define method initialize (result :: , #key fill = $not-given, address, element-count, #all-keys) => (); if(~address & fill) if (~given?(fill)) // defaults to space if not given fill := ' '; end; let raw-fill :: = integer-as-raw(as(, fill)); let raw-pointer :: = primitive-unwrap-c-pointer(result); for(i from 0 below element-count) primitive-c-unsigned-char-at-setter(raw-fill, raw-pointer, integer-as-raw(i), integer-as-raw(0)); end; primitive-c-unsigned-char-at-setter(integer-as-raw(0), raw-pointer, integer-as-raw(element-count), integer-as-raw(0)); end; values(); end; */ /// pointer-value in a C-string returns a character. define method pointer-value (c-str :: , #key index :: = 0) => (c :: ); as(, raw-as-integer (primitive-c-unsigned-char-at(primitive-unwrap-c-pointer(c-str), integer-as-raw(index), integer-as-raw(0)))); end; define method pointer-value-setter (c :: , c-str :: , #key index :: = 0) => (c :: ); primitive-c-unsigned-char-at-setter(integer-as-raw(as(, c)), primitive-unwrap-c-pointer(c-str), integer-as-raw(index), integer-as-raw(0)); c end; /// define method export-c-string (obj :: ) => (obj :: ); obj end; define method export-c-string (obj :: ) => (p :: ); make-c-pointer(, primitive-cast-pointer-as-raw(primitive-string-as-raw(obj)), #[]) end; define function cstr-next-state (collection :: , state :: ) => (next :: ); state + 1 end; define function cstr-finished-state (collection :: , state :: , limit :: ) => (b :: ) null-pointer?(collection) | 0 = raw-as-integer (primitive-c-unsigned-char-at(primitive-unwrap-c-pointer(collection), integer-as-raw(0), integer-as-raw(state))) end; define function cstr-current-key (c :: , i :: ) => (i :: ); i end; define function cstr-current-element (c :: , i :: ) => (i :: ); pointer-value(c, index: i) end; define function cstr-current-element-setter (new :: , c :: , i :: ) => (i :: ); pointer-value-setter(new, c, index: i) end; define function cstr-copy-state (c :: , s :: ) => (s :: ); s end; define method forward-iteration-protocol (cstr :: ) => (initial-state :: , limit :: , next-state :: , finished-state? :: , current-key :: , current-element :: , current-element-setter :: , copy-state :: ); values(0, #f, cstr-next-state, cstr-finished-state, cstr-current-key, cstr-current-element, cstr-current-element-setter, cstr-copy-state) end; define constant $not-given = list(#"not-given"); define function given? (obj :: ) => (b :: ); obj ~== $not-given end; define method element (cstr :: , key :: , #key default = $not-given) => (c :: ); if (key < 0) if (given?(default)) default else error("ELEMENT outside of range: %= %=", cstr, key) end; else pointer-value(cstr, index: key); end; end; define method element-setter (c :: , cstr :: , key :: ) => (c :: ); if (key < 0) error("ELEMENT outside of range: %= %=", cstr, key) end; pointer-value-setter(c, cstr, index: key); end; define method size (c :: ) => (s :: ); if (null-pointer?(c)) 0 else raw-as-integer(primitive-strlen(primitive-unwrap-c-pointer(c))) end; end; define method empty? (obj :: ) => (b :: ); null-pointer?(obj) | 0 = raw-as-integer (primitive-c-unsigned-char-at(primitive-unwrap-c-pointer(obj), integer-as-raw(0), integer-as-raw(0))) end; define method \= (string-1 :: , string-2 :: ) => (result :: ) if ((string-1 == string-2) | (empty?(string-1) & empty?(string-2))) #t; elseif (empty?(string-1) | empty?(string-2)) #f; else for (c1 :: in string-1, c2 :: in string-2, eq = #t then c1 = c2, while: eq) finally eq end for; end if; end method; // // .... /// top level exported /// notice that import-map is not needed since the default import for /// any pointer is itself define open /* abstract */ simple-c-mapped-subtype (, ) export-map type-union(, ), export-function: export-c-string; pointer-type ; end; /// the instantiation of define sealed concrete c-subtype () end; /// make a . Notice size and fill keywords accepted as /// any collection. define method make (class == , #rest other-keys, #key size = #f, element-count = #f, fill = $not-given, address = #f) => (o :: ); if (address) apply(make, , other-keys); else let (size, element-count) = check-c-string-size-options(class, size, element-count); // Extra-bytes and that stuff is handled in the default method, we just // arrange to default element-count to 1 + size to account for the null // terminator. let result = apply(make, , element-count: element-count, other-keys); if (size) let size :: = size; let raw-pointer :: = primitive-unwrap-c-pointer(result); if (fill) // Check type and fill in default. let fill :: = as(, if (given?(fill)) fill else ' ' end); let raw-fill :: = integer-as-raw(as(, fill)); for (i :: from 0 below size) primitive-c-unsigned-short-at-setter (raw-fill, raw-pointer, integer-as-raw(i), integer-as-raw(0)); end; end; // Null terminate. primitive-c-unsigned-short-at-setter (integer-as-raw(0), raw-pointer, integer-as-raw(size), integer-as-raw(0)); end; result end; end; define method C-unicode-string-constant (string :: ) => (value :: ) as(, string); end method; define method C-unicode-string-constant (string :: ) => (value :: ) make(, address: primitive-wrap-machine-word (primitive-cast-pointer-as-raw (primitive-string-as-raw(string)))) end method; /* // For some reason this doesn't get called with the right stuff. /// This initialize method that accepts a fill: keyword can be inherited. define method initialize (result :: , #key fill = $not-given, address, element-count, #all-keys) => (); if(~address & fill) if (~given?(fill)) // defaults to space if not given fill := ' '; end; let raw-fill :: = integer-as-raw(as(, fill)); let raw-pointer :: = primitive-unwrap-c-pointer(result); for(i from 0 below element-count) primitive-c-unsigned-short-at-setter(raw-fill, raw-pointer, integer-as-raw(i), integer-as-raw(0)); end; primitive-c-unsigned-short-at-setter(integer-as-raw(0), raw-pointer, integer-as-raw(element-count), integer-as-raw(0)); end; values(); end; */ /// pointer-value in a C-unicode-string returns a character. define inline function pointer-integer-value (c-str :: , #key index :: = 0) => (val :: ) raw-as-integer (primitive-c-unsigned-short-at (primitive-unwrap-c-pointer(c-str), integer-as-raw(index), integer-as-raw(0))); end function; define method pointer-value (c-str :: , #key index :: = 0) => (c :: ); as(, pointer-integer-value(c-str, index: index)) end; define method pointer-value-setter (c :: , c-str :: , #key index :: = 0) => (c :: ); primitive-c-unsigned-short-at-setter (integer-as-raw(as(, c)), primitive-unwrap-c-pointer(c-str), integer-as-raw(index), integer-as-raw(0)); c end; define method pointer-value-setter (c :: , c-str :: , #key index :: = 0) => (c :: ); primitive-c-unsigned-short-at-setter (integer-as-raw(as(, as(, c))), primitive-unwrap-c-pointer(c-str), integer-as-raw(index), integer-as-raw(0)); c end; /// define method export-c-string (obj :: ) => (p :: ); make-c-pointer(, primitive-cast-pointer-as-raw(primitive-string-as-raw(obj)), #[]) end; define inline function custr-next-state (collection :: , state :: ) => (next :: ); state + 1 end; define inline function custr-finished-state (collection :: , state :: , limit :: ) => (b :: ) null-pointer?(collection) | 0 = pointer-integer-value(collection, index: state) end; define inline function custr-current-key (c :: , i :: ) => (i :: ); i end; define inline function custr-current-element (c :: , i :: ) => (i :: ); pointer-value(c, index: i) end; define inline function custr-current-element-setter (new :: , c :: , i :: ) => (i :: ); pointer-value-setter(new, c, index: i) end; define inline function custr-copy-state (c :: , s :: ) => (s :: ); s end; define method forward-iteration-protocol (custr :: ) => (initial-state :: , limit :: , next-state :: , finished-state? :: , current-key :: , current-element :: , current-element-setter :: , copy-state :: ); values(0, #f, custr-next-state, custr-finished-state, custr-current-key, custr-current-element, custr-current-element-setter, custr-copy-state) end; define method element (custr :: , key :: , #key default = $not-given) => (c :: ); if (key < 0) if (given?(default)) default else error("ELEMENT outside of range: %= %=", custr, key) end; else pointer-value(custr, index: key); end; end; define method element-setter (c :: , custr :: , key :: ) => (c :: ); if (key < 0) error("ELEMENT outside of range: %= %=", custr, key) end; pointer-value-setter(c, custr, index: key); end; define method size (c :: ) => (s :: ); if (null-pointer?(c)) 0 else for (i from 0, until: pointer-integer-value(c, index: i) = 0) finally i end; end; end; define method empty? (obj :: ) => (b :: ); null-pointer?(obj) | 0 = pointer-integer-value(obj, index: 0) end; define method \= (string-1 :: , string-2 :: ) => (result :: ) if ((string-1 == string-2) | (empty?(string-1) & empty?(string-2))) #t; elseif (empty?(string-1) | empty?(string-2)) #f; else for (c1 :: in string-1, c2 :: in string-2, eq = #t then c1 = c2, while: eq) finally eq end for; end if; end method; // ------ /// define open simple-c-mapped-subtype () export-map , export-function: method (x :: ) => (m :: ); as(, as(, x)); end; import-map , import-function: method (x :: ) => (i :: ); as(, as-unsigned(, x)); end; pointer-type ; end; /* /// define open simple-c-mapped-subtype () export-map type-union(, ), export-function: method (x :: ) => (m :: ); as(, maybe-register-c-object(x)); end; import-map , import-function: method (x :: ) => (i :: ); lookup-c-object(as-unsigned(, x)); end; pointer-type ; end; // maybe both these tables should be weak with respect to object define constant $c-object-integer-table = make(); define constant $c-integer-object-table = make(); define constant $c-no-object = pair(#"no", #"object"); define variable *c-next-object-index* = 0; define method maybe-register-c-object (obj :: ) => (i :: ); let maybe-answer = element($c-object-integer-table, obj, default: #f); if (maybe-answer) maybe-answer else // !@#$ this should really be interlocked against other threads let answer = *c-next-object-index*; *c-next-object-index* := *c-next-object-index* + 1; $c-object-integer-table[obj] := answer; $c-integer-object-table[answer] := obj; answer end if end method; define method lookup-c-object (i :: ) => (obj :: ); let obj = element($c-integer-object-table, i, default: $c-no-object); if (obj = $c-no-object) // !@#$ raise an error here #f else obj end if; end method; */ /* // not used yet, but clearly useful define method unregister-c-object (obj :: ) => (); let index = element($c-object-integer-table, obj, default: #f); if (index) remove-key!($c-object-integer-table, obj); $c-integer-object-table[index] := $c-no-object; end if; values(); end; */ // ---- /// define method import-c-boolean (i :: ) => (b :: ); if (as(, i) = 0) #f else #t end end; define method export-c-boolean (b :: ) => (m :: ); if (b) as(, 1) else as(, 0) end end; define simple-c-mapped-subtype () map , import-function: method (i :: ) => (b :: ); if (i = as(, 0)) #f else #t end end, export-function: method (b :: ) => (m :: ); if (b) as(, 1) else as(, 0) end end; pointer-type ; end; ///---*** NOTE: I think this code needs work w.r.t s ///---*** being larger than s! (-Palter) /// define open simple-c-mapped-subtype () pointer-type ; end; /// implemented as a table/handle define class () constant slot object-handle :: , required-init-keyword: handle:; slot ref-count :: = 0; end; define constant $c-dylan-object-table = make(
); define constant $c-dylan-handle-table = make(); define variable *c-dylan-object-current-handle* = 0; define method generate-c-dylan-object-handle (obj :: ) => (i :: ) // locking may be necessary here. block (return) for (i from 1 to *c-dylan-object-current-handle*) if ($c-dylan-handle-table[i] == #f) $c-dylan-handle-table[i] := obj; // set it to something to reserve slot return(i) end if; end for; *c-dylan-object-current-handle* := *c-dylan-object-current-handle* + 1; return(*c-dylan-object-current-handle*); end block; end; define method as (c == , handle :: ) => (int :: ) as(, pointer-address(handle)); end; define method initialize (ref :: , #key handle, object) element($c-dylan-handle-table, as(, handle)) := object; element($c-dylan-object-table, object) := ref; end; /// exported function define method register-c-dylan-object (obj :: ) => () let existing-reference = element($c-dylan-object-table, obj, default: #f); let reference = existing-reference | make(, handle: make(, address: generate-c-dylan-object-handle(obj)), object: obj); reference.ref-count := reference.ref-count + 1; values(); end; /// exported function define method unregister-c-dylan-object (obj :: ) => (); let reference = element($c-dylan-object-table, obj, default: #f); if (reference) reference.ref-count := reference.ref-count - 1; if (reference.ref-count == 0) remove-key!($c-dylan-object-table, obj); $c-dylan-handle-table[as(, reference.object-handle)] := #f; end; end; values(); end; /// exported function define method export-c-dylan-object (obj :: ) => (handle :: ) let existing-reference = element($c-dylan-object-table, obj, default: #f); unless (existing-reference) // error out here end; existing-reference.object-handle; end; /// exported function define method import-c-dylan-object (handle :: ) => (obj :: ) let existing-object = element($c-dylan-handle-table, as(, handle), default: #f); unless (existing-object) // error out here end; existing-object end; // eof