Suppose that instead of using a static variable for your bit array, you want to pass it a Perl scalar and let the program store its data there. To do so, you need to know a little about how Perl stores its data internally.
All Perl scalar variables are represented by the type SV defined in the perl.h file. The definition of this type is
struct sv { void* sv_any; /* pointer to something */ U32 sv_refcnt; /* how many references to us */ U32 sv_flags; /* what we are */ }; typedef struct sv SV;
The sv_any field points to the data held in the scalar. This can be a Perl string (called a byte array by C programmers) or a reference to a hash, array, code, scalar, or typeglob.
The sv_refcnt is a reference count used by Perl to determine when to free the variable. Finally, the sv_flags are flags used by Perl to handle variable type information and booking information.
You don’t normally access the contents of an SV directly. Instead, Perl provides a rich set of functions that let you create and access SV variables.
For example, the SvIV macro turns a scalar into an integer. (If the scalar doesn’t contain an integer value, the conversion does the best it can.) For example:
void do_square(SV *value) { int number = SvIV(value);
If you want to set a scalar value to an integer, you can use the sv_setiv macro:
sv_setiv(value, 56);
Finally, if for some reason you need to create a scalar, there is a set of newSV... macros. For example:
unsigned char array[5] = {1, 2, 3, 4, 5}; new_value = newSVpv(array, 5);
The online document perldoc perlguts gives you a good introduction to the SV variable type and the functions that manipulate it. Table 14.2 contains a short list of some of the more important functions that can be used with SVs. For a complete list of all the functions you can use (all variable types), see the online document perlapi.
Now rewrite the bits program to let the user pass in an array. First change the function header so that it accepts a Perl scalar:
void set_bit(SV *bits, int x, int y, int value) {
The next step is to turn the SV into something you can use, namely an array and a length. This is done through the SvPV macro:
int len; /* Length of the bit array */ /* The bit array */ unsigned char *array = SvPV(bits, len);
Now that you have the array, check to see whether it has the correct number of bytes in it. If it’s the wrong size, write out an error message and call the Perl internal function croak. (This is the equivalent of the Perl language function die.) For example:
if (len != SIZE_BYTES) { fprintf(stderr, "Bit array must be %d bytes long ", SIZE_BYTES); croak("Can not continue"); }
Since SvPV is a macro, it is able to modify len and stores the length of the scalar there.
Except for a little bit of glue and some math, that is all you have to do. Listing 14.3 shows the full program.
use strict; use warnings; use Inline "C"; sub set_bit($$$$); sub test_bit($$$); # The bit array we are using my $bits = pack("C*", (0) x 32); set_bit($bits, 1,1,1); set_bit($bits, 1,2,1); if ((test_bit($bits, 1,1) != 1) || (test_bit($bits, 1,2) != 1) || (test_bit($bits, 1,3) != 0)) { die("Test #1 Failed"); } set_bit($bits, 1,1,0); if (test_bit($bits, 1,1) != 0) { die("Test #2 Failed"); } print "Tests passed "; __END__ __C__ #define X_SIZE 16 #define Y_SIZE 16 /* Size (in bytes) of the bit array */ #define SIZE_BYTES ((X_SIZE * Y_SIZE) / 8) /* Index into an array which is actually a string */ static unsigned char *element(unsigned char *data, int x, int y) { return (&data[y * (X_SIZE/8) + x]); } void set_bit(SV *bits, int x, int y, int value) { int len; /* Length of the bit array */ /* The bit array */ unsigned char *array = SvPV(bits, len); if (len != SIZE_BYTES) { fprintf(stderr, "Bit array must be %d bytes long ", SIZE_BYTES); croak("Can not continue"); } if (value != 0) *element(array, x/8, y) |= (0x80 >> (x % 8)); else *element(array, x/8, y) &= ~(0x80 >> (x % 8)); } int test_bit(SV *bits, int x, int y) { int len; /* Length of the bit array */ /* The bit array */ unsigned char *array = SvPV(bits, len); if (len != SIZE_BYTES) { fprintf(stderr, "Bit array must be %d bytes long ", SIZE_BYTES); croak("Can not continue"); } return ( ((*element(array, x/8, y)) & (0x80 >> (x % 8))) != 0); } |