Rebol [ Title: "Peek-Poke" File: %peekpoke.r Author: "Ladislav Mecir" Date: 8-Jun-2010/19:35:10+2:00 Purpose: {structure manipulations} ] comment [ ; What is the best counterpart of a C pointer in REBOL? ; In C, there are many distinct pointer types, e.g.: ; void*, char*, double*, int*, float*, etc. ; The advantage is, that these pointers have all the same size, i.e. ; sizeof char* is the same as sizeof void*, etc., and it is easy ; to convert one pointer type to another, the only difference being, ; that pointer arithmetic expressions like p + 1 depend on pointer type. ; It looks, like the most convenient counterpart of C pointers ; are integer! values in REBOL (this suggestion can also be found ; in http://www.rebol.com/docs/library.html#section-25) ; Advantages: ; integer arithmetic can be used to manipulate pointers ; (without performing additional conversions) ; integers can be used as both return values and arguments ; of routines, if sufficient care is taken to not ; allow REBOL garbage collector to collect the respective ; parts of memory prematurely ; Disadvantages: ; this solution is not general, since it may happen, ; that sizeof 'integer! is not equal to sizeof 'char*, ; in which case adjustments have to be made to the code below ; Notes: ; REBOL STRUCT! datatype is not directly compatible with C struct, ; it is usually handled by the interpreter as a pointer, ; i.e. as a C struct* datatype. ; Usage examples: ; this is how to get the sizes of some datatypes in bytes: sizeof 'double ; == 8 sizeof 'float ; == 4 sizeof 'long ; == 4 sizeof 'int ; == 4 sizeof 'short ; == 2 sizeof 'char* ; == 4 ; pointer sizeof [struct! []] ; == 4 ; pointer sizeof 'integer! ; == 4 sizeof 'char ; == 1 ; REBOL decimal! is 64-bit IEEE 754 FP number (C double) ; in little endian byte order for Intel processors e.g. ; This is how to convert it to binary convert [0.2] [double] ; == #{9A9999999999C93F} ; (Using an Intel processor,) if you prefer a big endian byte order, use reverse convert [0.2] [double] ; == #{3FC999999999999A} ; conversion to the 32-bit IEEE 754 FP format (C float) convert [0.2] [float] ; == #{CDCC4C3E} ; the endianness of the processor can be found as follows: endian?: pick [little big] 1 = first convert [1] [int] ; another way: endian?: get-modes system/ports/system 'endian ; this is how to convert from binary to a REBOL block, ; supposing, that the endianness of the binary ; is the same as the endianness of the processor ; (otherwise the bytes shall be reversed before the conversion) convert #{9A9999999999C93F} [double] ; == [0.2] ; this is how to convert more values at once convert [1 2] [int int] ; == #{0100000002000000} convert #{0100000002000000} [int int] ; == [1 2] ; Library function call example ; If a library function f is declared as follows: ; ; int f (double* a, int size); ; ; taking a double array a with a defined size, ; ; this is how its REBOL counterpart routine can be defined: f: make routine! [ a [integer!] ; using REBOL integer! in place of a C pointer size [int] return: [int] ] library "f" ; ; and this is how we can call the routine ; using a size = 6 element array filled with 0.0s: size: 6 array: make binary! size * sizeof 'double insert/dup array convert [0.0] [double] size result: f string-address? array size ] use [ int-struct string-struct struct-struct ] [ int-struct: make struct! [int [integer!]] none string-struct: make struct! [string [char*]] none struct-struct: make struct! [struct [struct! [[save] c [char]]]] none sizeof: func [ {get the size of a datatype in bytes} datatype [word! block!] ] [ length? third make struct! compose/deep [value [(datatype)]] none ] string-address?: func [ { get the address of the given string Warning! Works only if sizeof 'integer! is equal to sizeof 'char* } string [any-string!] ] [ string-struct/string: string change third int-struct third string-struct int-struct/int ] struct-address?: func [ { get the address of the given struct Warning! Needs STRING-ADDRESS? to work } struct [struct!] ] [ string-address? third struct ] get-memory: func [ { copy a region of memory given at address with the given size Warning! Works only if sizeof 'integer! is equal to sizeof 'char* } address [integer!] size [integer!] /local struct ] [ int-struct/int: address struct: make struct! compose/deep [string [char-array (size)]] none change third struct third int-struct as-binary struct/string ] set-memory: func [ { set a region of memory at address Warning! Works only if sizeof 'integer! is equal to sizeof 'char* } address [integer!] contents [binary! string!] ] [ int-struct/int: address foreach char as-string contents [ change third struct-struct third int-struct struct-struct/struct/c: char int-struct/int: int-struct/int + 1 ] ] convert: func [ {convert block to binary, or binary to block} from [block! binary!] spec [block!] { type specification, supported types are listed in http://www.rebol.com/docs/library.html } /local result struct size ] [ either block? from [ result: copy #{} repeat i min length? from length? spec [ append result third make struct! compose/deep [ value [(pick spec i)] ] reduce [pick from i] ] ] [ result: copy [] foreach type spec [ struct: make struct! compose/deep [value [(type)]] none size: length? third struct if size > length? from [break] change third struct copy/part from size append result struct/value from: skip from size ] ] result ] ]