File Coverage

arrays.c
Criterion Covered Total %
statement 66 228 28.9
branch 62 300 20.6
condition n/a
subroutine n/a
pod n/a
total 128 528 24.2


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 492           int is_scalar_ref (SV* arg) { /* Utility to determine if ref to scalar */
34             SV* foo;
35 492 50         if (!SvROK(arg))
36 0           return 0;
37 492           foo = SvRV(arg);
38 492 50         if (SvPOK(foo))
39 0           return 1;
40             else
41 492           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 164           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 164 50         if (is_scalar_ref(arg)) /* Scalar ref */
89 0           return (void*) SvPV(SvRV(arg), len);
90            
91 164 50         if (packtype!='f' && packtype!='i' && packtype!='d' && packtype!='s'
    100          
    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 164           work = sv_2mortal(newSVpv("", 0));
103            
104             /* Is arg a scalar? Return scalar*/
105            
106 164 50         if (!SvROK(arg) && SvTYPE(arg)!=SVt_PVGV) {
    0          
107            
108 0 0         if (packtype=='f') {
109 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           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           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           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           uscalar = (unsigned char) SvNV(arg); /*Get the scalar value */
126 0           sv_setpvn(work, (char *) &uscalar, sizeof(char)); /* Pack it in */
127             }
128 0           return (void *) SvPV(work, PL_na); /* Return the pointer */
129             }
130            
131             /* Is it a glob or reference to an array? */
132            
133 164 50         if (SvTYPE(arg)==SVt_PVGV || (SvROK(arg) && SvTYPE(SvRV(arg))==SVt_PVAV)) {
    50          
    50          
134            
135 164 50         if (SvTYPE(arg)==SVt_PVGV) {
136 0 0         array = (AV *) GvAVn((GV*) arg); /* glob */
137             }else{
138 164           array = (AV *) SvRV(arg); /* reference */
139             }
140            
141 164           n = av_len(array);
142            
143 164 50         if (packtype=='f')
144 0 0         SvGROW( work, sizeof(float)*(n+1) ); /* Pregrow for efficiency */
    0          
145 164 100         if (packtype=='i')
146 3 50         SvGROW( work, sizeof(int)*(n+1) );
    50          
147 164 100         if (packtype=='d')
148 161 50         SvGROW( work, sizeof(double)*(n+1) );
    100          
149 164 50         if (packtype=='s')
150 0 0         SvGROW( work, sizeof(short)*(n+1) );
    0          
151 164 50         if (packtype=='u')
152 0 0         SvGROW( work, sizeof(char)*(n+1) );
    0          
153            
154              
155             /* Pack array into string */
156            
157 6486 100         for(i=0; i<=n; i++) {
158            
159 6322           work2 = av_fetch( array, i, 0 ); /* Fetch */
160 6322 50         if (work2==NULL)
161 0           nval = 0.0; /* Undefined */
162             else {
163 6322 50         if (SvROK(*work2))
164 0           goto errexit; /* Croak if reference [i.e. not 1D] */
165 6322           nval = SvNV(*work2);
166             }
167            
168 6322 50         if (packtype=='f') {
169 0           scalar = (float) nval;
170 0           sv_catpvn( work, (char *) &scalar, sizeof(float));
171             }
172 6322 100         if (packtype=='i') {
173 9           iscalar = (int) nval;
174 9           sv_catpvn( work, (char *) &iscalar, sizeof(int));
175             }
176 6322 100         if (packtype=='d') {
177 6313           dscalar = (double) nval;
178 6313           sv_catpvn( work, (char *) &dscalar, sizeof(double));
179             }
180 6322 50         if (packtype=='s') {
181 0           sscalar = (short) nval;
182 0           sv_catpvn( work, (char *) &sscalar, sizeof(short));
183             }
184 6322 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 164           return (void *) SvPV(work, PL_na);
193            
194             }
195            
196 0           errexit:
197            
198 0           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 0           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 0 0         if (is_scalar_ref(arg)) /* Scalar ref */
254 0           return (void*) SvPV(SvRV(arg), len);
255              
256 0 0         if (packtype!='f' && packtype!='i' && packtype!='d' && packtype!='s'
    0          
    0          
    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 0 0         if (!SvROK(arg) && SvTYPE(arg)!=SVt_PVGV) { return (void *) SvPV(arg, PL_na); }
    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 0           work = sv_2mortal(newSVpv("", 0));
272            
273             /* Is it a glob or reference to an array? */
274            
275 0 0         if (SvTYPE(arg)==SVt_PVGV || (SvROK(arg) && SvTYPE(SvRV(arg))==SVt_PVAV)) {
    0          
    0          
276            
277 0 0         if (SvTYPE(arg)==SVt_PVGV) {
278 0 0         array = GvAVn((GV*) arg); /* glob */
279             }else{
280 0           array = (AV *) SvRV(arg); /* reference */
281             }
282            
283 0           n = av_len(array);
284            
285             /* Pack array into string */
286            
287 0 0         for(i=0; i<=n; i++) { /* Loop over 1st dimension */
288            
289 0           work2 = av_fetch( array, i, 0 ); /* Fetch */
290            
291 0 0         isref = work2!=NULL && SvROK(*work2); /* Is is a reference */
    0          
292            
293 0 0         if (isref) {
294 0           array2 = (AV *) SvRV(*work2); /* array of 2nd dimension */
295 0           m = av_len(array2); /* Length */
296             }else{
297 0           m=0; /* 1D array */
298 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 0 0         if (i==0) {
305 0 0         if (packtype=='f')
306 0 0         SvGROW( work, sizeof(float)*(n+1)*(m+1) );
    0          
307 0 0         if (packtype=='i')
308 0 0         SvGROW( work, sizeof(int)*(n+1)*(m+1) );
    0          
309 0 0         if (packtype=='s')
310 0 0         SvGROW( work, sizeof(short)*(n+1)*(m+1) );
    0          
311 0 0         if (packtype=='u')
312 0 0         SvGROW( work, sizeof(char)*(n+1)*(m+1) );
    0          
313 0 0         if (packtype=='d')
314 0 0         SvGROW( work, sizeof(double)*(n+1)*(m+1) );
    0          
315             }
316            
317 0 0         for(j=0; j<=m; j++) { /* Loop over 2nd dimension */
318            
319 0 0         if (isref) {
320 0           work2 = av_fetch( array2, j, 0 ); /* Fetch element */
321 0 0         if (work2==NULL)
322 0           nval = 0.0; /* Undefined */
323             else {
324 0 0         if (SvROK(*work2))
325 0           goto errexit; /* Croak if reference [i.e. not 1D] */
326 0           nval = SvNV(*work2);
327             }
328             }
329            
330 0 0         if (packtype=='d') {
331 0           dscalar = (double) nval;
332 0           sv_catpvn( work, (char *) &dscalar, sizeof(double));
333             }
334 0 0         if (packtype=='f') {
335 0           scalar = (float) nval;
336 0           sv_catpvn( work, (char *) &scalar, sizeof(float));
337             }
338 0 0         if (packtype=='i') {
339 0           iscalar = (int) nval;
340 0           sv_catpvn( work, (char *) &iscalar, sizeof(int));
341             }
342 0 0         if (packtype=='s') {
343 0           sscalar = (short) nval;
344 0           sv_catpvn( work, (char *) &sscalar, sizeof(short));
345             }
346 0 0         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 0           return (void *) SvPV(work, PL_na);
356            
357             }
358            
359 0           errexit:
360            
361 0           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           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           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           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 0           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 164           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 164 50         if (is_scalar_ref(arg)) /* Do nothing */
524 0           return;
525              
526 164 50         if (packtype!='f' && packtype!='i' && packtype!= 'd' &&
    100          
    50          
    0          
527 0 0         packtype!='u' && packtype!='s')
528 0           croak("Programming error: invalid type conversion specified to unpack1D");
529            
530 164           m=n; array = coerce1D( arg, m ); /* Get array ref and coerce */
531            
532 164 50         if (m==0)
533 164           m = av_len( array )+1;
534            
535 164 100         if (packtype=='i') /* Cast void array var[] to appropriate type */
536 3           ivar = (int *) var;
537 164 50         if (packtype=='f')
538 0           fvar = (float *) var;
539 164 100         if (packtype=='d')
540 161           dvar = (double *) var;
541 164 50         if (packtype=='u')
542 0           uvar = (unsigned char *) var;
543 164 50         if (packtype=='s')
544 0           svar = (short *) var;
545            
546             /* Unpack into the array */
547            
548 6486 100         for(i=0; i
549 6322 100         if (packtype=='i')
550 9           av_store( array, i, newSViv( (IV)ivar[i] ) );
551 6322 50         if (packtype=='f')
552 0           av_store( array, i, newSVnv( (double)fvar[i] ) );
553 6322 100         if (packtype=='d')
554 6313           av_store( array, i, newSVnv( (double)dvar[i] ) );
555 6322 50         if (packtype=='u')
556 0           av_store( array, i, newSViv( (IV)uvar[i] ) );
557 6322 50         if (packtype=='s')
558 0           av_store( array, i, newSViv( (IV)svar[i] ) );
559             }
560            
561 164           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 164           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 164 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 164 50         if (SvTYPE(arg)==SVt_PVGV) {
590 0 0         array = GvAVn((GV*)arg); /* glob */
591 164 50         }else if (SvROK(arg) && SvTYPE(SvRV(arg))==SVt_PVAV) {
    50          
592 164           array = (AV *) SvRV(arg); /* reference */
593             }else{
594 0           array = newAV(); /* Create */
595 0           sv_setsv(arg, newRV((SV*) array));
596             }
597            
598 164           m = av_len(array);
599            
600 164 50         for (i=m+1; i
601 0           av_store( array, i, newSViv( (IV) 0 ) );
602             }
603            
604 164           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           return (void *) SvPV(work, PL_na);
641             }