File Coverage

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