_Homogeneous_Vectors_ Library

to load directly: (require (lib "4.ss" "srfi"))

to test: (require (lib "run-tests.ss" "srfi" "4"))

The Homogeneous Vectors library is intended to allow interoperability
between Scheme programs and those that represent data in the manner of
C's arrays.  In particular, this library supplies a module called
homo-vectors which allows scheme code to generate and manipulate
"C"-style vectors which consist entirely of a given type of data.

This library follows _SRFI_4_ closely w.r.t. naming.  Currently, the
external syntax specified by the SRFI is not implemented, either for
input or for output.

Currently, the following types of "C" data are supported:
- f64 (double),
- f32 (float), 
- s32 (int32_t),
- s16 (int16_t),
- s8 (int8_t),
- u32 (u_int32_t),
- u16 (u_int16_t), and
- u8 (u_int8_t)

The module _homo-vectors.ss_ supplies functions for manipulating
homogeneous vectors for each of these types.  There is a complete set
of functions for each homogeneous vector type.

There is (currently) exactly one function which operates on all 
homogeneous vectors:

> (homo-vector-length v)

Given any kind of homo-vector, return its length.

In all creation functions specified below, the arguments must be
"reals", in the mzscheme sense.  That is, they must be numeric, and
not complex.  However, they may certainly be integers or exact
numbers.  Further, all creation construct an appropriate "C"
representation by means of a "C" cast, with the exception that range
checks are performed for integer representations.

> (vector->f64vector v)

Given a vector v, create an f64vector of the same length as v, and
   populate it with the values of v, cast to "C" doubles.

> (vector->f32vector v)
> (vector->s32vector v)
> (vector->s16vector v)
> (vector->s8vector v)
> (vector->u32vector v)
> (vector->u16vector v)
> (vector->u8vector v) : similar to above

> (f64vector v ...)

Given a series of real arguments, produce an f64vector containing
   those values.

> (f32vector v ...)
> (s32vector v ...)
> (s16vector v ...)
> (s8vector v ...)
> (u32vector v ...)
> (u16vector v ...)
> (u8vector v ...) : similar to above

> (make-f64vector l [ v ])

Given a length, construct an f64vector of that length.  If a value
is supplied, the vector is initialized to that value.

> (make-f32vector l [ v ])
> (make-s32vector l [ v ])
> (make-s16vector l [ v ])
> (make-s8vector l [ v ])
> (make-u32vector l [ v ])
> (make-u16vector l [ v ])
> (make-u8vector l [ v ]) : similar to above

> (f64vector->vector v)

Given an f64vector, produce a scheme vector containing those values.

> (f32vector->vector v)
> (s32vector->vector v)
> (s16vector->vector v)
> (s8vector->vector v)
> (u32vector->vector v)
> (u16vector->vector v)
> (u8vector->vector v) : similar to above

> (list->f64vector l)

Given a list of real numbers, produce an f64vector containing those
numbers.

> (list->f32vector l)
> (list->s32vector l)
> (list->s16vector l)
> (list->s8vector l)
> (list->u32vector l)
> (list->u16vector l)
> (list->u8vector l) : similar to above

> (f64vector->list v)

Given an f64vector, produce a list containing the values in the vector

> (f32vector->list v)
> (s32vector->list v)
> (s16vector->list v)
> (s8vector->list v)
> (u32vector->list v)
> (u16vector->list v)
> (u8vector->list v) : similar to above

> (f64vector-length v)

Given an f64vector, return its length.

> (f32vector-length v)
> (s32vector-length v)
> (s16vector-length v)
> (s8vector-length v)
> (u32vector-length v)
> (u16vector-length v)
> (u8vector-length v) : similar to above

> (f64vector-ref v i)

Given an f64vector, return the i-th element.

> (f32vector-ref v i)
> (s32vector-ref v i)
> (s16vector-ref v i)
> (s8vector-ref v i)
> (u32vector-ref v i)
> (u16vector-ref v i)
> (u8vector-ref v i) : similar to above

> (f64vector-set! v i x) 

Given an f64vector and an index i and a value x, mutate the i-th
   element of v to contain x (cast to a double).

> (f32vector-set! v i x)
> (s32vector-set! v i x)
> (s16vector-set! v i x)
> (s8vector-set! v i x)
> (u32vector-set! v i x)
> (u16vector-set! v i x)
> (u8vector-set! v i x) : similar to above

> (f64vector? v) 

Return true if v is an f64vector, false otherwise.

> (f32vector? v)
> (s32vector? v)
> (s16vector? v)
> (s8vector? v)
> (u32vector? v)
> (u16vector? v)
> (u8vector? v) : similar to above

> (f64vector-sum v v) 

Given two f64vectors, produce a new f64vector which is their pointwise
   sum.

> (f32vector-sum v v)
> (s32vector-sum v v)
> (s16vector-sum v v)
> (s8vector-sum v v)
> (u32vector-sum v v)
> (u16vector-sum v v)
> (u8vector-sum v v) : similar to above

> (f64vector-difference v v) 

Given two f64vectors, produce a new f64vector which is their pointwise
   difference.

> (f32vector-difference v v)
> (s32vector-difference v v)
> (s16vector-difference v v)
> (s8vector-difference v v)
> (u32vector-difference v v)
> (u16vector-difference v v)
> (u8vector-difference v v) : similar to above

> (f64vector-scale v x) 

