File Coverage

blib/lib/PDL/IO/Storable.pm
Criterion Covered Total %
statement 78 88 88.6
branch 21 40 52.5
condition 6 15 40.0
subroutine 11 12 91.6
pod 0 7 0.0
total 116 162 71.6


line stmt bran cond sub pod time code
1              
2             #
3             # GENERATED WITH PDL::PP! Don't modify!
4             #
5             package PDL::IO::Storable;
6              
7             @EXPORT_OK = qw( );
8             %EXPORT_TAGS = (Func=>[@EXPORT_OK]);
9              
10 48     48   1094 use PDL::Core;
  48         101  
  48         359  
11 48     48   378 use PDL::Exporter;
  48         116  
  48         300  
12 48     48   314 use DynaLoader;
  48         121  
  48         4602  
13              
14              
15              
16            
17             @ISA = ( 'PDL::Exporter','DynaLoader' );
18             push @PDL::Core::PP, __PACKAGE__;
19             bootstrap PDL::IO::Storable ;
20              
21              
22              
23              
24              
25              
26              
27              
28              
29             =head1 NAME
30              
31             PDL::IO::Storable - helper functions to make PDL usable with Storable
32              
33             =head1 SYNOPSIS
34              
35             use Storable;
36             use PDL::IO::Storable;
37             $hash = {
38             'foo' => 42,
39             'bar' => zeroes(23,45),
40             };
41             store $hash, 'perlhash.dat';
42              
43             =head1 DESCRIPTION
44              
45             C implements object persistence for Perl data structures that can
46             contain arbitrary Perl objects. This module implements the relevant methods to
47             be able to store and retrieve piddles via Storable.
48              
49             =head1 FUNCTIONS
50              
51             =cut
52              
53              
54              
55              
56 48     48   335 use Carp;
  48         105  
  48         11615  
