File Coverage

blib/lib/PDL/IO/XLSX.pm
Criterion Covered Total %
statement 21 23 91.3
branch 1 2 50.0
condition n/a
subroutine 8 8 100.0
pod n/a
total 30 33 90.9


line stmt bran cond sub pod time code
1             package PDL::IO::XLSX;
2 3     3   519036 use 5.010;
  3         14  
3 3     3   15 use strict;
  3         5  
  3         79  
4 3     3   14 use warnings;
  3         9  
  3         421  
5              
6             require Exporter;
7             our @ISA = qw(Exporter);
8             our @EXPORT_OK = qw(rxlsx1D rxlsx2D wxlsx1D wxlsx2D);
9             our %EXPORT_TAGS = (all => \@EXPORT_OK);
10              
11             our $VERSION = '0.003';
12              
13 3     3   22 use Config;
  3         5  
  3         260  
14 3 50   3   19 use constant DEBUG => $ENV{PDL_IO_XLSX_DEBUG} ? 1 : 0;
  3         5  
  3         321  
15              
16 3     3   1916 use PDL;
  3         15  
  3         16  
17 3     3   343139 use PDL::IO::XLSX::Writer;
  3         19  
  3         1705  
18 3     3   3275 use PDL::IO::XLSX::Reader;
  0            
  0            
