Commit | Line | Data |
f0ffaed8 |
1 | ;# $Id: Storable.pm,v 0.7.1.3 2000/08/23 22:49:25 ram Exp $ |
7a6a85bf |
2 | ;# |
3 | ;# Copyright (c) 1995-2000, Raphael Manfredi |
4 | ;# |
5 | ;# You may redistribute only under the terms of the Artistic License, |
6 | ;# as specified in the README file that comes with the distribution. |
7 | ;# |
8 | ;# $Log: Storable.pm,v $ |
f0ffaed8 |
9 | ;# Revision 0.7.1.3 2000/08/23 22:49:25 ram |
10 | ;# patch3: updated version number |
11 | ;# |
7a6a85bf |
12 | ;# Revision 0.7.1.2 2000/08/14 07:18:40 ram |
13 | ;# patch2: increased version number |
14 | ;# |
15 | ;# Revision 0.7.1.1 2000/08/13 20:08:58 ram |
16 | ;# patch1: mention new Clone(3) extension in SEE ALSO |
17 | ;# patch1: contributor Marc Lehmann added overloading and ref to tied items |
18 | ;# patch1: updated e-mail from Benjamin Holzman |
19 | ;# |
20 | ;# Revision 0.7 2000/08/03 22:04:44 ram |
21 | ;# Baseline for second beta release. |
22 | ;# |
23 | |
24 | require DynaLoader; |
25 | require Exporter; |
26 | package Storable; @ISA = qw(Exporter DynaLoader); |
27 | |
28 | @EXPORT = qw(store retrieve); |
29 | @EXPORT_OK = qw( |
30 | nstore store_fd nstore_fd retrieve_fd |
31 | freeze nfreeze thaw |
32 | dclone |
33 | ); |
34 | |
35 | use AutoLoader; |
36 | use vars qw($forgive_me $VERSION); |
37 | |
f0ffaed8 |
38 | $VERSION = '0.703'; |
7a6a85bf |
39 | *AUTOLOAD = \&AutoLoader::AUTOLOAD; # Grrr... |
40 | |
41 | # |
42 | # Use of Log::Agent is optional |
43 | # |
44 | |
45 | eval "use Log::Agent"; |
46 | |
47 | unless (defined @Log::Agent::EXPORT) { |
48 | eval q{ |
49 | sub logcroak { |
50 | require Carp; |
51 | Carp::croak(@_); |
52 | } |
53 | }; |
54 | } |
55 | |
56 | sub logcroak; |
57 | |
cb3d9de5 |
58 | # 8.3 limitation avoidance trickery. --mjtguy |
59 | sub retrieve_fd { goto &fdretrieve }; |
60 | |
7a6a85bf |
61 | bootstrap Storable; |
62 | 1; |
63 | __END__ |
64 | |
65 | # |
66 | # store |
67 | # |
68 | # Store target object hierarchy, identified by a reference to its root. |
69 | # The stored object tree may later be retrieved to memory via retrieve. |
70 | # Returns undef if an I/O error occurred, in which case the file is |
71 | # removed. |
72 | # |
73 | sub store { |
74 | return _store(\&pstore, @_); |
75 | } |
76 | |
77 | # |
78 | # nstore |
79 | # |
80 | # Same as store, but in network order. |
81 | # |
82 | sub nstore { |
83 | return _store(\&net_pstore, @_); |
84 | } |
85 | |
86 | # Internal store to file routine |
87 | sub _store { |
88 | my $xsptr = shift; |
89 | my $self = shift; |
90 | my ($file) = @_; |
91 | logcroak "not a reference" unless ref($self); |
92 | logcroak "too many arguments" unless @_ == 1; # No @foo in arglist |
93 | local *FILE; |
94 | open(FILE, ">$file") || logcroak "can't create $file: $!"; |
95 | binmode FILE; # Archaic systems... |
96 | my $da = $@; # Don't mess if called from exception handler |
97 | my $ret; |
98 | # Call C routine nstore or pstore, depending on network order |
99 | eval { $ret = &$xsptr(*FILE, $self) }; |
100 | close(FILE) or $ret = undef; |
101 | unlink($file) or warn "Can't unlink $file: $!\n" if $@ || !defined $ret; |
102 | logcroak $@ if $@ =~ s/\.?\n$/,/; |
103 | $@ = $da; |
104 | return $ret ? $ret : undef; |
105 | } |
106 | |
107 | # |
108 | # store_fd |
109 | # |
110 | # Same as store, but perform on an already opened file descriptor instead. |
111 | # Returns undef if an I/O error occurred. |
112 | # |
113 | sub store_fd { |
114 | return _store_fd(\&pstore, @_); |
115 | } |
116 | |
117 | # |
118 | # nstore_fd |
119 | # |
120 | # Same as store_fd, but in network order. |
121 | # |
122 | sub nstore_fd { |
123 | my ($self, $file) = @_; |
124 | return _store_fd(\&net_pstore, @_); |
125 | } |
126 | |
127 | # Internal store routine on opened file descriptor |
128 | sub _store_fd { |
129 | my $xsptr = shift; |
130 | my $self = shift; |
131 | my ($file) = @_; |
132 | logcroak "not a reference" unless ref($self); |
133 | logcroak "too many arguments" unless @_ == 1; # No @foo in arglist |
134 | my $fd = fileno($file); |
135 | logcroak "not a valid file descriptor" unless defined $fd; |
136 | my $da = $@; # Don't mess if called from exception handler |
137 | my $ret; |
138 | # Call C routine nstore or pstore, depending on network order |
139 | eval { $ret = &$xsptr($file, $self) }; |
140 | logcroak $@ if $@ =~ s/\.?\n$/,/; |
141 | $@ = $da; |
142 | return $ret ? $ret : undef; |
143 | } |
144 | |
145 | # |
146 | # freeze |
147 | # |
148 | # Store oject and its hierarchy in memory and return a scalar |
149 | # containing the result. |
150 | # |
151 | sub freeze { |
152 | _freeze(\&mstore, @_); |
153 | } |
154 | |
155 | # |
156 | # nfreeze |
157 | # |
158 | # Same as freeze but in network order. |
159 | # |
160 | sub nfreeze { |
161 | _freeze(\&net_mstore, @_); |
162 | } |
163 | |
164 | # Internal freeze routine |
165 | sub _freeze { |
166 | my $xsptr = shift; |
167 | my $self = shift; |
168 | logcroak "not a reference" unless ref($self); |
169 | logcroak "too many arguments" unless @_ == 0; # No @foo in arglist |
170 | my $da = $@; # Don't mess if called from exception handler |
171 | my $ret; |
172 | # Call C routine mstore or net_mstore, depending on network order |
173 | eval { $ret = &$xsptr($self) }; |
174 | logcroak $@ if $@ =~ s/\.?\n$/,/; |
175 | $@ = $da; |
176 | return $ret ? $ret : undef; |
177 | } |
178 | |
179 | # |
180 | # retrieve |
181 | # |
182 | # Retrieve object hierarchy from disk, returning a reference to the root |
183 | # object of that tree. |
184 | # |
185 | sub retrieve { |
186 | my ($file) = @_; |
187 | local *FILE; |
188 | open(FILE, "$file") || logcroak "can't open $file: $!"; |
189 | binmode FILE; # Archaic systems... |
190 | my $self; |
191 | my $da = $@; # Could be from exception handler |
192 | eval { $self = pretrieve(*FILE) }; # Call C routine |
193 | close(FILE); |
194 | logcroak $@ if $@ =~ s/\.?\n$/,/; |
195 | $@ = $da; |
196 | return $self; |
197 | } |
198 | |
199 | # |
cb3d9de5 |
200 | # fdretrieve |
7a6a85bf |
201 | # |
202 | # Same as retrieve, but perform from an already opened file descriptor instead. |
203 | # |
cb3d9de5 |
204 | sub fdretrieve { |
7a6a85bf |
205 | my ($file) = @_; |
206 | my $fd = fileno($file); |
207 | logcroak "not a valid file descriptor" unless defined $fd; |
208 | my $self; |
209 | my $da = $@; # Could be from exception handler |
210 | eval { $self = pretrieve($file) }; # Call C routine |
211 | logcroak $@ if $@ =~ s/\.?\n$/,/; |
212 | $@ = $da; |
213 | return $self; |
214 | } |
215 | |
216 | # |
217 | # thaw |
218 | # |
219 | # Recreate objects in memory from an existing frozen image created |
220 | # by freeze. If the frozen image passed is undef, return undef. |
221 | # |
222 | sub thaw { |
223 | my ($frozen) = @_; |
224 | return undef unless defined $frozen; |
225 | my $self; |
226 | my $da = $@; # Could be from exception handler |
227 | eval { $self = mretrieve($frozen) }; # Call C routine |
228 | logcroak $@ if $@ =~ s/\.?\n$/,/; |
229 | $@ = $da; |
230 | return $self; |
231 | } |
232 | |
233 | =head1 NAME |
234 | |
235 | Storable - persistency for perl data structures |
236 | |
237 | =head1 SYNOPSIS |
238 | |
239 | use Storable; |
240 | store \%table, 'file'; |
241 | $hashref = retrieve('file'); |
242 | |
243 | use Storable qw(nstore store_fd nstore_fd freeze thaw dclone); |
244 | |
245 | # Network order |
246 | nstore \%table, 'file'; |
247 | $hashref = retrieve('file'); # There is NO nretrieve() |
248 | |
249 | # Storing to and retrieving from an already opened file |
250 | store_fd \@array, \*STDOUT; |
251 | nstore_fd \%table, \*STDOUT; |
252 | $aryref = retrieve_fd(\*SOCKET); |
253 | $hashref = retrieve_fd(\*SOCKET); |
254 | |
255 | # Serializing to memory |
256 | $serialized = freeze \%table; |
257 | %table_clone = %{ thaw($serialized) }; |
258 | |
259 | # Deep (recursive) cloning |
260 | $cloneref = dclone($ref); |
261 | |
262 | =head1 DESCRIPTION |
263 | |
264 | The Storable package brings persistency to your perl data structures |
265 | containing SCALAR, ARRAY, HASH or REF objects, i.e. anything that can be |
266 | convenientely stored to disk and retrieved at a later time. |
267 | |
268 | It can be used in the regular procedural way by calling C<store> with |
269 | a reference to the object to be stored, along with the file name where |
270 | the image should be written. |
271 | The routine returns C<undef> for I/O problems or other internal error, |
272 | a true value otherwise. Serious errors are propagated as a C<die> exception. |
273 | |
274 | To retrieve data stored to disk, use C<retrieve> with a file name, |
275 | and the objects stored into that file are recreated into memory for you, |
276 | a I<reference> to the root object being returned. In case an I/O error |
277 | occurs while reading, C<undef> is returned instead. Other serious |
278 | errors are propagated via C<die>. |
279 | |
280 | Since storage is performed recursively, you might want to stuff references |
281 | to objects that share a lot of common data into a single array or hash |
282 | table, and then store that object. That way, when you retrieve back the |
283 | whole thing, the objects will continue to share what they originally shared. |
284 | |
285 | At the cost of a slight header overhead, you may store to an already |
286 | opened file descriptor using the C<store_fd> routine, and retrieve |
287 | from a file via C<retrieve_fd>. Those names aren't imported by default, |
288 | so you will have to do that explicitely if you need those routines. |
289 | The file descriptor you supply must be already opened, for read |
290 | if you're going to retrieve and for write if you wish to store. |
291 | |
292 | store_fd(\%table, *STDOUT) || die "can't store to stdout\n"; |
293 | $hashref = retrieve_fd(*STDIN); |
294 | |
295 | You can also store data in network order to allow easy sharing across |
296 | multiple platforms, or when storing on a socket known to be remotely |
297 | connected. The routines to call have an initial C<n> prefix for I<network>, |
298 | as in C<nstore> and C<nstore_fd>. At retrieval time, your data will be |
299 | correctly restored so you don't have to know whether you're restoring |
300 | from native or network ordered data. |
301 | |
302 | When using C<retrieve_fd>, objects are retrieved in sequence, one |
303 | object (i.e. one recursive tree) per associated C<store_fd>. |
304 | |
305 | If you're more from the object-oriented camp, you can inherit from |
306 | Storable and directly store your objects by invoking C<store> as |
307 | a method. The fact that the root of the to-be-stored tree is a |
308 | blessed reference (i.e. an object) is special-cased so that the |
309 | retrieve does not provide a reference to that object but rather the |
310 | blessed object reference itself. (Otherwise, you'd get a reference |
311 | to that blessed object). |
312 | |
313 | =head1 MEMORY STORE |
314 | |
315 | The Storable engine can also store data into a Perl scalar instead, to |
316 | later retrieve them. This is mainly used to freeze a complex structure in |
317 | some safe compact memory place (where it can possibly be sent to another |
318 | process via some IPC, since freezing the structure also serializes it in |
319 | effect). Later on, and maybe somewhere else, you can thaw the Perl scalar |
320 | out and recreate the original complex structure in memory. |
321 | |
322 | Surprisingly, the routines to be called are named C<freeze> and C<thaw>. |
323 | If you wish to send out the frozen scalar to another machine, use |
324 | C<nfreeze> instead to get a portable image. |
325 | |
326 | Note that freezing an object structure and immediately thawing it |
327 | actually achieves a deep cloning of that structure: |
328 | |
329 | dclone(.) = thaw(freeze(.)) |
330 | |
331 | Storable provides you with a C<dclone> interface which does not create |
332 | that intermediary scalar but instead freezes the structure in some |
333 | internal memory space and then immediatly thaws it out. |
334 | |
335 | =head1 SPEED |
336 | |
337 | The heart of Storable is written in C for decent speed. Extra low-level |
338 | optimization have been made when manipulating perl internals, to |
339 | sacrifice encapsulation for the benefit of a greater speed. |
340 | |
341 | =head1 CANONICAL REPRESENTATION |
342 | |
343 | Normally Storable stores elements of hashes in the order they are |
344 | stored internally by Perl, i.e. pseudo-randomly. If you set |
345 | C<$Storable::canonical> to some C<TRUE> value, Storable will store |
346 | hashes with the elements sorted by their key. This allows you to |
347 | compare data structures by comparing their frozen representations (or |
348 | even the compressed frozen representations), which can be useful for |
349 | creating lookup tables for complicated queries. |
350 | |
351 | Canonical order does not imply network order, those are two orthogonal |
352 | settings. |
353 | |
354 | =head1 ERROR REPORTING |
355 | |
356 | Storable uses the "exception" paradigm, in that it does not try to workaround |
357 | failures: if something bad happens, an exception is generated from the |
358 | caller's perspective (see L<Carp> and C<croak()>). Use eval {} to trap |
359 | those exceptions. |
360 | |
361 | When Storable croaks, it tries to report the error via the C<logcroak()> |
362 | routine from the C<Log::Agent> package, if it is available. |
363 | |
364 | =head1 WIZARDS ONLY |
365 | |
366 | =head2 Hooks |
367 | |
368 | Any class may define hooks that will be called during the serialization |
369 | and deserialization process on objects that are instances of that class. |
370 | Those hooks can redefine the way serialization is performed (and therefore, |
371 | how the symetrical deserialization should be conducted). |
372 | |
373 | Since we said earlier: |
374 | |
375 | dclone(.) = thaw(freeze(.)) |
376 | |
377 | everything we say about hooks should also hold for deep cloning. However, |
378 | hooks get to know whether the operation is a mere serialization, or a cloning. |
379 | |
380 | Therefore, when serializing hooks are involved, |
381 | |
382 | dclone(.) <> thaw(freeze(.)) |
383 | |
384 | Well, you could keep them in sync, but there's no guarantee it will always |
385 | hold on classes somebody else wrote. Besides, there is little to gain in |
386 | doing so: a serializing hook could only keep one attribute of an object, |
387 | which is probably not what should happen during a deep cloning of that |
388 | same object. |
389 | |
390 | Here is the hooking interface: |
391 | |
392 | =over |
393 | |
394 | =item C<STORABLE_freeze> I<obj>, I<cloning> |
395 | |
396 | The serializing hook, called on the object during serialization. It can be |
397 | inherited, or defined in the class itself, like any other method. |
398 | |
399 | Arguments: I<obj> is the object to serialize, I<cloning> is a flag indicating |
400 | whether we're in a dclone() or a regular serialization via store() or freeze(). |
401 | |
402 | Returned value: A LIST C<($serialized, $ref1, $ref2, ...)> where $serialized |
403 | is the serialized form to be used, and the optional $ref1, $ref2, etc... are |
404 | extra references that you wish to let the Storable engine serialize. |
405 | |
406 | At deserialization time, you will be given back the same LIST, but all the |
407 | extra references will be pointing into the deserialized structure. |
408 | |
409 | The B<first time> the hook is hit in a serialization flow, you may have it |
410 | return an empty list. That will signal the Storable engine to further |
411 | discard that hook for this class and to therefore revert to the default |
412 | serialization of the underlying Perl data. The hook will again be normally |
413 | processed in the next serialization. |
414 | |
415 | Unless you know better, serializing hook should always say: |
416 | |
417 | sub STORABLE_freeze { |
418 | my ($self, $cloning) = @_; |
419 | return if $cloning; # Regular default serialization |
420 | .... |
421 | } |
422 | |
423 | in order to keep reasonable dclone() semantics. |
424 | |
425 | =item C<STORABLE_thaw> I<obj>, I<cloning>, I<serialized>, ... |
426 | |
427 | The deserializing hook called on the object during deserialization. |
428 | But wait. If we're deserializing, there's no object yet... right? |
429 | |
430 | Wrong: the Storable engine creates an empty one for you. If you know Eiffel, |
431 | you can view C<STORABLE_thaw> as an alternate creation routine. |
432 | |
433 | This means the hook can be inherited like any other method, and that |
434 | I<obj> is your blessed reference for this particular instance. |
435 | |
436 | The other arguments should look familiar if you know C<STORABLE_freeze>: |
437 | I<cloning> is true when we're part of a deep clone operation, I<serialized> |
438 | is the serialized string you returned to the engine in C<STORABLE_freeze>, |
439 | and there may be an optional list of references, in the same order you gave |
440 | them at serialization time, pointing to the deserialized objects (which |
441 | have been processed courtesy of the Storable engine). |
442 | |
443 | It is up to you to use these information to populate I<obj> the way you want. |
444 | |
445 | Returned value: none. |
446 | |
447 | =back |
448 | |
449 | =head2 Predicates |
450 | |
451 | Predicates are not exportable. They must be called by explicitely prefixing |
452 | them with the Storable package name. |
453 | |
454 | =over |
455 | |
456 | =item C<Storable::last_op_in_netorder> |
457 | |
458 | The C<Storable::last_op_in_netorder()> predicate will tell you whether |
459 | network order was used in the last store or retrieve operation. If you |
460 | don't know how to use this, just forget about it. |
461 | |
462 | =item C<Storable::is_storing> |
463 | |
464 | Returns true if within a store operation (via STORABLE_freeze hook). |
465 | |
466 | =item C<Storable::is_retrieving> |
467 | |
468 | Returns true if within a retrieve operation, (via STORABLE_thaw hook). |
469 | |
470 | =back |
471 | |
472 | =head2 Recursion |
473 | |
474 | With hooks comes the ability to recurse back to the Storable engine. Indeed, |
475 | hooks are regular Perl code, and Storable is convenient when it comes to |
476 | serialize and deserialize things, so why not use it to handle the |
477 | serialization string? |
478 | |
479 | There are a few things you need to know however: |
480 | |
481 | =over |
482 | |
483 | =item * |
484 | |
485 | You can create endless loops if the things you serialize via freeze() |
486 | (for instance) point back to the object we're trying to serialize in the hook. |
487 | |
488 | =item * |
489 | |
490 | Shared references among objects will not stay shared: if we're serializing |
491 | the list of object [A, C] where both object A and C refer to the SAME object |
492 | B, and if there is a serializing hook in A that says freeze(B), then when |
493 | deserializing, we'll get [A', C'] where A' refers to B', but C' refers to D, |
494 | a deep clone of B'. The topology was not preserved. |
495 | |
496 | =back |
497 | |
498 | That's why C<STORABLE_freeze> lets you provide a list of references |
499 | to serialize. The engine guarantees that those will be serialized in the |
500 | same context as the other objects, and therefore that shared objects will |
501 | stay shared. |
502 | |
503 | In the above [A, C] example, the C<STORABLE_freeze> hook could return: |
504 | |
505 | ("something", $self->{B}) |
506 | |
507 | and the B part would be serialized by the engine. In C<STORABLE_thaw>, you |
508 | would get back the reference to the B' object, deserialized for you. |
509 | |
510 | Therefore, recursion should normally be avoided, but is nonetheless supported. |
511 | |
512 | =head2 Deep Cloning |
513 | |
514 | There is a new Clone module available on CPAN which implements deep cloning |
515 | natively, i.e. without freezing to memory and thawing the result. It is |
516 | aimed to replace Storable's dclone() some day. However, it does not currently |
517 | support Storable hooks to redefine the way deep cloning is performed. |
518 | |
519 | =head1 EXAMPLES |
520 | |
521 | Here are some code samples showing a possible usage of Storable: |
522 | |
523 | use Storable qw(store retrieve freeze thaw dclone); |
524 | |
525 | %color = ('Blue' => 0.1, 'Red' => 0.8, 'Black' => 0, 'White' => 1); |
526 | |
527 | store(\%color, '/tmp/colors') or die "Can't store %a in /tmp/colors!\n"; |
528 | |
529 | $colref = retrieve('/tmp/colors'); |
530 | die "Unable to retrieve from /tmp/colors!\n" unless defined $colref; |
531 | printf "Blue is still %lf\n", $colref->{'Blue'}; |
532 | |
533 | $colref2 = dclone(\%color); |
534 | |
535 | $str = freeze(\%color); |
536 | printf "Serialization of %%color is %d bytes long.\n", length($str); |
537 | $colref3 = thaw($str); |
538 | |
539 | which prints (on my machine): |
540 | |
541 | Blue is still 0.100000 |
542 | Serialization of %color is 102 bytes long. |
543 | |
544 | =head1 WARNING |
545 | |
546 | If you're using references as keys within your hash tables, you're bound |
547 | to disapointment when retrieving your data. Indeed, Perl stringifies |
548 | references used as hash table keys. If you later wish to access the |
549 | items via another reference stringification (i.e. using the same |
550 | reference that was used for the key originally to record the value into |
551 | the hash table), it will work because both references stringify to the |
552 | same string. |
553 | |
554 | It won't work across a C<store> and C<retrieve> operations however, because |
555 | the addresses in the retrieved objects, which are part of the stringified |
556 | references, will probably differ from the original addresses. The |
557 | topology of your structure is preserved, but not hidden semantics |
558 | like those. |
559 | |
560 | On platforms where it matters, be sure to call C<binmode()> on the |
561 | descriptors that you pass to Storable functions. |
562 | |
563 | Storing data canonically that contains large hashes can be |
564 | significantly slower than storing the same data normally, as |
565 | temprorary arrays to hold the keys for each hash have to be allocated, |
566 | populated, sorted and freed. Some tests have shown a halving of the |
567 | speed of storing -- the exact penalty will depend on the complexity of |
568 | your data. There is no slowdown on retrieval. |
569 | |
570 | =head1 BUGS |
571 | |
572 | You can't store GLOB, CODE, FORMLINE, etc... If you can define |
573 | semantics for those operations, feel free to enhance Storable so that |
574 | it can deal with them. |
575 | |
576 | The store functions will C<croak> if they run into such references |
577 | unless you set C<$Storable::forgive_me> to some C<TRUE> value. In that |
578 | case, the fatal message is turned in a warning and some |
579 | meaningless string is stored instead. |
580 | |
581 | Setting C<$Storable::canonical> may not yield frozen strings that |
582 | compare equal due to possible stringification of numbers. When the |
583 | string version of a scalar exists, it is the form stored, therefore |
584 | if you happen to use your numbers as strings between two freezing |
585 | operations on the same data structures, you will get different |
586 | results. |
587 | |
588 | Due to the aforementionned optimizations, Storable is at the mercy |
589 | of perl's internal redesign or structure changes. If that bothers |
590 | you, you can try convincing Larry that what is used in Storable |
591 | should be documented and consistently kept in future revisions. |
592 | |
593 | =head1 CREDITS |
594 | |
595 | Thank you to (in chronological order): |
596 | |
597 | Jarkko Hietaniemi <jhi@iki.fi> |
598 | Ulrich Pfeifer <pfeifer@charly.informatik.uni-dortmund.de> |
599 | Benjamin A. Holzman <bah@ecnvantage.com> |
600 | Andrew Ford <A.Ford@ford-mason.co.uk> |
601 | Gisle Aas <gisle@aas.no> |
602 | Jeff Gresham <gresham_jeffrey@jpmorgan.com> |
603 | Murray Nesbitt <murray@activestate.com> |
604 | Marc Lehmann <pcg@opengroup.org> |
605 | |
606 | for their bug reports, suggestions and contributions. |
607 | |
608 | Benjamin Holzman contributed the tied variable support, Andrew Ford |
609 | contributed the canonical order for hashes, and Gisle Aas fixed |
610 | a few misunderstandings of mine regarding the Perl internals, |
611 | and optimized the emission of "tags" in the output streams by |
612 | simply counting the objects instead of tagging them (leading to |
613 | a binary incompatibility for the Storable image starting at version |
614 | 0.6--older images are of course still properly understood). |
615 | Murray Nesbitt made Storable thread-safe. Marc Lehmann added overloading |
616 | and reference to tied items support. |
617 | |
618 | =head1 TRANSLATIONS |
619 | |
620 | There is a Japanese translation of this man page available at |
621 | http://member.nifty.ne.jp/hippo2000/perltips/storable.htm , |
622 | courtesy of Kawai, Takanori <kawai@nippon-rad.co.jp>. |
623 | |
624 | =head1 AUTHOR |
625 | |
626 | Raphael Manfredi F<E<lt>Raphael_Manfredi@pobox.comE<gt>> |
627 | |
628 | =head1 SEE ALSO |
629 | |
630 | Clone(3). |
631 | |
632 | =cut |
633 | |