};
close(FILE);
}
sub get_data{
open(FILE,"info.dat") || print "Ooops..$!";
my @data = ;
my $data = join('',@data);
close (FILE);
return $data;
}
The checkout_header.inc file was transformed as follows:
Shoppingcircus.com: @@TITLE
The checkout_footer.inc file was transformed as follows:
Note:
ALL ITEMS ARE AVAILABLE IN DIFFERENT SIZES AND COLOURS. IN THE AREA
BELOW PLEASE TYPE IN THE NAME OF THE ITEM YOU ARE PURCHASING AND THE
COLOUR AND SIZE YOU WISH TO PURCHASE THEN PRESS SUBMIT. AFTER THIS YOU CAN
PRESS CONTINUE TO FINISH THE ORDERING PROCEDURE.
#!/usr/bin/perl
#
# checkout2.cgi v1.0 5/8/98
#
# Starts the checkout process.
# Uses Evantide::Web and Evantide::State to deal with cookies.
#
use Evantide::Web;
use Evantide::State;
use Products;
#
# If someone hitting this page doesn't already have a cookie, we have a
# problem!
#
# I store 3 things in state now:
#
# orders - a hash of hashes contaning each line of the order
#
# items - a number contaning the total number of differient items in
# an order. used to figure out how big the basket table images
# should be. just for looks.
# info - a hash of hashes contaning the ship to and billing info.
#
main();
sub main {
cgi_parse();
if ( $state = get_state_from_cookie () ) { # if we have a cookie...
print "Content-type: text/html\n\n"; # send the http header for html
print inject_vars ( "checkout2_header.inc",
"TITLE", "Checkout"); # send the top of the html document
$orders = $state->get ( 'orders' ); # extract the order from state
$items = $state->get ( 'items' ); # extract the number of differient
items in basket
show_basket ( ); # output contents of basket
print expand_path ( "checkout_footer.inc" ); # finish html
$state->put ( 'shipping', \%cgi ); # save the shipping info
$state->sync(); # write it out to state
}
else { # guess we don't have a cookie
$state = new Evantide::State; # get some state
$items = "0";
$place = "0";
$state->put ( 'orders', {} );
$state->put ( 'items', $items );
$state->put ( 'place', \$place );
print "Content-type: text/html\n"; # fist line of http header
print get_state_cookie_header ($state, '/', $domain), "\n"; # the cookie
print inject_vars ( "header.inc",
"TITLE", $section{'default-0'}->[0],
"BUYNOW", $section{'default-0'}->[2]); # show header html
print inject_vars ( "footer.inc",
"MAP", $section{'default-0'}->[1] ); # show footer html
}
}
sub show_basket {
$height = 48 + ( $items * 16 ); # figure out the size of the basket
area
print inject_vars ( "order_header.inc",
"HEIGHT", $height ); # send top of basket html
$total = 0; # initialize total
foreach $order (keys %$orders) {
$quantity = $orders->{$order}->[1]; # extract for simplicity
$description = $products{$order}->[0];
$price = $products{$order}->[1];
$price = $price * $quantity; # total for this line item
$price = sprintf ("%.2f", $price); # force 2 decimal places
$total = $total + $price; # keep running total
show ( $quantity, $description, $price ); # show a line item
}
$total = sprintf ("%.2f", $total); # force 2 decimal places
print inject_vars ( "checkout_order_footer.inc",
"TOTAL", $total,
"HEIGHT", $height ); # finish basket html
}
sub show {
($quantity, $description, $price) = @_;
print inject_vars ( "order_item.inc",
"QUANTITY", $quantity,
"DESCRIPTION", $description,
"PRICE", $price ); # print a line item
}
use CGI qw(:cgi-lib);
ReadParse();
# if ($in{'button1'})
# {
# store_data();
# }
# my $data = get_data();
print qq{
Information_Items
$data
};
sub store_data{
open(FILE,">>info.dat") || print "Ooops..$!";
print FILE qq{
Item name: $in{'name'} Size: $in{'size'} Colour: $in{'colour'}
};
close(FILE);
}
sub get_data{
open(FILE,"info.dat") || print "Ooops..$!";
my @data = ;
my $data = join('',@data);
close (FILE);
return $data;
}
Below are the files checkout2_header.inc and checkout_order_footer.inc:
Shoppingcircus.com: @@TITLE
Checkout_order_footer.inc:
Total:
$@@TOTAL
# The footer.inc file here is the same as the one printed above and I #will not
repeat printing the code. What follows is the checkout3.cgi #along with the
header and footer files used with it:
#!/usr/bin/perl
#
# checkout2.cgi v1.0 5/8/98
#
# Starts the checkout process.
# Uses Evantide::Web and Evantide::State to deal with cookies.
#
use Evantide::Web;
use Evantide::State;
use Products;
#
# If someone hitting this page doesn't already have a cookie, we have a
# problem!
#
# I store 3 things in state now:
#
# orders - a hash of hashes contaning each line of the order
#
# items - a number contaning the total number of differient items in
# an order. used to figure out how big the basket table images
# should be. just for looks.
# info - a hash of hashes contaning the ship to and billing info.
#
main();
sub main {
cgi_parse();
if ( $state = get_state_from_cookie () ) { # if we have a cookie...
print "Content-type: text/html\n\n"; # send the http header for html
print inject_vars ( "checkout3_header.inc",
"TITLE", "Checkout"); # send the top of the html document
$orders = $state->get ( 'orders' ); # extract the order from state
$items = $state->get ( 'items' ); # extract and the number of
differient items in basket
show_basket ( ); # output contents of basket
print expand_path ( "checkout_footer.inc" ); # finish html
$state->put ( 'billing', \%cgi ); # save the billing info
$state->sync(); # write it out to state
}
else { # guess we don't have a cookie
$state = new Evantide::State; # get some state
$items = "0";
$place = "0";
$state->put ( 'orders', {} );
$state->put ( 'items', $items );
$state->put ( 'place', \$place );
print "Content-type: text/html\n"; # fist line of http header
print get_state_cookie_header ($state, '/', $domain), "\n"; # the cookie
print inject_vars ( "header.inc",
"TITLE", $section{'default-0'}->[0],
"BUYNOW", $section{'default-0'}->[2]); # show header html
print inject_vars ( "footer.inc",
"MAP", $section{'default-0'}->[1] ); # show footer html
}
}
sub show_basket {
$height = 48 + ( $items * 16 ); # figure out the size of the basket
area
print inject_vars ( "order_header.inc",
"HEIGHT", $height ); # send top of basket html
$total = 0; # initialize total
foreach $order (keys %$orders) {
$quantity = $orders->{$order}->[1]; # extract for simplicity
$description = $products{$order}->[0];
$price = $products{$order}->[1];
$price = $price * $quantity; # total for this line item
$price = sprintf ("%.2f", $price); # force 2 decimal places
$total = $total + $price; # keep running total
show ( $quantity, $description, $price ); # show a line item
}
$total = sprintf ("%.2f", $total); # force 2 decimal places
print inject_vars ( "checkout_order_footer.inc",
"TOTAL", $total,
"HEIGHT", $height ); # finish basket html
}
sub show {
($quantity, $description, $price) = @_;
print inject_vars ( "order_item.inc",
"QUANTITY", $quantity,
"DESCRIPTION", $description,
"PRICE", $price ); # print a line item
}
use CGI qw(:cgi-lib);
ReadParse();
if ($in{'button1'})
{
store_data();
}
my $data = get_data();
print qq{
Information_Items
$data
};
sub store_data{
open(FILE,">>info.dat") || print "Ooops..$!";
print FILE qq{
Item name: $in{'name'} Size: $in{'size'} Colour: $in{'colour'}
};
close(FILE);
}
sub get_data{
open(FILE,"info.dat") || print "Ooops..$!";
my @data = ;
my $data = join('',@data);
close (FILE);
return $data;
}
7. Adding directories: a directory called Storable-0.7.2 was downloaded from the
web for the cart was running but hanging. There were no changes to Storable's
codes and the group used Storable as is. Below are the most important codes used
in Storable:
README Read this first
MANIFEST This shipping list
Makefile.PL Generic Makefile template
Storable.pm The perl side of Storable
Storable.xs The C side of Storable
patchlevel.h Records current patchlevel
t/blessed.t Test blessed objects
t/canonical.t Test canonical hash table dumping
t/compat-0.6.t Test backward compatibility with 0.6@11
t/dclone.t Test deep cloning
t/dump.pl Small utility to dump data structures
t/forgive.t Test forgiveness
t/freeze.t Test memory store (freeze/thaw) operations
t/recurse.t Test recursive calls
t/retrieve.t Test retrieve operation
t/store.t Test store operation
t/forgive.t Test if $Storable::forgive_me works
t/tied.t Test serialization of tied SVs.
t/tied_hook.t Test tied SVs with hooks
ChangeLog Changes since baseline
Mon Aug 14 09:22:04 MEST 2000 Raphael Manfredi
. Description:
Added a refcnt dec in retrieve_tied_key(): sv_magic() increases
the refcnt on the mg_ptr as well.
Removed spurious dependency to Devel::Peek, which was used for
testing only in t/tied_items.t. Thanks to Conrad Heiney
for spotting it first.
Sun Aug 13 22:12:59 MEST 2000 Raphael Manfredi
. Description:
Marc Lehmann kindly contributed code to add overloading support
and to handle references to tied variables.
Rewrote leading blurb about compatibility to make it clearer what
"backward compatibility" is about: when I say 0.7 is backward
compatible with 0.6, it means the revision 0.7 can read files
produced by 0.6.
Mention new Clone(3) extension in SEE ALSO.
Was wrongly optimizing for "undef" values in hashes by not
fully recursing: as a result, tied "undef" values were incorrectly
serialized.
Sun Jul 30 12:59:17 MEST 2000 Raphael Manfredi
First revision of Storable 0.7.
The serializing format is new, known as version 2.0. It is fully
backward compatible with 0.6. Earlier formats are deprecated and
have not even been tested: next version will drop pre-0.6 format.
Changes since 0.6@11:
- Moved interface to the "beta" status. Some tiny parts are still
subject to change, but nothing important enough to warrant an "alpha"
status any longer.
- Slightly reduced the size of the Storable image by factorizing
object class names and removing final object storage notification due
to a redesign of the blessed object storing.
- Classes can now redefine how they wish their instances to be serialized
and/or deep cloned. Serializing hooks are written in Perl code.
- The engine is now fully re-entrant.
Sun Apr 2 23:47:50 MEST 2000 Raphael Manfredi
. Description:
Added provision to detect more recent binary formats, since
the new upcoming Storable-0.7 will use a different format.
In order to prevent attempting the de-serialization of newer
formats by older versions, I'm adding this now to the 0.6 series.
I'm expecting this revision to be the last of the 0.6 series.
Unless it does not work with perl 5.6, which I don't use yet,
and therefore against which I cannot test.
Wed Mar 29 19:55:21 MEST 2000 Raphael Manfredi
. Description:
Added note about format incompatibilities with old versions
(i.e. pre 0.5@9 formats, which cannot be understood as there
was no versionning information in the file by then).
Protect all $@ variables when eval {} used, to avoid corrupting
it when store/retrieve is called within an exception handler.
Mistakenly included "patchlevel.h" instead of ,
preventing Perl's patchlevel from being included, which is
needed starting from 5.6.
Tue May 12 09:15:15 METDST 1998 Raphael Manfredi
. Description:
Fixed shared "undef" bug in hashes, which did not remain shared
through store/retrieve.
Thu Feb 10 19:48:16 MET 2000 Raphael Manfredi
. Description:
added last_op_in_netorder() predicate
documented last_op_in_netorder()
added tests for the new last_op_in_netorder() predicate
Wed Oct 20 19:07:36 MEST 1999 Raphael Manfredi
. Description:
Forgot to update VERSION
Tue Oct 19 21:25:02 MEST 1999 Raphael Manfredi
. Description:
Added mention of japanese translation for the manual page.
Fixed typo in macro that made threaded code not compilable,
especially on Win32 platforms.
Changed detection of older perls (pre-5.005) by testing PATCHLEVEL
directly instead of relying on internal symbols.
Tue Sep 14 22:13:28 MEST 1999 Raphael Manfredi
. Description:
Integrated "thread-safe" patch from Murray Nesbitt.
Note that this may not be very efficient for threaded code,
see comment in the code.
Try to avoid compilation warning on 64-bit CPUs. Can't test it,
since I don't have access to such machines.
Mon Jul 12 14:37:19 METDST 1999 Raphael Manfredi
. Description:
changed my e-mail to pobox.
mentionned it is not thread-safe.
updated version number.
uses new internal PL_* naming convention.
Fri Jul 3 13:38:16 METDST 1998 Raphael Manfredi
. Description:
Updated benchmark figures due to recent optimizations done in
store(): tagnums are now stored as-is in the hash table, so
no surrounding SV is created. And the "shared keys" mode for
hash table was turned off.
Fixed backward compatibility (wrt 0.5@9) for retrieval of
blessed refs. That old version did something wrong, but the
bugfix prevented correct retrieval of the old format.
Mon Jun 22 11:00:48 METDST 1998 Raphael Manfredi
. Description:
Changed benchmark figures.
Adjust refcnt of tied objects after calling sv_magic() to avoid
memory leaks. Contributed by Jeff Gresham.
Fri Jun 12 11:50:04 METDST 1998 Raphael Manfredi
. Description:
Added workaround for persistent LVALUE-ness in perl5.004. All
scalars tagged as being an lvalue are handled as if they were
not an lvalue at all. Added test for that LVALUE bug workaround.
Now handles Perl immortal scalars explicitely, by storing &sv_yes
as such, explicitely.
Retrieval of non-immortal undef cannot be shared. Previous
version was over-optimizing by not creating a separate SV for
all undefined scalars seen.
Thu Jun 4 17:21:51 METDST 1998 Raphael Manfredi
. Description:
Baseline for Storable-0.6@0.
This version introduces a binary incompatibility in the generated
binary image, which is more compact than older ones by approximatively
15%, depending on the exact degree of sharing in your structures.
The good news is that your older images can still be retrieved with
this version, i.e. backward compatibility is preserved. This version
of Storable can only generate new binaries however.
Another good news is that the retrieval of data structure is
significantly quicker than before, because a Perl array is used
instead of a hash table to keep track of retrieved objects, and
also because the image being smaller, less I/O function calls are
made.
Tue May 12 09:15:15 METDST 1998 Raphael Manfredi
. Description:
Version number now got from Storable.pm directly.
Fixed overzealous sv_type() optimization, which would make
Storable fail when faced with an "upgraded" SV to the PVIV
or PVNV kind containing a reference.
Thu Apr 30 15:11:30 METDST 1998 Raphael Manfredi
. Description:
Extended the SYNOPSIS section to give quick overview of the
routines and their signature.
Optimized sv_type() to avoid flags checking when not needed, i.e.
when their type makes it impossible for them to be refs or tied.
This slightly increases throughput by a few percents when refs
and tied variables are marginal occurrences in your data.
Stubs for XS now use OutputStream and InputStream file types to
make it work when the given file is actually a socket. Perl
makes a distinction for sockets in its internal I/O structures
by having both a read and a write structure, whereas plain files
share the same one.
Tue Jun 3 09:41:33 METDST 1997 Raphael Manfredi
. Description:
Thanks to a contribution from Benjamin A. Holzman, Storable is now
able to correctly serialize tied SVs, i.e. tied arrays, hashes
and scalars.
Thu Apr 9 18:07:51 METDST 1998 Raphael Manfredi
. Description:
I said SvPOK() had changed to SvPOKp(), but that was a lie...
Wed Apr 8 13:14:29 METDST 1998 Raphael Manfredi
. Description:
Wrote sizeof(SV *) instead of sizeof(I32) when portable, which
in effect mangled the object tags and prevented portability
accross 32/64 bit architectures!
Wed Mar 25 14:57:02 MET 1998 Raphael Manfredi
. Description:
Added code example for store_fd() and retrieve_fd() in the
man page, to emphasize that file descriptors must be passed as
globs, not as plain strings.
Cannot use SV addresses as tag when using nstore() on LP64. This
was the cause of problems when creating a storable image on an
LP64 machine and retrieving it on an ILP32 system, which is
exactly what nstore() is meant for...
However, we continue to use SV addresses as tags for plain store(),
because benchamarking shows that it saves up to 8% of the store
time, and store() is meant to be fast at the expense of lack
of portability.
This means there will be approximately an 8% degradation of
performance for nstore(), but it's now working as expected.
That cost may vary on your machine of course, since it is
solely caused by the memory allocation overhead used to create
unique SV tags for each distinct stored SV.
Tue Jan 20 09:21:53 MET 1998 Raphael Manfredi
. Description:
Don't use any '_' in version number.
Tue Jan 13 17:51:50 MET 1998 Raphael Manfredi
. Description:
Updated version number.
added binmode() calls for systems where it matters.
Be sure to pass globs, not plain file strings, to C routines,
so that Storable can be used under the Perl debugger.
Wed Nov 5 10:53:22 MET 1997 Raphael Manfredi
. Description:
Fix memory leaks on seen hash table and returned SV refs.
Storable did not work properly when tainting enabled.
Fixed "Allocation too large" messages in freeze/thaw and added.
proper regression test in t/freeze.t.
Tue Jun 3 09:41:33 METDST 1997 Raphael Manfredi
. Description:
Updated version number
Added freeze/thaw interface and dclone.
Fri May 16 10:45:47 METDST 1997 Raphael Manfredi
. Description:
Forgot that AutoLoader does not export its own AUTOLOAD.
I could use
use AutoLoader 'AUTOLOAD';
but that would not be backward compatible. So the export is
done by hand...
Tue Mar 25 11:21:32 MET 1997 Raphael Manfredi
. Description:
Empty scalar strings are now "defined" at retrieval time.
New test to ensure an empty string is defined when retrieved.
Thu Feb 27 16:32:44 MET 1997 Raphael Manfredi
. Description:
Updated version number
Declare VERSION as being used
Fixed a typo in the PerlIO_putc remapping.
PerlIO_read and perlIO_write inverted size/nb_items.
(only relevant for pre-perl5.004 versions)
Thu Feb 27 15:58:31 MET 1997 Raphael Manfredi
. Description:
Updated version number
Added VERSION identification
Allow build with perl5.003, which is ante perlIO time
Mon Jan 13 17:53:18 MET 1997 Raphael Manfredi
. Description:
Random code fixes.
Wed Jan 22 15:19:56 MET 1997 Raphael Manfredi
. Description:
Updated version number in Makefile.PL.
Added "thanks to" section to README.
Documented new forgive_me variable.
Made 64-bit clean.
Added forgive_me support to allow store() of data structures
containing non-storable items like CODE refs.
;# $Id: Storable.pm,v 0.7.1.2 2000/08/14 07:18:40 ram Exp $
;#
;# Copyright (c) 1995-2000, Raphael Manfredi
;#
;# You may redistribute only under the terms of the Artistic License,
;# as specified in the README file that comes with the distribution.
;#
;# $Log: Storable.pm,v $
;# Revision 0.7.1.2 2000/08/14 07:18:40 ram
;# patch2: increased version number
;#
;# Revision 0.7.1.1 2000/08/13 20:08:58 ram
;# patch1: mention new Clone(3) extension in SEE ALSO
;# patch1: contributor Marc Lehmann added overloading and ref to tied items
;# patch1: updated e-mail from Benjamin Holzman
;#
;# Revision 0.7 2000/08/03 22:04:44 ram
;# Baseline for second beta release.
;#
require DynaLoader;
require Exporter;
package Storable; @ISA = qw(Exporter DynaLoader);
@EXPORT = qw(store retrieve);
@EXPORT_OK = qw(
nstore store_fd nstore_fd retrieve_fd
freeze nfreeze thaw
dclone
);
use AutoLoader;
use vars qw($forgive_me $VERSION);
$VERSION = '0.702';
*AUTOLOAD = \&AutoLoader::AUTOLOAD; # Grrr...
#
# Use of Log::Agent is optional
#
eval "use Log::Agent";
unless (defined @Log::Agent::EXPORT) {
eval q{
sub logcroak {
require Carp;
Carp::croak(@_);
}
};
}
sub logcroak;
bootstrap Storable;
1;
__END__
#
# store
#
# Store target object hierarchy, identified by a reference to its root.
# The stored object tree may later be retrieved to memory via retrieve.
# Returns undef if an I/O error occurred, in which case the file is
# removed.
#
sub store {
return _store(\&pstore, @_);
}
#
# nstore
#
# Same as store, but in network order.
#
sub nstore {
return _store(\&net_pstore, @_);
}
# Internal store to file routine
sub _store {
my $xsptr = shift;
my $self = shift;
my ($file) = @_;
logcroak "not a reference" unless ref($self);
logcroak "too many arguments" unless @_ == 1; # No @foo in arglist
local *FILE;
open(FILE, ">$file") || logcroak "can't create $file: $!";
binmode FILE; # Archaic systems...
my $da = $@; # Don't mess if called from exception
handler
my $ret;
# Call C routine nstore or pstore, depending on network order
eval { $ret = &$xsptr(*FILE, $self) };
close(FILE) or $ret = undef;
unlink($file) or warn "Can't unlink $file: $!\n" if $@ || !defined $ret;
logcroak $@ if $@ =~ s/\.?\n$/,/;
$@ = $da;
return $ret ? $ret : undef;
}
#
# store_fd
#
# Same as store, but perform on an already opened file descriptor instead.
# Returns undef if an I/O error occurred.
#
sub store_fd {
return _store_fd(\&pstore, @_);
}
#
# nstore_fd
#
# Same as store_fd, but in network order.
#
sub nstore_fd {
my ($self, $file) = @_;
return _store_fd(\&net_pstore, @_);
}
# Internal store routine on opened file descriptor
sub _store_fd {
my $xsptr = shift;
my $self = shift;
my ($file) = @_;
logcroak "not a reference" unless ref($self);
logcroak "too many arguments" unless @_ == 1; # No @foo in arglist
my $fd = fileno($file);
logcroak "not a valid file descriptor" unless defined $fd;
my $da = $@; # Don't mess if called from exception
handler
my $ret;
# Call C routine nstore or pstore, depending on network order
eval { $ret = &$xsptr($file, $self) };
logcroak $@ if $@ =~ s/\.?\n$/,/;
$@ = $da;
return $ret ? $ret : undef;
}
#
# freeze
#
# Store oject and its hierarchy in memory and return a scalar
# containing the result.
#
sub freeze {
_freeze(\&mstore, @_);
}
#
# nfreeze
#
# Same as freeze but in network order.
#
sub nfreeze {
_freeze(\&net_mstore, @_);
}
# Internal freeze routine
sub _freeze {
my $xsptr = shift;
my $self = shift;
logcroak "not a reference" unless ref($self);
logcroak "too many arguments" unless @_ == 0; # No @foo in arglist
my $da = $@; # Don't mess if called from exception
handler
my $ret;
# Call C routine mstore or net_mstore, depending on network order
eval { $ret = &$xsptr($self) };
logcroak $@ if $@ =~ s/\.?\n$/,/;
$@ = $da;
return $ret ? $ret : undef;
}
#
# retrieve
#
# Retrieve object hierarchy from disk, returning a reference to the root
# object of that tree.
#
sub retrieve {
my ($file) = @_;
local *FILE;
open(FILE, "$file") || logcroak "can't open $file: $!";
binmode FILE; # Archaic systems...
my $self;
my $da = $@; # Could be from exception
handler
eval { $self = pretrieve(*FILE) }; # Call C routine
close(FILE);
logcroak $@ if $@ =~ s/\.?\n$/,/;
$@ = $da;
return $self;
}
#
# retrieve_fd
#
# Same as retrieve, but perform from an already opened file descriptor instead.
#
sub retrieve_fd {
my ($file) = @_;
my $fd = fileno($file);
logcroak "not a valid file descriptor" unless defined $fd;
my $self;
my $da = $@; # Could be from exception
handler
eval { $self = pretrieve($file) }; # Call C routine
logcroak $@ if $@ =~ s/\.?\n$/,/;
$@ = $da;
return $self;
}
#
# thaw
#
# Recreate objects in memory from an existing frozen image created
# by freeze. If the frozen image passed is undef, return undef.
#
sub thaw {
my ($frozen) = @_;
return undef unless defined $frozen;
my $self;
my $da = $@; # Could be from exception
handler
eval { $self = mretrieve($frozen) }; # Call C routine
logcroak $@ if $@ =~ s/\.?\n$/,/;
$@ = $da;
return $self;
}
=head1 NAME
Storable - persistency for perl data structures
=head1 SYNOPSIS
use Storable;
store \%table, 'file';
$hashref = retrieve('file');
use Storable qw(nstore store_fd nstore_fd freeze thaw dclone);
# Network order
nstore \%table, 'file';
$hashref = retrieve('file'); # There is NO nretrieve()
# Storing to and retrieving from an already opened file
store_fd \@array, \*STDOUT;
nstore_fd \%table, \*STDOUT;
$aryref = retrieve_fd(\*SOCKET);
$hashref = retrieve_fd(\*SOCKET);
# Serializing to memory
$serialized = freeze \%table;
%table_clone = %{ thaw($serialized) };
# Deep (recursive) cloning
$cloneref = dclone($ref);
=head1 DESCRIPTION
The Storable package brings persistency to your perl data structures
containing SCALAR, ARRAY, HASH or REF objects, i.e. anything that can be
conveniently stored to disk and retrieved at a later time.
It can be used in the regular procedural way by calling C with
a reference to the object to be stored, along with the file name where
the image should be written.
The routine returns C for I/O problems or other internal error,
a true value otherwise. Serious errors are propagated as a C exception.
To retrieve data stored to disk, use C with a file name,
and the objects stored into that file are recreated into memory for you, a
I to the root object being returned. In case an I/O error occurs
while reading, C is returned instead. Other serious
errors are propagated via C.
Since storage is performed recursively, you might want to stuff references to
objects that share a lot of common data into a single array or hash table, and
then store that object. That way, when you retrieve back the whole thing, the
objects will continue to share what they originally shared.
At the cost of a slight header overhead, you may store to an already
opened file descriptor using the C routine, and retrieve
from a file via C. Those names aren't imported by default,
so you will have to do that explicitly if you need those routines.
The file descriptor you supply must be already opened, for read,
if you're going to retrieve and for write if you wish to store.
store_fd(\%table, *STDOUT) || die "can't store to stdout\n";
$hashref = retrieve_fd(*STDIN);
You can also store data in network order to allow easy sharing across
multiple platforms, or when storing on a socket known to be remotely
connected. The routines to call have an initial C prefix for I, as
in C and C. At retrieval time, your data will be correctly
restored so you don't have to know whether you're restoring from native or
network ordered data.
When using C, objects are retrieved in sequence, one
object (i.e. one recursive tree) per associated C.
If you're more from the object-oriented camp, you can inherit from
Storable and directly store your objects by invoking C as
a method. The fact that the root of the to-be-stored tree is a
blessed reference (i.e. an object) is special-cased so that the
retrieve does not provide a reference to that object but rather the
blessed object reference itself. (Otherwise, you'd get a reference
to that blessed object).
=head1 MEMORY STORE
The Storable engine can also store data into a Perl scalar instead, to
later retrieve them. This is mainly used to freeze a complex structure in some
safe compact memory place (where it can possibly be sent to another process via
some IPC, since freezing the structure also serializes it in effect). Later on,
and maybe somewhere else, you can thaw the Perl scalar out and recreate the
original complex structure in memory.
Surprisingly, the routines to be called are named C and C.
If you wish to send out the frozen scalar to another machine, use
C instead to get a portable image.
Note that freezing an object structure and immediately thawing it
actually achieves a deep cloning of that structure:
dclone(.) = thaw(freeze(.))
Storable provides you with a C interface which does not create
that intermediary scalar but instead freezes the structure in some
internal memory space and then immediately thaws it out.
=head1 SPEED
The heart of Storable is written in C for decent speed. Extra low-level
optimization have been made when manipulating perl internals, to
sacrifice encapsulation for the benefit of a greater speed.
=head1 CANONICAL REPRESENTATION
Normally Storable stores elements of hashes in the order they are
stored internally by Perl, i.e. pseudo-randomly. If you set
C<$Storable::canonical> to some C value, Storable will store
hashes with the elements sorted by their key. This allows you to
compare data structures by comparing their frozen representations (or
even the compressed frozen representations), which can be useful for
creating lookup tables for complicated queries.
Canonical order does not imply network order, those are two orthogonal
settings.
=head1 ERROR REPORTING
Storable uses the "exception" paradigm, in that it does not try to workaround
failures: if something bad happens, an exception is generated from the
caller's perspective (see L and C). Use eval {} to trap
those exceptions.
When Storable croaks, it tries to report the error via the C
routine from the C package, if it is available.
=head1 WIZARDS ONLY
=head2 Hooks
Any class may define hooks that will be called during the serialization
and deserialization process on objects that are instances of that class.
Those hooks can redefine the way serialization is performed (and therefore,
how the symetrical deserialization should be conducted).
Since we said earlier:
dclone(.) = thaw(freeze(.))
everything we say about hooks should also hold for deep cloning. However,
hooks get to know whether the operation is a mere serialization, or a cloning.
Therefore, when serializing hooks are involved,
dclone(.) <> thaw(freeze(.))
Well, you could keep them in sync, but there's no guarantee it will always hold
on classes somebody else wrote. Besides, there is little to gain in doing so: a
serializing hook could only keep one attribute of an object, which is probably
not what should happen during a deep cloning of that same object.
Here is the hooking interface:
=over
=item C I, I
The serializing hook, called on the object during serialization. It can be
inherited, or defined in the class itself, like any other method.
Arguments: I is the object to serialize, I is a flag indicating
whether we're in a dclone() or a regular serialization via store() or freeze().
Returned value: A LIST C<($serialized, $ref1, $ref2, ...)> where $serialized is
the serialized form to be used, and the optional $ref1, $ref2, etc... are extra
references that you wish to let the Storable engine serialize.
At de-serialization time, you will be given back the same LIST, but all the
extra references will be pointing into the de-serialized structure.
The B the hook is hit in a serialization flow, you may have it
return an empty list. That will signal the Storable engine to further discard
that hook for this class and to therefore revert to the default serialization of
the underlying Perl data. The hook will again be normally processed in the next
serialization.
Unless you know better, serializing hook should always say:
sub STORABLE_freeze {
my ($self, $cloning) = @_;
return if $cloning; # Regular default serialization
....
}
in order to keep reasonable dclone() semantics.
=item C I, I, I, ...
The deserializing hook called on the object during deserialization.
But wait. If we're deserializing, there's no object yet... right?
Wrong: the Storable engine creates an empty one for you. If you know Eiffel,
you can view C as an alternate creation routine.
This means the hook can be inherited like any other method, and that
I is your blessed reference for this particular instance.
The other arguments should look familiar if you know C:
I is true when we're part of a deep clone operation, I is
the serialized string you returned to the engine in C, and
there may be an optional list of references, in the same order you gave them at
serialization time, pointing to the de-serialized objects (which have been
processed courtesy of the Storable engine). It is up to you to use these
information to populate I the way you want.
Returned value: none.
=back
=head2 Predicates
Predicates are not exportable. They must be called by explicitely prefixing
them with the Storable package name.
=over
=item C
The C predicate will tell you whether
network order was used in the last store or retrieve operation. If you
don't know how to use this, just forget about it.
=item C
Returns true if within a store operation (via STORABLE_freeze hook).
=item C
Returns true if within a retrieve operation, (via STORABLE_thaw hook).
=back
=head2 Recursion
With hooks comes the ability to recurse back to the Storable engine. Indeed,
hooks are regular Perl code, and Storable is convenient when it comes to
serialize and de-serialize things, so why not use it to handle the serialization
string?
There are a few things you need to know however:
=over
=item *
You can create endless loops if the things you serialize via freeze()
(for instance) point back to the object we're trying to serialize in the hook.
=item *
Shared references among objects will not stay shared: if we're serializing the
list of object [A, C] where both object A and C refer to the SAME object B, and
if there is a serializing hook in A that says freeze(B), then when
de-serializing, we'll get [A', C'] where A' refers to B', but C' refers to D, a
deep clone of B'. The topology was not preserved.
=back
That's why C lets you provide a list of references
to serialize. The engine guarantees that those will be serialized in the same
context as the other objects, and therefore that shared objects will stay
shared.
In the above [A, C] example, the C hook could return:
("something", $self->{B})
and the B part would be serialized by the engine. In C, you
would get back the reference to the B' object, deserialized for you.
Therefore, recursion should normally be avoided, but is nonetheless supported.
=head2 Deep Cloning
There is a new Clone module available on CPAN which implements deep cloning
natively, i.e. without freezing to memory and thawing the result. It is aimed
to replace Storable's dclone() some day. However, it does not currently support
Storable hooks to redefine the way deep cloning is performed.
=head1 EXAMPLES
Here are some code samples showing a possible usage of Storable:
use Storable qw(store retrieve freeze thaw dclone);
%color = ('Blue' => 0.1, 'Red' => 0.8, 'Black' => 0, 'White' => 1);
store(\%color, '/tmp/colors') or die "Can't store %a in /tmp/colors!\n";
$colref = retrieve('/tmp/colors');
die "Unable to retrieve from /tmp/colors!\n" unless defined $colref;
printf "Blue is still %lf\n", $colref->{'Blue'};
$colref2 = dclone(\%color);
$str = freeze(\%color);
printf "Serialization of %%color is %d bytes long.\n", length($str);
$colref3 = thaw($str);
which prints (on my machine):
Blue is still 0.100000
Serialization of %color is 102 bytes long.
=head1 WARNING
If you're using references as keys within your hash tables, you're bound to
disappointment when retrieving your data. Indeed, Perl converts to string
references used as hash table keys. If you later wish to access the items via
another reference stringification (i.e. using the same reference that was used
for the key originally to record the value into the hash table), it will work
because both references stringify to the same string.
It won't work across a C and C operations however, because the
addresses in the retrieved objects, which are part of the stringified
references, will probably differ from the original addresses. The topology of
your structure is preserved, but not hidden semantics like those.
On platforms where it matters, be sure to call C on the
descriptors that you pass to Storable functions.
Storing data canonically that contains large hashes can be
significantly slower than storing the same data normally, as
temporary arrays to hold the keys for each hash have to be allocated,
populated, sorted and freed. Some tests have shown a halving of the
speed of storing -- the exact penalty will depend on the complexity of
your data. There is no slowdown on retrieval.
=head1 BUGS
You can't store GLOB, CODE, FORMLINE, etc... If you can define
semantics for those operations, feel free to enhance Storable so that
it can deal with them.
The store functions will C if they run into such references
unless you set C<$Storable::forgive_me> to some C value. In that
case, the fatal message is turned in a warning and some
meaningless string is stored instead.
Setting C<$Storable::canonical> may not yield frozen strings that
compare equal due to possible stringification of numbers. When the
string version of a scalar exists, it is the form stored, therefore
if you happen to use your numbers as strings between two freezing
operations on the same data structures, you will get different
results.
Due to the aforementioned optimizations, Storable is at the mercy
of perl's internal redesign or structure changes. If that bothers
you, you can try convincing Larry that what is used in Storable
should be documented and consistently kept in future revisions.
=head1 CREDITS
Thank you to (in chronological order):
Jarkko Hietaniemi
Ulrich Pfeifer
Benjamin A. Holzman
Andrew Ford
Gisle Aas
Jeff Gresham
Murray Nesbitt
Marc Lehmann
for their bug reports, suggestions and contributions.
Benjamin Holzman contributed the tied variable support, Andrew Ford
contributed the canonical order for hashes, and Gisle Aas fixed
a few misunderstandings of mine regarding the Perl internals,
and optimized the emission of "tags" in the output streams by
simply counting the objects instead of tagging them (leading to
a binary incompatibility for the Storable image starting at version
0.6--older images are of course still properly understood).
Murray Nesbitt made Storable thread-safe. Marc Lehmann added overloading
and reference to tied items support.
=head1 TRANSLATIONS
There is a Japanese translation of this man page available at
http://member.nifty.ne.jp/hippo2000/perltips/storable.htm ,
courtesy of Kawai, Takanori .
=head1 AUTHOR
Raphael Manfredi FRaphael_Manfredi@pobox.comE>
=head1 SEE ALSO
Clone(3).
=cut
# $Id: Makefile.PL,v 0.7 2000/08/03 22:04:44 ram Exp $
#
# Copyright (c) 1995-2000, Raphael Manfredi
#
# You may redistribute only under the terms of the Artistic License,
# as specified in the README file that comes with the distribution.
#
# $Log: Makefile.PL,v $
# Revision 0.7 2000/08/03 22:04:44 ram
# Baseline for second beta release.
#
use ExtUtils::MakeMaker;
use Config;
WriteMakefile(
'NAME' => 'Storable',
'DISTNAME' => "Storable",
'VERSION_FROM' => 'Storable.pm',
'dist' => { SUFFIX => 'gz', COMPRESS => 'gzip -f' },
'clean' => {'FILES' => '*%'},
);
/*
* Store and retrieve mechanism.
*/
/*
* $Id: Storable.xs,v 0.7.1.2 2000/08/14 07:19:27 ram Exp $
*
* Copyright (c) 1995-2000, Raphael Manfredi
*
* You may redistribute only under the terms of the Artistic License,
* as specified in the README file that comes with the distribution.
*
* $Log: Storable.xs,v $
* Revision 0.7.1.2 2000/08/14 07:19:27 ram
* patch2: added a refcnt dec in retrieve_tied_key()
*
* Revision 0.7.1.1 2000/08/13 20:10:06 ram
* patch1: was wrongly optimizing for "undef" values in hashes
* patch1: added support for ref to tied items in hash/array
* patch1: added overloading support
*
* Revision 0.7 2000/08/03 22:04:44 ram
* Baseline for second beta release.
*
*/
#include
#include
#include /* Perl's one, needed since 5.6 */
#include
/*#define DEBUGME /* Debug mode, turns assertions on as well */
/*#define DASSERT /* Assertion mode */
/*
* Pre PerlIO time when none of USE_PERLIO and PERLIO_IS_STDIO is defined
* Provide them with the necessary defines so they can build with pre-5.004.
*/
#ifndef USE_PERLIO
#ifndef PERLIO_IS_STDIO
#define PerlIO FILE
#define PerlIO_getc(x) getc(x)
#define PerlIO_putc(f,x) putc(x,f)
#define PerlIO_read(x,y,z) fread(y,1,z,x)
#define PerlIO_write(x,y,z) fwrite(y,1,z,x)
#define PerlIO_stdoutf printf
#endif /* PERLIO_IS_STDIO */
#endif /* USE_PERLIO */
/*
* Earlier versions of perl might be used, we can't assume they have the latest!
*/
#ifndef newRV_noinc
#define newRV_noinc(sv) ((Sv = newRV(sv)), --SvREFCNT(SvRV(Sv)), Sv)
#endif
#if (PATCHLEVEL <= 4) /* Older perls (<= 5.004) lack PL_ namespace */
#define PL_sv_yes sv_yes
#define PL_sv_no sv_no
#define PL_sv_undef sv_undef
#endif
#ifndef HvSHAREKEYS_off
#define HvSHAREKEYS_off(hv) /* Ignore */
#endif
#ifdef DEBUGME
#ifndef DASSERT
#define DASSERT
#endif
#define TRACEME(x) do { PerlIO_stdoutf x; PerlIO_stdoutf("\n"); } while (0)
#else
#define TRACEME(x)
#endif
#ifdef DASSERT
#define ASSERT(x,y) do { \
if (!(x)) {
\
PerlIO_stdoutf("ASSERT FAILED (\"%s\", line %d): ", \
__FILE__, __LINE__); \
PerlIO_stdoutf y; PerlIO_stdoutf("\n"); \
}
\
} while (0)
#else
#define ASSERT(x,y)
#endif
/*
* Type markers.
*/
#define C(x) ((char) (x)) /* For markers with dynamic retrieval handling */
#define SX_OBJECT C(0) /* Already stored object */
#define SX_LSCALAR C(1) /* Scalar (string) forthcoming (length, data)
*/
#define SX_ARRAY C(2) /* Array forthcoming (size, item list) */
#define SX_HASH C(3) /* Hash forthcoming (size, key/value pair
list) */
#define SX_REF C(4) /* Reference to object forthcoming */
#define SX_UNDEF C(5) /* Undefined scalar */
#define SX_INTEGER C(6) /* Integer forthcoming */
#define SX_DOUBLE C(7) /* Double forthcoming */
#define SX_BYTE C(8) /* (signed) byte forthcoming */
#define SX_NETINT C(9) /* Integer in network order forthcoming */
#define SX_SCALAR C(10) /* Scalar (small) forthcoming (length, data)
*/
#define SX_TIED_ARRAY C(11) /* Tied array forthcoming */
#define SX_TIED_HASH C(12) /* Tied hash forthcoming */
#define SX_TIED_SCALAR C(13) /* Tied scalar forthcoming */
#define SX_SV_UNDEF C(14) /* Perl's immortal PL_sv_undef */
#define SX_SV_YES C(15) /* Perl's immortal PL_sv_yes */
#define SX_SV_NO C(16) /* Perl's immortal PL_sv_no */
#define SX_BLESS C(17) /* Object is blessed */
#define SX_IX_BLESS C(18) /* Object is blessed, classname given by
index */
#define SX_HOOK C(19) /* Stored via hook, user-defined */
#define SX_OVERLOAD C(20) /* Overloaded reference */
#define SX_TIED_KEY C(21) /* Tied magic key forthcoming */
#define SX_TIED_IDX C(22) /* Tied magic index forthcoming */
#define SX_ERROR C(23) /* Error */
/*
* Those are only used to retrieve "old" pre-0.6 binary images.
*/
#define SX_ITEM 'i' /* An array item introducer */
#define SX_IT_UNDEF 'I' /* Undefined array item */
#define SX_KEY 'k' /* An hash key introducer */
#define SX_VALUE 'v' /* An hash value introducer */
#define SX_VL_UNDEF 'V' /* Undefined hash value */
/*
* Those are only used to retrieve "old" pre-0.7 binary images
*/
#define SX_CLASS 'b' /* Object is blessed, class name length <255
*/
#define SX_LG_CLASS 'B' /* Object is blessed, class name length >255 */
#define SX_STORED 'X' /* End of object */
/*
* Limits between short/long length representation.
*/
#define LG_SCALAR 255 /* Large scalar length limit */
#define LG_BLESS 127 /* Large classname bless limit */
/*
* Operation types
*/
#define ST_STORE 0x1 /* Store operation */
#define ST_RETRIEVE 0x2 /* Retrieval operation */
#define ST_CLONE 0x4 /* Deep cloning operation */
/*
* The following structure is used for hash table key retrieval. Since, when
* retrieving objects, we'll be facing blessed hash references, it's best
* to pre-allocate that buffer once and resize it as the need arises, never
* freeing it (keys will be saved away someplace else anyway, so even large
* keys are not enough a motivation to reclaim that space).
*
* This structure is also used for memory store/retrieve operations which
* happen in a fixed place before being malloc'ed elsewhere if persistency
* is required. Hence the aptr pointer.
*/
struct extendable {
char *arena; /* Will hold hash key strings, resized as needed */
STRLEN asiz; /* Size of aforementionned buffer */
char *aptr; /* Arena pointer, for in-place read/write ops
*/
char *aend; /* First invalid address */
};
/*
* At store time:
* An hash table records the objects which have already been stored.
* Those are referred to as SX_OBJECT in the file, and their "tag" (i.e.
* an arbitrary sequence number) is used to identify them.
*
* At retrieve time:
* An array table records the objects which have already been retrieved,
* as seen by the tag determind by counting the objects themselves. The
* reference to that retrieved object is kept in the table, and is returned
* when an SX_OBJECT is found bearing that same tag.
*
* The same processing is used to record "classname" for blessed objects:
* indexing by a hash at store time, and via an array at retrieve time.
*/
typedef unsigned long stag_t; /* Used by pre-0.6 binary format */
/*
* The following "thread-safe" related defines were contributed by
* Murray Nesbitt and integrated by RAM, who
* only renamed things a little bit to ensure consistency with surrounding
* code. -- RAM, 14/09/1999
*
* The original patch suffered from the fact that the stcxt_t structure
* was global. Murray tried to minimize the impact on the code as much as
* possible.
*
* Starting with 0.7, Storable can be re-entrant, via the STORABLE_xxx hooks
* on objects. Therefore, the notion of context needs to be generalized,
* threading or not.
*/
#define MY_VERSION "Storable(" XS_VERSION ")"
typedef struct stcxt {
int entry; /* flags recursion */
int optype; /* type of traversal operation */
HV *hseen; /* which objects have been seen, store time */
AV *aseen; /* which objects have been seen, retrieve time */
HV *hclass; /* which classnames have been seen, store time */
AV *aclass; /* which classnames have been seen, retrieve time
*/
HV *hook; /* cache for hook methods per class name */
I32 tagnum; /* incremented at store time for each seen
object */
I32 classnum; /* incremented at store time for each seen classname
*/
int netorder; /* true if network order used */
int forgive_me; /* whether to be forgiving... */
int canonical; /* whether to store hashes sorted by key */
int dirty; /* context is dirty due to CROAK() -- can be
cleaned */
struct extendable keybuf; /* for hash key retrieval */
struct extendable membuf; /* for memory store/retrieve operations */
PerlIO *fio; /* where I/O are performed, NULL for memory */
int ver_major; /* major of version for retrieved object */
int ver_minor; /* minor of version for retrieved object */
SV *(**retrieve_vtbl)(); /* retrieve dispatch table */
struct stcxt *prev; /* contexts chained backwards in real recursion
*/
} stcxt_t;
#if defined(MULTIPLICITY) || defined(PERL_OBJECT) || defined(PERL_CAPI)
#if (PATCHLEVEL <= 4) && (SUBVERSION < 68)
#define dSTCXT_SV \
SV *perinterp_sv = perl_get_sv(MY_VERSION, FALSE)
#else /* >= perl5.004_68 */
#define dSTCXT_SV \
SV *perinterp_sv = *hv_fetch(PL_modglobal, \
MY_VERSION, sizeof(MY_VERSION)-1, TRUE)
#endif /* < perl5.004_68 */
#define dSTCXT_PTR(T,name) \
T name = (T)(perinterp_sv && SvIOK(perinterp_sv)\
? SvIVX(perinterp_sv) : NULL)
#define dSTCXT \
dSTCXT_SV; \
dSTCXT_PTR(stcxt_t *, cxt)
#define INIT_STCXT \
dSTCXT; \
Newz(0, cxt, 1, stcxt_t); \
sv_setiv(perinterp_sv, (IV) cxt)
#define SET_STCXT(x) do { \
dSTCXT_SV; \
sv_setiv(perinterp_sv, (IV) (x)); \
} while (0)
#else /* !MULTIPLICITY && !PERL_OBJECT && !PERL_CAPI */
static stcxt_t Context;
static stcxt_t *Context_ptr = &Context;
#define dSTCXT stcxt_t *cxt = Context_ptr
#define INIT_STCXT dSTCXT
#define SET_STCXT(x) Context_ptr = x
#endif /* MULTIPLICITY || PERL_OBJECT || PERL_CAPI */
/*
* KNOWN BUG:
* Croaking implies a memory leak, since we don't use setjmp/longjmp
* to catch the exit and free memory used during store or retrieve
* operations. This is not too difficult to fix, but I need to understand
* how Perl does it, and croaking is exceptional anyway, so I lack the
* motivation to do it.
*
* The current workaround is to mark the context as dirty when croaking,
* so that data structures can be freed whenever we renter Storable code
* (but only *then*: it's a workaround, not a fix).
*
* This is also imperfect, because we don't really know how far they trapped
* the croak(), and when we were recursing, we won't be able to clean anything
* but the topmost context stacked.
*/
#define CROAK(x) do { cxt->dirty = 1; croak x; } while (0)
/*
* End of "thread-safe" related definitions.
*/
/*
* key buffer handling
*/
#define kbuf (cxt->keybuf).arena
#define ksiz (cxt->keybuf).asiz
#define KBUFINIT() do { \
if (!kbuf) { \
TRACEME(("** allocating kbuf of 128 bytes")); \
New(10003, kbuf, 128, char); \
ksiz = 128; \
} \
} while (0)
#define KBUFCHK(x) do { \
if (x >= ksiz) { \
TRACEME(("** extending kbuf to %d bytes", x+1)); \
Renew(kbuf, x+1, char); \
ksiz = x+1; \
} \
} while (0)
/*
* memory buffer handling
*/
#define mbase (cxt->membuf).arena
#define msiz (cxt->membuf).asiz
#define mptr (cxt->membuf).aptr
#define mend (cxt->membuf).aend
#define MGROW (1 << 13)
#define MMASK (MGROW - 1)
#define round_mgrow(x) \
((unsigned long) (((unsigned long) (x) + MMASK) & ~MMASK))
#define trunc_int(x) \
((unsigned long) ((unsigned long) (x) & ~(sizeof(int)-1)))
#define int_aligned(x) \
((unsigned long) (x) == trunc_int(x))
#define MBUF_INIT(x) do { \
if (!mbase) { \
TRACEME(("** allocating mbase of %d bytes", MGROW)); \
New(10003, mbase, MGROW, char); \
msiz = MGROW; \
} \
mptr = mbase; \
if (x) \
mend = mbase + x; \
else \
mend = mbase + msiz; \
} while (0)
#define MBUF_TRUNC(x) mptr = mbase + x
#define MBUF_SIZE() (mptr - mbase)
/*
* Use SvPOKp(), because SvPOK() fails on tainted scalars.
* See store_scalar() for other usage of this workaround.
*/
#define MBUF_LOAD(v) do { \
if (!SvPOKp(v)) \
CROAK(("Not a scalar string")); \
mptr = mbase = SvPV(v, msiz); \
mend = mbase + msiz; \
} while (0)
#define MBUF_XTEND(x) do { \
int nsz = (int) round_mgrow((x)+msiz); \
int offset = mptr - mbase; \
TRACEME(("** extending mbase to %d bytes", nsz)); \
Renew(mbase, nsz, char); \
msiz = nsz; \
mptr = mbase + offset; \
mend = mbase + nsz; \
} while (0)
#define MBUF_CHK(x) do { \
if ((mptr + (x)) > mend) \
MBUF_XTEND(x); \
} while (0)
#define MBUF_GETC(x) do { \
if (mptr < mend) \
x = (int) (unsigned char) *mptr++; \
else \
return (SV *) 0; \
} while (0)
#define MBUF_GETINT(x) do { \
if ((mptr + sizeof(int)) <= mend) { \
if (int_aligned(mptr)) \
x = *(int *) mptr; \
else \
memcpy(&x, mptr, sizeof(int)); \
mptr += sizeof(int); \
} else \
return (SV *) 0; \
} while (0)
#define MBUF_READ(x,s) do { \
if ((mptr + (s)) <= mend) { \
memcpy(x, mptr, s); \
mptr += s; \
} else \
return (SV *) 0; \
} while (0)
#define MBUF_SAFEREAD(x,s,z) do { \
if ((mptr + (s)) <= mend) { \
memcpy(x, mptr, s); \
mptr += s; \
} else { \
sv_free(z); \
return (SV *) 0; \
} \
} while (0)
#define MBUF_PUTC(c) do { \
if (mptr < mend) \
*mptr++ = (char) c; \
else { \
MBUF_XTEND(1); \
*mptr++ = (char) c; \
} \
} while (0)
#define MBUF_PUTINT(i) do { \
MBUF_CHK(sizeof(int)); \
if (int_aligned(mptr)) \
*(int *) mptr = i; \
else \
memcpy(mptr, &i, sizeof(int)); \
mptr += sizeof(int); \
} while (0)
#define MBUF_WRITE(x,s) do { \
MBUF_CHK(s); \
memcpy(mptr, x, s); \
mptr += s; \
} while (0)
/*
* LOW_32BITS
*
* Keep only the low 32 bits of a pointer (used for tags, which are not
* really pointers).
*/
#if PTRSIZE <= 4
#define LOW_32BITS(x) ((I32) (x))
#else
#if BYTEORDER == 0x4321
#define LOW_32BITS(x) ((I32) ((unsigned long) (x) & 0xffffffff))
#else
#define LOW_32BITS(x) ((I32) ((unsigned long) (x) & 0xffffffff00000000L))
#endif
#endif
/*
* Possible return values for sv_type().
*/
#define svis_REF 0
#define svis_SCALAR 1
#define svis_ARRAY 2
#define svis_HASH 3
#define svis_TIED 4
#define svis_TIED_ITEM 5
#define svis_OTHER 6
/*
* Flags for SX_HOOK.
*/
#define SHF_TYPE_MASK 0x03
#define SHF_LARGE_CLASSLEN 0x04
#define SHF_LARGE_STRLEN 0x08
#define SHF_LARGE_LISTLEN 0x10
#define SHF_IDX_CLASSNAME 0x20
#define SHF_NEED_RECURSE 0x40
#define SHF_HAS_LIST 0x80
/*
* Types for SX_HOOK (2 bits).
*/
#define SHT_SCALAR 0
#define SHT_ARRAY 1
#define SHT_HASH 2
/*
* Before 0.6, the magic string was "perl-store" (binary version number 0).
*
* Since 0.6 introduced many binary incompatibilities, the magic string has
* been changed to "pst0" to allow an old image to be properly retrieved by
* a newer Storable, but ensure a newer image cannot be retrieved with an
* older version.
*
* At 0.7, objects are given the ability to serialize themselves, and the
* set of markers is extended, backward compatibility is not jeopardized,
* so the binary version number could have remained unchanged. To correctly
* spot errors if a file making use of 0.7-specific extensions is given to
* 0.6 for retrieval, the binary version was moved to "2". And I'm introducing
* a "minor" version, to better track this kind of evolution from now on.
*
*/
static char old_magicstr[] = "perl-store"; /* Magic number before 0.6 */
static char magicstr[] = "pst0"; /* Used as a magic number */
#define STORABLE_BIN_MAJOR 2 /* Binary major
"version" */
#define STORABLE_BIN_MINOR 1 /* Binary minor
"version" */
/*
* Useful store shortcuts...
*/
#define PUTMARK(x) do { \
if (!cxt->fio) \
MBUF_PUTC(x); \
else if (PerlIO_putc(cxt->fio, x) == EOF) \
return -1; \
} while (0)
#ifdef HAS_HTONL
#define WLEN(x) do { \
if (cxt->netorder) { \
int y = (int) htonl(x); \
if (!cxt->fio) \
MBUF_PUTINT(y); \
else if (PerlIO_write(cxt->fio, &y, sizeof(y)) != sizeof(y)) \
return -1; \
} else { \
if (!cxt->fio) \
MBUF_PUTINT(x); \
else if (PerlIO_write(cxt->fio, &x, sizeof(x)) != sizeof(x)) \
return -1; \
} \
} while (0)
#else
#define WLEN(x) do { \
if (!cxt->fio) \
MBUF_PUTINT(x); \
else if (PerlIO_write(cxt->fio, &x, sizeof(x)) != sizeof(x)) \
return -1; \
} while (0)
#endif
#define WRITE(x,y) do { \
if (!cxt->fio) \
MBUF_WRITE(x,y); \
else if (PerlIO_write(cxt->fio, x, y) != y) \
return -1; \
} while (0)
#define STORE_SCALAR(pv, len) do { \
if (len <= LG_SCALAR) { \
unsigned char clen = (unsigned char) len; \
PUTMARK(SX_SCALAR); \
PUTMARK(clen); \
if (len) \
WRITE(pv, len); \
} else { \
PUTMARK(SX_LSCALAR); \
WLEN(len); \
WRITE(pv, len); \
} \
} while (0)
/*
* Store undef in arrays and hashes without recursing through store().
*/
#define STORE_UNDEF() do { \
cxt->tagnum++; \
PUTMARK(SX_UNDEF); \
} while (0)
/*
* Useful retrieve shortcuts...
*/
#define GETCHAR() \
(cxt->fio ? PerlIO_getc(cxt->fio) : (mptr >= mend ? EOF : (int) *mptr++))
#define GETMARK(x) do { \
if (!cxt->fio) \
MBUF_GETC(x); \
else if ((x = PerlIO_getc(cxt->fio)) == EOF) \
return (SV *) 0; \
} while (0)
#ifdef HAS_NTOHL
#define RLEN(x) do { \
if (!cxt->fio) \
MBUF_GETINT(x); \
else if (PerlIO_read(cxt->fio, &x, sizeof(x)) != sizeof(x)) \
return (SV *) 0; \
if (cxt->netorder) \
x = (int) ntohl(x); \
} while (0)
#else
#define RLEN(x) do { \
if (!cxt->fio) \
MBUF_GETINT(x); \
else if (PerlIO_read(cxt->fio, &x, sizeof(x)) != sizeof(x)) \
return (SV *) 0; \
} while (0)
#endif
#define READ(x,y) do { \
if (!cxt->fio) \
MBUF_READ(x, y); \
else if (PerlIO_read(cxt->fio, x, y) != y) \
return (SV *) 0; \
} while (0)
#define SAFEREAD(x,y,z) do { \
if (!cxt->fio) \
MBUF_SAFEREAD(x,y,z); \
else if (PerlIO_read(cxt->fio, x, y) != y) { \
sv_free(z); \
return (SV *) 0; \
} \
} while (0)
/*
* This macro is used at retrieve time, to remember where object 'y', bearing a
* given tag 'tagnum', has been retrieved. Next time we see an SX_OBJECT marker,
* we'll therefore know where it has been retrieved and will be able to
* share the same reference, as in the original stored memory image.
*/
#define SEEN(y) do { \
if (!y) \
return (SV *) 0; \
if (av_store(cxt->aseen, cxt->tagnum++, SvREFCNT_inc(y)) == 0) \
return (SV *) 0; \
TRACEME(("aseen(#%d) = 0x%lx (refcnt=%d)", cxt->tagnum-1, \
(unsigned long) y, SvREFCNT(y)-1)); \
} while (0)
/*
* Bless `s' in `p', via a temporary reference, required by sv_bless().
*/
#define BLESS(s,p) do { \
SV *ref; \
HV *stash; \
TRACEME(("blessing 0x%lx in %s", (unsigned long) (s), (p))); \
stash = gv_stashpv((p), TRUE); \
ref = newRV_noinc(s); \
(void) sv_bless(ref, stash); \
SvRV(ref) = 0; \
SvREFCNT_dec(ref); \
} while (0)
static int store();
static SV *retrieve();
static int (*sv_store[])();
#define SV_STORE(x) (*sv_store[x])
static SV *(*sv_old_retrieve[])();
static SV *(*sv_retrieve[])();
static SV *mbuf2sv();
/***
*** Context management.
***/
/*
* init_perinterp
*
* Called once per "thread" (interpreter) to initialize some global context.
*/
static void init_perinterp() {
INIT_STCXT;
cxt->netorder = 0; /* true if network order used */
cxt->forgive_me = -1; /* whether to be forgiving... */
}
/*
* init_store_context
*
* Initialize a new store context for real recursion.
*/
static void init_store_context(cxt, f, optype, network_order)
stcxt_t *cxt;
PerlIO *f;
int optype;
int network_order;
{
TRACEME(("init_store_context"));
cxt->netorder = network_order;
cxt->forgive_me = -1; /* Fetched from perl if needed */
cxt->canonical = -1; /* Idem */
cxt->tagnum = -1; /* Reset tag numbers */
cxt->classnum = -1; /* Reset class numbers */
cxt->fio = f; /* Where I/O are performed */
cxt->optype = optype; /* A store, or a deep clone */
cxt->entry = 1; /* No recursion yet */
/*
* The `hseen' table is used to keep track of each SV stored and their
* associated tag numbers is special. It is "abused" because the
* values stored are not real SV, just integers cast to (SV *),
* which explains the freeing below.
*
* It is also one possible bottlneck to achieve good storing speed,
* so the "shared keys" optimization is turned off (unlikely to be
* of any use here), and the hash table is "pre-extended". Together,
* those optimizations increase the throughput by 12%.
*/
cxt->hseen = newHV(); /* Table where seen objects are
stored */
HvSHAREKEYS_off(cxt->hseen);
/*
* The following does not work well with perl5.004_04, and causes
* a core dump later on, in a completely unrelated spot, which
* makes me think there is a memory corruption going on.
*
* Calling hv_ksplit(hseen, HBUCKETS) instead of manually hacking
* it below does not make any difference. It seems to work fine
* with perl5.004_68 but given the probable nature of the bug,
* that does not prove anything.
*
* It's a shame because increasing the amount of buckets raises
* store() throughput by 5%, but until I figure this out, I can't
* allow for this to go into production.
*
* It is reported fixed in 5.005, hence the #if.
*/
#if PATCHLEVEL < 5
#define HBUCKETS 4096 /* Buckets for %hseen */
HvMAX(cxt->hseen) = HBUCKETS - 1; /* keys %hseen = $HBUCKETS; */
#endif
/*
* The `hclass' hash uses the same settings as `hseen' above, but it is
* used to assign sequential tags (numbers) to class names for blessed
* objects.
*
* We turn the shared key optimization on.
*/
cxt->hclass = newHV(); /* Where seen classnames are stored
*/
#if PATCHLEVEL < 5
HvMAX(cxt->hclass) = HBUCKETS - 1; /* keys %hclass = $HBUCKETS; */
#endif
/*
* The `hook' hash table is used to keep track of the references on
* the STORABLE_freeze hook routines, when found in some class name.
*
* It is assumed that the inheritance tree will not be changed during
* storing, and that no new method will be dynamically created by the
* hooks.
*/
cxt->hook = newHV(); /* Table where hooks are cached */
}
/*
* clean_store_context
*
* Clean store context by
*/
static void clean_store_context(cxt)
stcxt_t *cxt;
{
HE *he;
TRACEME(("clean_store_context"));
ASSERT(cxt->optype & ST_STORE, ("was performing a store()"));
/*
* Insert real values into hashes where we stored faked pointers.
*/
hv_iterinit(cxt->hseen);
while (he = hv_iternext(cxt->hseen))
HeVAL(he) = &PL_sv_undef;
hv_iterinit(cxt->hclass);
while (he = hv_iternext(cxt->hclass))
HeVAL(he) = &PL_sv_undef;
/*
* And now dispose of them...
*/
hv_undef(cxt->hseen);
sv_free((SV *) cxt->hseen);
hv_undef(cxt->hclass);
sv_free((SV *) cxt->hclass);
hv_undef(cxt->hook);
sv_free((SV *) cxt->hook);
cxt->entry = 0;
cxt->dirty = 0;
}
/*
* init_retrieve_context
*
* Initialize a new retrieve context for real recursion.
*/
static void init_retrieve_context(cxt, optype)
stcxt_t *cxt;
int optype;
{
TRACEME(("init_retrieve_context"));
/*
* The hook hash table is used to keep track of the references on
* the STORABLE_thaw hook routines, when found in some class name.
*
* It is assumed that the inheritance tree will not be changed during
* storing, and that no new method will be dynamically created by the
* hooks.
*/
cxt->hook = newHV(); /* Caches STORABLE_thaw */
/*
* If retrieving an old binary version, the cxt->retrieve_vtbl variable
* was set to sv_old_retrieve. We'll need a hash table to keep track of
* the correspondance between the tags and the tag number used by the
* new retrieve routines.
*/
cxt->hseen = (cxt->retrieve_vtbl == sv_old_retrieve) ? newHV() : 0;
cxt->aseen = newAV(); /* Where retrieved objects are kept
*/
cxt->aclass = newAV(); /* Where seen classnames are kept */
cxt->tagnum = 0; /* Have to count objects... */
cxt->classnum = 0; /* ...and class names as well */
cxt->optype = optype;
cxt->entry = 1; /* No recursion yet */
}
/*
* clean_retrieve_context
*
* Clean retrieve context by
*/
static void clean_retrieve_context(cxt)
stcxt_t *cxt;
{
TRACEME(("clean_retrieve_context"));
ASSERT(cxt->optype & ST_RETRIEVE, ("was performing a retrieve()"));
av_undef(cxt->aseen);
sv_free((SV *) cxt->aseen);
av_undef(cxt->aclass);
sv_free((SV *) cxt->aclass);
hv_undef(cxt->hook);
sv_free((SV *) cxt->hook);
if (cxt->hseen)
sv_free((SV *) cxt->hseen); /* optional HV, for backward
compat. */
cxt->entry = 0;
cxt->dirty = 0;
}
/*
* clean_context
*
* A workaround for the CROAK bug: cleanup the last context.
*/
static void clean_context(cxt)
stcxt_t *cxt;
{
TRACEME(("clean_context"));
ASSERT(cxt->dirty, ("dirty context"));
if (cxt->optype & ST_RETRIEVE)
clean_retrieve_context(cxt);
else
clean_store_context(cxt);
}
/*
* allocate_context
*
* Allocate a new context and push it on top of the parent one.
* This new context is made globally visible via SET_STCXT().
*/
static stcxt_t *allocate_context(parent_cxt)
stcxt_t *parent_cxt;
{
stcxt_t *cxt;
TRACEME(("allocate_context"));
ASSERT(!parent_cxt->dirty, ("parent context clean"));
Newz(0, cxt, 1, stcxt_t);
cxt->prev = parent_cxt;
SET_STCXT(cxt);
return cxt;
}
/*
* free_context
*
* Free current context, which cannot be the "root" one.
* Make the context underneath globally visible via SET_STCXT().
*/
static void free_context(cxt)
stcxt_t *cxt;
{
stcxt_t *prev = cxt->prev;
TRACEME(("free_context"));
ASSERT(!cxt->dirty, ("clean context"));
ASSERT(prev, ("not freeing root context"));
if (kbuf)
Safefree(kbuf);
if (mbase)
Safefree(mbase);
Safefree(cxt);
SET_STCXT(prev);
}
/***
*** Predicates.
***/
/*
* is_storing
*
* Tells whether we're in the middle of a store operation.
*/
int is_storing()
{
dSTCXT;
return cxt->entry && (cxt->optype & ST_STORE);
}
/*
* is_retrieving
*
* Tells whether we're in the middle of a retrieve operation.
*/
int is_retrieving()
{
dSTCXT;
return cxt->entry && (cxt->optype & ST_RETRIEVE);
}
/*
* last_op_in_netorder
*
* Returns whether last operation was made using network order.
*
* This is typically out-of-band information that might prove useful
* to people wishing to convert native to network order data when used.
*/
int last_op_in_netorder()
{
dSTCXT;
return cxt->netorder;
}
/***
*** Hook lookup and calling routines.
***/
/*
* pkg_fetchmeth
*
* A wrapper on gv_fetchmethod_autoload() which caches results.
*
* Returns the routine reference as an SV*, or null if neither the package
* nor its ancestors know about the method.
*/
static SV *pkg_fetchmeth(cache, pkg, method)
HV *cache;
HV *pkg;
char *method;
{
GV *gv;
SV *sv;
SV **svh;
/*
* The following code is the same as the one performed by UNIVERSAL::can
* in the Perl core.
*/
gv = gv_fetchmethod_autoload(pkg, method, FALSE);
if (gv && isGV(gv)) {
sv = newRV((SV*) GvCV(gv));
TRACEME(("%s->%s: 0x%lx", HvNAME(pkg), method, (unsigned long) sv));
} else {
sv = newSVsv(&PL_sv_undef);
TRACEME(("%s->%s: not found", HvNAME(pkg), method));
}
/*
* Cache the result, ignoring failure: if we can't store the value,
* it just won't be cached.
*/
(void) hv_store(cache, HvNAME(pkg), strlen(HvNAME(pkg)), sv, 0);
return SvOK(sv) ? sv : (SV *) 0;
}
/*
* pkg_hide
*
* Force cached value to be undef: hook ignored even if present.
*/
static void pkg_hide(cache, pkg, method)
HV *cache;
HV *pkg;
char *method;
{
(void) hv_store(cache,
HvNAME(pkg), strlen(HvNAME(pkg)), newSVsv(&PL_sv_undef), 0);
}
/*
* pkg_can
*
* Our own "UNIVERSAL::can", which caches results.
*
* Returns the routine reference as an SV*, or null if the object does not
* know about the method.
*/
static SV *pkg_can(cache, pkg, method)
HV *cache;
HV *pkg;
char *method;
{
SV **svh;
SV *sv;
TRACEME(("pkg_can for %s->%s", HvNAME(pkg), method));
/*
* Look into the cache to see whether we already have determined
* where the routine was, if any.
*
* NOTA BENE: we don't use `method' at all in our lookup, since we know
* that only one hook (i.e. always the same) is cached in a given cache.
*/
svh = hv_fetch(cache, HvNAME(pkg), strlen(HvNAME(pkg)), FALSE);
if (svh) {
sv = *svh;
if (!SvOK(sv)) {
TRACEME(("cached %s->%s: not found", HvNAME(pkg), method));
return (SV *) 0;
} else {
TRACEME(("cached %s->%s: 0x%lx", HvNAME(pkg), method,
(unsigned long) sv));
return sv;
}
}
TRACEME(("not cached yet"));
return pkg_fetchmeth(cache, pkg, method); /* Fetch and cache */
}
/*
* scalar_call
*
* Call routine as obj->hook(av) in scalar context.
* Propagates the single returned value if not called in void context.
*/
static SV *scalar_call(obj, hook, cloning, av, flags)
SV *obj;
SV *hook;
int cloning;
AV *av;
I32 flags;
{
dSP;
int count;
SV *sv = 0;
TRACEME(("scalar_call (cloning=%d)", cloning));
ENTER;
SAVETMPS;
PUSHMARK(sp);
XPUSHs(obj);
XPUSHs(sv_2mortal(newSViv(cloning))); /* Cloning flag */
if (av) {
SV **ary = AvARRAY(av);
int cnt = AvFILLp(av) + 1;
int i;
XPUSHs(ary[0]); /* Frozen
string */
for (i = 1; i < cnt; i++) {
TRACEME(("pushing arg #%d (0x%lx)...", i, (unsigned long)
ary[i]));
XPUSHs(sv_2mortal(newRV(ary[i])));
}
}
PUTBACK;
TRACEME(("calling..."));
count = perl_call_sv(hook, flags); /* Go back to Perl code */
TRACEME(("count = %d", count));
SPAGAIN;
if (count) {
sv = POPs;
SvREFCNT_inc(sv); /* We're returning it, must stay alive! */
}
PUTBACK;
FREETMPS;
LEAVE;
return sv;
}
/*
* array_call
*
* Call routine obj->hook(cloning) in array context.
* Returns the list of returned values in an array.
*/
static AV *array_call(obj, hook, cloning)
SV *obj;
SV *hook;
int cloning;
{
dSP;
int count;
AV *av;
int i;
TRACEME(("arrary_call (cloning=%d), cloning"));
ENTER;
SAVETMPS;
PUSHMARK(sp);
XPUSHs(obj); /* Target
object */
XPUSHs(sv_2mortal(newSViv(cloning))); /* Cloning flag */
PUTBACK;
count = perl_call_sv(hook, G_ARRAY); /* Go back to Perl code */
SPAGAIN;
av = newAV();
for (i = count - 1; i >= 0; i--) {
SV *sv = POPs;
av_store(av, i, SvREFCNT_inc(sv));
}
PUTBACK;
FREETMPS;
LEAVE;
return av;
}
/*
* known_class
*
* Lookup the class name in the `hclass' table and either assign it a new ID
* or return the existing one, by filling in `classnum'.
*
* Return true if the class was known, false if the ID was just generated.
*/
static int known_class(cxt, name, len, classnum)
stcxt_t *cxt;
char *name; /* Class name */
int len; /* Name length */
I32 *classnum;
{
SV **svh;
HV *hclass = cxt->hclass;
TRACEME(("known_class (%s)", name));
/*
* Recall that we don't store pointers in this hash table, but tags.
* Therefore, we need LOW_32BITS() to extract the relevant parts.
*/
svh = hv_fetch(hclass, name, len, FALSE);
if (svh) {
*classnum = LOW_32BITS(*svh);
return TRUE;
}
/*
* Unknown classname, we need to record it.
* The (IV) cast below is for 64-bit machines, to avoid compiler warnings.
*/
cxt->classnum++;
if (!hv_store(hclass, name, len, (SV*)(IV) cxt->classnum, 0))
CROAK(("Unable to record new classname"));
*classnum = cxt->classnum;
return FALSE;
}
/***
*** Sepcific store routines.
***/
/*
* store_ref
*
* Store a reference.
* Layout is SX_REF