File Coverage

arrays.c
Criterion Covered Total %
statement 102 225 45.3
branch 93 336 27.6
condition n/a
subroutine n/a
pod n/a
total 195 561 34.7


line stmt bran cond sub pod time code
1             /*
2              
3             Library of typemap functions for C arrays, idea is to provide
4             automatic conversion between references to perl arrays and C arrays.
5             If the argument is a scalar this is automatically detected and handles
6             as a one element array.
7              
8             Thanks go to Tim Bunce for the pointer to gv.h so I could figure
9             out how to handle glob values.
10              
11             Karl Glazebrook [kgb@aaoepp.aao.gov.au]
12              
13              
14             Dec 95: Add double precision arrays - frossie@jach.hawaii.edu
15             Dec 96: Add 'ref to scalar is binary' handling - kgb@aaoepp.aao.gov.au
16             Jan 97: Handles undefined values as zero - kgb@aaoepp.aao.gov.au
17             Feb 97: Fixed a few type cast howlers+bugs - kgb@aaoepp.aao.gov.au
18             Apr 97: Add support for unsigned char and shorts- timj@jach.hawaii.edu
19              
20             */
21              
22              
23             #include "EXTERN.h" /* std perl include */
24             #include "perl.h" /* std perl include */
25             #include "XSUB.h" /* XSUB include */
26              
27              
28             /* Functions defined in this module, see header comments on each one
29             for more details: */
30              
31             #include "arrays.h"
32              
33 360           int is_scalar_ref (SV* arg) { /* Utility to determine if ref to scalar */
34             SV* foo;
35 360 50         if (!SvROK(arg))
36 0           return 0;
37 360           foo = SvRV(arg);
38 360 100         if (SvPOK(foo))
39 110           return 1;
40             else
41 250           return 0;
42             }
43              
44              
45             /* ####################################################################################
46              
47             pack1D - argument is perl scalar variable and one char pack type.
48             If it is a reference to a 1D array pack it and return pointer.
49             If it is a glob pack the 1D array of the same name.
50             If it is a scalar pack as 1 element array.
51             If it is a reference to a scalar then assume scalar is prepacked binary data
52              
53             [1D-ness is checked - routine croaks if any of the array elements
54             themselves are references.]
55              
56             Can be used in a typemap file (uses mortal scratch space and perl
57             arrays know how big they are), e.g.:
58              
59             TYPEMAP
60             int * T_INTP
61             float * T_FLOATP
62             double * T_DOUBLEP
63             INPUT
64              
65             T_INTP
66             $var = ($type)pack1D($arg,'i')
67             T_FLOATP
68             $var = ($type)pack1D($arg,'f')
69             T_DOUBLEP
70             $var = ($type)pack1D($arg,'d')
71              
72             */
73              
74 203           void* pack1D ( SV* arg, char packtype ) {
75              
76             int iscalar;
77             float scalar;
78             double dscalar;
79             short sscalar;
80             unsigned char uscalar;
81             AV* array;
82             I32 i,n;
83             SV* work;
84             SV** work2;
85             double nval;
86             STRLEN len;
87              
88 203 100         if (is_scalar_ref(arg)) /* Scalar ref */
89 110 50         return (void*) SvPV(SvRV(arg), len);
90              
91 93 50         if (packtype!='f' && packtype!='i' && packtype!='d' && packtype!='s'
    50          
    50          
    0          
92 0 0         && packtype != 'u')
93 0           croak("Programming error: invalid type conversion specified to pack1D");
94              
95             /*
96             Create a work char variable - be cunning and make it a mortal *SV
97             which will go away automagically when we leave the current
98             context, i.e. no need to malloc and worry about freeing - thus
99             we can use pack1D in a typemap!
100             */
101              
102 93           work = sv_2mortal(newSVpv("", 0));
103              
104             /* Is arg a scalar? Return scalar*/
105              
106 93 50         if (!SvROK(arg) && SvTYPE(arg)!=SVt_PVGV) {
    0          
107              
108 0 0         if (packtype=='f') {
109 0 0         scalar = (float) SvNV(arg); /* Get the scalar value */
110 0           sv_setpvn(work, (char *) &scalar, sizeof(float)); /* Pack it in */
111             }
112 0 0         if (packtype=='i') {
113 0 0         iscalar = (int) SvNV(arg); /* Get the scalar value */
114 0           sv_setpvn(work, (char *) &iscalar, sizeof(int)); /* Pack it in */
115             }
116 0 0         if (packtype=='d') {
117 0 0         dscalar = (double) SvNV(arg); /*Get the scalar value */
118 0           sv_setpvn(work, (char *) &dscalar, sizeof(double)); /* Pack it in */
119             }
120 0 0         if (packtype=='s') {
121 0 0         sscalar = (short) SvNV(arg); /*Get the scalar value */
122 0           sv_setpvn(work, (char *) &sscalar, sizeof(short)); /* Pack it in */
123             }
124 0 0         if (packtype=='u') {
125 0 0         uscalar = (unsigned char) SvNV(arg); /*Get the scalar value */
126 0           sv_setpvn(work, (char *) &uscalar, sizeof(char)); /* Pack it in */
127             }
128 0 0         return (void *) SvPV(work, PL_na); /* Return the pointer */
129             }
130              
131             /* Is it a glob or reference to an array? */
132              
133 93 50         if (SvTYPE(arg)==SVt_PVGV || (SvROK(arg) && SvTYPE(SvRV(arg))==SVt_PVAV)) {
    50          
    50          
134              
135 93 50         if (SvTYPE(arg)==SVt_PVGV) {
136 0 0         array = (AV *) GvAVn((GV*) arg); /* glob */
137             }else{
138 93           array = (AV *) SvRV(arg); /* reference */
139             }
140              
141 93           n = av_len(array);
142              
143 93 50         if (packtype=='f')
144 0 0         SvGROW( work, sizeof(float)*(n+1) ); /* Pregrow for efficiency */
    0          
145 93 50         if (packtype=='i')
146 0 0         SvGROW( work, sizeof(int)*(n+1) );
    0          
147 93 50         if (packtype=='d')
148 93 50         SvGROW( work, sizeof(double)*(n+1) );
    100          
149 93 50         if (packtype=='s')
150 0 0         SvGROW( work, sizeof(short)*(n+1) );
    0          
151 93 50         if (packtype=='u')
152 0 0         SvGROW( work, sizeof(char)*(n+1) );
    0          
153              
154              
155             /* Pack array into string */
156              
157 591165 100         for(i=0; i<=n; i++) {
158              
159 591072           work2 = av_fetch( array, i, 0 ); /* Fetch */
160 591072 50         if (work2==NULL)
161 0           nval = 0.0; /* Undefined */
162             else {
163 591072 50         if (SvROK(*work2))
164 0           goto errexit; /* Croak if reference [i.e. not 1D] */
165 591072 100         nval = SvNV(*work2);
166             }
167              
168 591072 50         if (packtype=='f') {
169 0           scalar = (float) nval;
170 0           sv_catpvn( work, (char *) &scalar, sizeof(float));
171             }
172 591072 50         if (packtype=='i') {
173 0           iscalar = (int) nval;
174 0           sv_catpvn( work, (char *) &iscalar, sizeof(int));
175             }
176 591072 50         if (packtype=='d') {
177 591072           dscalar = (double) nval;
178 591072           sv_catpvn( work, (char *) &dscalar, sizeof(double));
179             }
180 591072 50         if (packtype=='s') {
181 0           sscalar = (short) nval;
182 0           sv_catpvn( work, (char *) &sscalar, sizeof(short));
183             }
184 591072 50         if (packtype=='u') {
185 0           uscalar = (unsigned char) nval;
186 0           sv_catpvn( work, (char *) &uscalar, sizeof(char));
187             }
188             }
189              
190             /* Return a pointer to the byte array */
191              
192 93 50         return (void *) SvPV(work, PL_na);
193              
194             }
195              
196             errexit:
197              
198 203           croak("Routine can only handle scalar values or refs to 1D arrays of scalars");
199              
200             }
201              
202              
203              
204             /* #####################################################################################
205              
206             pack2D - argument is perl scalar variable and one char pack type.
207             If it is a reference to a 1D/2D array pack it and return pointer.
208             If it is a glob pack the 1D/2D array of the same name.
209             If it is a scalar assume it is a prepacked array and return pointer
210             to char part of scalar.
211             If it is a reference to a scalar then assume scalar is prepacked binary data
212              
213             [2Dness is checked - program croaks if any of the array elements
214             themselves are references. Packs each row sequentially even if
215             they are not all the same dimension - it is up to the programmer
216             to decide if this is sensible or not.]
217              
218             Can be used in a typemap file (uses mortal scratch space and perl
219             arrays know how big they are), e.g.:
220              
221             TYPEMAP
222             int2D * T_INT2DP
223             float2D * T_FLOAT2DP
224              
225             INPUT
226              
227             T_INT2DP
228             $var = ($type)pack2D($arg,'i')
229             T_FLOAT2DP
230             $var = ($type)pack2D($arg,'f')
231              
232             [int2D/float2D would be typedef'd to int/float]
233              
234             */
235              
236              
237 10           void* pack2D ( SV* arg, char packtype ) {
238              
239             int iscalar;
240             float scalar;
241             short sscalar;
242             double dscalar;
243             unsigned char uscalar;
244             AV* array;
245             AV* array2;
246             I32 i,j,n,m;
247             SV* work;
248             SV** work2;
249             double nval;
250             int isref;
251             STRLEN len;
252              
253 10 50         if (is_scalar_ref(arg)) /* Scalar ref */
254 0 0         return (void*) SvPV(SvRV(arg), len);
255              
256 10 50         if (packtype!='f' && packtype!='i' && packtype!='d' && packtype!='s'
    50          
    50          
    0          
257 0 0         && packtype!='u')
258 0           croak("Programming error: invalid type conversion specified to pack2D");
259              
260             /* Is arg a scalar? Return pointer to char part */
261              
262 10 50         if (!SvROK(arg) && SvTYPE(arg)!=SVt_PVGV) { return (void *) SvPV(arg, PL_na); }
    0          
    0          
263              
264             /*
265             Create a work char variable - be cunning and make it a mortal *SV
266             which will go away automagically when we leave the current
267             context, i.e. no need to malloc and worry about freeing - thus
268             we can use pack2D in a typemap!
269             */
270              
271 10           work = sv_2mortal(newSVpv("", 0));
272              
273             /* Is it a glob or reference to an array? */
274              
275 10 50         if (SvTYPE(arg)==SVt_PVGV || (SvROK(arg) && SvTYPE(SvRV(arg))==SVt_PVAV)) {
    50          
    50          
276              
277 10 50         if (SvTYPE(arg)==SVt_PVGV) {
278 0 0         array = GvAVn((GV*) arg); /* glob */
279             }else{
280 10           array = (AV *) SvRV(arg); /* reference */
281             }
282              
283 10           n = av_len(array);
284              
285             /* Pack array into string */
286              
287 250 100         for(i=0; i<=n; i++) { /* Loop over 1st dimension */
288              
289 240           work2 = av_fetch( array, i, 0 ); /* Fetch */
290              
291 240 50         isref = work2!=NULL && SvROK(*work2); /* Is is a reference */
    50          
292              
293 240 50         if (isref) {
294 240           array2 = (AV *) SvRV(*work2); /* array of 2nd dimension */
295 240           m = av_len(array2); /* Length */
296             }else{
297 0           m=0; /* 1D array */
298 0 0         nval = SvNV(*work2);
299             }
300              
301             /* Pregrow storage for efficiency on first row - note assumes
302             array is rectangular but better than nothing */
303              
304 240 100         if (i==0) {
305 10 50         if (packtype=='f')
306 0 0         SvGROW( work, sizeof(float)*(n+1)*(m+1) );
    0          
307 10 50         if (packtype=='i')
308 0 0         SvGROW( work, sizeof(int)*(n+1)*(m+1) );
    0          
309 10 50         if (packtype=='s')
310 0 0         SvGROW( work, sizeof(short)*(n+1)*(m+1) );
    0          
311 10 50         if (packtype=='u')
312 0 0         SvGROW( work, sizeof(char)*(n+1)*(m+1) );
    0          
313 10 50         if (packtype=='d')
314 10 50         SvGROW( work, sizeof(double)*(n+1)*(m+1) );
    50          
315             }
316              
317 7920 100         for(j=0; j<=m; j++) { /* Loop over 2nd dimension */
318              
319 7680 50         if (isref) {
320 7680           work2 = av_fetch( array2, j, 0 ); /* Fetch element */
321 7680 50         if (work2==NULL)
322 0           nval = 0.0; /* Undefined */
323             else {
324 7680 50         if (SvROK(*work2))
325 0           goto errexit; /* Croak if reference [i.e. not 1D] */
326 7680 100         nval = SvNV(*work2);
327             }
328             }
329              
330 7680 50         if (packtype=='d') {
331 7680           dscalar = (double) nval;
332 7680           sv_catpvn( work, (char *) &dscalar, sizeof(double));
333             }
334 7680 50         if (packtype=='f') {
335 0           scalar = (float) nval;
336 0           sv_catpvn( work, (char *) &scalar, sizeof(float));
337             }
338 7680 50         if (packtype=='i') {
339 0           iscalar = (int) nval;
340 0           sv_catpvn( work, (char *) &iscalar, sizeof(int));
341             }
342 7680 50         if (packtype=='s') {
343 0           sscalar = (short) nval;
344 0           sv_catpvn( work, (char *) &sscalar, sizeof(short));
345             }
346 7680 50         if (packtype=='u') {
347 0           uscalar = (unsigned char) nval;
348 0           sv_catpvn( work, (char *) &uscalar, sizeof(char));
349             }
350             }
351             }
352              
353             /* Return a pointer to the byte array */
354              
355 10 50         return (void *) SvPV(work, PL_na);
356              
357             }
358              
359             errexit:
360              
361 10           croak("Routine can only handle scalar packed char values or refs to 1D or 2D arrays");
362              
363             }
364              
365             /* ###################################################################################
366              
367             packND - argument is perl scalar variable and one char pack type.
368             arg is treated as a reference to an array of arbitrary dimensions.
369             Pointer to packed data is returned.
370              
371             It is packed recursively, i.e. if an element is a scalar it is
372             packed on the end of the string, if it is a reference the array it
373             points to is packed on the end with further recursive traversal. For
374             a 2D input will produce the same result as pack2D though without,
375             obviously, dimensional checking. Since we don't know in advance how
376             big it is we can't preallocate the storage so this may be inefficient.
377             Note, as in other pack routines globs are handled as the equivalent
378             1D array.
379              
380             e.g. [1,[2,2,[-4,-4]]],-1,0,1, 2,3,4] is packed as 1,2,2,-4,-4,-1,0,1,2,3,4
381              
382             If arg is a reference to a scalar then assume scalar is prepacked binary data.
383              
384             Can be used in a typemap file (uses mortal scratch space).
385              
386             */
387              
388 0           void* packND ( SV* arg, char packtype ) {
389              
390             SV* work;
391             STRLEN len;
392             void pack_element(SV* work, SV** arg, char packtype); /* Called by packND */
393              
394 0 0         if (is_scalar_ref(arg)) /* Scalar ref */
395 0 0         return (void*) SvPV(SvRV(arg), len);
396              
397 0 0         if (packtype!='f' && packtype!='i' && packtype!='d'
    0          
    0          
398 0 0         && packtype!='s' && packtype!='u')
    0          
399 0           croak("Programming error: invalid type conversion specified to packND");
400              
401             /*
402             Create a work char variable - be cunning and make it a mortal *SV
403             which will go away automagically when we leave the current
404             context, i.e. no need to malloc and worry about freeing - thus
405             we can use packND in a typemap!
406             */
407              
408 0           work = sv_2mortal(newSVpv("", 0));
409              
410 0           pack_element(work, &arg, packtype);
411              
412 0 0         return (void *) SvPV(work, PL_na);
413              
414             }
415              
416             /* Internal function of packND - pack an element recursively */
417              
418 0           void pack_element(SV* work, SV** arg, char packtype) {
419              
420             I32 i,n;
421             AV* array;
422             int iscalar;
423             float scalar;
424             short sscalar;
425             unsigned char uscalar;
426             double nval;
427              
428             /* Pack element arg onto work recursively */
429              
430             /* Is arg a scalar? Pack and return */
431              
432 0 0         if (arg==NULL || (!SvROK(*arg) && SvTYPE(*arg)!=SVt_PVGV)) {
    0          
    0          
433              
434 0 0         if (arg==NULL)
435 0           nval = 0.0;
436             else
437 0 0         nval = SvNV(*arg);
438              
439 0 0         if (packtype=='f') {
440 0           scalar = (float) nval; /* Get the scalar value */
441 0           sv_catpvn(work, (char *) &scalar, sizeof(float)); /* Pack it in */
442             }
443 0 0         if (packtype=='i') {
444 0           iscalar = (int) nval; /* Get the scalar value */
445 0           sv_catpvn(work, (char *) &iscalar, sizeof(int)); /* Pack it in */
446             }
447 0 0         if (packtype=='d') {
448 0           sv_catpvn(work, (char *) &nval, sizeof(double)); /* Pack it in */
449             }
450 0 0         if (packtype=='s') {
451 0           sscalar = (short) nval; /* Get the scalar value */
452 0           sv_catpvn(work, (char *) &sscalar, sizeof(short)); /* Pack it in */
453             }
454 0 0         if (packtype=='u') {
455 0           uscalar = (unsigned char) nval;
456 0           sv_catpvn(work, (char *) &uscalar, sizeof(char)); /* Pack it in */
457             }
458              
459 0           return;
460             }
461              
462             /* Is it a glob or reference to an array? */
463              
464 0 0         if (SvTYPE(*arg)==SVt_PVGV || (SvROK(*arg) && SvTYPE(SvRV(*arg))==SVt_PVAV)) {
    0          
    0          
465              
466             /* Dereference */
467              
468 0 0         if (SvTYPE(*arg)==SVt_PVGV) {
469 0 0         array = GvAVn((GV*)*arg); /* glob */
470             }else{
471 0           array = (AV *) SvRV(*arg); /* reference */
472             }
473              
474             /* Pack each array element */
475              
476 0           n = av_len(array);
477              
478 0 0         for (i=0; i<=n; i++) {
479              
480             /* To curse is human, to recurse divine */
481              
482 0           pack_element(work, av_fetch(array, i, 0), packtype );
483             }
484 0           return;
485             }
486              
487             errexit:
488              
489 0           croak("Routine can only handle scalars or refs to N-D arrays of scalars");
490              
491             }
492              
493              
494             /* ##################################################################################
495              
496             unpack1D - take packed string (C array) and write back into perl 1D array.
497             If 1st argument is a reference, unpack into this array.
498             If 1st argument is a glob, unpack into the 1D array of the same name.
499              
500             Can only be used in a typemap if the size of the array is known
501             in advance or is the size of a preexisting perl array (n=0). If it
502             is determined by another variable you may have to put in in some
503             direct CODE: lines in the XSUB file.
504              
505             */
506              
507 55           void unpack1D ( SV* arg, void * var, char packtype, int n ) {
508              
509             /* n is the size of array var[] (n=1 for 1 element, etc.) If n=0 take
510             var[] as having the same dimension as array referenced by arg */
511              
512             int* ivar;
513             float* fvar;
514             double* dvar;
515             short* svar;
516             unsigned char* uvar;
517             /* SV* work; */
518             AV* array;
519             I32 i,m;
520              
521             /* Note in ref to scalar case data is already changed */
522              
523 55 50         if (is_scalar_ref(arg)) /* Do nothing */
524 0           return;
525              
526 55 50         if (packtype!='f' && packtype!='i' && packtype!= 'd' &&
    50          
    50          
    0          
527 0 0         packtype!='u' && packtype!='s')
528 0           croak("Programming error: invalid type conversion specified to unpack1D");
529              
530 55           m=n; array = coerce1D( arg, m ); /* Get array ref and coerce */
531              
532 55 50         if (m==0)
533 55           m = av_len( array )+1;
534              
535 55 50         if (packtype=='i') /* Cast void array var[] to appropriate type */
536 0           ivar = (int *) var;
537 55 50         if (packtype=='f')
538 0           fvar = (float *) var;
539 55 50         if (packtype=='d')
540 55           dvar = (double *) var;
541 55 50         if (packtype=='u')
542 0           uvar = (unsigned char *) var;
543 55 50         if (packtype=='s')
544 0           svar = (short *) var;
545              
546             /* Unpack into the array */
547              
548 524967 100         for(i=0; i
549 524912 50         if (packtype=='i')
550 0           av_store( array, i, newSViv( (IV)ivar[i] ) );
551 524912 50         if (packtype=='f')
552 0           av_store( array, i, newSVnv( (double)fvar[i] ) );
553 524912 50         if (packtype=='d')
554 524912           av_store( array, i, newSVnv( (double)dvar[i] ) );
555 524912 50         if (packtype=='u')
556 0           av_store( array, i, newSViv( (IV)uvar[i] ) );
557 524912 50         if (packtype=='s')
558 0           av_store( array, i, newSViv( (IV)svar[i] ) );
559             }
560              
561 55           return;
562             }
563              
564              
565             /* #################################################################################
566              
567             coerce1D - utility function. Make sure arg is a reference to a 1D array
568             of size at least n, creating/extending as necessary. Fill with zeroes.
569             Return reference to array. If n=0 just returns reference to array,
570             creating as necessary.
571             */
572              
573 92           AV* coerce1D ( SV* arg, int n ) {
574              
575             /* n is the size of array var[] (n=1 for 1 element, etc.) */
576              
577             AV* array;
578             I32 i,m;
579              
580             /* In ref to scalar case we can do nothing - we can only hope the
581             caller made the scalar the right size in the first place */
582              
583 92 50         if (is_scalar_ref(arg)) /* Do nothing */
584 0           return (AV*)NULL;
585              
586             /* Check what has been passed and create array reference whether it
587             exists or not */
588              
589 92 50         if (SvTYPE(arg)==SVt_PVGV) {
590 0 0         array = GvAVn((GV*)arg); /* glob */
591 92 50         }else if (SvROK(arg) && SvTYPE(SvRV(arg))==SVt_PVAV) {
    50          
592 92           array = (AV *) SvRV(arg); /* reference */
593             }else{
594 0           array = newAV(); /* Create */
595 0           sv_setsv(arg, newRV((SV*) array));
596             }
597              
598 92           m = av_len(array);
599              
600 66296 100         for (i=m+1; i
601 66204           av_store( array, i, newSViv( (IV) 0 ) );
602             }
603              
604 92           return array;
605             }
606              
607              
608             /* ################################################################################
609              
610             get_mortalspace - utility to get temporary memory space. Uses
611             a mortal *SV for this so it is automatically freed when the current
612             context is terminated. Useful in typemap's for OUTPUT only arrays.
613              
614             */
615              
616              
617 0           void* get_mortalspace( int n, char packtype ) {
618              
619             /* n is the number of elements of space required, packtype is 'f' or 'i' */
620              
621             SV* work;
622              
623 0 0         if (packtype!='f' && packtype!='i' && packtype!='d'
    0          
    0          
624 0 0         && packtype!='u' && packtype!='s')
    0          
625 0           croak("Programming error: invalid type conversion specified to get_mortalspace");
626              
627 0           work = sv_2mortal(newSVpv("", 0));
628              
629 0 0         if (packtype=='f')
630 0 0         SvGROW( work, sizeof(float)*n ); /* Pregrow for efficiency */
    0          
631 0 0         if (packtype=='i')
632 0 0         SvGROW( work, sizeof(int)*n );
    0          
633 0 0         if (packtype=='d')
634 0 0         SvGROW( work, sizeof(double)*n);
    0          
635 0 0         if (packtype=='u')
636 0 0         SvGROW( work, sizeof(char)*n);
    0          
637 0 0         if (packtype=='s')
638 0 0         SvGROW( work, sizeof(short)*n);
    0          
639              
640 0 0         return (void *) SvPV(work, PL_na);
641             }