19             use Scalar::Util qw(looks_like_number blessed);
20              
21             use Carp;
22             $Carp::Internal{ (__PACKAGE__) }++;
23              
24             sub import {
25             my $package = shift;
26             {
27             no strict 'refs';
28             *{'PDL::wxlsx2D'} = \&wxlsx2D if grep { /^(:all|wxlsx2D)$/ } @_;
29             *{'PDL::wxlsx1D'} = \&wxlsx1D if grep { /^(:all|wxlsx1D)$/ } @_;
30             }
31             __PACKAGE__->export_to_level(1, $package, @_) if @_;
32             }
33              
34             my %pck = (
35             byte => "C",
36             short => "s",
37             ushort => "S",
38             long => "l",
39             longlong => "q",
40             float => "f",
41             double => "d",
42             );
43              
44             sub _serialdate2longlong {
45             my $v = shift; # MS Excel serial datetime
46             my $epoch_miliseconds_float = ($v - 25569) * 24 * 60 * 60 * 1000;
47             return 1000 * int POSIX::floor($epoch_miliseconds_float + 0.5); # longlong epoch microseconds
48             }
49              
50             sub _longlong2serialdate {
51             my $v = shift;
52             # return MS Excel serial datetime (with microsecond-only precision)
53             return 25569 + ( int($v / 1000) / (24 * 60 * 60 * 1000) );
54             }
55              
56             sub wxlsx1D {
57             my ($fh, $O, $C) = _proc_wargs('1D', @_);
58              
59             my $cols = 0;
60             my $rows = 0;
61             my @c_pdl;
62             my @c_rows;
63             my @c_type;
64             my @c_size;
65             my @c_pack;
66             my @c_dataref;
67             my @c_offset;
68             my @c_max_offset;
69             my @c_bad;
70              
71             my %alias = (
72             '%Y-%m-%d' => [ 10, 'yyyy\-mm\-dd' ],
73             '%Y-%m-%dT%H:%M' => [ 16, 'yyyy\-mm\-dd\ hh:mm' ],
74             '%Y-%m-%dT%H:%M:%S' => [ 19, 'yyyy\-mm\-dd\ hh:mm:ss' ],
75             '%Y-%m-%dT%H:%M:%S.%3N' => [ 23, 'yyyy\-mm\-dd\ hh:mm:ss.000' ],
76             '%Y-%m-%dT%H:%M:%S.%6N' => [ 23, 'yyyy\-mm\-dd\ hh:mm:ss.000' ],
77             );
78              
79             my $bad2empty = $O->{bad2empty};
80              
81             my @xlsx_format_array;
82             my %xlsx_width_hash;
83              
84             while (blessed $_[0] && $_[0]->isa('PDL')) {
85             $c_pdl[$cols] = shift;
86             croak "FATAL: wxlsx1D() expects 1D piddles" unless $c_pdl[$cols]->ndims == 1;
87             $c_size[$cols] = PDL::Core::howbig($c_pdl[$cols]->get_datatype);
88             $c_dataref[$cols] = $c_pdl[$cols]->get_dataref;
89             $c_offset[$cols] = 0;
90             my $type = $c_pdl[$cols]->type;
91             my $dim = $c_pdl[$cols]->dim(0);
92             $c_pack[$cols] = $pck{$type};
93             $c_max_offset[$cols] = $c_size[$cols] * $dim;
94             $rows = $dim if $rows < $dim;
95             if ($bad2empty && $c_pdl[$cols]->check_badflag) {
96             my $b = pdl($type, 1)->setbadif(1);
97             my $d = $b->get_dataref;
98             $c_bad[$cols] = substr($$d, 0, $c_size[$cols]); # raw bytes representind BAD value
99             }
100             if (ref $c_pdl[$cols] eq 'PDL::DateTime') {
101             my $strf = $c_pdl[$cols]->_autodetect_strftime_format; #XXX-TODO _autodetect_strftime_format is a hack!!
102             my ($len, $fmt) = @{$alias{$strf}};
103             if (defined $len && defined $fmt) {
104             $xlsx_format_array[$cols] = $fmt; # 0-based index
105             $xlsx_width_hash{$cols + 1} = $len ; # 1-based index
106             }
107             }
108             $cols++;
109             }
110              
111             my $xlsx = PDL::IO::XLSX::Writer->new(%$C);
112             $xlsx->sheets->start($O->{sheet_name} // "Sheet1", \%xlsx_width_hash, \@xlsx_format_array);
113              
114             if (ref $O->{header} eq 'ARRAY') {
115             croak "FATAL: wrong header (expected $cols items)" if $cols != scalar @{$O->{header}};
116             $xlsx->sheets->add_row($O->{header}); #XXX-TODO apply a special style for header cells (gray background)
117             }
118              
119             for my $r (0..$rows-1) {
120             my @v = ('') x $cols;
121             for my $c (0..$cols-1) {
122             if ($c_max_offset[$c] >= $c_offset[$c]) {
123             if ($bad2empty && $c_bad[$c]) {
124             my $v = substr(${$c_dataref[$c]}, $c_offset[$c], $c_size[$c]);
125             if ($v ne $c_bad[$c]) {
126             $v[$c] = unpack($c_pack[$c], $v);
127             $v[$c] = _longlong2serialdate($v[$c]) if ref $c_pdl[$c] eq 'PDL::DateTime';
128             }
129             }
130             else {
131             my $v = substr(${$c_dataref[$c]}, $c_offset[$c], $c_size[$c]);
132             $v[$c] = unpack($c_pack[$c], $v);
133             $v[$c] = _longlong2serialdate($v[$c]) if ref $c_pdl[$c] eq 'PDL::DateTime';
134             }
135             }
136             $c_offset[$c] += $c_size[$c];
137             }
138             $xlsx->sheets->add_row(\@v);
139             }
140             $xlsx->sheets->save;
141             $xlsx->xlsx_save($fh, $C->{overwrite});
142             }
143              
144             sub wxlsx2D {
145             my $pdl = shift;
146             my ($fh, $O, $C) = _proc_wargs('2D', @_);
147              
148             croak "FATAL: wxlsx2D() expects 2D piddle" unless $pdl->ndims == 2;
149             my $p = $pdl->transpose;
150              
151             my ($cols, $rows) = $p->dims;
152             my $type = $p->type;
153             my $size = PDL::Core::howbig($p->get_datatype);
154             my $packC = $pck{$type} . "[$cols]";
155             my $pack1 = $pck{$type};
156             my $dataref = $p->get_dataref;
157             my $offset = 0;
158             my $colsize = $size * $cols;
159             my $max_offset = $colsize * ($rows - 1);
160             my $bad;
161             if ($O->{bad2empty} && $p->check_badflag) {
162             my $b = pdl($type, 1)->setbadif(1);
163             my $d = $b->get_dataref;
164             $bad = substr($$d, 0, $size); # raw bytes representing BAD value
165             }
166              
167             my $xlsx = PDL::IO::XLSX::Writer->new(%$C);
168             $xlsx->sheets->start($O->{sheet_name} // "Sheet1");
169              
170             if ($O->{header}) {
171             my $n = scalar @{$O->{header}};
172             croak "FATAL: wrong header (expected $cols items, got $n)" if $cols != $n;
173             $xlsx->sheets->add_row($O->{header});
174             }
175             while ($offset <= $max_offset) {
176             if (defined $bad) {
177             my @v = map { my $v = substr($$dataref, $offset + $_*$size, $size); $v eq $bad ? '' : unpack($pack1, $v) } (0..$cols-1);
178             $xlsx->sheets->add_row(\@v);
179             }
180             else {
181             my @v = unpack($packC, substr($$dataref, $offset, $colsize));
182             $xlsx->sheets->add_row(\@v);
183             }
184             $offset += $colsize;
185             }
186             $xlsx->sheets->save;
187              
188             $xlsx->xlsx_save($fh, $C->{overwrite});
189             }
190              
191             sub rxlsx1D {
192             my ($fh, $coli, $O, $C) = _proc_rargs('1D', @_);
193              
194             my ($c_type, $c_pack, $c_sizeof, $c_pdl, $c_bad, $c_dataref, $c_idx, $c_dt, $allocated, $cols); # initialize after we get 1st line
195              
196             my $xlsx = PDL::IO::XLSX::Reader->new($fh, %$C);
197             my $processed = 0;
198             my $finished = 0;
199             my $reshape_inc = $O->{reshape_inc};
200             my $empty2bad = $O->{empty2bad};
201             my $text2bad = $O->{text2bad};
202              
203             my $headerline;
204             my $auto_detect_headerline;
205             my $skip_before_headerline;
206             my $rows = 0;
207             my @bytes;
208              
209             if (looks_like_number($O->{header}) && $O->{header} >= 1) {
210             $skip_before_headerline = $O->{header} - 1;
211             }
212             elsif (($O->{header}//'') eq 'auto') {
213             $auto_detect_headerline = 1;
214             }
215              
216             my $proc_line = sub {
217             my $r = shift;
218             my $f = shift;
219             if (defined $r) {
220             if (defined $skip_before_headerline) {
221             if ($skip_before_headerline == 0) {
222             $headerline = $r;
223             $skip_before_headerline = undef;
224             }
225             else {
226             $skip_before_headerline--;
227             }
228             return; # go to the next line
229             }
230             elsif (defined $auto_detect_headerline) {
231             my $numeric = 0;
232             for (@$r) { $numeric++ if looks_like_number($_) || defined PDL::DateTime::dt2ll($_) }
233             if ($numeric == 0) {
234             # no numeric values found => skip this line but keep it as a potential header
235             $headerline = $r;
236             return; # go to the next line
237             }
238             $auto_detect_headerline = undef;
239             }
240             unless (defined $c_type) {
241             ($c_type, $c_pack, $c_sizeof, $c_pdl, $c_bad, $c_dataref, $c_idx, $c_dt, $allocated, $cols) = _init_1D($coli, $r, $f, $O);
242             warn "Initialized size=$allocated, cols=$cols, type=".join(",",@$c_type)."\n" if $O->{debug};
243             }
244             if ($empty2bad) {
245             if (defined $coli) {
246             for (0..$cols-1) {
247             my $i = $coli->[$_];
248             unless (defined $r->[$i]) { $r->[$i] = $c_bad->[$_]; $c_pdl->[$_]->badflag(1) }
249             }
250             }
251             else {
252             for (0..$cols-1) {
253             unless (defined $r->[$_]) { $r->[$_] = $c_bad->[$_]; $c_pdl->[$_]->badflag(1) }
254             }
255             }
256             }
257             if (defined $c_dt) {
258             for (0..$cols-1) {
259             next unless defined $c_dt->[$_];
260             my $v = _serialdate2longlong($r->[$_]);
261             if (defined $v) {
262             $r->[$_] = $v;
263             }
264             else {
265             $r->[$_] = $c_bad->[$_];
266             $c_pdl->[$_]->badflag(1);
267             }
268             }
269             }
270             if ($text2bad) {
271             if (defined $coli) {
272             for (0..$cols-1) {
273             my $i = $coli->[$_];
274             unless (looks_like_number($r->[$i])) { $r->[$i] = $c_bad->[$_]; $c_pdl->[$_]->badflag(1) }
275             }
276             }
277             else {
278             for (0..$cols-1) {
279             unless (looks_like_number($r->[$_])) { $r->[$_] = $c_bad->[$_]; $c_pdl->[$_]->badflag(1) }
280             }
281             }
282             }
283             if (defined $coli) { # only selected columns
284             no warnings 'pack'; # intentionally disable all pack related warnings
285             no warnings 'numeric'; # disable: Argument ??? isn't numeric in pack
286             no warnings 'uninitialized'; # disable: Use of uninitialized value in pack
287             $bytes[$_] .= pack($c_pack->[$_], $r->[$coli->[$_]]) for (0..$cols-1);
288             }
289             else { # all columns
290             no warnings 'pack'; # intentionally disable all pack related warnings
291             no warnings 'numeric'; # disable: Argument ??? isn't numeric in pack
292             no warnings 'uninitialized'; # disable: Use of uninitialized value in pack
293             $bytes[$_] .= pack($c_pack->[$_], $r->[$_]) for (0..$cols-1);
294             }
295             $rows++;
296             }
297             if ($rows >= $reshape_inc || !defined $r) {
298             $processed += $rows;
299             if (!defined $r) {
300             # flush/finalize
301             $allocated = $processed;
302             warn "Reshape to: '$allocated'\n" if $O->{debug};
303             for (0..$cols-1) {
304             $c_pdl->[$_]->reshape($allocated);
305             $c_dataref->[$_] = $c_pdl->[$_]->get_dataref;
306             }
307             }
308             elsif ($allocated < $processed) {
309             $allocated += $reshape_inc;
310             warn "Reshape to: '$allocated'\n" if $O->{debug};
311             for (0..$cols-1) {
312             $c_pdl->[$_]->reshape($allocated);
313             $c_dataref->[$_] = $c_pdl->[$_]->get_dataref;
314             }
315             }
316             for my $ci (0..$cols-1) {
317             my $len = length $bytes[$ci];
318             my $expected_len = $c_sizeof->[$ci] * $rows;
319             croak "FATAL: len mismatch $len != $expected_len" if $len != $expected_len;
320             substr(${$c_dataref->[$ci]}, $c_idx->[$ci], $len) = $bytes[$ci];
321             $c_idx->[$ci] += $expected_len;
322             }
323             @bytes = ();
324             $rows = 0;
325             if (!defined $r) {
326             # flush/finalize
327             $c_pdl->[$_]->upd_data for (0..$cols-1);
328             }
329             }
330             };
331              
332             warn "Fetching 1D " . _dbg_msg($O, $C) . "\n" if $O->{debug};
333             if ($O->{sheet_name}) {
334             $xlsx->parse_sheet_by_name($O->{sheet_name}, sub {
335             my $r = [ map { $_->{v} } @{$_[0]} ]; #values
336             my $f = [ map { $_->{f} } @{$_[0]} ]; #formats
337             $proc_line->($r, $f);
338             });
339             }
340             else {
341             $xlsx->parse_sheet_by_id(1, sub {
342             my $r = [ map { $_->{v} } @{$_[0]} ]; #values
343             my $f = [ map { $_->{f} } @{$_[0]} ]; #formats
344             $proc_line->($r, $f);
345             });
346             }
347              
348             $proc_line->(undef); # flush/finalize
349             if (ref $headerline eq 'ARRAY') {
350             for (0..$cols-1) {
351             $c_pdl->[$_]->hdr->{col_name} = $headerline->[$_] if $headerline->[$_] && $headerline->[$_] ne '';
352             };
353             }
354              
355             return @$c_pdl if ref $c_pdl eq 'ARRAY';
356             warn "rxlsx1D: no data\n";
357             return undef;
358             }
359              
360             sub rxlsx2D {
361             my ($fh, $coli, $O, $C) = _proc_rargs('2D', @_);
362              
363             my ($c_type, $c_pack, $c_sizeof, $c_pdl, $c_bad, $c_dataref, $allocated, $cols);
364             my $xlsx = PDL::IO::XLSX::Reader->new($fh, %$C);
365             my $processed = 0;
366             my $c_idx = 0;
367             my $pck;
368             my $reshape_inc = $O->{reshape_inc};
369             my $empty2bad = $O->{empty2bad};
370             my $text2bad = $O->{text2bad};
371             my $bcount = 0;
372             my $bytes = '';
373             my $rows = 0;
374             my $headers_to_skip = looks_like_number($O->{header}) ? $O->{header} : 0;
375             my $formats;
376              
377             my $proc_line = sub {
378             my $r = shift;
379             if (defined $r) {
380             if ($headers_to_skip > 0) {
381             $headers_to_skip--;
382             return;
383             }
384             unless (defined $pck) {
385             ($c_type, $c_pack, $c_sizeof, $c_pdl, $c_bad, $c_dataref, $allocated, $cols) = _init_2D($coli, scalar @$r, $O);
386             warn "Initialized size=$allocated, cols=$cols, type=$c_type\n" if $O->{debug};
387             $pck = "$c_pack\[$cols\]";
388             }
389             if ($empty2bad) {
390             if (defined $coli) {
391             for (0..$cols-1) {
392             my $i = $coli->[$_];
393             if (($r->[$i]//'') eq '') {
394             $r->[$i] = $c_bad;
395             $c_pdl->badflag(1);
396             }
397             }
398             }
399             else {
400             for (0..$cols-1) {
401             if (($r->[$_]//'') eq '') {
402             $r->[$_] = $c_bad;
403             $c_pdl->badflag(1);
404             }
405             }
406             }
407             }
408             if ($text2bad) {
409             if (defined $coli) {
410             for (0..$cols-1) {
411             my $i = $coli->[$_];
412             unless (looks_like_number($r->[$i])) { $r->[$i] = $c_bad; $c_pdl->badflag(1) }
413             }
414             }
415             else {
416             for (0..$cols-1) {
417             unless (looks_like_number($r->[$_])) { $r->[$_] = $c_bad; $c_pdl->badflag(1) }
418             }
419             }
420             }
421             if (defined $coli) { # only selected columns
422             no warnings 'pack'; # intentionally disable all pack related warnings
423             no warnings 'numeric'; # disable: Argument ??? isn't numeric in pack
424             no warnings 'uninitialized'; # disable: Use of uninitialized value in pack
425             $bytes .= pack($pck, map { $r->[$_] } @$coli);
426             }
427             else { # all columns
428             no warnings 'pack'; # intentionally disable all pack related warnings
429             no warnings 'numeric'; # disable: Argument ??? isn't numeric in pack
430             no warnings 'uninitialized'; # disable: Use of uninitialized value in pack
431             $bytes .= pack($pck, @$r);
432             }
433             $rows++;
434             }
435             if ($rows >= $reshape_inc || !defined $r) {
436             $processed += $rows;
437             if (!defined $r) {
438             # flush/finalize
439             $allocated = $processed;
440             warn "Reshaping to $allocated\n" if $O->{debug};
441             $c_pdl->reshape($cols, $allocated);
442             $c_dataref = $c_pdl->get_dataref;
443             }
444             elsif ($allocated < $processed) {
445             $allocated += $reshape_inc;
446             warn "Reshaping to $allocated\n" if $O->{debug};
447             $c_pdl->reshape($cols, $allocated);
448             $c_dataref = $c_pdl->get_dataref;
449             }
450             my $len = length $bytes;
451             my $expected_len = $c_sizeof * $cols * $rows;
452             croak "FATAL: len mismatch $len != $expected_len" if $len != $expected_len;
453             substr($$c_dataref, $c_idx, $len) = $bytes;
454             $c_idx += $len;
455             $bytes = '';
456             $rows = 0;
457             if (!defined $r) {
458             # flush/finalize
459             $c_pdl->upd_data;
460             $c_pdl = $c_pdl->transpose;
461             }
462             }
463             };
464              
465             warn "Fetching 2D " . _dbg_msg($O, $C) . "\n" if $O->{debug};
466             if ($O->{sheet_name}) {
467             $xlsx->parse_sheet_by_name($O->{sheet_name}, sub {
468             my $r = [ map { $_->{v} } @{$_[0]} ]; #values
469             my $f = [ map { $_->{f} } @{$_[0]} ]; #formats
470             $proc_line->($r, $f);
471             });
472             }
473             else {
474             $xlsx->parse_sheet_by_id(1, sub {
475             my $r = [ map { $_->{v} } @{$_[0]} ]; #values
476             my $f = [ map { $_->{f} } @{$_[0]} ]; #formats
477             $proc_line->($r, $f);
478             });
479             }
480              
481             $proc_line->(undef); # flush/finalize
482              
483             warn "rxlsx2D: no data\n" unless blessed $c_pdl && $c_pdl->isa('PDL');
484             return $c_pdl;
485             }
486              
487             sub _dbg_msg {
488             my ($O, $C) = @_;
489             sprintf "reshape=%s, bad=%s/%s",
490             $O->{reshape_inc} ||= '?',
491             $O->{empty2bad} ||= '?',
492             $O->{text2bad} ||= '?',
493             }
494              
495             sub _proc_wargs {
496             my $options = ref $_[-1] eq 'HASH' ? pop : {};
497             my $filename_or_fh = !blessed $_[-1] || !$_[-1]->isa('PDL') ? pop : undef;
498             my $fn = shift;
499              
500             my $C = { %$options }; # make a copy
501              
502             my @keys = qw/ debug header bad2empty sheet_name /;
503             my $O = { map { $_ => delete $C->{$_} } @keys };
504             $O->{debug} //= DEBUG;
505             $O->{bad2empty} //= 1;
506             $O->{header} //= ($fn eq '1D' ? 'auto' : undef); #XXX-TODO backport to PDL::IO::CSV
507              
508             if (defined $O->{header}) {
509             croak "FATAL: header should be arrayref" unless ref $O->{header} eq 'ARRAY' || $O->{header} eq 'auto';
510             if ($O->{header} eq 'auto') {
511             my @n;
512             my $count = 0;
513             for (@_) {
514             push @n, my $n = $_->hdr->{col_name};
515             $count++ if defined $n;
516             }
517             $O->{header} = $count > 0 ? \@n : undef;
518             }
519             }
520              
521             return ($filename_or_fh, $O, $C);
522             }
523              
524             sub _proc_rargs {
525             my $options = ref $_[-1] eq 'HASH' ? pop : {};
526             my ($fn, $filename_or_fh, $coli) = @_;
527              
528             croak "FATAL: invalid column ids" if defined $coli && ref $coli ne 'ARRAY';
529             croak "FATAL: invalid filename" unless defined $filename_or_fh;
530             my $C = { %$options }; # make a copy
531              
532             # get options related to this module the rest will be passed to PDL::IO::XLSX::Reader|Writer
533             my @keys = qw/ reshape_inc type debug empty2bad text2bad header sheet_name /;
534             my $O = { map { $_ => delete $C->{$_} } @keys };
535             $O->{reshape_inc} ||= 80_000;
536             $O->{type} ||= ($fn eq '1D' ? 'auto' : double); #XXX-TODO backport to PDL::IO::CSV
537             $O->{header} ||= ($fn eq '1D' ? 'auto' : 0); #XXX-TODO backport to PDL::IO::CSV
538             $O->{debug} = DEBUG unless defined $O->{debug};
539              
540             # empty2bad implies some PDL::IO::XLSX::Reader extra options
541             if ($O->{empty2bad}) {
542             $C->{blank_is_undef} = 1;
543             $C->{empty_is_undef} = 1;
544             }
545              
546             return ($filename_or_fh, $coli, $O, $C);
547             }
548              
549             sub _init_1D {
550             my ($coli, $firstline_v, $firstline_f, $O) = @_;
551             my $colcount = scalar @$firstline_v;
552             my $cols;
553             if (!defined $coli) { # take all columns
554             $cols = $colcount;
555             }
556             else {
557             $cols = scalar @$coli;
558             ($_<0 || $_>$colcount) and croak "FATAL: invalid column '$_' (column count=$colcount)" for (@$coli);
559             }
560             croak "FATAL: invalid column count" unless $cols && $cols > 0 && $cols <= $colcount;
561              
562             my @c_type;
563             my @c_pack;
564             my @c_sizeof;
565             my @c_pdl;
566             my @c_bad;
567             my @c_dataref;
568             my @c_idx;
569              
570             if (ref $O->{type} eq 'ARRAY') {
571             $c_type[$_] = $O->{type}->[$_] for (0..$cols-1);
572             }
573             else {
574             $c_type[$_] = $O->{type} for (0..$cols-1);
575             }
576              
577             for (0..$cols-1) {
578             if (!defined $c_type[$_] || $c_type[$_] eq 'auto') {
579             if ($firstline_f->[$_] =~ /^datetime\.(date|time|datetime)$/) {
580             $c_type[$_] = 'datetime';
581             }
582             elsif ($firstline_f->[$_] eq 'int') {
583             $c_type[$_] = longlong;
584             }
585             else {
586             $c_type[$_] = double;
587             }
588             }
589             }
590              
591             my @c_dt;
592             for (0..$cols-1) {
593             if ($c_type[$_] eq 'datetime') {
594             $c_type[$_] = longlong;
595             $c_dt[$_] = 'datetime';
596             }
597             }
598              
599             my $allocated = $O->{reshape_inc};
600             for (0..$cols-1) {
601             $c_type[$_] = double if !defined $c_type[$_];
602             $c_pack[$_] = $pck{$c_type[$_]};
603             croak "FATAL: invalid type '$c_type[$_]' for column $_" if !$c_pack[$_];
604             $c_sizeof[$_] = length pack($c_pack[$_], 1);
605             $c_pdl[$_] = $c_dt[$_] ? PDL::DateTime->new(zeroes(longlong, $allocated)) : zeroes($c_type[$_], $allocated);
606             $c_dataref[$_] = $c_pdl[$_]->get_dataref;
607             $c_bad[$_] = $c_pdl[$_]->badvalue;
608             $c_idx[$_] = 0;
609             my $big = PDL::Core::howbig($c_pdl[$_]->get_datatype);
610             croak "FATAL: column $_ mismatch (type=$c_type[$_], sizeof=$c_sizeof[$_], big=$big)" if $big != $c_sizeof[$_];
611             }
612              
613             return (\@c_type, \@c_pack, \@c_sizeof, \@c_pdl, \@c_bad, \@c_dataref, \@c_idx, (@c_dt > 0 ? \@c_dt : undef), $allocated, $cols);
614             }
615              
616             sub _init_2D {
617             my ($coli, $colcount, $O) = @_;
618              
619             my $cols;
620             if (!defined $coli) { # take all columns
621             $cols = $colcount;
622             }
623             else {
624             $cols = scalar @$coli;
625             ($_<0 || $_>$colcount) and croak "FATAL: invalid column '$_' (column count=$colcount)" for (@$coli);
626             }
627             croak "FATAL: invalid column count" unless $cols && $cols > 0 && $cols <= $colcount;
628              
629             my $c_type = $O->{type};
630             my $c_pack = $pck{$c_type};
631             croak "FATAL: invalid type '$c_type' for column $_" if !$c_pack;
632              
633             my $allocated = $O->{reshape_inc};
634             my $c_sizeof = length pack($c_pack, 1);
635             my $c_pdl = zeroes($c_type, $cols, $allocated);
636             my $c_dataref = $c_pdl->get_dataref;
637             my $c_bad = $c_pdl->badvalue;
638              
639             my $big = PDL::Core::howbig($c_pdl->get_datatype);
640             croak "FATAL: column $_ size mismatch (type=$c_type, sizeof=$c_sizeof, big=$big)" if $big != $c_sizeof;
641              
642             return ($c_type, $c_pack, $c_sizeof, $c_pdl, $c_bad, $c_dataref, $allocated, $cols);
643             }
644              
645             1;
646              
647             __END__