57              
58             { package PDL;
59             # routines to make PDL work with Storable >= 1.03
60              
61             # pdlpack() serializes a piddle, while pdlunpack() unserializes it. Earlier
62             # versions of PDL didn't control for endianness, type sizes and enumerated type
63             # values; this made stored data unportable across different architectures and
64             # PDL versions. This is no longer the case, but the reading code is still able
65             # to read the old files. The old files have no meta-information in them so it's
66             # impossible to read them correctly with 100% accuracy, but we try to make an
67             # educated guess
68             #
69             # Old data format:
70             #
71             # int type
72             # int ndims
73             # int dims[ndims]
74             # data
75             #
76             # Note that here all the sizes and endiannesses are the native. This is
77             # un-portable. Furthermore, the "type" is an enum, and its values could change
78             # between PDL versions. Here I assume that the old format input data is indeed
79             # native, so the old data files have the same portability issues, but at least
80             # things will remain working and broken in the same way they were before
81             #
82             #
83             # New format:
84             #
85             # uint64 0xFFFF FFFF FFFF FFFF # meant to be different from the old-style data
86             # char type[16] # ' '-padded, left-aligned type string such as 'PDL_LL'
87             # uint32 sizeof(type) # little-endian
88             # uint32 one # native-endian. Used to determine the endianness
89             # uint64 ndims # little-endian
90             # uint64 dims[ndims] # little-endian
91             # data
92             #
93             # The header data is all little-endian here. The data is stored with native
94             # endianness. On load it is checked, and a swap happens, if it is required
95              
96             sub pdlpack {
97 5     5 0 10 my ($pdl) = @_;
98              
99 5         22 my $hdr = pack( 'c8A16VL',
100             (-1) x 8,
101             $pdl->type->symbol,
102             PDL::Core::howbig( $pdl->get_datatype ), 1 );
103              
104             # I'd like this to be simply
105             # my $dimhdr = pack( 'Q<*', $pdl->getndims, $pdl->dims )
106             # but my pack() may not support Q, so I break it up manually
107             #
108             # if sizeof(int) == 4 here, then $_>>32 will not return 0 necessarily (this in
109             # undefined). I thus manually make sure this is the case
110             #
111 5 50       25 my $noMSW = (PDL::Core::howbig($PDL::Types::PDL_IND) < 8) ? 1 : 0;
112             my $dimhdr = pack( 'V*',
113 5 50       23 map( { $_ & 0xFFFFFFFF, $noMSW ? 0 : ($_ >> 32) } ($pdl->getndims, $pdl->dims ) ) );
  11         39  
114              
115 5         15 my $dref = $pdl->get_dataref;
116 5         238 return $hdr . $dimhdr . $$dref;
117             }
118              
119             sub pdlunpack {
120 48     48   380 use Config ();
  48         108  
  48         47432  
121 9     9 0 17 my ($pdl,$pack) = @_;
122              
123 9         13 my ($type, $ndims);
124 9         15 my @dims = ();
125              
126 9         9 my $do_swap = 0;
127              
128             # first I try to infer the type of this storable
129 9         14 my $offset = 0;
130 9         35 my @magicheader = unpack( "ll", substr( $pack, $offset ) );
131 9         16 $offset += 8;
132              
133 9 100 66     39 if( $magicheader[0] != -1 ||
134             $magicheader[1] != -1 )
135             {
136 2 50       5 print "PDL::IO::Storable detected an old-style pdl\n" if $PDL::verbose;
137              
138             # old-style data. I leave the data sizes, endianness native, since I don't
139             # know any better. This at least won't break anything.
140             #
141             # The "type" however needs attention. Most-recent old-format data had these
142             # values for the type:
143             #
144             # enum { byte,
145             # short,
146             # unsigned short,
147             # long,
148             # long long,
149             # float,
150             # double }
151             #
152             # The $type I read from the file is assumed to be in this enum even though
153             # PDL may have added other types in the middle of this enum.
154 2         6 my @reftypes = ($PDL::Types::PDL_B,
155             $PDL::Types::PDL_S,
156             $PDL::Types::PDL_U,
157             $PDL::Types::PDL_L,
158             $PDL::Types::PDL_LL,
159             $PDL::Types::PDL_F,
160             $PDL::Types::PDL_D);
161              
162 2         23 my $stride = $Config::Config{intsize};
163 2         8 ($type,$ndims) = unpack 'i2', $pack;
164 2 50       11 @dims = $ndims > 0 ? unpack 'i*', substr $pack, 2*$stride,
165             $ndims*$stride : ();
166              
167 2         3 $offset = (2+$ndims)*$stride;
168              
169 2 50 33     11 if( $type < 0 || $type >= @reftypes )
170             {
171 0         0 croak "Reading in old-style pdl with unknown type: $type. Giving up.";
172             }
173 2         5 $type = $reftypes[$type];
174             }
175             else
176             {
177 7 50       16 print "PDL::IO::Storable detected a new-style pdl\n" if $PDL::verbose;
178              
179             # new-style data. I KNOW the data sizes, endianness and the type enum
180 7         24 my ($typestring) = unpack( 'A16', substr( $pack, $offset ) );
181 7         10 $offset += 16;
182              
183 7         361 $type = eval( '$PDL::Types::' . $typestring );
184 7 50       25 if( $@ )
185             {
186 0         0 croak "PDL::IO::Storable couldn't parse type string '$typestring'. Giving up";
187             }
188              
189 7         24 my ($sizeof) = unpack( 'V', substr( $pack, $offset ) );
190 7         14 $offset += 4;
191 7 50       22 if( $sizeof != PDL::Core::howbig( $type ) )
192             {
193 0         0 croak
194             "PDL::IO::Storable sees mismatched data type sizes when reading data of type '$typestring'\n" .
195             "Stored data has sizeof = $sizeof, while here it is " . PDL::Core::howbig( $type ) . ".\n" .
196             "Giving up";
197             }
198              
199             # check the endianness, if the "1" I read is interpreted as "1" on my
200             # machine then the endiannesses match, and I can just read the data
201 7         17 my ($one) = unpack( 'L', substr( $pack, $offset ) );
202 7         12 $offset += 4;
203              
204 7 50       16 if( $one == 1 )
205             {
206 7 50       15 print "PDL::IO::Storable detected matching endianness\n" if $PDL::verbose;
207             }
208             else
209             {
210 0 0       0 print "PDL::IO::Storable detected non-matching endianness. Correcting data on load\n" if $PDL::verbose;
211              
212             # mismatched endianness. Let's make sure it's a big/little issue, not
213             # something weird. If mismatched, the '00000001' should be seen as
214             # '01000000'
215 0 0       0 if( $one != 0x01000000 )
216             {
217 0         0 croak
218             "PDL::IO::Storable sees confused endianness. A '1' was read as '$one'.\n" .
219             "This is neither matching nor swapped endianness. I don't know what's going on,\n" .
220             "so I'm giving up."
221             }
222              
223             # all righty. Everything's fine, but I need to swap all the data
224 0         0 $do_swap = 1;
225             }
226              
227              
228              
229             # mostly this acts like unpack('Q<'...), but works even if my unpack()
230             # doesn't support 'Q'. This also makes sure that my PDL_Indx is large enough
231             # to read this piddle
232             sub unpack64bit
233             {
234 14     14 0 24 my ($count, $pack, $offset) = @_;
235              
236             return map
237             {
238 14         28 my ($lsw, $msw) = unpack('VV', substr($$pack, $$offset));
  16         36  
239 16         24 $$offset += 8;
240              
241 16 50 33     31 croak( "PDL::IO::Storable tried reading a file with dimensions that don't fit into 32 bits.\n" .
242             "However here PDL_Indx can't store a number so large. Giving up." )
243             if( PDL::Core::howbig($PDL::Types::PDL_IND) < 8 && $msw != 0 );
244              
245 16         47 (($msw << 32) | $lsw)
246             } (1..$count);
247             }
248              
249 7         16 ($ndims) = unpack64bit( 1, \$pack, \$offset );
250 7 50       24 @dims = unpack64bit( $ndims, \$pack, \$offset ) if $ndims > 0;
251             }
252              
253 9 50       23 print "thawing PDL, Dims: [",join(',',@dims),"]\n" if $PDL::verbose;
254 9         50 $pdl->make_null; # make this a real piddle -- this is the tricky bit!
255 9         46 $pdl->set_datatype($type);
256 9         43 $pdl->setdims([@dims]);
257 9         31 my $dref = $pdl->get_dataref;
258              
259 9         19 $$dref = substr $pack, $offset;
260 9 50 33     75 if( $do_swap && PDL::Core::howbig( $type ) != 1 )
261             {
262 0         0 swapEndian( $$dref, PDL::Core::howbig( $type ) );
263             }
264 9         29 $pdl->upd_data;
265 9         112 return $pdl;
266             }
267              
268             sub STORABLE_freeze {
269 6     6 0 141 my ($self, $cloning) = @_;
270             # return if $cloning; # Regular default serialization
271 6 100       46 return UNIVERSAL::isa($self, "HASH") ? ("",{%$self}) # hash ref -> Storable
272             : (pdlpack $self); # pack the piddle into a long string
273             }
274              
275             sub STORABLE_thaw {
276 10     10 0 583 my ($pdl,$cloning,$serial,$hashref) = @_;
277             # print "in STORABLE_thaw\n";
278             # return if $cloning;
279 10         18 my $class = ref $pdl;
280 10 100       22 if (defined $hashref) {
281 1 50 33     5 croak "serial data with hashref!" unless !defined $serial ||
282             $serial eq "";
283 1         5 for (keys %$hashref) { $pdl->{$_} = $hashref->{$_} }
  1         9  
284             } else {
285             # all the magic is happening in pdlunpack
286 9         21 $pdl->pdlunpack($serial); # unpack our serial into this sv
287             }
288             }
289              
290             # have these as PDL methods
291              
292             =head2 store
293              
294             =for ref
295              
296             store a piddle using L
297              
298             =for example
299              
300             $x = random 12,10;
301             $x->store('myfile');
302              
303             =cut
304              
305             =head2 freeze
306              
307             =for ref
308              
309             freeze a piddle using L
310              
311             =for example
312              
313             $x = random 12,10;
314             $frozen = $x->freeze;
315              
316             =cut
317              
318 0     0 0 0 sub store { require Storable; Storable::store(@_) }
  0         0  
319 1     1 0 433 sub freeze { require Storable; Storable::freeze(@_) }
  1         7  
320             }
321              
322             =head1 AUTHOR
323              
324             Copyright (C) 2013 Dima Kogan
325             Copyright (C) 2002 Christian Soeller
326             All rights reserved. There is no warranty. You are allowed
327             to redistribute this software / documentation under certain
328             conditions. For details, see the file COPYING in the PDL
329             distribution. If this file is separated from the PDL distribution,
330             the copyright notice should be included in the file.
331              
332             =cut
333              
334              
335              
336              
337             ;
338              
339              
340              
341             # Exit with OK status
342              
343             1;
344              
345