Given an f64vectors and a real, produce a new f64vector which is their
   scalar multiple.

> (f32vector-scale v x)
> (s32vector-scale v x)
> (s16vector-scale v x)
> (s8vector-scale v x)
> (u32vector-scale v x)
> (u16vector-scale v x)
> (u8vector-scale v x) : similar to above

> (f64vector-norm v) 

Given two f64vectors, produce their norm; that is, the root of the sum
   of the squares of the vector's elements.

> (f32vector-norm v)
> (s32vector-norm v)
> (s16vector-norm v)
> (s8vector-norm v)
> (u32vector-norm v)
> (u16vector-norm v)
> (u8vector-norm v) : similar to above

> (f64vector-type) 

The vector-type is the Scheme_Type assigned (at runtime) to this type
of vector.  This information must be supplied at runtime to other 
scheme extensions which wish to distinguish the various types of 
vectors without upcalling into scheme.  A means for doing this 
(semi-)automatically is described below.

> (f32vector-type)
> (s32vector-type)
> (s16vector-type)
> (s8vector-type)
> (u32vector-type)
> (u16vector-type)
> (u8vector-type) : similar to above

In order to use these data types fruitfully, it will generally be the
case that you want to write code in another language that can
manipulate these records directly (unless you're really just after the
space savings).  As an example, suppose you wish to build a shared
library in C that operates on these vectors.  In order to do this, you
will need to include the appropriate header file, which can be found
(after a successful build of this library) in the subdirectory
"c-generation" of this library.  So, to work with vectors of unsigned
16-bit integers, you'd include "c-generation/homo-u16-vector-prims.h".
There's also a file called "c-generation/homo-all-vector-prims.h",
which directly includes all of the others.

The task of locating this library's location in an
installation-independent manner is left to the user.  Presumably, the
easiest way to do it would be to have a scheme-controlled build
process that could supply this information as (for instance) a '-I'
argument to 'cc'.

This header file provides several useful definitions (please
substitute for u16 where appropriate):

> homo_u16_vector
...

This is a typedef for a structure containing a homo-u16-vector.  The
relevant fields of this structure are 'length', giving the vector's
length, the array 'els', containing the actual elements of the vector,
and the field 'keyex', whose low bit may be set to one to indicate
that the vector is immutable.

> make_homo_u16_vector(int length)
...

This function returns a pointer to a scheme-allocated u16vector of the
specified length.  Since this memory is scheme-allocated, it will be
collected if it is not referred to in a way that the garbage collector
can identify.  See the manual Inside MzScheme for more information.

> HOMO_U16_VECTORP(Scheme_Object *)
...

This function tests whether a given pointer is a pointer to a homo-u16-vector.

*** HERE'S THE CATCH ***

Unfortunately, there's a small catch.  The problem is that in order 
to create or test for membership in a given homo-vector type, your
C code needs to know the Scheme_Type assigned to each homo-vector
type.  There are two ways around this problem.  The easiest (and
least satisfying) is simply to avoid these operations.  That is, 
your C code will have to know by some other means the types of the
vectors that it manipulates.  This is simple if, for instance, your
code only creates and never receives these vectors, or if it only
handles one kind of vector.

However, in general, this is not a satisfying solution.  To this
end, this library provides a glue function which can be used to
set these dynamically assigned Scheme_Type values.  In order to 
use this mechanism:

1) include the file "homo-all-vector-glue.c" (in the c-generation 
directory) in your project.
2) Arrange as an initialization step in your library to call the
set_all_vector_types function defined in homo-all-vector-glue.c with
the type-assoc list provided by the homo-vectors module.  The easiest
way to do this is to include this function in the list of functions
provided by your C library, and (in Scheme) call this function with
the type-assoc list.

To illustrate this, I include a short C file which can be compiled
into a mzscheme extension and used as suggested by the scheme file which 
follows it.

*** BEGIN test-linking-mechanism.c:

#include "escheme.h"

#include "homo-all-vector-glue.c"

Scheme_Object *get_vector(int argc, Scheme_Object **argv)
{
  Scheme_Object *new_vector = (Scheme_Object *)make_homo_u16_vector(10);

  return new_vector;
}

Scheme_Object *scheme_reload(Scheme_Env *env)
{
  return scheme_false;
}

Scheme_Object *scheme_initialize(Scheme_Env *env)
{
  Scheme_Object *funs[2];

  funs[0] = scheme_make_prim_w_arity(set_all_vector_types,
				     "init",
				     1,1);
  funs[1] = scheme_make_prim_w_arity(get_vector,
				     "get-vector",
				     0,0);

  return scheme_values(2,funs);
}

Scheme_Object *scheme_module_name()
{
  return scheme_false;
}

*** END test-linking-mechanism.c

*** BEGIN test.ss :

(require (lib "compile.ss" "dynext"))
(require (lib "link.ss" "dynext"))
(require (lib "4.ss" "srfi"))

(current-directory "/Users/clements/clements/collects/homogeneous-vectors")

(compile-extension #f "test-linking-mechanism.c" "test-linking-mechanism.o" `("c-generation"))
(link-extension #f `("test-linking-mechanism.o") "test-linking-mechanism.so")

(define-values (init get-vector) (load-extension "test-linking-mechanism.so"))
type-assoc

(init type-assoc)

(define new-vector (get-vector))

*** END test.ss


I freely admit that this solution is yicky, and would gratefully
accept any and all suggestions for its modification or replacement.

