diff options
author | Nicholas Clark <nick@ccl4.org> | 2009-10-02 18:12:44 +0100 |
---|---|---|
committer | Nicholas Clark <nick@ccl4.org> | 2009-10-02 18:12:44 +0100 |
commit | 8b88b2b2a83b118e358c4d04557659e32d47c01b (patch) | |
tree | c696c6c5c77b90417d6640dc151bca8bd3596dc2 /ext | |
parent | 43aed010bf8b6e3fe32d5f9e8e086dda22b5b4c6 (diff) | |
download | perl-8b88b2b2a83b118e358c4d04557659e32d47c01b.tar.gz |
Move Storable from ext/ to dist/
Diffstat (limited to 'ext')
49 files changed, 0 insertions, 14224 deletions
diff --git a/ext/Storable/ChangeLog b/ext/Storable/ChangeLog deleted file mode 100644 index ea3995324a..0000000000 --- a/ext/Storable/ChangeLog +++ /dev/null @@ -1,753 +0,0 @@ -Mon May 18 09:38:20 IST 2009 Abhijit Menon-Sen <ams@toroid.org> - - Version 2.20 - - Fix bug handling blessed references to overloaded objects, plus - other miscellaneous fixes. - - (Version 2.19 was released with 5.8.9.) - -Thu Nov 22 13:24:18 IST 2007 Abhijit Menon-Sen <ams@toroid.org> - - Version 2.18 - - Compile fixes for older Perls. (No functional changes.) - -Sat Nov 17 02:12:12 IST 2007 Abhijit Menon-Sen <ams@toroid.org> - - Version 2.17 - - Various broken tests fixed. (No functional changes.) - -Sat Mar 31 06:11:06 IST 2007 Abhijit Menon-Sen <ams@toroid.org> - - Version 2.16 - - 1. Fixes to Storable::dclone, read_magic, retrieve_lscalar - 2. Storable 0.1 compatibility - 3. Miscellaneous compile/leak/test/portability fixes - -Mon May 23 22:48:49 IST 2005 Abhijit Menon-Sen <ams@wiw.org> - - Version 2.15 - - Minor changes to address a couple of compile problems. - -Mon Apr 25 07:29:14 IST 2005 Abhijit Menon-Sen <ams@wiw.org> - - Version 2.14 - - 1. Store weak references - 2. Add STORABLE_attach hook. - -Thu Jun 17 12:26:43 BST 2004 Nicholas Clark <nick@ccl4.org> - - Version 2.13 - - 1. Don't change the type of top level overloaded references to RV - - they are perfectly correct as PVMG - 2. Storable needs to cope with incoming frozen data that happens to be - utf8 encoded. - -Wed Mar 17 15:40:29 GMT 2004 Nicholas Clark <nick@ccl4.org> - - Version 2.12 - - 1. Add regression tests for the auto-require of STORABLE_thaw - 2. Add auto-require of modules to restore overloading (and tests) - 3. Change to no context (should give speedup with ithreads) - -Sat Mar 13 20:11:03 GMT 2004 Nicholas Clark <nick@ccl4.org> - - Version 2.11 - - 1. Storing restricted hashes in canonical order would SEGV. Fixed. - 2. It was impossible to retrieve references to PL_sv_no and and - PL_sv_undef from STORABLE_thaw hooks. - 3. restrict.t was failing on 5.8.0, due to 5.8.0's unique - implementation of restricted hashes using PL_sv_undef - 4. These changes allow a space optimisation for restricted hashes. - -Sat Jan 24 16:22:32 IST 2004 Abhijit Menon-Sen <ams@wiw.org> - - Version 2.10 - - 1. Thread safety: Storable::CLONE/init_perlinterp() now create - a new Perl context for each new ithread. - (From Stas Bekman and Jan Dubois.) - 2. Fix a tag count mismatch with $Storable::Deparse that caused - all back-references after a stored sub to be off-by-N (where - N was the number of code references in between). - (From Sam Vilain.) - 3. Prevent CODE references from turning into SCALAR references. - (From Slaven Rezic.) - -Sat Jan 3 18:49:18 GMT 2004 Nicholas Clark <nick@ccl4.org> - - Version 2.09 - - Fix minor problems with the CPAN release - 1: Make Storable.xs work on 5.8.2 and later (already in the core) - 2: Ship the linux hints file - 3: Ship Test::More for the benefit of Perls pre 5.6.2 - 4: Correct Makefile.PL to only install in core for 5.8.0 and later - -Sat Sep 6 01:08:20 IST 2003 Abhijit Menon-Sen <ams@wiw.org> - - Version 2.08 - - This release works around a 5.8.0 bug which caused hashes to not - be marked as having key flags even though an HEK had HEK_WASUTF8 - set. (Note that the only reasonable solution is to silently drop - the flag from the affected key.) - - Users of RT 3 who were seeing assertion failures should upgrade. - (Perl 5.8.1 will have the bug fixed.) - -Mon May 5 10:24:16 IST 2003 Abhijit Menon-Sen <ams@wiw.org> - - Version 2.07 - - Minor bugfixes (self-tied objects are now correctly stored, as - are the results of additions larger than INT_MAX). - -Mon Oct 7 21:56:38 BST 2002 Nicholas Clark <nick@ccl4.org> - - Version 2.06 - - Remove qr// from t/downgrade.t so that it will run on 5.004 - Mention $File::Spec::VERSION a second time in t/forgive.t so that it - runs without warnings in 5.004 (this may be a 5.00405 bug I'm working - round) - Fix t/integer.t initialisation to actually generate 64 bits of 9c - Fix comparison tests to use eval to get around 64 bit IV conversion - issues on 5.6.x, following my t/integer.t ^ precedence bug found by - Rafael Garcia-Suarez - Alter t/malice.t to work with Test/More.pm in t/, and skip individual - subtests that use $Config{ptrsize}, so that the rest of the test can - now be run with 5.004 - Change t/malice.t and the error message in check_magic in Storable.xs - from "Pointer integer size" to "Pointer size" - Remove prerequisite of Test::More from Makefile.PL - Ship Test::Builder, Test::Simple and Test::More in t - -Thu Oct 3 08:57:22 IST 2002 Abhijit Menon-Sen <ams@wiw.org> - - Version 2.05 - - Adds support for CODE references from Slaven Rezic - <slaven.rezic@berlin.de>. - -Fri Jun 7 23:55:41 BST 2002 Nicholas Clark - - Version 2.04 - - Bug fix from Radu Greab <radu@netsoft.ro> (plus regression test) - to fix a recently introduced bug detected by Dave Rolsky. - Bug was that for a non threaded build, the class information was - being lost at freeze time on the first object with a STORABLE_freeze - hook. Consequentially the object was not blessed at all when thawed. - (The presence (or lack) of STORABLE_thaw was irrelevant; this was - a store-time data lost bug, caused by failure to initialize internal - context) - The bug was introduced as development perl change 16442 (on - 2002/05/07), so has been present since 2.00. - Patches to introduce more regression tests to reduce the chance of - a reoccurance of this sort of goof are always welcome. - -Thu May 30 20:31:08 BST 2002 Nicholas Clark <nick@ccl4.org> - - Version 2.03 Header changes on 5.6.x on Unix where IV is long long - - 5.6.x introduced the ability to have IVs as long long. However, - Configure still defined BYTEORDER based on the size of a long. - Storable uses the BYTEORDER value as part of the header, but - doesn't explicity store sizeof(IV) anywhere in the header. - Hence on 5.6.x built with IV as long long on a platform that - uses Configure (ie most things except VMS and Windows) headers - are identical for the different IV sizes, despite the files - containing some fields based on sizeof(IV) - - 5.8.0 is consistent; all platforms have BYTEORDER in config.h - based on sizeof(IV) rather than sizeof(long). This means that - the value of BYTEORDER will change from (say) 4321 to 87654321 - between 5.6.1 and 5.8.0 built with the same options to Configure - on the same machine. This means that the Storable header will - differ, and the two versions will wrongly thing that they are - incompatible. - - For the benefit of long term consistency, Storable now - implements the 5.8.0 BYTEORDER policy on 5.6.x. This means that - 2.03 onwards default to be incompatible with 2.02 and earlier - (ie the large 1.0.x installed base) on the same 5.6.x perl. - - To allow interworking, a new variable - $Storable::interwork_56_64bit is introduced. It defaults to - false. Set it to true to read and write old format files. Don't - use it unless you have existing stored data written with 5.6.x - that you couldn't otherwise read, or you need to interwork with - a machine running older Storable on a 5.6.x with long long IVs - (i.e., you probably don't need to use it). - -Sat May 25 22:38:39 BST 2002 Nicholas Clark <nick@ccl4.org> - - Version 2.02 - - Rewrite Storable.xs so that the file header structure for write_magic - is built at compile time, and check_magic attempts to the header in - blocks rather than byte per byte. These changes make the compiled - extension 2.25% smaller, but are not significant enough to give a - noticeable speed up. - -Thu May 23 22:50:41 BST 2002 Nicholas Clark <nick@ccl4.org> - - Version 2.01 - - - New regression tests integer.t - - Add code to safely store large unsigned integers. - - Change code not to attempt to store large integers (ie > 32 bits) - in network order as 32 bits. - - *Never* underestimate the value of a pathological test suite carefully - crafted with maximum malice before writing a line of real code. It - prevents crafty bugs from stowing away in your released code. - It's much less embarrassing to find them before you ship. - (Well, never underestimate it if you ever want to work for me) - -Fri May 17 22:48:59 BST 2002 Nicholas Clark <nick@ccl4.org> - - Version 2.0, binary format 2.5 (but writes format 2.4 on pre 5.7.3) - - The perl5 porters have decided to make sure that Storable still - builds on pre-5.8 perls, and make the 5.8 version available on CPAN. - The VERSION is now 2.0, and it passes all tests on 5.005_03, 5.6.1 - and 5.6.1 with threads. On 5.6.0 t/downgrade.t fails tests 34 and 37, - due to a bug in 5.6.0 - upgrade to 5.6.1. - - Jarkko and I have collated the list of changes the perl5 porters have - from the perl5 Changes file: - - - data features of upcoming perl 5.8.0 are supported: Unicode hash - keys (Unicode hash values have been supported since Storable 1.0.1) - and "restricted hashes" (readonly hashes and hash entries) - - a newer version of perl can now be used to serialize data which is - not supported in earlier perls: Storable will attempt to do the - right thing for as long as possible, croaking only when safe data - conversion simply isn't possible. Alternatively earlier perls can - opt to have a lossy downgrade data instead of croaking - - when built with perls pre 5.7.3 this Storable writes out files - with binary format 2.4, the same format as Storable 1.0.8 onwards. - This should mean that this Storable will inter-operate seamlessly - with any Storable 1.0.8 or newer on perls pre 5.7.3 - - dclone() now works with empty string scalar objects - - retrieving of large hashes is now more efficient - - more routines autosplit out of the main module, so Storable should - load slightly more quickly - - better documentation - - the internal context objects are now freed explicitly, rather than - relying on thread or process exit - - bugs fixed in debugging trace code affecting builds made with 64 bit - IVs - - code tidy-ups to allow clean compiles with more warning options - turned on avoid problems with $@ getting corrupted on 5.005_03 if - Carp wasn't already loaded - - added &show_file_magic, so you can add to /etc/magic and teach - Unix's file command about Storable files - - We plan to keep Storable on CPAN in sync with the Perl core, so - if you encounter bugs or other problems building or using Storable, - please let us know at perl5-porters@perl.org - Patches welcome! - -Sat Dec 1 14:37:54 MET 2001 Raphael Manfredi <Raphael_Manfredi@pobox.com> - - This is the LAST maintenance release of the Storable module. - Indeed, Storable is now part of perl 5.8, and will be maintained - as part of Perl. The CPAN module will remain available there - for people running pre-5.8 perls. - - Avoid requiring Fcntl upfront, useful to embedded runtimes. - Use an eval {} for testing, instead of making Storable.pm - simply fail its compilation in the BEGIN block. - - store_fd() will now correctly autoflush file if needed. - -Tue Aug 28 23:53:20 MEST 2001 Raphael Manfredi <Raphael_Manfredi@pobox.com> - - Fixed truncation race with lock_retrieve() in lock_store(). - The file has to be truncated only once the exclusive lock is held. - - Removed spurious debugging messages in .xs file. - -Sun Jul 1 13:27:32 MEST 2001 Raphael Manfredi <Raphael_Manfredi@pobox.com> - - Systematically use "=over 4" for POD linters. - Apparently, POD linters are much stricter than would - otherwise be needed, but that's OK. - - Fixed memory corruption on croaks during thaw(). Thanks - to Claudio Garcia for reproducing this bug and providing the - code to exercise it. Added test cases for this bug, adapted - from Claudio's code. - - Made code compile cleanly with -Wall (from Jarkko Hietaniemi). - - Changed tagnum and classnum from I32 to IV in context. Also - from Jarkko. - -Thu Mar 15 01:22:32 MET 2001 Raphael Manfredi <Raphael_Manfredi@pobox.com> - - Last version was wrongly compiling with assertions on, due - to an edit glitch. That did not cause any problem (apart from - a slight performance loss) excepted on Win* platforms, where the - assertion code does not compile. - -Sat Feb 17 13:37:37 MET 2001 Raphael Manfredi <Raphael_Manfredi@pobox.com> - - Version 1.0.10. - - Forgot to increase version number at previous patch (there were - two of them, which is why we jump from 1.0.8 to 1.0.10). - -Sat Feb 17 13:35:00 MET 2001 Raphael Manfredi <Raphael_Manfredi@pobox.com> - - Version 1.0.8, binary format 2.4. - - Fixed incorrect error message. - - Now bless objects ASAP at retrieve time, which is meant to fix - two bugs: - - * Indirect references to overloaded object were not able to - restore overloading if the object was not blessed yet, - which was possible since blessing occurred only after the - recursive retrieval. - - * Storable hooks asking for serialization of blessed ref could - get un-blessed refs at retrieval time, for the very same - reason. - - The fix implemented here was suggested by Nick Ing-Simmons. - - Added support for blessed ref to tied structures. This is the - cause for the binary format change. - - Added EBCDIC version of the compatibility test with 0.6.11, - from Peter Prymmer - - Added tests for the new features, and to make sure the bugs they - are meant to fix are indeed fixed. - -Wed Jan 3 10:43:18 MET 2001 Raphael Manfredi <Raphael_Manfredi@pobox.com> - - Removed spurious 'clean' entry in Makefile.PL. - - Added CAN_FLOCK to determine whether we can flock() or not, - by inspecting Perl's configuration parameters, as determined - by Configure. - - Trace offending package when overloading cannot be restored - on a scalar. - - Made context cleanup safer to avoid dup freeing, mostly in the - presence of repeated exceptions during store/retrieve (which can - cause memory leaks anyway, so it's just additional safety, not a - definite fix). - -Sun Nov 5 18:23:48 MET 2000 Raphael Manfredi <Raphael_Manfredi@pobox.com> - - Version 1.0.6. - - Fixed severe "object lost" bug for STORABLE_freeze returns, - when refs to lexicals, taken within the hook, were to be - serialized by Storable. Enhanced the t/recurse.t test to - stress hook a little more with refs to lexicals. - -Thu Oct 26 19:14:38 MEST 2000 Raphael Manfredi <Raphael_Manfredi@pobox.com> - - Version 1.0.5. - - Documented that store() and retrieve() can return undef. - That is, the error reporting is not always made via exceptions, - as the paragraph on error reporting was implying. - - Auto requires module of blessed ref when STORABLE_thaw misses. - When the Storable engine looks for the STORABLE_thaw hook and - does not find it, it now tries to require the package into which - the blessed reference is. - - Just check $^O, in t/lock.t: there's no need to pull the whole - Config module for that. - -Mon Oct 23 20:03:49 MEST 2000 Raphael Manfredi <Raphael_Manfredi@pobox.com> - - Version 1.0.4. - - Protected calls to flock() for DOS platform: apparently, the - flock/fcnlt emulation is reported to be broken on that - platform. - - Added logcarp emulation if they don't have Log::Agent, since - we now use it to carp when lock_store/lock_retrieve is used - on DOS. - -Fri Sep 29 21:52:29 MEST 2000 Raphael Manfredi <Raphael_Manfredi@pobox.com> - - Version 1.0.3. - - Avoid using "tainted" and "dirty" since Perl remaps them via - cpp (i.e. #define). This is deeply harmful when threading - is enabled. This concerned both the context structure and - local variable and argument names. Brrr..., scary! - -Thu Sep 28 23:46:39 MEST 2000 Raphael Manfredi <Raphael_Manfredi@pobox.com> - - Version 1.0.2. - - Fixed spelling in README. - - Added lock_store, lock_nstore, and lock_retrieve (advisory locking) - after a proposal from Erik Haugan <erik@solbors.no>. - - Perls before 5.004_04 lack newSVpvn, added remapping in XS. - - Fixed stupid typo in the t/utf8.t test. - -Sun Sep 17 18:51:10 MEST 2000 Raphael Manfredi <Raphael_Manfredi@pobox.com> - - Version 1.0.1, binary format 2.3. - - Documented that doubles are stored stringified by nstore(). - - Added Salvador Ortiz Garcia in CREDITS section, He identified - a bug in the store hooks and proposed the right fix: the class - id was allocated too soon. His bug case was also added to - the regression test suite. - - Now only taint retrieved data when source was tainted. A bug - discovered by Marc Lehmann. - - Added support for UTF-8 strings, a contribution of Marc Lehmann. - This is normally only activated in post-5.6 perls. - -Thu Aug 31 23:06:06 MEST 2000 Raphael Manfredi <Raphael_Manfredi@pobox.com> - - First official release Storable 1.0, for inclusion in perl 5.7.0. - The license scheme is now compatible with Perl's. - -Thu Aug 24 01:02:02 MEST 2000 Raphael Manfredi <Raphael_Manfredi@pobox.com> - - ANSI-fied most of the code, preparing for Perl core integration. - The next version of Storable will be 0.8, and will be integrated - into the Perl core (development branch). - - Dispatch tables were moved upfront to relieve some compilers, - especially on AIX and Windows platforms. - - Merged 64-bit fixes from perl5-porters. - -Mon Aug 14 09:22:04 MEST 2000 Raphael Manfredi <Raphael_Manfredi@pobox.com> - - 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 - <conrad@fringehead.org> for spotting it first. - -Sun Aug 13 22:12:59 MEST 2000 Raphael Manfredi <Raphael_Manfredi@pobox.com> - - 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 <Raphael_Manfredi@pobox.com> - - 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 <Raphael_Manfredi@pobox.com> - - 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 <Raphael_Manfredi@pobox.com> - - 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 <patchlevel.h>, - preventing Perl's patchlevel from being included, which is - needed starting from 5.6. - -Tue May 12 09:15:15 METDST 1998 Raphael Manfredi <Raphael_Manfredi@grenoble.hp.com> - - Fixed shared "undef" bug in hashes, which did not remain shared - through store/retrieve. - -Thu Feb 10 19:48:16 MET 2000 Raphael Manfredi <Raphael_Manfredi@pobox.com> - - 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 <Raphael_Manfredi@pobox.com> - - Forgot to update VERSION - -Tue Oct 19 21:25:02 MEST 1999 Raphael Manfredi <Raphael_Manfredi@pobox.com> - - 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 <Raphael_Manfredi@pobox.com> - - 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 <Raphael_Manfredi@pobox.com> - - 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 <Raphael_Manfredi@grenoble.hp.com> - - 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 <Raphael_Manfredi@grenoble.hp.com> - - 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 <Raphael_Manfredi@grenoble.hp.com> - - 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 <Raphael_Manfredi@grenoble.hp.com> - - 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 <Raphael_Manfredi@grenoble.hp.com> - - 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 <Raphael_Manfredi@grenoble.hp.com> - - 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 <Raphael_Manfredi@grenoble.hp.com> - - 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 <Raphael_Manfredi@grenoble.hp.com> - - I said SvPOK() had changed to SvPOKp(), but that was a lie... - -Wed Apr 8 13:14:29 METDST 1998 Raphael Manfredi <Raphael_Manfredi@grenoble.hp.com> - - 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 <Raphael_Manfredi@grenoble.hp.com> - - 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 <Raphael_Manfredi@grenoble.hp.com> - - Don't use any '_' in version number. - -Tue Jan 13 17:51:50 MET 1998 Raphael Manfredi <Raphael_Manfredi@grenoble.hp.com> - - 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 <Raphael_Manfredi@grenoble.hp.com> - - 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 <Raphael_Manfredi@grenoble.hp.com> - - Updated version number - - Added freeze/thaw interface and dclone. - -Fri May 16 10:45:47 METDST 1997 Raphael Manfredi <Raphael_Manfredi@grenoble.hp.com> - - 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 <Raphael_Manfredi@grenoble.hp.com> - - 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 <Raphael_Manfredi@grenoble.hp.com> - - 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 <Raphael_Manfredi@grenoble.hp.com> - - 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 <Raphael_Manfredi@grenoble.hp.com> - - Random code fixes. - -Wed Jan 22 15:19:56 MET 1997 Raphael Manfredi <Raphael_Manfredi@grenoble.hp.com> - - 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. diff --git a/ext/Storable/Makefile.PL b/ext/Storable/Makefile.PL deleted file mode 100644 index b840b3919a..0000000000 --- a/ext/Storable/Makefile.PL +++ /dev/null @@ -1,39 +0,0 @@ -# -# Copyright (c) 1995-2000, Raphael Manfredi -# -# You may redistribute only under the same terms as Perl 5, as specified -# in the README file that comes with the distribution. -# - -use ExtUtils::MakeMaker; -use Config; - -WriteMakefile( - NAME => 'Storable', - DISTNAME => "Storable", -# We now ship this in t/ -# PREREQ_PM => { 'Test::More' => '0.41' }, - INSTALLDIRS => $] >= 5.007 ? 'perl' : 'site', - VERSION_FROM => 'Storable.pm', - dist => { SUFFIX => 'gz', COMPRESS => 'gzip -f' }, -); - -my $ivtype = $Config{ivtype}; - -# I don't know if the VMS folks ever supported long long on 5.6.x -if ($ivtype and $ivtype eq 'long long' and $^O !~ /^MSWin/) { - print <<'EOM'; - -You appear to have a perl configured to use 64 bit integers in its scalar -variables. If you have existing data written with an earlier version of -Storable which this version of Storable refuses to load with a - - Byte order is not compatible - -error, then please read the section "64 bit data in perl 5.6.0 and 5.6.1" -in the Storable documentation for instructions on how to read your data. - -(You can find the documentation at the end of Storable.pm in POD format) - -EOM -} diff --git a/ext/Storable/README b/ext/Storable/README deleted file mode 100644 index cb4589e161..0000000000 --- a/ext/Storable/README +++ /dev/null @@ -1,109 +0,0 @@ - Storable 2.14 - Copyright (c) 1995-2000, Raphael Manfredi - Copyright (c) 2001-2004, Larry Wall - ------------------------------------------------------------------------- - This program is free software; you can redistribute it and/or modify - it under the same terms as Perl 5 itself. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - Perl 5 License schemes for more details. ------------------------------------------------------------------------- - -+======================================================================= -| Storable is distributed as a module, but is also part of the official -| Perl core distribution, as of perl 5.8. -| Maintenance is now done by the perl5-porters. We thank Raphael -| Manfredi for providing us with this very useful module. -+======================================================================= - -The Storable extension brings persistency to your data. - -You may recursively store to disk any data structure, no matter how -complex and circular it is, provided it contains only SCALAR, ARRAY, -HASH (possibly tied) and references (possibly blessed) to those items. - -At a later stage, or in another program, you may retrieve data from -the stored file and recreate the same hiearchy in memory. If you -had blessed references, the retrieved references are blessed into -the same package, so you must make sure you have access to the -same perl class than the one used to create the relevant objects. - -There is also a dclone() routine which performs an optimized mirroring -of any data structure, preserving its topology. - -Objects (blessed references) may also redefine the way storage and -retrieval is performed, and/or what deep cloning should do on those -objects. - -To compile this extension, run: - - perl Makefile.PL [PERL_SRC=...where you put perl sources...] - make - make install - -There is an embeded POD manual page in Storable.pm. - -Storable was written by Raphael Manfredi <Raphael_Manfredi@pobox.com> -Maitainance is now done by the perl5-porters <perl5-porters@perl.org> - -Please e-mail us with problems, bug fixes, comments and complaints, -although if you have complements you should send them to Raphael. -Please don't e-mail Raphael with problems, as he no longer works on -Storable, and your message will be delayed while he forwards it to us. - ------------------------------------------------------------------------- -Thanks to (in chronological order): - - Jarkko Hietaniemi <jhi@iki.fi> - Ulrich Pfeifer <pfeifer@charly.informatik.uni-dortmund.de> - Benjamin A. Holzman <bah@ecnvantage.com> - Andrew Ford <A.Ford@ford-mason.co.uk> - Gisle Aas <gisle@aas.no> - Jeff Gresham <gresham_jeffrey@jpmorgan.com> - Murray Nesbitt <murray@activestate.com> - Albert N. Micheev <Albert.N.Micheev@f80.n5049.z2.fidonet.org> - Marc Lehmann <pcg@opengroup.org> - Justin Banks <justinb@wamnet.com> - Jarkko Hietaniemi <jhi@iki.fi> (AGAIN, as perl 5.7.0 Pumpkin!) - -for their contributions. - -A Japanese translation of this man page is available at the Japanized -Perl Resources Project <https://sourceforge.jp/projects/perldocjp/>. ------------------------------------------------------------------------- - -The perl5-porters would like to thank - - Raphael Manfredi <Raphael_Manfredi@pobox.com> - -According to the perl5.8 Changes file, the following people have helped -bring you this Storable release: - - Abhijit Menon-Sen <ams@wiw.org> - Andreas J. Koenig <andreas.koenig@anima.de> - Archer Sully <archer@meer.net> - Craig A. Berry <craig.berry@psinetcs.com> - Dan Kogai <dankogai@dan.co.jp> - Doug MacEachern <dougm@covalent.net> - Gurusamy Sarathy <gsar@ActiveState.com> - H.Merijn Brand <h.m.brand@xs4all.nl> - Jarkko Hietaniemi <jhi@iki.fi> - Mark Bixby - Michael Stevens <michael@etla.org> - Mike Guy <mjtg@cam.ac.uk> - Nicholas Clark <nick@unfortu.net> - Peter J. Farley III <pjfarley@banet.net> - Peter Prymmer <pvhp@forte.com> - Philip Newton <pne@cpan.org> - Raphael Manfredi <Raphael_Manfredi@pobox.com> - Robin Barker <rmb1@cise.npl.co.uk> - Radu Greab <radu@netsoft.ro> - Tim Bunce <Tim.Bunce@pobox.com> - VMSperlers - Yitzchak Scott-Thoennes <sthoenna@efn.org> - -If I've missed you out, please accept my apologies, and e-mail your -patch to perl5-porters@perl.org. diff --git a/ext/Storable/Storable.pm b/ext/Storable/Storable.pm deleted file mode 100644 index 20df4f1e7d..0000000000 --- a/ext/Storable/Storable.pm +++ /dev/null @@ -1,1188 +0,0 @@ -# -# Copyright (c) 1995-2000, Raphael Manfredi -# -# You may redistribute only under the same terms as Perl 5, as specified -# in the README file that comes with the distribution. -# - -require DynaLoader; -require Exporter; -package Storable; @ISA = qw(Exporter DynaLoader); - -@EXPORT = qw(store retrieve); -@EXPORT_OK = qw( - nstore store_fd nstore_fd fd_retrieve - freeze nfreeze thaw - dclone - retrieve_fd - lock_store lock_nstore lock_retrieve - file_magic read_magic -); - -use AutoLoader; -use FileHandle; -use vars qw($canonical $forgive_me $VERSION); - -$VERSION = '2.20'; -*AUTOLOAD = \&AutoLoader::AUTOLOAD; # Grrr... - -# -# Use of Log::Agent is optional -# - -{ - local $SIG{__DIE__}; - eval "use Log::Agent"; -} - -require Carp; - -# -# They might miss :flock in Fcntl -# - -BEGIN { - if (eval { require Fcntl; 1 } && exists $Fcntl::EXPORT_TAGS{'flock'}) { - Fcntl->import(':flock'); - } else { - eval q{ - sub LOCK_SH () {1} - sub LOCK_EX () {2} - }; - } -} - -sub CLONE { - # clone context under threads - Storable::init_perinterp(); -} - -# Can't Autoload cleanly as this clashes 8.3 with &retrieve -sub retrieve_fd { &fd_retrieve } # Backward compatibility - -# By default restricted hashes are downgraded on earlier perls. - -$Storable::downgrade_restricted = 1; -$Storable::accept_future_minor = 1; -bootstrap Storable; -1; -__END__ -# -# Use of Log::Agent is optional. If it hasn't imported these subs then -# Autoloader will kindly supply our fallback implementation. -# - -sub logcroak { - Carp::croak(@_); -} - -sub logcarp { - Carp::carp(@_); -} - -# -# Determine whether locking is possible, but only when needed. -# - -sub CAN_FLOCK; my $CAN_FLOCK; sub CAN_FLOCK { - return $CAN_FLOCK if defined $CAN_FLOCK; - require Config; import Config; - return $CAN_FLOCK = - $Config{'d_flock'} || - $Config{'d_fcntl_can_lock'} || - $Config{'d_lockf'}; -} - -sub show_file_magic { - print <<EOM; -# -# To recognize the data files of the Perl module Storable, -# the following lines need to be added to the local magic(5) file, -# usually either /usr/share/misc/magic or /etc/magic. -# -0 string perl-store perl Storable(v0.6) data ->4 byte >0 (net-order %d) ->>4 byte &01 (network-ordered) ->>4 byte =3 (major 1) ->>4 byte =2 (major 1) - -0 string pst0 perl Storable(v0.7) data ->4 byte >0 ->>4 byte &01 (network-ordered) ->>4 byte =5 (major 2) ->>4 byte =4 (major 2) ->>5 byte >0 (minor %d) -EOM -} - -sub file_magic { - my $file = shift; - my $fh = new FileHandle; - open($fh, "<". $file) || die "Can't open '$file': $!"; - binmode($fh); - defined(sysread($fh, my $buf, 32)) || die "Can't read from '$file': $!"; - close($fh); - - $file = "./$file" unless $file; # ensure TRUE value - - return read_magic($buf, $file); -} - -sub read_magic { - my($buf, $file) = @_; - my %info; - - my $buflen = length($buf); - my $magic; - if ($buf =~ s/^(pst0|perl-store)//) { - $magic = $1; - $info{file} = $file || 1; - } - else { - return undef if $file; - $magic = ""; - } - - return undef unless length($buf); - - my $net_order; - if ($magic eq "perl-store" && ord(substr($buf, 0, 1)) > 1) { - $info{version} = -1; - $net_order = 0; - } - else { - $net_order = ord(substr($buf, 0, 1, "")); - my $major = $net_order >> 1; - return undef if $major > 4; # sanity (assuming we never go that high) - $info{major} = $major; - $net_order &= 0x01; - if ($major > 1) { - return undef unless length($buf); - my $minor = ord(substr($buf, 0, 1, "")); - $info{minor} = $minor; - $info{version} = "$major.$minor"; - $info{version_nv} = sprintf "%d.%03d", $major, $minor; - } - else { - $info{version} = $major; - } - } - $info{version_nv} ||= $info{version}; - $info{netorder} = $net_order; - - unless ($net_order) { - return undef unless length($buf); - my $len = ord(substr($buf, 0, 1, "")); - return undef unless length($buf) >= $len; - return undef unless $len == 4 || $len == 8; # sanity - $info{byteorder} = substr($buf, 0, $len, ""); - $info{intsize} = ord(substr($buf, 0, 1, "")); - $info{longsize} = ord(substr($buf, 0, 1, "")); - $info{ptrsize} = ord(substr($buf, 0, 1, "")); - if ($info{version_nv} >= 2.002) { - return undef unless length($buf); - $info{nvsize} = ord(substr($buf, 0, 1, "")); - } - } - $info{hdrsize} = $buflen - length($buf); - - return \%info; -} - -sub BIN_VERSION_NV { - sprintf "%d.%03d", BIN_MAJOR(), BIN_MINOR(); -} - -sub BIN_WRITE_VERSION_NV { - sprintf "%d.%03d", BIN_MAJOR(), BIN_WRITE_MINOR(); -} - -# -# 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, @_, 0); -} - -# -# nstore -# -# Same as store, but in network order. -# -sub nstore { - return _store(\&net_pstore, @_, 0); -} - -# -# lock_store -# -# Same as store, but flock the file first (advisory locking). -# -sub lock_store { - return _store(\&pstore, @_, 1); -} - -# -# lock_nstore -# -# Same as nstore, but flock the file first (advisory locking). -# -sub lock_nstore { - return _store(\&net_pstore, @_, 1); -} - -# Internal store to file routine -sub _store { - my $xsptr = shift; - my $self = shift; - my ($file, $use_locking) = @_; - logcroak "not a reference" unless ref($self); - logcroak "wrong argument number" unless @_ == 2; # No @foo in arglist - local *FILE; - if ($use_locking) { - open(FILE, ">>$file") || logcroak "can't write into $file: $!"; - unless (&CAN_FLOCK) { - logcarp "Storable::lock_store: fcntl/flock emulation broken on $^O"; - return undef; - } - flock(FILE, LOCK_EX) || - logcroak "can't get exclusive lock on $file: $!"; - truncate FILE, 0; - # Unlocking will happen when FILE is closed - } else { - 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$/,/; - local $\; print $file ''; # Autoflush the file if wanted - $@ = $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 { - _retrieve($_[0], 0); -} - -# -# lock_retrieve -# -# Same as retrieve, but with advisory locking. -# -sub lock_retrieve { - _retrieve($_[0], 1); -} - -# Internal retrieve routine -sub _retrieve { - my ($file, $use_locking) = @_; - local *FILE; - open(FILE, $file) || logcroak "can't open $file: $!"; - binmode FILE; # Archaic systems... - my $self; - my $da = $@; # Could be from exception handler - if ($use_locking) { - unless (&CAN_FLOCK) { - logcarp "Storable::lock_store: fcntl/flock emulation broken on $^O"; - return undef; - } - flock(FILE, LOCK_SH) || logcroak "can't get shared lock on $file: $!"; - # Unlocking will happen when FILE is closed - } - eval { $self = pretrieve(*FILE) }; # Call C routine - close(FILE); - logcroak $@ if $@ =~ s/\.?\n$/,/; - $@ = $da; - return $self; -} - -# -# fd_retrieve -# -# Same as retrieve, but perform from an already opened file descriptor instead. -# -sub fd_retrieve { - 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; -} - -1; -__END__ - -=head1 NAME - -Storable - persistence 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 = fd_retrieve(\*SOCKET); - $hashref = fd_retrieve(\*SOCKET); - - # Serializing to memory - $serialized = freeze \%table; - %table_clone = %{ thaw($serialized) }; - - # Deep (recursive) cloning - $cloneref = dclone($ref); - - # Advisory locking - use Storable qw(lock_store lock_nstore lock_retrieve) - lock_store \%table, 'file'; - lock_nstore \%table, 'file'; - $hashref = lock_retrieve('file'); - -=head1 DESCRIPTION - -The Storable package brings persistence 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<store> with -a reference to the object to be stored, along with the file name where -the image should be written. - -The routine returns C<undef> for I/O problems or other internal error, -a true value otherwise. Serious errors are propagated as a C<die> exception. - -To retrieve data stored to disk, use C<retrieve> with a file name. -The objects stored into that file are recreated into memory for you, -and a I<reference> to the root object is returned. In case an I/O error -occurs while reading, C<undef> is returned instead. Other serious -errors are propagated via C<die>. - -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<store_fd> routine, and retrieve -from a file via C<fd_retrieve>. 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 = fd_retrieve(*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<n> prefix for I<network>, -as in C<nstore> and C<nstore_fd>. 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. Double values are stored stringified -to ensure portability as well, at the slight risk of loosing some precision -in the last decimals. - -When using C<fd_retrieve>, objects are retrieved in sequence, one -object (i.e. one recursive tree) per associated C<store_fd>. - -If you're more from the object-oriented camp, you can inherit from -Storable and directly store your objects by invoking C<store> 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<freeze> and C<thaw>. -If you wish to send out the frozen scalar to another machine, use -C<nfreeze> 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<dclone> 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 ADVISORY LOCKING - -The C<lock_store> and C<lock_nstore> routine are equivalent to -C<store> and C<nstore>, except that they get an exclusive lock on -the file before writing. Likewise, C<lock_retrieve> does the same -as C<retrieve>, but also gets a shared lock on the file before reading. - -As with any advisory locking scheme, the protection only works if you -systematically use C<lock_store> and C<lock_retrieve>. If one side of -your application uses C<store> whilst the other uses C<lock_retrieve>, -you will get no protection at all. - -The internal advisory locking is implemented using Perl's flock() -routine. If your system does not support any form of flock(), or if -you share your files across NFS, you might wish to use other forms -of locking by using modules such as LockFile::Simple which lock a -file using a filesystem entry, instead of locking the file descriptor. - -=head1 SPEED - -The heart of Storable is written in C for decent speed. Extra low-level -optimizations have been made when manipulating perl internals, to -sacrifice encapsulation for the benefit of 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<TRUE> 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 CODE REFERENCES - -Since Storable version 2.05, CODE references may be serialized with -the help of L<B::Deparse>. To enable this feature, set -C<$Storable::Deparse> to a true value. To enable deserialization, -C<$Storable::Eval> should be set to a true value. Be aware that -deserialization is done through C<eval>, which is dangerous if the -Storable file contains malicious data. You can set C<$Storable::Eval> -to a subroutine reference which would be used instead of C<eval>. See -below for an example using a L<Safe> compartment for deserialization -of CODE references. - -If C<$Storable::Deparse> and/or C<$Storable::Eval> are set to false -values, then the value of C<$Storable::forgive_me> (see below) is -respected while serializing and deserializing. - -=head1 FORWARD COMPATIBILITY - -This release of Storable can be used on a newer version of Perl to -serialize data which is not supported by earlier Perls. By default, -Storable will attempt to do the right thing, by C<croak()>ing if it -encounters data that it cannot deserialize. However, the defaults -can be changed as follows: - -=over 4 - -=item utf8 data - -Perl 5.6 added support for Unicode characters with code points > 255, -and Perl 5.8 has full support for Unicode characters in hash keys. -Perl internally encodes strings with these characters using utf8, and -Storable serializes them as utf8. By default, if an older version of -Perl encounters a utf8 value it cannot represent, it will C<croak()>. -To change this behaviour so that Storable deserializes utf8 encoded -values as the string of bytes (effectively dropping the I<is_utf8> flag) -set C<$Storable::drop_utf8> to some C<TRUE> value. This is a form of -data loss, because with C<$drop_utf8> true, it becomes impossible to tell -whether the original data was the Unicode string, or a series of bytes -that happen to be valid utf8. - -=item restricted hashes - -Perl 5.8 adds support for restricted hashes, which have keys -restricted to a given set, and can have values locked to be read only. -By default, when Storable encounters a restricted hash on a perl -that doesn't support them, it will deserialize it as a normal hash, -silently discarding any placeholder keys and leaving the keys and -all values unlocked. To make Storable C<croak()> instead, set -C<$Storable::downgrade_restricted> to a C<FALSE> value. To restore -the default set it back to some C<TRUE> value. - -=item files from future versions of Storable - -Earlier versions of Storable would immediately croak if they encountered -a file with a higher internal version number than the reading Storable -knew about. Internal version numbers are increased each time new data -types (such as restricted hashes) are added to the vocabulary of the file -format. This meant that a newer Storable module had no way of writing a -file readable by an older Storable, even if the writer didn't store newer -data types. - -This version of Storable will defer croaking until it encounters a data -type in the file that it does not recognize. This means that it will -continue to read files generated by newer Storable modules which are careful -in what they write out, making it easier to upgrade Storable modules in a -mixed environment. - -The old behaviour of immediate croaking can be re-instated by setting -C<$Storable::accept_future_minor> to some C<FALSE> value. - -=back - -All these variables have no effect on a newer Perl which supports the -relevant feature. - -=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<Carp> and C<croak()>). Use eval {} to trap -those exceptions. - -When Storable croaks, it tries to report the error via the C<logcroak()> -routine from the C<Log::Agent> package, if it is available. - -Normal errors are reported by having store() or retrieve() return C<undef>. -Such errors are usually I/O errors (or truncated stream errors at retrieval). - -=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 symmetrical 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 keep only 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 4 - -=item C<STORABLE_freeze> I<obj>, I<cloning> - -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<obj> is the object to serialize, I<cloning> 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 deserialization time, you will be given back the same LIST, but all the -extra references will be pointing into the deserialized structure. - -The B<first time> 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<STORABLE_thaw> I<obj>, I<cloning>, I<serialized>, ... - -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<STORABLE_thaw> as an alternate creation routine. - -This means the hook can be inherited like any other method, and that -I<obj> is your blessed reference for this particular instance. - -The other arguments should look familiar if you know C<STORABLE_freeze>: -I<cloning> is true when we're part of a deep clone operation, I<serialized> -is the serialized string you returned to the engine in C<STORABLE_freeze>, -and there may be an optional list of references, in the same order you gave -them at serialization time, pointing to the deserialized objects (which -have been processed courtesy of the Storable engine). - -When the Storable engine does not find any C<STORABLE_thaw> hook routine, -it tries to load the class by requiring the package dynamically (using -the blessed package name), and then re-attempts the lookup. If at that -time the hook cannot be located, the engine croaks. Note that this mechanism -will fail if you define several classes in the same file, but L<perlmod> -warned you. - -It is up to you to use this information to populate I<obj> the way you want. - -Returned value: none. - -=item C<STORABLE_attach> I<class>, I<cloning>, I<serialized> - -While C<STORABLE_freeze> and C<STORABLE_thaw> are useful for classes where -each instance is independent, this mechanism has difficulty (or is -incompatible) with objects that exist as common process-level or -system-level resources, such as singleton objects, database pools, caches -or memoized objects. - -The alternative C<STORABLE_attach> method provides a solution for these -shared objects. Instead of C<STORABLE_freeze> --E<gt> C<STORABLE_thaw>, -you implement C<STORABLE_freeze> --E<gt> C<STORABLE_attach> instead. - -Arguments: I<class> is the class we are attaching to, I<cloning> is a flag -indicating whether we're in a dclone() or a regular de-serialization via -thaw(), and I<serialized> is the stored string for the resource object. - -Because these resource objects are considered to be owned by the entire -process/system, and not the "property" of whatever is being serialized, -no references underneath the object should be included in the serialized -string. Thus, in any class that implements C<STORABLE_attach>, the -C<STORABLE_freeze> method cannot return any references, and C<Storable> -will throw an error if C<STORABLE_freeze> tries to return references. - -All information required to "attach" back to the shared resource object -B<must> be contained B<only> in the C<STORABLE_freeze> return string. -Otherwise, C<STORABLE_freeze> behaves as normal for C<STORABLE_attach> -classes. - -Because C<STORABLE_attach> is passed the class (rather than an object), -it also returns the object directly, rather than modifying the passed -object. - -Returned value: object of type C<class> - -=back - -=head2 Predicates - -Predicates are not exportable. They must be called by explicitly prefixing -them with the Storable package name. - -=over 4 - -=item C<Storable::last_op_in_netorder> - -The C<Storable::last_op_in_netorder()> 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<Storable::is_storing> - -Returns true if within a store operation (via STORABLE_freeze hook). - -=item C<Storable::is_retrieving> - -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 serializing and deserializing things, so why not use it -to handle the serialization string? - -There are a few things you need to know, however: - -=over 4 - -=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 -deserializing, 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<STORABLE_freeze> 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<STORABLE_freeze> hook could return: - - ("something", $self->{B}) - -and the B part would be serialized by the engine. In C<STORABLE_thaw>, 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 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 Storable magic - -Yes, there's a lot of that :-) But more precisely, in UNIX systems -there's a utility called C<file>, which recognizes data files based on -their contents (usually their first few bytes). For this to work, -a certain file called F<magic> needs to taught about the I<signature> -of the data. Where that configuration file lives depends on the UNIX -flavour; often it's something like F</usr/share/misc/magic> or -F</etc/magic>. Your system administrator needs to do the updating of -the F<magic> file. The necessary signature information is output to -STDOUT by invoking Storable::show_file_magic(). Note that the GNU -implementation of the C<file> utility, version 3.38 or later, -is expected to contain support for recognising Storable files -out-of-the-box, in addition to other kinds of Perl files. - -You can also use the following functions to extract the file header -information from Storable images: - -=over - -=item $info = Storable::file_magic( $filename ) - -If the given file is a Storable image return a hash describing it. If -the file is readable, but not a Storable image return C<undef>. If -the file does not exist or is unreadable then croak. - -The hash returned has the following elements: - -=over - -=item C<version> - -This returns the file format version. It is a string like "2.7". - -Note that this version number is not the same as the version number of -the Storable module itself. For instance Storable v0.7 create files -in format v2.0 and Storable v2.15 create files in format v2.7. The -file format version number only increment when additional features -that would confuse older versions of the module are added. - -Files older than v2.0 will have the one of the version numbers "-1", -"0" or "1". No minor number was used at that time. - -=item C<version_nv> - -This returns the file format version as number. It is a string like -"2.007". This value is suitable for numeric comparisons. - -The constant function C<Storable::BIN_VERSION_NV> returns a comparable -number that represent the highest file version number that this -version of Storable fully support (but see discussion of -C<$Storable::accept_future_minor> above). The constant -C<Storable::BIN_WRITE_VERSION_NV> function returns what file version -is written and might be less than C<Storable::BIN_VERSION_NV> in some -configuations. - -=item C<major>, C<minor> - -This also returns the file format version. If the version is "2.7" -then major would be 2 and minor would be 7. The minor element is -missing for when major is less than 2. - -=item C<hdrsize> - -The is the number of bytes that the Storable header occupies. - -=item C<netorder> - -This is TRUE if the image store data in network order. This means -that it was created with nstore() or similar. - -=item C<byteorder> - -This is only present when C<netorder> is FALSE. It is the -$Config{byteorder} string of the perl that created this image. It is -a string like "1234" (32 bit little endian) or "87654321" (64 bit big -endian). This must match the current perl for the image to be -readable by Storable. - -=item C<intsize>, C<longsize>, C<ptrsize>, C<nvsize> - -These are only present when C<netorder> is FALSE. These are the sizes of -various C datatypes of the perl that created this image. These must -match the current perl for the image to be readable by Storable. - -The C<nvsize> element is only present for file format v2.2 and -higher. - -=item C<file> - -The name of the file. - -=back - -=item $info = Storable::read_magic( $buffer ) - -=item $info = Storable::read_magic( $buffer, $must_be_file ) - -The $buffer should be a Storable image or the first few bytes of it. -If $buffer starts with a Storable header, then a hash describing the -image is returned, otherwise C<undef> is returned. - -The hash has the same structure as the one returned by -Storable::file_magic(). The C<file> element is true if the image is a -file image. - -If the $must_be_file argument is provided and is TRUE, then return -C<undef> unless the image looks like it belongs to a file dump. - -The maximum size of a Storable header is currently 21 bytes. If the -provided $buffer is only the first part of a Storable image it should -at least be this long to ensure that read_magic() will recognize it as -such. - -=back - -=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, 'mycolors') or die "Can't store %a in mycolors!\n"; - - $colref = retrieve('mycolors'); - die "Unable to retrieve from mycolors!\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. - -Serialization of CODE references and deserialization in a safe -compartment: - -=for example begin - - use Storable qw(freeze thaw); - use Safe; - use strict; - my $safe = new Safe; - # because of opcodes used in "use strict": - $safe->permit(qw(:default require)); - local $Storable::Deparse = 1; - local $Storable::Eval = sub { $safe->reval($_[0]) }; - my $serialized = freeze(sub { 42 }); - my $code = thaw($serialized); - $code->() == 42; - -=for example end - -=for example_testing - is( $code->(), 42 ); - -=head1 WARNING - -If you're using references as keys within your hash tables, you're bound -to be disappointed when retrieving your data. Indeed, Perl stringifies -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 sequence of C<store> and C<retrieve> 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<binmode()> 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, 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<croak> if they run into such references -unless you set C<$Storable::forgive_me> to some C<TRUE> 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. - -When storing doubles in network order, their value is stored as text. -However, you should also not expect non-numeric floating-point values -such as infinity and "not a number" to pass successfully through a -nstore()/retrieve() pair. - -As Storable neither knows nor cares about character sets (although it -does know that characters may be more than eight bits wide), any difference -in the interpretation of character codes between a host and a target -system is your problem. In particular, if host and target use different -code points to represent the characters used in the text representation -of floating-point numbers, you will not be able be able to exchange -floating-point data, even with nstore(). - -C<Storable::drop_utf8> is a blunt tool. There is no facility either to -return B<all> strings as utf8 sequences, or to attempt to convert utf8 -data back to 8 bit and C<croak()> if the conversion fails. - -Prior to Storable 2.01, no distinction was made between signed and -unsigned integers on storing. By default Storable prefers to store a -scalars string representation (if it has one) so this would only cause -problems when storing large unsigned integers that had never been converted -to string or floating point. In other words values that had been generated -by integer operations such as logic ops and then not used in any string or -arithmetic context before storing. - -=head2 64 bit data in perl 5.6.0 and 5.6.1 - -This section only applies to you if you have existing data written out -by Storable 2.02 or earlier on perl 5.6.0 or 5.6.1 on Unix or Linux which -has been configured with 64 bit integer support (not the default) -If you got a precompiled perl, rather than running Configure to build -your own perl from source, then it almost certainly does not affect you, -and you can stop reading now (unless you're curious). If you're using perl -on Windows it does not affect you. - -Storable writes a file header which contains the sizes of various C -language types for the C compiler that built Storable (when not writing in -network order), and will refuse to load files written by a Storable not -on the same (or compatible) architecture. This check and a check on -machine byteorder is needed because the size of various fields in the file -are given by the sizes of the C language types, and so files written on -different architectures are incompatible. This is done for increased speed. -(When writing in network order, all fields are written out as standard -lengths, which allows full interworking, but takes longer to read and write) - -Perl 5.6.x introduced the ability to optional configure the perl interpreter -to use C's C<long long> type to allow scalars to store 64 bit integers on 32 -bit systems. However, due to the way the Perl configuration system -generated the C configuration files on non-Windows platforms, and the way -Storable generates its header, nothing in the Storable file header reflected -whether the perl writing was using 32 or 64 bit integers, despite the fact -that Storable was storing some data differently in the file. Hence Storable -running on perl with 64 bit integers will read the header from a file -written by a 32 bit perl, not realise that the data is actually in a subtly -incompatible format, and then go horribly wrong (possibly crashing) if it -encountered a stored integer. This is a design failure. - -Storable has now been changed to write out and read in a file header with -information about the size of integers. It's impossible to detect whether -an old file being read in was written with 32 or 64 bit integers (they have -the same header) so it's impossible to automatically switch to a correct -backwards compatibility mode. Hence this Storable defaults to the new, -correct behaviour. - -What this means is that if you have data written by Storable 1.x running -on perl 5.6.0 or 5.6.1 configured with 64 bit integers on Unix or Linux -then by default this Storable will refuse to read it, giving the error -I<Byte order is not compatible>. If you have such data then you you -should set C<$Storable::interwork_56_64bit> to a true value to make this -Storable read and write files with the old header. You should also -migrate your data, or any older perl you are communicating with, to this -current version of Storable. - -If you don't have data written with specific configuration of perl described -above, then you do not and should not do anything. Don't set the flag - -not only will Storable on an identically configured perl refuse to load them, -but Storable a differently configured perl will load them believing them -to be correct for it, and then may well fail or crash part way through -reading them. - -=head1 CREDITS - -Thank you to (in chronological order): - - Jarkko Hietaniemi <jhi@iki.fi> - Ulrich Pfeifer <pfeifer@charly.informatik.uni-dortmund.de> - Benjamin A. Holzman <bah@ecnvantage.com> - Andrew Ford <A.Ford@ford-mason.co.uk> - Gisle Aas <gisle@aas.no> - Jeff Gresham <gresham_jeffrey@jpmorgan.com> - Murray Nesbitt <murray@activestate.com> - Marc Lehmann <pcg@opengroup.org> - Justin Banks <justinb@wamnet.com> - Jarkko Hietaniemi <jhi@iki.fi> (AGAIN, as perl 5.7.0 Pumpkin!) - Salvador Ortiz Garcia <sog@msg.com.mx> - Dominic Dunlop <domo@computer.org> - Erik Haugan <erik@solbors.no> - -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 references to tied items support. - -=head1 AUTHOR - -Storable was written by Raphael Manfredi F<E<lt>Raphael_Manfredi@pobox.comE<gt>> -Maintenance is now done by the perl5-porters F<E<lt>perl5-porters@perl.orgE<gt>> - -Please e-mail us with problems, bug fixes, comments and complaints, -although if you have compliments you should send them to Raphael. -Please don't e-mail Raphael with problems, as he no longer works on -Storable, and your message will be delayed while he forwards it to us. - -=head1 SEE ALSO - -L<Clone>. - -=cut diff --git a/ext/Storable/Storable.xs b/ext/Storable/Storable.xs deleted file mode 100644 index 2741c7d30e..0000000000 --- a/ext/Storable/Storable.xs +++ /dev/null @@ -1,6491 +0,0 @@ -/* - * Store and retrieve mechanism. - * - * Copyright (c) 1995-2000, Raphael Manfredi - * - * You may redistribute only under the same terms as Perl 5, as specified - * in the README file that comes with the distribution. - * - */ - -#define PERL_NO_GET_CONTEXT /* we want efficiency */ -#include <EXTERN.h> -#include <perl.h> -#include <XSUB.h> - -#ifndef PATCHLEVEL -#include <patchlevel.h> /* Perl's one, needed since 5.6 */ -#endif - -#if !defined(PERL_VERSION) || PERL_VERSION < 8 -#define NEED_load_module -#define NEED_vload_module -#define NEED_newCONSTSUB -#include "ppport.h" /* handle old perls */ -#endif - -#if 0 -#define DEBUGME /* Debug mode, turns assertions on as well */ -#define DASSERT /* Assertion mode */ -#endif - -/* - * 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 PERL_VERSION /* For perls < 5.6 */ -#define PERL_VERSION PATCHLEVEL -#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 -#if (SUBVERSION <= 4) /* 5.004_04 has been reported to lack newSVpvn */ -#define newSVpvn newSVpv -#endif -#endif /* PATCHLEVEL <= 4 */ -#ifndef HvSHAREKEYS_off -#define HvSHAREKEYS_off(hv) /* Ignore */ -#endif -#ifndef AvFILLp /* Older perls (<=5.003) lack AvFILLp */ -#define AvFILLp AvFILL -#endif -typedef double NV; /* Older perls lack the NV type */ -#define IVdf "ld" /* Various printf formats for Perl types */ -#define UVuf "lu" -#define UVof "lo" -#define UVxf "lx" -#define INT2PTR(t,v) (t)(IV)(v) -#define PTR2UV(v) (unsigned long)(v) -#endif /* PERL_VERSION -- perls < 5.6 */ - -#ifndef NVef /* The following were not part of perl 5.6 */ -#if defined(USE_LONG_DOUBLE) && \ - defined(HAS_LONG_DOUBLE) && defined(PERL_PRIfldbl) -#define NVef PERL_PRIeldbl -#define NVff PERL_PRIfldbl -#define NVgf PERL_PRIgldbl -#else -#define NVef "e" -#define NVff "f" -#define NVgf "g" -#endif -#endif - -#ifndef SvRV_set -#define SvRV_set(sv, val) \ - STMT_START { \ - assert(SvTYPE(sv) >= SVt_RV); \ - (((XRV*)SvANY(sv))->xrv_rv = (val)); \ - } STMT_END -#endif - -#ifndef PERL_UNUSED_DECL -# ifdef HASATTRIBUTE -# if (defined(__GNUC__) && defined(__cplusplus)) || defined(__INTEL_COMPILER) -# define PERL_UNUSED_DECL -# else -# define PERL_UNUSED_DECL __attribute__((unused)) -# endif -# else -# define PERL_UNUSED_DECL -# endif -#endif - -#ifndef dNOOP -#define dNOOP extern int Perl___notused PERL_UNUSED_DECL -#endif - -#ifndef dVAR -#define dVAR dNOOP -#endif - -#ifndef HvRITER_set -# define HvRITER_set(hv,r) (HvRITER(hv) = r) -#endif -#ifndef HvEITER_set -# define HvEITER_set(hv,r) (HvEITER(hv) = r) -#endif - -#ifndef HvRITER_get -# define HvRITER_get HvRITER -#endif -#ifndef HvEITER_get -# define HvEITER_get HvEITER -#endif - -#ifndef HvNAME_get -#define HvNAME_get HvNAME -#endif - -#ifndef HvPLACEHOLDERS_get -# define HvPLACEHOLDERS_get HvPLACEHOLDERS -#endif - -#ifdef DEBUGME - -#ifndef DASSERT -#define DASSERT -#endif - -/* - * TRACEME() will only output things when the $Storable::DEBUGME is true. - */ - -#define TRACEME(x) \ - STMT_START { \ - if (SvTRUE(perl_get_sv("Storable::DEBUGME", GV_ADD))) \ - { PerlIO_stdoutf x; PerlIO_stdoutf("\n"); } \ - } STMT_END -#else -#define TRACEME(x) -#endif /* DEBUGME */ - -#ifdef DASSERT -#define ASSERT(x,y) \ - STMT_START { \ - if (!(x)) { \ - PerlIO_stdoutf("ASSERT FAILED (\"%s\", line %d): ", \ - __FILE__, __LINE__); \ - PerlIO_stdoutf y; PerlIO_stdoutf("\n"); \ - } \ - } STMT_END -#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 (large binary) follows (length, data) */ -#define SX_ARRAY C(2) /* Array forthcominng (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 (binary, small) follows (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_UTF8STR C(23) /* UTF-8 string forthcoming (small) */ -#define SX_LUTF8STR C(24) /* UTF-8 string forthcoming (large) */ -#define SX_FLAG_HASH C(25) /* Hash with flags forthcoming (size, flags, key/flags/value triplet list) */ -#define SX_CODE C(26) /* Code references as perl source code */ -#define SX_WEAKREF C(27) /* Weak reference to object forthcoming */ -#define SX_WEAKOVERLOAD C(28) /* Overloaded weak reference */ -#define SX_ERROR C(29) /* 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' /* A hash key introducer */ -#define SX_VALUE 'v' /* A 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: - * A 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 <murray@activestate.com> 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 ")" - - -/* - * Conditional UTF8 support. - * - */ -#ifdef SvUTF8_on -#define STORE_UTF8STR(pv, len) STORE_PV_LEN(pv, len, SX_UTF8STR, SX_LUTF8STR) -#define HAS_UTF8_SCALARS -#ifdef HeKUTF8 -#define HAS_UTF8_HASHES -#define HAS_UTF8_ALL -#else -/* 5.6 perl has utf8 scalars but not hashes */ -#endif -#else -#define SvUTF8(sv) 0 -#define STORE_UTF8STR(pv, len) CROAK(("panic: storing UTF8 in non-UTF8 perl")) -#endif -#ifndef HAS_UTF8_ALL -#define UTF8_CROAK() CROAK(("Cannot retrieve UTF8 data in non-UTF8 perl")) -#endif -#ifndef SvWEAKREF -#define WEAKREF_CROAK() CROAK(("Cannot retrieve weak references in this perl")) -#endif - -#ifdef HvPLACEHOLDERS -#define HAS_RESTRICTED_HASHES -#else -#define HVhek_PLACEHOLD 0x200 -#define RESTRICTED_HASH_CROAK() CROAK(("Cannot retrieve restricted hash")) -#endif - -#ifdef HvHASKFLAGS -#define HAS_HASH_KEY_FLAGS -#endif - -#ifdef ptr_table_new -#define USE_PTR_TABLE -#endif - -/* - * Fields s_tainted and s_dirty are prefixed with s_ because Perl's include - * files remap tainted and dirty when threading is enabled. That's bad for - * perl to remap such common words. -- RAM, 29/09/00 - */ - -struct stcxt; -typedef struct stcxt { - int entry; /* flags recursion */ - int optype; /* type of traversal operation */ - /* which objects have been seen, store time. - tags are numbers, which are cast to (SV *) and stored directly */ -#ifdef USE_PTR_TABLE - /* use pseen if we have ptr_tables. We have to store tag+1, because - tag numbers start at 0, and we can't store (SV *) 0 in a ptr_table - without it being confused for a fetch lookup failure. */ - struct ptr_tbl *pseen; - /* Still need hseen for the 0.6 file format code. */ -#endif - HV *hseen; - AV *hook_seen; /* which SVs were returned by STORABLE_freeze() */ - AV *aseen; /* which objects have been seen, retrieve time */ - IV where_is_undef; /* index in aseen of PL_sv_undef */ - 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 */ - IV tagnum; /* incremented at store time for each seen object */ - IV classnum; /* incremented at store time for each seen classname */ - int netorder; /* true if network order used */ - int s_tainted; /* true if input source is tainted, at retrieve time */ - int forgive_me; /* whether to be forgiving... */ - int deparse; /* whether to deparse code refs */ - SV *eval; /* whether to eval source code */ - int canonical; /* whether to store hashes sorted by key */ -#ifndef HAS_RESTRICTED_HASHES - int derestrict; /* whether to downgrade restrcted hashes */ -#endif -#ifndef HAS_UTF8_ALL - int use_bytes; /* whether to bytes-ify utf8 */ -#endif - int accept_future_minor; /* croak immediately on future minor versions? */ - int s_dirty; /* context is dirty due to CROAK() -- can be cleaned */ - int membuf_ro; /* true means membuf is read-only and msaved is rw */ - struct extendable keybuf; /* for hash key retrieval */ - struct extendable membuf; /* for memory store/retrieve operations */ - struct extendable msaved; /* where potentially valid mbuf is saved */ - 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)(pTHX_ struct stcxt *, const char *); /* retrieve dispatch table */ - SV *prev; /* contexts chained backwards in real recursion */ - SV *my_sv; /* the blessed scalar who's SvPVX() I am */ -} stcxt_t; - -#define NEW_STORABLE_CXT_OBJ(cxt) \ - STMT_START { \ - SV *self = newSV(sizeof(stcxt_t) - 1); \ - SV *my_sv = newRV_noinc(self); \ - sv_bless(my_sv, gv_stashpv("Storable::Cxt", GV_ADD)); \ - cxt = (stcxt_t *)SvPVX(self); \ - Zero(cxt, 1, stcxt_t); \ - cxt->my_sv = my_sv; \ - } STMT_END - -#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, 0) -#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 = ((perinterp_sv && SvIOK(perinterp_sv) && SvIVX(perinterp_sv) \ - ? (T)SvPVX(SvRV(INT2PTR(SV*,SvIVX(perinterp_sv)))) : (T) 0)) -#define dSTCXT \ - dSTCXT_SV; \ - dSTCXT_PTR(stcxt_t *, cxt) - -#define INIT_STCXT \ - dSTCXT; \ - NEW_STORABLE_CXT_OBJ(cxt); \ - sv_setiv(perinterp_sv, PTR2IV(cxt->my_sv)) - -#define SET_STCXT(x) \ - STMT_START { \ - dSTCXT_SV; \ - sv_setiv(perinterp_sv, PTR2IV(x->my_sv)); \ - } STMT_END - -#else /* !MULTIPLICITY && !PERL_OBJECT && !PERL_CAPI */ - -static stcxt_t *Context_ptr = NULL; -#define dSTCXT stcxt_t *cxt = Context_ptr -#define SET_STCXT(x) Context_ptr = x -#define INIT_STCXT \ - dSTCXT; \ - NEW_STORABLE_CXT_OBJ(cxt); \ - SET_STCXT(cxt) - - -#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) STMT_START { cxt->s_dirty = 1; croak x; } STMT_END - -/* - * End of "thread-safe" related definitions. - */ - -/* - * 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 -#define LOW_32BITS(x) ((I32) ((unsigned long) (x) & 0xffffffffUL)) -#endif - -/* - * oI, oS, oC - * - * Hack for Crays, where sizeof(I32) == 8, and which are big-endians. - * Used in the WLEN and RLEN macros. - */ - -#if INTSIZE > 4 -#define oI(x) ((I32 *) ((char *) (x) + 4)) -#define oS(x) ((x) - 4) -#define oC(x) (x = 0) -#define CRAY_HACK -#else -#define oI(x) (x) -#define oS(x) (x) -#define oC(x) -#endif - -/* - * key buffer handling - */ -#define kbuf (cxt->keybuf).arena -#define ksiz (cxt->keybuf).asiz -#define KBUFINIT() \ - STMT_START { \ - if (!kbuf) { \ - TRACEME(("** allocating kbuf of 128 bytes")); \ - New(10003, kbuf, 128, char); \ - ksiz = 128; \ - } \ - } STMT_END -#define KBUFCHK(x) \ - STMT_START { \ - if (x >= ksiz) { \ - TRACEME(("** extending kbuf to %d bytes (had %d)", x+1, ksiz)); \ - Renew(kbuf, x+1, char); \ - ksiz = x+1; \ - } \ - } STMT_END - -/* - * 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) \ - STMT_START { \ - if (!mbase) { \ - TRACEME(("** allocating mbase of %d bytes", MGROW)); \ - New(10003, mbase, MGROW, char); \ - msiz = (STRLEN)MGROW; \ - } \ - mptr = mbase; \ - if (x) \ - mend = mbase + x; \ - else \ - mend = mbase + msiz; \ - } STMT_END - -#define MBUF_TRUNC(x) mptr = mbase + x -#define MBUF_SIZE() (mptr - mbase) - -/* - * MBUF_SAVE_AND_LOAD - * MBUF_RESTORE - * - * Those macros are used in do_retrieve() to save the current memory - * buffer into cxt->msaved, before MBUF_LOAD() can be used to retrieve - * data from a string. - */ -#define MBUF_SAVE_AND_LOAD(in) \ - STMT_START { \ - ASSERT(!cxt->membuf_ro, ("mbase not already saved")); \ - cxt->membuf_ro = 1; \ - TRACEME(("saving mbuf")); \ - StructCopy(&cxt->membuf, &cxt->msaved, struct extendable); \ - MBUF_LOAD(in); \ - } STMT_END - -#define MBUF_RESTORE() \ - STMT_START { \ - ASSERT(cxt->membuf_ro, ("mbase is read-only")); \ - cxt->membuf_ro = 0; \ - TRACEME(("restoring mbuf")); \ - StructCopy(&cxt->msaved, &cxt->membuf, struct extendable); \ - } STMT_END - -/* - * Use SvPOKp(), because SvPOK() fails on tainted scalars. - * See store_scalar() for other usage of this workaround. - */ -#define MBUF_LOAD(v) \ - STMT_START { \ - ASSERT(cxt->membuf_ro, ("mbase is read-only")); \ - if (!SvPOKp(v)) \ - CROAK(("Not a scalar string")); \ - mptr = mbase = SvPV(v, msiz); \ - mend = mbase + msiz; \ - } STMT_END - -#define MBUF_XTEND(x) \ - STMT_START { \ - int nsz = (int) round_mgrow((x)+msiz); \ - int offset = mptr - mbase; \ - ASSERT(!cxt->membuf_ro, ("mbase is not read-only")); \ - TRACEME(("** extending mbase from %d to %d bytes (wants %d new)", \ - msiz, nsz, (x))); \ - Renew(mbase, nsz, char); \ - msiz = nsz; \ - mptr = mbase + offset; \ - mend = mbase + nsz; \ - } STMT_END - -#define MBUF_CHK(x) \ - STMT_START { \ - if ((mptr + (x)) > mend) \ - MBUF_XTEND(x); \ - } STMT_END - -#define MBUF_GETC(x) \ - STMT_START { \ - if (mptr < mend) \ - x = (int) (unsigned char) *mptr++; \ - else \ - return (SV *) 0; \ - } STMT_END - -#ifdef CRAY_HACK -#define MBUF_GETINT(x) \ - STMT_START { \ - oC(x); \ - if ((mptr + 4) <= mend) { \ - memcpy(oI(&x), mptr, 4); \ - mptr += 4; \ - } else \ - return (SV *) 0; \ - } STMT_END -#else -#define MBUF_GETINT(x) \ - STMT_START { \ - 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; \ - } STMT_END -#endif - -#define MBUF_READ(x,s) \ - STMT_START { \ - if ((mptr + (s)) <= mend) { \ - memcpy(x, mptr, s); \ - mptr += s; \ - } else \ - return (SV *) 0; \ - } STMT_END - -#define MBUF_SAFEREAD(x,s,z) \ - STMT_START { \ - if ((mptr + (s)) <= mend) { \ - memcpy(x, mptr, s); \ - mptr += s; \ - } else { \ - sv_free(z); \ - return (SV *) 0; \ - } \ - } STMT_END - -#define MBUF_SAFEPVREAD(x,s,z) \ - STMT_START { \ - if ((mptr + (s)) <= mend) { \ - memcpy(x, mptr, s); \ - mptr += s; \ - } else { \ - Safefree(z); \ - return (SV *) 0; \ - } \ - } STMT_END - -#define MBUF_PUTC(c) \ - STMT_START { \ - if (mptr < mend) \ - *mptr++ = (char) c; \ - else { \ - MBUF_XTEND(1); \ - *mptr++ = (char) c; \ - } \ - } STMT_END - -#ifdef CRAY_HACK -#define MBUF_PUTINT(i) \ - STMT_START { \ - MBUF_CHK(4); \ - memcpy(mptr, oI(&i), 4); \ - mptr += 4; \ - } STMT_END -#else -#define MBUF_PUTINT(i) \ - STMT_START { \ - MBUF_CHK(sizeof(int)); \ - if (int_aligned(mptr)) \ - *(int *) mptr = i; \ - else \ - memcpy(mptr, &i, sizeof(int)); \ - mptr += sizeof(int); \ - } STMT_END -#endif - -#define MBUF_WRITE(x,s) \ - STMT_START { \ - MBUF_CHK(s); \ - memcpy(mptr, x, s); \ - mptr += s; \ - } STMT_END - -/* - * 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_CODE 6 -#define svis_OTHER 7 - -/* - * 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 (last 2 bits in flags). - */ - -#define SHT_SCALAR 0 -#define SHT_ARRAY 1 -#define SHT_HASH 2 -#define SHT_EXTRA 3 /* Read extra byte for type */ - -/* - * The following are held in the "extra byte"... - */ - -#define SHT_TSCALAR 4 /* 4 + 0 -- tied scalar */ -#define SHT_TARRAY 5 /* 4 + 1 -- tied array */ -#define SHT_THASH 6 /* 4 + 2 -- tied hash */ - -/* - * per hash flags for flagged hashes - */ - -#define SHV_RESTRICTED 0x01 - -/* - * per key flags for flagged hashes - */ - -#define SHV_K_UTF8 0x01 -#define SHV_K_WASUTF8 0x02 -#define SHV_K_LOCKED 0x04 -#define SHV_K_ISSV 0x08 -#define SHV_K_PLACEHOLDER 0x10 - -/* - * 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 const char old_magicstr[] = "perl-store"; /* Magic number before 0.6 */ -static const char magicstr[] = "pst0"; /* Used as a magic number */ - -#define MAGICSTR_BYTES 'p','s','t','0' -#define OLDMAGICSTR_BYTES 'p','e','r','l','-','s','t','o','r','e' - -/* 5.6.x introduced the ability to have IVs as long long. - However, Configure still defined BYTEORDER based on the size of a long. - Storable uses the BYTEORDER value as part of the header, but doesn't - explicity store sizeof(IV) anywhere in the header. Hence on 5.6.x built - with IV as long long on a platform that uses Configure (ie most things - except VMS and Windows) headers are identical for the different IV sizes, - despite the files containing some fields based on sizeof(IV) - Erk. Broken-ness. - 5.8 is consistent - the following redifinition kludge is only needed on - 5.6.x, but the interwork is needed on 5.8 while data survives in files - with the 5.6 header. - -*/ - -#if defined (IVSIZE) && (IVSIZE == 8) && (LONGSIZE == 4) -#ifndef NO_56_INTERWORK_KLUDGE -#define USE_56_INTERWORK_KLUDGE -#endif -#if BYTEORDER == 0x1234 -#undef BYTEORDER -#define BYTEORDER 0x12345678 -#else -#if BYTEORDER == 0x4321 -#undef BYTEORDER -#define BYTEORDER 0x87654321 -#endif -#endif -#endif - -#if BYTEORDER == 0x1234 -#define BYTEORDER_BYTES '1','2','3','4' -#else -#if BYTEORDER == 0x12345678 -#define BYTEORDER_BYTES '1','2','3','4','5','6','7','8' -#ifdef USE_56_INTERWORK_KLUDGE -#define BYTEORDER_BYTES_56 '1','2','3','4' -#endif -#else -#if BYTEORDER == 0x87654321 -#define BYTEORDER_BYTES '8','7','6','5','4','3','2','1' -#ifdef USE_56_INTERWORK_KLUDGE -#define BYTEORDER_BYTES_56 '4','3','2','1' -#endif -#else -#if BYTEORDER == 0x4321 -#define BYTEORDER_BYTES '4','3','2','1' -#else -#error Unknown byteorder. Please append your byteorder to Storable.xs -#endif -#endif -#endif -#endif - -static const char byteorderstr[] = {BYTEORDER_BYTES, 0}; -#ifdef USE_56_INTERWORK_KLUDGE -static const char byteorderstr_56[] = {BYTEORDER_BYTES_56, 0}; -#endif - -#define STORABLE_BIN_MAJOR 2 /* Binary major "version" */ -#define STORABLE_BIN_MINOR 7 /* Binary minor "version" */ - -#if (PATCHLEVEL <= 5) -#define STORABLE_BIN_WRITE_MINOR 4 -#else -/* - * Perl 5.6.0 onwards can do weak references. -*/ -#define STORABLE_BIN_WRITE_MINOR 7 -#endif /* (PATCHLEVEL <= 5) */ - -#if (PATCHLEVEL < 8 || (PATCHLEVEL == 8 && SUBVERSION < 1)) -#define PL_sv_placeholder PL_sv_undef -#endif - -/* - * Useful store shortcuts... - */ - -/* - * Note that if you put more than one mark for storing a particular - * type of thing, *and* in the retrieve_foo() function you mark both - * the thingy's you get off with SEEN(), you *must* increase the - * tagnum with cxt->tagnum++ along with this macro! - * - samv 20Jan04 - */ -#define PUTMARK(x) \ - STMT_START { \ - if (!cxt->fio) \ - MBUF_PUTC(x); \ - else if (PerlIO_putc(cxt->fio, x) == EOF) \ - return -1; \ - } STMT_END - -#define WRITE_I32(x) \ - STMT_START { \ - ASSERT(sizeof(x) == sizeof(I32), ("writing an I32")); \ - if (!cxt->fio) \ - MBUF_PUTINT(x); \ - else if (PerlIO_write(cxt->fio, oI(&x), oS(sizeof(x))) != oS(sizeof(x))) \ - return -1; \ - } STMT_END - -#ifdef HAS_HTONL -#define WLEN(x) \ - STMT_START { \ - if (cxt->netorder) { \ - int y = (int) htonl(x); \ - if (!cxt->fio) \ - MBUF_PUTINT(y); \ - else if (PerlIO_write(cxt->fio,oI(&y),oS(sizeof(y))) != oS(sizeof(y))) \ - return -1; \ - } else { \ - if (!cxt->fio) \ - MBUF_PUTINT(x); \ - else if (PerlIO_write(cxt->fio,oI(&x),oS(sizeof(x))) != oS(sizeof(x))) \ - return -1; \ - } \ - } STMT_END -#else -#define WLEN(x) WRITE_I32(x) -#endif - -#define WRITE(x,y) \ - STMT_START { \ - if (!cxt->fio) \ - MBUF_WRITE(x,y); \ - else if (PerlIO_write(cxt->fio, x, y) != y) \ - return -1; \ - } STMT_END - -#define STORE_PV_LEN(pv, len, small, large) \ - STMT_START { \ - if (len <= LG_SCALAR) { \ - unsigned char clen = (unsigned char) len; \ - PUTMARK(small); \ - PUTMARK(clen); \ - if (len) \ - WRITE(pv, len); \ - } else { \ - PUTMARK(large); \ - WLEN(len); \ - WRITE(pv, len); \ - } \ - } STMT_END - -#define STORE_SCALAR(pv, len) STORE_PV_LEN(pv, len, SX_SCALAR, SX_LSCALAR) - -/* - * Store &PL_sv_undef in arrays without recursing through store(). - */ -#define STORE_SV_UNDEF() \ - STMT_START { \ - cxt->tagnum++; \ - PUTMARK(SX_SV_UNDEF); \ - } STMT_END - -/* - * Useful retrieve shortcuts... - */ - -#define GETCHAR() \ - (cxt->fio ? PerlIO_getc(cxt->fio) : (mptr >= mend ? EOF : (int) *mptr++)) - -#define GETMARK(x) \ - STMT_START { \ - if (!cxt->fio) \ - MBUF_GETC(x); \ - else if ((int) (x = PerlIO_getc(cxt->fio)) == EOF) \ - return (SV *) 0; \ - } STMT_END - -#define READ_I32(x) \ - STMT_START { \ - ASSERT(sizeof(x) == sizeof(I32), ("reading an I32")); \ - oC(x); \ - if (!cxt->fio) \ - MBUF_GETINT(x); \ - else if (PerlIO_read(cxt->fio, oI(&x), oS(sizeof(x))) != oS(sizeof(x))) \ - return (SV *) 0; \ - } STMT_END - -#ifdef HAS_NTOHL -#define RLEN(x) \ - STMT_START { \ - oC(x); \ - if (!cxt->fio) \ - MBUF_GETINT(x); \ - else if (PerlIO_read(cxt->fio, oI(&x), oS(sizeof(x))) != oS(sizeof(x))) \ - return (SV *) 0; \ - if (cxt->netorder) \ - x = (int) ntohl(x); \ - } STMT_END -#else -#define RLEN(x) READ_I32(x) -#endif - -#define READ(x,y) \ - STMT_START { \ - if (!cxt->fio) \ - MBUF_READ(x, y); \ - else if (PerlIO_read(cxt->fio, x, y) != y) \ - return (SV *) 0; \ - } STMT_END - -#define SAFEREAD(x,y,z) \ - STMT_START { \ - if (!cxt->fio) \ - MBUF_SAFEREAD(x,y,z); \ - else if (PerlIO_read(cxt->fio, x, y) != y) { \ - sv_free(z); \ - return (SV *) 0; \ - } \ - } STMT_END - -#define SAFEPVREAD(x,y,z) \ - STMT_START { \ - if (!cxt->fio) \ - MBUF_SAFEPVREAD(x,y,z); \ - else if (PerlIO_read(cxt->fio, x, y) != y) { \ - Safefree(z); \ - return (SV *) 0; \ - } \ - } STMT_END - -/* - * 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. - * - * We also need to bless objects ASAP for hooks (which may compute "ref $x" - * on the objects given to STORABLE_thaw and expect that to be defined), and - * also for overloaded objects (for which we might not find the stash if the - * object is not blessed yet--this might occur for overloaded objects that - * refer to themselves indirectly: if we blessed upon return from a sub - * retrieve(), the SX_OBJECT marker we'd found could not have overloading - * restored on it because the underlying object would not be blessed yet!). - * - * To achieve that, the class name of the last retrieved object is passed down - * recursively, and the first SEEN() call for which the class name is not NULL - * will bless the object. - * - * i should be true iff sv is immortal (ie PL_sv_yes, PL_sv_no or PL_sv_undef) - */ -#define SEEN(y,c,i) \ - STMT_START { \ - if (!y) \ - return (SV *) 0; \ - if (av_store(cxt->aseen, cxt->tagnum++, i ? (SV*)(y) : SvREFCNT_inc(y)) == 0) \ - return (SV *) 0; \ - TRACEME(("aseen(#%d) = 0x%"UVxf" (refcnt=%d)", cxt->tagnum-1, \ - PTR2UV(y), SvREFCNT(y)-1)); \ - if (c) \ - BLESS((SV *) (y), c); \ - } STMT_END - -/* - * Bless `s' in `p', via a temporary reference, required by sv_bless(). - */ -#define BLESS(s,p) \ - STMT_START { \ - SV *ref; \ - HV *stash; \ - TRACEME(("blessing 0x%"UVxf" in %s", PTR2UV(s), (p))); \ - stash = gv_stashpv((p), GV_ADD); \ - ref = newRV_noinc(s); \ - (void) sv_bless(ref, stash); \ - SvRV_set(ref, NULL); \ - SvREFCNT_dec(ref); \ - } STMT_END -/* - * sort (used in store_hash) - conditionally use qsort when - * sortsv is not available ( <= 5.6.1 ). - */ - -#if (PATCHLEVEL <= 6) - -#if defined(USE_ITHREADS) - -#define STORE_HASH_SORT \ - ENTER; { \ - PerlInterpreter *orig_perl = PERL_GET_CONTEXT; \ - SAVESPTR(orig_perl); \ - PERL_SET_CONTEXT(aTHX); \ - qsort((char *) AvARRAY(av), len, sizeof(SV *), sortcmp); \ - } LEAVE; - -#else /* ! USE_ITHREADS */ - -#define STORE_HASH_SORT \ - qsort((char *) AvARRAY(av), len, sizeof(SV *), sortcmp); - -#endif /* USE_ITHREADS */ - -#else /* PATCHLEVEL > 6 */ - -#define STORE_HASH_SORT \ - sortsv(AvARRAY(av), len, Perl_sv_cmp); - -#endif /* PATCHLEVEL <= 6 */ - -static int store(pTHX_ stcxt_t *cxt, SV *sv); -static SV *retrieve(pTHX_ stcxt_t *cxt, const char *cname); - -/* - * Dynamic dispatching table for SV store. - */ - -static int store_ref(pTHX_ stcxt_t *cxt, SV *sv); -static int store_scalar(pTHX_ stcxt_t *cxt, SV *sv); -static int store_array(pTHX_ stcxt_t *cxt, AV *av); -static int store_hash(pTHX_ stcxt_t *cxt, HV *hv); -static int store_tied(pTHX_ stcxt_t *cxt, SV *sv); -static int store_tied_item(pTHX_ stcxt_t *cxt, SV *sv); -static int store_code(pTHX_ stcxt_t *cxt, CV *cv); -static int store_other(pTHX_ stcxt_t *cxt, SV *sv); -static int store_blessed(pTHX_ stcxt_t *cxt, SV *sv, int type, HV *pkg); - -typedef int (*sv_store_t)(pTHX_ stcxt_t *cxt, SV *sv); - -static const sv_store_t sv_store[] = { - (sv_store_t)store_ref, /* svis_REF */ - (sv_store_t)store_scalar, /* svis_SCALAR */ - (sv_store_t)store_array, /* svis_ARRAY */ - (sv_store_t)store_hash, /* svis_HASH */ - (sv_store_t)store_tied, /* svis_TIED */ - (sv_store_t)store_tied_item, /* svis_TIED_ITEM */ - (sv_store_t)store_code, /* svis_CODE */ - (sv_store_t)store_other, /* svis_OTHER */ -}; - -#define SV_STORE(x) (*sv_store[x]) - -/* - * Dynamic dispatching tables for SV retrieval. - */ - -static SV *retrieve_lscalar(pTHX_ stcxt_t *cxt, const char *cname); -static SV *retrieve_lutf8str(pTHX_ stcxt_t *cxt, const char *cname); -static SV *old_retrieve_array(pTHX_ stcxt_t *cxt, const char *cname); -static SV *old_retrieve_hash(pTHX_ stcxt_t *cxt, const char *cname); -static SV *retrieve_ref(pTHX_ stcxt_t *cxt, const char *cname); -static SV *retrieve_undef(pTHX_ stcxt_t *cxt, const char *cname); -static SV *retrieve_integer(pTHX_ stcxt_t *cxt, const char *cname); -static SV *retrieve_double(pTHX_ stcxt_t *cxt, const char *cname); -static SV *retrieve_byte(pTHX_ stcxt_t *cxt, const char *cname); -static SV *retrieve_netint(pTHX_ stcxt_t *cxt, const char *cname); -static SV *retrieve_scalar(pTHX_ stcxt_t *cxt, const char *cname); -static SV *retrieve_utf8str(pTHX_ stcxt_t *cxt, const char *cname); -static SV *retrieve_tied_array(pTHX_ stcxt_t *cxt, const char *cname); -static SV *retrieve_tied_hash(pTHX_ stcxt_t *cxt, const char *cname); -static SV *retrieve_tied_scalar(pTHX_ stcxt_t *cxt, const char *cname); -static SV *retrieve_other(pTHX_ stcxt_t *cxt, const char *cname); - -typedef SV* (*sv_retrieve_t)(pTHX_ stcxt_t *cxt, const char *name); - -static const sv_retrieve_t sv_old_retrieve[] = { - 0, /* SX_OBJECT -- entry unused dynamically */ - (sv_retrieve_t)retrieve_lscalar, /* SX_LSCALAR */ - (sv_retrieve_t)old_retrieve_array, /* SX_ARRAY -- for pre-0.6 binaries */ - (sv_retrieve_t)old_retrieve_hash, /* SX_HASH -- for pre-0.6 binaries */ - (sv_retrieve_t)retrieve_ref, /* SX_REF */ - (sv_retrieve_t)retrieve_undef, /* SX_UNDEF */ - (sv_retrieve_t)retrieve_integer, /* SX_INTEGER */ - (sv_retrieve_t)retrieve_double, /* SX_DOUBLE */ - (sv_retrieve_t)retrieve_byte, /* SX_BYTE */ - (sv_retrieve_t)retrieve_netint, /* SX_NETINT */ - (sv_retrieve_t)retrieve_scalar, /* SX_SCALAR */ - (sv_retrieve_t)retrieve_tied_array, /* SX_ARRAY */ - (sv_retrieve_t)retrieve_tied_hash, /* SX_HASH */ - (sv_retrieve_t)retrieve_tied_scalar, /* SX_SCALAR */ - (sv_retrieve_t)retrieve_other, /* SX_SV_UNDEF not supported */ - (sv_retrieve_t)retrieve_other, /* SX_SV_YES not supported */ - (sv_retrieve_t)retrieve_other, /* SX_SV_NO not supported */ - (sv_retrieve_t)retrieve_other, /* SX_BLESS not supported */ - (sv_retrieve_t)retrieve_other, /* SX_IX_BLESS not supported */ - (sv_retrieve_t)retrieve_other, /* SX_HOOK not supported */ - (sv_retrieve_t)retrieve_other, /* SX_OVERLOADED not supported */ - (sv_retrieve_t)retrieve_other, /* SX_TIED_KEY not supported */ - (sv_retrieve_t)retrieve_other, /* SX_TIED_IDX not supported */ - (sv_retrieve_t)retrieve_other, /* SX_UTF8STR not supported */ - (sv_retrieve_t)retrieve_other, /* SX_LUTF8STR not supported */ - (sv_retrieve_t)retrieve_other, /* SX_FLAG_HASH not supported */ - (sv_retrieve_t)retrieve_other, /* SX_CODE not supported */ - (sv_retrieve_t)retrieve_other, /* SX_WEAKREF not supported */ - (sv_retrieve_t)retrieve_other, /* SX_WEAKOVERLOAD not supported */ - (sv_retrieve_t)retrieve_other, /* SX_ERROR */ -}; - -static SV *retrieve_array(pTHX_ stcxt_t *cxt, const char *cname); -static SV *retrieve_hash(pTHX_ stcxt_t *cxt, const char *cname); -static SV *retrieve_sv_undef(pTHX_ stcxt_t *cxt, const char *cname); -static SV *retrieve_sv_yes(pTHX_ stcxt_t *cxt, const char *cname); -static SV *retrieve_sv_no(pTHX_ stcxt_t *cxt, const char *cname); -static SV *retrieve_blessed(pTHX_ stcxt_t *cxt, const char *cname); -static SV *retrieve_idx_blessed(pTHX_ stcxt_t *cxt, const char *cname); -static SV *retrieve_hook(pTHX_ stcxt_t *cxt, const char *cname); -static SV *retrieve_overloaded(pTHX_ stcxt_t *cxt, const char *cname); -static SV *retrieve_tied_key(pTHX_ stcxt_t *cxt, const char *cname); -static SV *retrieve_tied_idx(pTHX_ stcxt_t *cxt, const char *cname); -static SV *retrieve_flag_hash(pTHX_ stcxt_t *cxt, const char *cname); -static SV *retrieve_code(pTHX_ stcxt_t *cxt, const char *cname); -static SV *retrieve_weakref(pTHX_ stcxt_t *cxt, const char *cname); -static SV *retrieve_weakoverloaded(pTHX_ stcxt_t *cxt, const char *cname); - -static const sv_retrieve_t sv_retrieve[] = { - 0, /* SX_OBJECT -- entry unused dynamically */ - (sv_retrieve_t)retrieve_lscalar, /* SX_LSCALAR */ - (sv_retrieve_t)retrieve_array, /* SX_ARRAY */ - (sv_retrieve_t)retrieve_hash, /* SX_HASH */ - (sv_retrieve_t)retrieve_ref, /* SX_REF */ - (sv_retrieve_t)retrieve_undef, /* SX_UNDEF */ - (sv_retrieve_t)retrieve_integer, /* SX_INTEGER */ - (sv_retrieve_t)retrieve_double, /* SX_DOUBLE */ - (sv_retrieve_t)retrieve_byte, /* SX_BYTE */ - (sv_retrieve_t)retrieve_netint, /* SX_NETINT */ - (sv_retrieve_t)retrieve_scalar, /* SX_SCALAR */ - (sv_retrieve_t)retrieve_tied_array, /* SX_ARRAY */ - (sv_retrieve_t)retrieve_tied_hash, /* SX_HASH */ - (sv_retrieve_t)retrieve_tied_scalar, /* SX_SCALAR */ - (sv_retrieve_t)retrieve_sv_undef, /* SX_SV_UNDEF */ - (sv_retrieve_t)retrieve_sv_yes, /* SX_SV_YES */ - (sv_retrieve_t)retrieve_sv_no, /* SX_SV_NO */ - (sv_retrieve_t)retrieve_blessed, /* SX_BLESS */ - (sv_retrieve_t)retrieve_idx_blessed, /* SX_IX_BLESS */ - (sv_retrieve_t)retrieve_hook, /* SX_HOOK */ - (sv_retrieve_t)retrieve_overloaded, /* SX_OVERLOAD */ - (sv_retrieve_t)retrieve_tied_key, /* SX_TIED_KEY */ - (sv_retrieve_t)retrieve_tied_idx, /* SX_TIED_IDX */ - (sv_retrieve_t)retrieve_utf8str, /* SX_UTF8STR */ - (sv_retrieve_t)retrieve_lutf8str, /* SX_LUTF8STR */ - (sv_retrieve_t)retrieve_flag_hash, /* SX_HASH */ - (sv_retrieve_t)retrieve_code, /* SX_CODE */ - (sv_retrieve_t)retrieve_weakref, /* SX_WEAKREF */ - (sv_retrieve_t)retrieve_weakoverloaded, /* SX_WEAKOVERLOAD */ - (sv_retrieve_t)retrieve_other, /* SX_ERROR */ -}; - -#define RETRIEVE(c,x) (*(c)->retrieve_vtbl[(x) >= SX_ERROR ? SX_ERROR : (x)]) - -static SV *mbuf2sv(pTHX); - -/*** - *** Context management. - ***/ - -/* - * init_perinterp - * - * Called once per "thread" (interpreter) to initialize some global context. - */ -static void init_perinterp(pTHX) -{ - INIT_STCXT; - - cxt->netorder = 0; /* true if network order used */ - cxt->forgive_me = -1; /* whether to be forgiving... */ - cxt->accept_future_minor = -1; /* would otherwise occur too late */ -} - -/* - * reset_context - * - * Called at the end of every context cleaning, to perform common reset - * operations. - */ -static void reset_context(stcxt_t *cxt) -{ - cxt->entry = 0; - cxt->s_dirty = 0; - cxt->optype &= ~(ST_STORE|ST_RETRIEVE); /* Leave ST_CLONE alone */ -} - -/* - * init_store_context - * - * Initialize a new store context for real recursion. - */ -static void init_store_context( - pTHX_ - 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->deparse = -1; /* Idem */ - cxt->eval = NULL; /* Idem */ - 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%. - */ - -#ifdef USE_PTR_TABLE - cxt->pseen = ptr_table_new(); - cxt->hseen = 0; -#else - cxt->hseen = newHV(); /* Table where seen objects are stored */ - HvSHAREKEYS_off(cxt->hseen); -#endif - /* - * 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 PERL_VERSION >= 5 -#define HBUCKETS 4096 /* Buckets for %hseen */ -#ifndef USE_PTR_TABLE - HvMAX(cxt->hseen) = HBUCKETS - 1; /* keys %hseen = $HBUCKETS; */ -#endif -#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 PERL_VERSION >= 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 */ - - /* - * The `hook_seen' array keeps track of all the SVs returned by - * STORABLE_freeze hooks for us to serialize, so that they are not - * reclaimed until the end of the serialization process. Each SV is - * only stored once, the first time it is seen. - */ - - cxt->hook_seen = newAV(); /* Lists SVs returned by STORABLE_freeze */ -} - -/* - * clean_store_context - * - * Clean store context by - */ -static void clean_store_context(pTHX_ 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. - */ - -#ifndef USE_PTR_TABLE - if (cxt->hseen) { - hv_iterinit(cxt->hseen); - while ((he = hv_iternext(cxt->hseen))) /* Extra () for -Wall, grr.. */ - HeVAL(he) = &PL_sv_undef; - } -#endif - - if (cxt->hclass) { - hv_iterinit(cxt->hclass); - while ((he = hv_iternext(cxt->hclass))) /* Extra () for -Wall, grr.. */ - HeVAL(he) = &PL_sv_undef; - } - - /* - * And now dispose of them... - * - * The surrounding if() protection has been added because there might be - * some cases where this routine is called more than once, during - * exceptionnal events. This was reported by Marc Lehmann when Storable - * is executed from mod_perl, and the fix was suggested by him. - * -- RAM, 20/12/2000 - */ - -#ifdef USE_PTR_TABLE - if (cxt->pseen) { - struct ptr_tbl *pseen = cxt->pseen; - cxt->pseen = 0; - ptr_table_free(pseen); - } - assert(!cxt->hseen); -#else - if (cxt->hseen) { - HV *hseen = cxt->hseen; - cxt->hseen = 0; - hv_undef(hseen); - sv_free((SV *) hseen); - } -#endif - - if (cxt->hclass) { - HV *hclass = cxt->hclass; - cxt->hclass = 0; - hv_undef(hclass); - sv_free((SV *) hclass); - } - - if (cxt->hook) { - HV *hook = cxt->hook; - cxt->hook = 0; - hv_undef(hook); - sv_free((SV *) hook); - } - - if (cxt->hook_seen) { - AV *hook_seen = cxt->hook_seen; - cxt->hook_seen = 0; - av_undef(hook_seen); - sv_free((SV *) hook_seen); - } - - cxt->forgive_me = -1; /* Fetched from perl if needed */ - cxt->deparse = -1; /* Idem */ - if (cxt->eval) { - SvREFCNT_dec(cxt->eval); - } - cxt->eval = NULL; /* Idem */ - cxt->canonical = -1; /* Idem */ - - reset_context(cxt); -} - -/* - * init_retrieve_context - * - * Initialize a new retrieve context for real recursion. - */ -static void init_retrieve_context(pTHX_ stcxt_t *cxt, int optype, int is_tainted) -{ - 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 */ - -#ifdef USE_PTR_TABLE - cxt->pseen = 0; -#endif - - /* - * 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 = (((void*)cxt->retrieve_vtbl == (void*)sv_old_retrieve) - ? newHV() : 0); - - cxt->aseen = newAV(); /* Where retrieved objects are kept */ - cxt->where_is_undef = -1; /* Special case for PL_sv_undef */ - 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->s_tainted = is_tainted; - cxt->entry = 1; /* No recursion yet */ -#ifndef HAS_RESTRICTED_HASHES - cxt->derestrict = -1; /* Fetched from perl if needed */ -#endif -#ifndef HAS_UTF8_ALL - cxt->use_bytes = -1; /* Fetched from perl if needed */ -#endif - cxt->accept_future_minor = -1; /* Fetched from perl if needed */ -} - -/* - * clean_retrieve_context - * - * Clean retrieve context by - */ -static void clean_retrieve_context(pTHX_ stcxt_t *cxt) -{ - TRACEME(("clean_retrieve_context")); - - ASSERT(cxt->optype & ST_RETRIEVE, ("was performing a retrieve()")); - - if (cxt->aseen) { - AV *aseen = cxt->aseen; - cxt->aseen = 0; - av_undef(aseen); - sv_free((SV *) aseen); - } - cxt->where_is_undef = -1; - - if (cxt->aclass) { - AV *aclass = cxt->aclass; - cxt->aclass = 0; - av_undef(aclass); - sv_free((SV *) aclass); - } - - if (cxt->hook) { - HV *hook = cxt->hook; - cxt->hook = 0; - hv_undef(hook); - sv_free((SV *) hook); - } - - if (cxt->hseen) { - HV *hseen = cxt->hseen; - cxt->hseen = 0; - hv_undef(hseen); - sv_free((SV *) hseen); /* optional HV, for backward compat. */ - } - -#ifndef HAS_RESTRICTED_HASHES - cxt->derestrict = -1; /* Fetched from perl if needed */ -#endif -#ifndef HAS_UTF8_ALL - cxt->use_bytes = -1; /* Fetched from perl if needed */ -#endif - cxt->accept_future_minor = -1; /* Fetched from perl if needed */ - - reset_context(cxt); -} - -/* - * clean_context - * - * A workaround for the CROAK bug: cleanup the last context. - */ -static void clean_context(pTHX_ stcxt_t *cxt) -{ - TRACEME(("clean_context")); - - ASSERT(cxt->s_dirty, ("dirty context")); - - if (cxt->membuf_ro) - MBUF_RESTORE(); - - ASSERT(!cxt->membuf_ro, ("mbase is not read-only")); - - if (cxt->optype & ST_RETRIEVE) - clean_retrieve_context(aTHX_ cxt); - else if (cxt->optype & ST_STORE) - clean_store_context(aTHX_ cxt); - else - reset_context(cxt); - - ASSERT(!cxt->s_dirty, ("context is clean")); - ASSERT(cxt->entry == 0, ("context is reset")); -} - -/* - * 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(pTHX_ stcxt_t *parent_cxt) -{ - stcxt_t *cxt; - - TRACEME(("allocate_context")); - - ASSERT(!parent_cxt->s_dirty, ("parent context clean")); - - NEW_STORABLE_CXT_OBJ(cxt); - cxt->prev = parent_cxt->my_sv; - SET_STCXT(cxt); - - ASSERT(!cxt->s_dirty, ("clean context")); - - 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(pTHX_ stcxt_t *cxt) -{ - stcxt_t *prev = (stcxt_t *)(cxt->prev ? SvPVX(SvRV(cxt->prev)) : 0); - - TRACEME(("free_context")); - - ASSERT(!cxt->s_dirty, ("clean context")); - ASSERT(prev, ("not freeing root context")); - - SvREFCNT_dec(cxt->my_sv); - SET_STCXT(prev); - - ASSERT(cxt, ("context not void")); -} - -/*** - *** Predicates. - ***/ - -/* - * is_storing - * - * Tells whether we're in the middle of a store operation. - */ -static int is_storing(pTHX) -{ - dSTCXT; - - return cxt->entry && (cxt->optype & ST_STORE); -} - -/* - * is_retrieving - * - * Tells whether we're in the middle of a retrieve operation. - */ -static int is_retrieving(pTHX) -{ - 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. - */ -static int last_op_in_netorder(pTHX) -{ - 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( - pTHX_ - HV *cache, - HV *pkg, - const char *method) -{ - GV *gv; - SV *sv; - const char *hvname = HvNAME_get(pkg); - - - /* - * 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%"UVxf, hvname, method, PTR2UV(sv))); - } else { - sv = newSVsv(&PL_sv_undef); - TRACEME(("%s->%s: not found", hvname, method)); - } - - /* - * Cache the result, ignoring failure: if we can't store the value, - * it just won't be cached. - */ - - (void) hv_store(cache, hvname, strlen(hvname), 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( - pTHX_ - HV *cache, - HV *pkg, - const char *method) -{ - const char *hvname = HvNAME_get(pkg); - (void) hv_store(cache, - hvname, strlen(hvname), newSVsv(&PL_sv_undef), 0); -} - -/* - * pkg_uncache - * - * Discard cached value: a whole fetch loop will be retried at next lookup. - */ -static void pkg_uncache( - pTHX_ - HV *cache, - HV *pkg, - const char *method) -{ - const char *hvname = HvNAME_get(pkg); - (void) hv_delete(cache, hvname, strlen(hvname), G_DISCARD); -} - -/* - * 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( - pTHX_ - HV *cache, - HV *pkg, - const char *method) -{ - SV **svh; - SV *sv; - const char *hvname = HvNAME_get(pkg); - - TRACEME(("pkg_can for %s->%s", hvname, 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, strlen(hvname), FALSE); - if (svh) { - sv = *svh; - if (!SvOK(sv)) { - TRACEME(("cached %s->%s: not found", hvname, method)); - return (SV *) 0; - } else { - TRACEME(("cached %s->%s: 0x%"UVxf, - hvname, method, PTR2UV(sv))); - return sv; - } - } - - TRACEME(("not cached yet")); - return pkg_fetchmeth(aTHX_ 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( - pTHX_ - 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%"UVxf")...", - i, PTR2UV(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 list context. - * Returns the list of returned values in an array. - */ -static AV *array_call( - pTHX_ - SV *obj, - SV *hook, - int cloning) -{ - dSP; - int count; - AV *av; - int i; - - TRACEME(("array_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( - pTHX_ - 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. - */ - - cxt->classnum++; - if (!hv_store(hclass, name, len, INT2PTR(SV*, 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 <object> or SX_OVERLOAD <object>. - */ -static int store_ref(pTHX_ stcxt_t *cxt, SV *sv) -{ - int is_weak = 0; - TRACEME(("store_ref (0x%"UVxf")", PTR2UV(sv))); - - /* - * Follow reference, and check if target is overloaded. - */ - -#ifdef SvWEAKREF - if (SvWEAKREF(sv)) - is_weak = 1; - TRACEME(("ref (0x%"UVxf") is%s weak", PTR2UV(sv), is_weak ? "" : "n't")); -#endif - sv = SvRV(sv); - - if (SvOBJECT(sv)) { - HV *stash = (HV *) SvSTASH(sv); - if (stash && Gv_AMG(stash)) { - TRACEME(("ref (0x%"UVxf") is overloaded", PTR2UV(sv))); - PUTMARK(is_weak ? SX_WEAKOVERLOAD : SX_OVERLOAD); - } else - PUTMARK(is_weak ? SX_WEAKREF : SX_REF); - } else - PUTMARK(is_weak ? SX_WEAKREF : SX_REF); - - return store(aTHX_ cxt, sv); -} - -/* - * store_scalar - * - * Store a scalar. - * - * Layout is SX_LSCALAR <length> <data>, SX_SCALAR <length> <data> or SX_UNDEF. - * The <data> section is omitted if <length> is 0. - * - * If integer or double, the layout is SX_INTEGER <data> or SX_DOUBLE <data>. - * Small integers (within [-127, +127]) are stored as SX_BYTE <byte>. - */ -static int store_scalar(pTHX_ stcxt_t *cxt, SV *sv) -{ - IV iv; - char *pv; - STRLEN len; - U32 flags = SvFLAGS(sv); /* "cc -O" may put it in register */ - - TRACEME(("store_scalar (0x%"UVxf")", PTR2UV(sv))); - - /* - * For efficiency, break the SV encapsulation by peaking at the flags - * directly without using the Perl macros to avoid dereferencing - * sv->sv_flags each time we wish to check the flags. - */ - - if (!(flags & SVf_OK)) { /* !SvOK(sv) */ - if (sv == &PL_sv_undef) { - TRACEME(("immortal undef")); - PUTMARK(SX_SV_UNDEF); - } else { - TRACEME(("undef at 0x%"UVxf, PTR2UV(sv))); - PUTMARK(SX_UNDEF); - } - return 0; - } - - /* - * Always store the string representation of a scalar if it exists. - * Gisle Aas provided me with this test case, better than a long speach: - * - * perl -MDevel::Peek -le '$a="abc"; $a+0; Dump($a)' - * SV = PVNV(0x80c8520) - * REFCNT = 1 - * FLAGS = (NOK,POK,pNOK,pPOK) - * IV = 0 - * NV = 0 - * PV = 0x80c83d0 "abc"\0 - * CUR = 3 - * LEN = 4 - * - * Write SX_SCALAR, length, followed by the actual data. - * - * Otherwise, write an SX_BYTE, SX_INTEGER or an SX_DOUBLE as - * appropriate, followed by the actual (binary) data. A double - * is written as a string if network order, for portability. - * - * NOTE: instead of using SvNOK(sv), we test for SvNOKp(sv). - * The reason is that when the scalar value is tainted, the SvNOK(sv) - * value is false. - * - * The test for a read-only scalar with both POK and NOK set is meant - * to quickly detect &PL_sv_yes and &PL_sv_no without having to pay the - * address comparison for each scalar we store. - */ - -#define SV_MAYBE_IMMORTAL (SVf_READONLY|SVf_POK|SVf_NOK) - - if ((flags & SV_MAYBE_IMMORTAL) == SV_MAYBE_IMMORTAL) { - if (sv == &PL_sv_yes) { - TRACEME(("immortal yes")); - PUTMARK(SX_SV_YES); - } else if (sv == &PL_sv_no) { - TRACEME(("immortal no")); - PUTMARK(SX_SV_NO); - } else { - pv = SvPV(sv, len); /* We know it's SvPOK */ - goto string; /* Share code below */ - } - } else if (flags & SVf_POK) { - /* public string - go direct to string read. */ - goto string_readlen; - } else if ( -#if (PATCHLEVEL <= 6) - /* For 5.6 and earlier NV flag trumps IV flag, so only use integer - direct if NV flag is off. */ - (flags & (SVf_NOK | SVf_IOK)) == SVf_IOK -#else - /* 5.7 rules are that if IV public flag is set, IV value is as - good, if not better, than NV value. */ - flags & SVf_IOK -#endif - ) { - iv = SvIV(sv); - /* - * Will come here from below with iv set if double is an integer. - */ - integer: - - /* Sorry. This isn't in 5.005_56 (IIRC) or earlier. */ -#ifdef SVf_IVisUV - /* Need to do this out here, else 0xFFFFFFFF becomes iv of -1 - * (for example) and that ends up in the optimised small integer - * case. - */ - if ((flags & SVf_IVisUV) && SvUV(sv) > IV_MAX) { - TRACEME(("large unsigned integer as string, value = %"UVuf, SvUV(sv))); - goto string_readlen; - } -#endif - /* - * Optimize small integers into a single byte, otherwise store as - * a real integer (converted into network order if they asked). - */ - - if (iv >= -128 && iv <= 127) { - unsigned char siv = (unsigned char) (iv + 128); /* [0,255] */ - PUTMARK(SX_BYTE); - PUTMARK(siv); - TRACEME(("small integer stored as %d", siv)); - } else if (cxt->netorder) { -#ifndef HAS_HTONL - TRACEME(("no htonl, fall back to string for integer")); - goto string_readlen; -#else - I32 niv; - - -#if IVSIZE > 4 - if ( -#ifdef SVf_IVisUV - /* Sorry. This isn't in 5.005_56 (IIRC) or earlier. */ - ((flags & SVf_IVisUV) && SvUV(sv) > 0x7FFFFFFF) || -#endif - (iv > 0x7FFFFFFF) || (iv < -0x80000000)) { - /* Bigger than 32 bits. */ - TRACEME(("large network order integer as string, value = %"IVdf, iv)); - goto string_readlen; - } -#endif - - niv = (I32) htonl((I32) iv); - TRACEME(("using network order")); - PUTMARK(SX_NETINT); - WRITE_I32(niv); -#endif - } else { - PUTMARK(SX_INTEGER); - WRITE(&iv, sizeof(iv)); - } - - TRACEME(("ok (integer 0x%"UVxf", value = %"IVdf")", PTR2UV(sv), iv)); - } else if (flags & SVf_NOK) { - NV nv; -#if (PATCHLEVEL <= 6) - nv = SvNV(sv); - /* - * Watch for number being an integer in disguise. - */ - if (nv == (NV) (iv = I_V(nv))) { - TRACEME(("double %"NVff" is actually integer %"IVdf, nv, iv)); - goto integer; /* Share code above */ - } -#else - - SvIV_please(sv); - if (SvIOK_notUV(sv)) { - iv = SvIV(sv); - goto integer; /* Share code above */ - } - nv = SvNV(sv); -#endif - - if (cxt->netorder) { - TRACEME(("double %"NVff" stored as string", nv)); - goto string_readlen; /* Share code below */ - } - - PUTMARK(SX_DOUBLE); - WRITE(&nv, sizeof(nv)); - - TRACEME(("ok (double 0x%"UVxf", value = %"NVff")", PTR2UV(sv), nv)); - - } else if (flags & (SVp_POK | SVp_NOK | SVp_IOK)) { - I32 wlen; /* For 64-bit machines */ - - string_readlen: - pv = SvPV(sv, len); - - /* - * Will come here from above if it was readonly, POK and NOK but - * neither &PL_sv_yes nor &PL_sv_no. - */ - string: - - wlen = (I32) len; /* WLEN via STORE_SCALAR expects I32 */ - if (SvUTF8 (sv)) - STORE_UTF8STR(pv, wlen); - else - STORE_SCALAR(pv, wlen); - TRACEME(("ok (scalar 0x%"UVxf" '%s', length = %"IVdf")", - PTR2UV(sv), SvPVX(sv), (IV)len)); - } else - CROAK(("Can't determine type of %s(0x%"UVxf")", - sv_reftype(sv, FALSE), - PTR2UV(sv))); - return 0; /* Ok, no recursion on scalars */ -} - -/* - * store_array - * - * Store an array. - * - * Layout is SX_ARRAY <size> followed by each item, in increading index order. - * Each item is stored as <object>. - */ -static int store_array(pTHX_ stcxt_t *cxt, AV *av) -{ - SV **sav; - I32 len = av_len(av) + 1; - I32 i; - int ret; - - TRACEME(("store_array (0x%"UVxf")", PTR2UV(av))); - - /* - * Signal array by emitting SX_ARRAY, followed by the array length. - */ - - PUTMARK(SX_ARRAY); - WLEN(len); - TRACEME(("size = %d", len)); - - /* - * Now store each item recursively. - */ - - for (i = 0; i < len; i++) { - sav = av_fetch(av, i, 0); - if (!sav) { - TRACEME(("(#%d) undef item", i)); - STORE_SV_UNDEF(); - continue; - } - TRACEME(("(#%d) item", i)); - if ((ret = store(aTHX_ cxt, *sav))) /* Extra () for -Wall, grr... */ - return ret; - } - - TRACEME(("ok (array)")); - - return 0; -} - - -#if (PATCHLEVEL <= 6) - -/* - * sortcmp - * - * Sort two SVs - * Borrowed from perl source file pp_ctl.c, where it is used by pp_sort. - */ -static int -sortcmp(const void *a, const void *b) -{ -#if defined(USE_ITHREADS) - dTHX; -#endif /* USE_ITHREADS */ - return sv_cmp(*(SV * const *) a, *(SV * const *) b); -} - -#endif /* PATCHLEVEL <= 6 */ - -/* - * store_hash - * - * Store a hash table. - * - * For a "normal" hash (not restricted, no utf8 keys): - * - * Layout is SX_HASH <size> followed by each key/value pair, in random order. - * Values are stored as <object>. - * Keys are stored as <length> <data>, the <data> section being omitted - * if length is 0. - * - * For a "fancy" hash (restricted or utf8 keys): - * - * Layout is SX_FLAG_HASH <size> <hash flags> followed by each key/value pair, - * in random order. - * Values are stored as <object>. - * Keys are stored as <flags> <length> <data>, the <data> section being omitted - * if length is 0. - * Currently the only hash flag is "restriced" - * Key flags are as for hv.h - */ -static int store_hash(pTHX_ stcxt_t *cxt, HV *hv) -{ - dVAR; - I32 len = -#ifdef HAS_RESTRICTED_HASHES - HvTOTALKEYS(hv); -#else - HvKEYS(hv); -#endif - I32 i; - int ret = 0; - I32 riter; - HE *eiter; - int flagged_hash = ((SvREADONLY(hv) -#ifdef HAS_HASH_KEY_FLAGS - || HvHASKFLAGS(hv) -#endif - ) ? 1 : 0); - unsigned char hash_flags = (SvREADONLY(hv) ? SHV_RESTRICTED : 0); - - if (flagged_hash) { - /* needs int cast for C++ compilers, doesn't it? */ - TRACEME(("store_hash (0x%"UVxf") (flags %x)", PTR2UV(hv), - (int) hash_flags)); - } else { - TRACEME(("store_hash (0x%"UVxf")", PTR2UV(hv))); - } - - /* - * Signal hash by emitting SX_HASH, followed by the table length. - */ - - if (flagged_hash) { - PUTMARK(SX_FLAG_HASH); - PUTMARK(hash_flags); - } else { - PUTMARK(SX_HASH); - } - WLEN(len); - TRACEME(("size = %d", len)); - - /* - * Save possible iteration state via each() on that table. - */ - - riter = HvRITER_get(hv); - eiter = HvEITER_get(hv); - hv_iterinit(hv); - - /* - * Now store each item recursively. - * - * If canonical is defined to some true value then store each - * key/value pair in sorted order otherwise the order is random. - * Canonical order is irrelevant when a deep clone operation is performed. - * - * Fetch the value from perl only once per store() operation, and only - * when needed. - */ - - if ( - !(cxt->optype & ST_CLONE) && (cxt->canonical == 1 || - (cxt->canonical < 0 && (cxt->canonical = - (SvTRUE(perl_get_sv("Storable::canonical", GV_ADD)) ? 1 : 0)))) - ) { - /* - * Storing in order, sorted by key. - * Run through the hash, building up an array of keys in a - * mortal array, sort the array and then run through the - * array. - */ - - AV *av = newAV(); - - /*av_extend (av, len);*/ - - TRACEME(("using canonical order")); - - for (i = 0; i < len; i++) { -#ifdef HAS_RESTRICTED_HASHES - HE *he = hv_iternext_flags(hv, HV_ITERNEXT_WANTPLACEHOLDERS); -#else - HE *he = hv_iternext(hv); -#endif - SV *key; - - if (!he) - CROAK(("Hash %p inconsistent - expected %d keys, %dth is NULL", hv, len, i)); - key = hv_iterkeysv(he); - av_store(av, AvFILLp(av)+1, key); /* av_push(), really */ - } - - STORE_HASH_SORT; - - for (i = 0; i < len; i++) { -#ifdef HAS_RESTRICTED_HASHES - int placeholders = (int)HvPLACEHOLDERS_get(hv); -#endif - unsigned char flags = 0; - char *keyval; - STRLEN keylen_tmp; - I32 keylen; - SV *key = av_shift(av); - /* This will fail if key is a placeholder. - Track how many placeholders we have, and error if we - "see" too many. */ - HE *he = hv_fetch_ent(hv, key, 0, 0); - SV *val; - - if (he) { - if (!(val = HeVAL(he))) { - /* Internal error, not I/O error */ - return 1; - } - } else { -#ifdef HAS_RESTRICTED_HASHES - /* Should be a placeholder. */ - if (placeholders-- < 0) { - /* This should not happen - number of - retrieves should be identical to - number of placeholders. */ - return 1; - } - /* Value is never needed, and PL_sv_undef is - more space efficient to store. */ - val = &PL_sv_undef; - ASSERT (flags == 0, - ("Flags not 0 but %d", flags)); - flags = SHV_K_PLACEHOLDER; -#else - return 1; -#endif - } - - /* - * Store value first. - */ - - TRACEME(("(#%d) value 0x%"UVxf, i, PTR2UV(val))); - - if ((ret = store(aTHX_ cxt, val))) /* Extra () for -Wall, grr... */ - goto out; - - /* - * Write key string. - * Keys are written after values to make sure retrieval - * can be optimal in terms of memory usage, where keys are - * read into a fixed unique buffer called kbuf. - * See retrieve_hash() for details. - */ - - /* Implementation of restricted hashes isn't nicely - abstracted: */ - if ((hash_flags & SHV_RESTRICTED) && SvREADONLY(val)) { - flags |= SHV_K_LOCKED; - } - - keyval = SvPV(key, keylen_tmp); - keylen = keylen_tmp; -#ifdef HAS_UTF8_HASHES - /* If you build without optimisation on pre 5.6 - then nothing spots that SvUTF8(key) is always 0, - so the block isn't optimised away, at which point - the linker dislikes the reference to - bytes_from_utf8. */ - if (SvUTF8(key)) { - const char *keysave = keyval; - bool is_utf8 = TRUE; - - /* Just casting the &klen to (STRLEN) won't work - well if STRLEN and I32 are of different widths. - --jhi */ - keyval = (char*)bytes_from_utf8((U8*)keyval, - &keylen_tmp, - &is_utf8); - - /* If we were able to downgrade here, then than - means that we have a key which only had chars - 0-255, but was utf8 encoded. */ - - if (keyval != keysave) { - keylen = keylen_tmp; - flags |= SHV_K_WASUTF8; - } else { - /* keylen_tmp can't have changed, so no need - to assign back to keylen. */ - flags |= SHV_K_UTF8; - } - } -#endif - - if (flagged_hash) { - PUTMARK(flags); - TRACEME(("(#%d) key '%s' flags %x %u", i, keyval, flags, *keyval)); - } else { - /* This is a workaround for a bug in 5.8.0 - that causes the HEK_WASUTF8 flag to be - set on an HEK without the hash being - marked as having key flags. We just - cross our fingers and drop the flag. - AMS 20030901 */ - assert (flags == 0 || flags == SHV_K_WASUTF8); - TRACEME(("(#%d) key '%s'", i, keyval)); - } - WLEN(keylen); - if (keylen) - WRITE(keyval, keylen); - if (flags & SHV_K_WASUTF8) - Safefree (keyval); - } - - /* - * Free up the temporary array - */ - - av_undef(av); - sv_free((SV *) av); - - } else { - - /* - * Storing in "random" order (in the order the keys are stored - * within the hash). This is the default and will be faster! - */ - - for (i = 0; i < len; i++) { - char *key = 0; - I32 len; - unsigned char flags; -#ifdef HV_ITERNEXT_WANTPLACEHOLDERS - HE *he = hv_iternext_flags(hv, HV_ITERNEXT_WANTPLACEHOLDERS); -#else - HE *he = hv_iternext(hv); -#endif - SV *val = (he ? hv_iterval(hv, he) : 0); - SV *key_sv = NULL; - HEK *hek; - - if (val == 0) - return 1; /* Internal error, not I/O error */ - - /* Implementation of restricted hashes isn't nicely - abstracted: */ - flags - = (((hash_flags & SHV_RESTRICTED) - && SvREADONLY(val)) - ? SHV_K_LOCKED : 0); - - if (val == &PL_sv_placeholder) { - flags |= SHV_K_PLACEHOLDER; - val = &PL_sv_undef; - } - - /* - * Store value first. - */ - - TRACEME(("(#%d) value 0x%"UVxf, i, PTR2UV(val))); - - if ((ret = store(aTHX_ cxt, val))) /* Extra () for -Wall, grr... */ - goto out; - - - hek = HeKEY_hek(he); - len = HEK_LEN(hek); - if (len == HEf_SVKEY) { - /* This is somewhat sick, but the internal APIs are - * such that XS code could put one of these in in - * a regular hash. - * Maybe we should be capable of storing one if - * found. - */ - key_sv = HeKEY_sv(he); - flags |= SHV_K_ISSV; - } else { - /* Regular string key. */ -#ifdef HAS_HASH_KEY_FLAGS - if (HEK_UTF8(hek)) - flags |= SHV_K_UTF8; - if (HEK_WASUTF8(hek)) - flags |= SHV_K_WASUTF8; -#endif - key = HEK_KEY(hek); - } - /* - * Write key string. - * Keys are written after values to make sure retrieval - * can be optimal in terms of memory usage, where keys are - * read into a fixed unique buffer called kbuf. - * See retrieve_hash() for details. - */ - - if (flagged_hash) { - PUTMARK(flags); - TRACEME(("(#%d) key '%s' flags %x", i, key, flags)); - } else { - /* This is a workaround for a bug in 5.8.0 - that causes the HEK_WASUTF8 flag to be - set on an HEK without the hash being - marked as having key flags. We just - cross our fingers and drop the flag. - AMS 20030901 */ - assert (flags == 0 || flags == SHV_K_WASUTF8); - TRACEME(("(#%d) key '%s'", i, key)); - } - if (flags & SHV_K_ISSV) { - store(aTHX_ cxt, key_sv); - } else { - WLEN(len); - if (len) - WRITE(key, len); - } - } - } - - TRACEME(("ok (hash 0x%"UVxf")", PTR2UV(hv))); - -out: - HvRITER_set(hv, riter); /* Restore hash iterator state */ - HvEITER_set(hv, eiter); - - return ret; -} - -/* - * store_code - * - * Store a code reference. - * - * Layout is SX_CODE <length> followed by a scalar containing the perl - * source code of the code reference. - */ -static int store_code(pTHX_ stcxt_t *cxt, CV *cv) -{ -#if PERL_VERSION < 6 - /* - * retrieve_code does not work with perl 5.005 or less - */ - return store_other(aTHX_ cxt, (SV*)cv); -#else - dSP; - I32 len; - int count, reallen; - SV *text, *bdeparse; - - TRACEME(("store_code (0x%"UVxf")", PTR2UV(cv))); - - if ( - cxt->deparse == 0 || - (cxt->deparse < 0 && !(cxt->deparse = - SvTRUE(perl_get_sv("Storable::Deparse", GV_ADD)) ? 1 : 0)) - ) { - return store_other(aTHX_ cxt, (SV*)cv); - } - - /* - * Require B::Deparse. At least B::Deparse 0.61 is needed for - * blessed code references. - */ - /* Ownership of both SVs is passed to load_module, which frees them. */ - load_module(PERL_LOADMOD_NOIMPORT, newSVpvn("B::Deparse",10), newSVnv(0.61)); - SPAGAIN; - - ENTER; - SAVETMPS; - - /* - * create the B::Deparse object - */ - - PUSHMARK(sp); - XPUSHs(sv_2mortal(newSVpvn("B::Deparse",10))); - PUTBACK; - count = call_method("new", G_SCALAR); - SPAGAIN; - if (count != 1) - CROAK(("Unexpected return value from B::Deparse::new\n")); - bdeparse = POPs; - - /* - * call the coderef2text method - */ - - PUSHMARK(sp); - XPUSHs(bdeparse); /* XXX is this already mortal? */ - XPUSHs(sv_2mortal(newRV_inc((SV*)cv))); - PUTBACK; - count = call_method("coderef2text", G_SCALAR); - SPAGAIN; - if (count != 1) - CROAK(("Unexpected return value from B::Deparse::coderef2text\n")); - - text = POPs; - len = SvCUR(text); - reallen = strlen(SvPV_nolen(text)); - - /* - * Empty code references or XS functions are deparsed as - * "(prototype) ;" or ";". - */ - - if (len == 0 || *(SvPV_nolen(text)+reallen-1) == ';') { - CROAK(("The result of B::Deparse::coderef2text was empty - maybe you're trying to serialize an XS function?\n")); - } - - /* - * Signal code by emitting SX_CODE. - */ - - PUTMARK(SX_CODE); - cxt->tagnum++; /* necessary, as SX_CODE is a SEEN() candidate */ - TRACEME(("size = %d", len)); - TRACEME(("code = %s", SvPV_nolen(text))); - - /* - * Now store the source code. - */ - - STORE_SCALAR(SvPV_nolen(text), len); - - FREETMPS; - LEAVE; - - TRACEME(("ok (code)")); - - return 0; -#endif -} - -/* - * store_tied - * - * When storing a tied object (be it a tied scalar, array or hash), we lay out - * a special mark, followed by the underlying tied object. For instance, when - * dealing with a tied hash, we store SX_TIED_HASH <hash object>, where - * <hash object> stands for the serialization of the tied hash. - */ -static int store_tied(pTHX_ stcxt_t *cxt, SV *sv) -{ - MAGIC *mg; - SV *obj = NULL; - int ret = 0; - int svt = SvTYPE(sv); - char mtype = 'P'; - - TRACEME(("store_tied (0x%"UVxf")", PTR2UV(sv))); - - /* - * We have a small run-time penalty here because we chose to factorise - * all tieds objects into the same routine, and not have a store_tied_hash, - * a store_tied_array, etc... - * - * Don't use a switch() statement, as most compilers don't optimize that - * well for 2/3 values. An if() else if() cascade is just fine. We put - * tied hashes first, as they are the most likely beasts. - */ - - if (svt == SVt_PVHV) { - TRACEME(("tied hash")); - PUTMARK(SX_TIED_HASH); /* Introduces tied hash */ - } else if (svt == SVt_PVAV) { - TRACEME(("tied array")); - PUTMARK(SX_TIED_ARRAY); /* Introduces tied array */ - } else { - TRACEME(("tied scalar")); - PUTMARK(SX_TIED_SCALAR); /* Introduces tied scalar */ - mtype = 'q'; - } - - if (!(mg = mg_find(sv, mtype))) - CROAK(("No magic '%c' found while storing tied %s", mtype, - (svt == SVt_PVHV) ? "hash" : - (svt == SVt_PVAV) ? "array" : "scalar")); - - /* - * The mg->mg_obj found by mg_find() above actually points to the - * underlying tied Perl object implementation. For instance, if the - * original SV was that of a tied array, then mg->mg_obj is an AV. - * - * Note that we store the Perl object as-is. We don't call its FETCH - * method along the way. At retrieval time, we won't call its STORE - * method either, but the tieing magic will be re-installed. In itself, - * that ensures that the tieing semantics are preserved since futher - * accesses on the retrieved object will indeed call the magic methods... - */ - - /* [#17040] mg_obj is NULL for scalar self-ties. AMS 20030416 */ - obj = mg->mg_obj ? mg->mg_obj : newSV(0); - if ((ret = store(aTHX_ cxt, obj))) - return ret; - - TRACEME(("ok (tied)")); - - return 0; -} - -/* - * store_tied_item - * - * Stores a reference to an item within a tied structure: - * - * . \$h{key}, stores both the (tied %h) object and 'key'. - * . \$a[idx], stores both the (tied @a) object and 'idx'. - * - * Layout is therefore either: - * SX_TIED_KEY <object> <key> - * SX_TIED_IDX <object> <index> - */ -static int store_tied_item(pTHX_ stcxt_t *cxt, SV *sv) -{ - MAGIC *mg; - int ret; - - TRACEME(("store_tied_item (0x%"UVxf")", PTR2UV(sv))); - - if (!(mg = mg_find(sv, 'p'))) - CROAK(("No magic 'p' found while storing reference to tied item")); - - /* - * We discriminate between \$h{key} and \$a[idx] via mg_ptr. - */ - - if (mg->mg_ptr) { - TRACEME(("store_tied_item: storing a ref to a tied hash item")); - PUTMARK(SX_TIED_KEY); - TRACEME(("store_tied_item: storing OBJ 0x%"UVxf, PTR2UV(mg->mg_obj))); - - if ((ret = store(aTHX_ cxt, mg->mg_obj))) /* Extra () for -Wall, grr... */ - return ret; - - TRACEME(("store_tied_item: storing PTR 0x%"UVxf, PTR2UV(mg->mg_ptr))); - - if ((ret = store(aTHX_ cxt, (SV *) mg->mg_ptr))) /* Idem, for -Wall */ - return ret; - } else { - I32 idx = mg->mg_len; - - TRACEME(("store_tied_item: storing a ref to a tied array item ")); - PUTMARK(SX_TIED_IDX); - TRACEME(("store_tied_item: storing OBJ 0x%"UVxf, PTR2UV(mg->mg_obj))); - - if ((ret = store(aTHX_ cxt, mg->mg_obj))) /* Idem, for -Wall */ - return ret; - - TRACEME(("store_tied_item: storing IDX %d", idx)); - - WLEN(idx); - } - - TRACEME(("ok (tied item)")); - - return 0; -} - -/* - * store_hook -- dispatched manually, not via sv_store[] - * - * The blessed SV is serialized by a hook. - * - * Simple Layout is: - * - * SX_HOOK <flags> <len> <classname> <len2> <str> [<len3> <object-IDs>] - * - * where <flags> indicates how long <len>, <len2> and <len3> are, whether - * the trailing part [] is present, the type of object (scalar, array or hash). - * There is also a bit which says how the classname is stored between: - * - * <len> <classname> - * <index> - * - * and when the <index> form is used (classname already seen), the "large - * classname" bit in <flags> indicates how large the <index> is. - * - * The serialized string returned by the hook is of length <len2> and comes - * next. It is an opaque string for us. - * - * Those <len3> object IDs which are listed last represent the extra references - * not directly serialized by the hook, but which are linked to the object. - * - * When recursion is mandated to resolve object-IDs not yet seen, we have - * instead, with <header> being flags with bits set to indicate the object type - * and that recursion was indeed needed: - * - * SX_HOOK <header> <object> <header> <object> <flags> - * - * that same header being repeated between serialized objects obtained through - * recursion, until we reach flags indicating no recursion, at which point - * we know we've resynchronized with a single layout, after <flags>. - * - * When storing a blessed ref to a tied variable, the following format is - * used: - * - * SX_HOOK <flags> <extra> ... [<len3> <object-IDs>] <magic object> - * - * The first <flags> indication carries an object of type SHT_EXTRA, and the - * real object type is held in the <extra> flag. At the very end of the - * serialization stream, the underlying magic object is serialized, just like - * any other tied variable. - */ -static int store_hook( - pTHX_ - stcxt_t *cxt, - SV *sv, - int type, - HV *pkg, - SV *hook) -{ - I32 len; - char *classname; - STRLEN len2; - SV *ref; - AV *av; - SV **ary; - int count; /* really len3 + 1 */ - unsigned char flags; - char *pv; - int i; - int recursed = 0; /* counts recursion */ - int obj_type; /* object type, on 2 bits */ - I32 classnum; - int ret; - int clone = cxt->optype & ST_CLONE; - char mtype = '\0'; /* for blessed ref to tied structures */ - unsigned char eflags = '\0'; /* used when object type is SHT_EXTRA */ - - TRACEME(("store_hook, classname \"%s\", tagged #%d", HvNAME_get(pkg), cxt->tagnum)); - - /* - * Determine object type on 2 bits. - */ - - switch (type) { - case svis_SCALAR: - obj_type = SHT_SCALAR; - break; - case svis_ARRAY: - obj_type = SHT_ARRAY; - break; - case svis_HASH: - obj_type = SHT_HASH; - break; - case svis_TIED: - /* - * Produced by a blessed ref to a tied data structure, $o in the - * following Perl code. - * - * my %h; - * tie %h, 'FOO'; - * my $o = bless \%h, 'BAR'; - * - * Signal the tie-ing magic by setting the object type as SHT_EXTRA - * (since we have only 2 bits in <flags> to store the type), and an - * <extra> byte flag will be emitted after the FIRST <flags> in the - * stream, carrying what we put in `eflags'. - */ - obj_type = SHT_EXTRA; - switch (SvTYPE(sv)) { - case SVt_PVHV: - eflags = (unsigned char) SHT_THASH; - mtype = 'P'; - break; - case SVt_PVAV: - eflags = (unsigned char) SHT_TARRAY; - mtype = 'P'; - break; - default: - eflags = (unsigned char) SHT_TSCALAR; - mtype = 'q'; - break; - } - break; - default: - CROAK(("Unexpected object type (%d) in store_hook()", type)); - } - flags = SHF_NEED_RECURSE | obj_type; - - classname = HvNAME_get(pkg); - len = strlen(classname); - - /* - * To call the hook, we need to fake a call like: - * - * $object->STORABLE_freeze($cloning); - * - * but we don't have the $object here. For instance, if $object is - * a blessed array, what we have in `sv' is the array, and we can't - * call a method on those. - * - * Therefore, we need to create a temporary reference to the object and - * make the call on that reference. - */ - - TRACEME(("about to call STORABLE_freeze on class %s", classname)); - - ref = newRV_noinc(sv); /* Temporary reference */ - av = array_call(aTHX_ ref, hook, clone); /* @a = $object->STORABLE_freeze($c) */ - SvRV_set(ref, NULL); - SvREFCNT_dec(ref); /* Reclaim temporary reference */ - - count = AvFILLp(av) + 1; - TRACEME(("store_hook, array holds %d items", count)); - - /* - * If they return an empty list, it means they wish to ignore the - * hook for this class (and not just this instance -- that's for them - * to handle if they so wish). - * - * Simply disable the cached entry for the hook (it won't be recomputed - * since it's present in the cache) and recurse to store_blessed(). - */ - - if (!count) { - /* - * They must not change their mind in the middle of a serialization. - */ - - if (hv_fetch(cxt->hclass, classname, len, FALSE)) - CROAK(("Too late to ignore hooks for %s class \"%s\"", - (cxt->optype & ST_CLONE) ? "cloning" : "storing", classname)); - - pkg_hide(aTHX_ cxt->hook, pkg, "STORABLE_freeze"); - - ASSERT(!pkg_can(aTHX_ cxt->hook, pkg, "STORABLE_freeze"), ("hook invisible")); - TRACEME(("ignoring STORABLE_freeze in class \"%s\"", classname)); - - return store_blessed(aTHX_ cxt, sv, type, pkg); - } - - /* - * Get frozen string. - */ - - ary = AvARRAY(av); - pv = SvPV(ary[0], len2); - /* We can't use pkg_can here because it only caches one method per - * package */ - { - GV* gv = gv_fetchmethod_autoload(pkg, "STORABLE_attach", FALSE); - if (gv && isGV(gv)) { - if (count > 1) - CROAK(("Freeze cannot return references if %s class is using STORABLE_attach", classname)); - goto check_done; - } - } - - /* - * If they returned more than one item, we need to serialize some - * extra references if not already done. - * - * Loop over the array, starting at position #1, and for each item, - * ensure it is a reference, serialize it if not already done, and - * replace the entry with the tag ID of the corresponding serialized - * object. - * - * We CHEAT by not calling av_fetch() and read directly within the - * array, for speed. - */ - - for (i = 1; i < count; i++) { -#ifdef USE_PTR_TABLE - char *fake_tag; -#else - SV **svh; -#endif - SV *rsv = ary[i]; - SV *xsv; - SV *tag; - AV *av_hook = cxt->hook_seen; - - if (!SvROK(rsv)) - CROAK(("Item #%d returned by STORABLE_freeze " - "for %s is not a reference", i, classname)); - xsv = SvRV(rsv); /* Follow ref to know what to look for */ - - /* - * Look in hseen and see if we have a tag already. - * Serialize entry if not done already, and get its tag. - */ - -#ifdef USE_PTR_TABLE - /* Fakery needed because ptr_table_fetch returns zero for a - failure, whereas the existing code assumes that it can - safely store a tag zero. So for ptr_tables we store tag+1 - */ - if ((fake_tag = (char *)ptr_table_fetch(cxt->pseen, xsv))) - goto sv_seen; /* Avoid moving code too far to the right */ -#else - if ((svh = hv_fetch(cxt->hseen, (char *) &xsv, sizeof(xsv), FALSE))) - goto sv_seen; /* Avoid moving code too far to the right */ -#endif - - TRACEME(("listed object %d at 0x%"UVxf" is unknown", i-1, PTR2UV(xsv))); - - /* - * We need to recurse to store that object and get it to be known - * so that we can resolve the list of object-IDs at retrieve time. - * - * The first time we do this, we need to emit the proper header - * indicating that we recursed, and what the type of object is (the - * object we're storing via a user-hook). Indeed, during retrieval, - * we'll have to create the object before recursing to retrieve the - * others, in case those would point back at that object. - */ - - /* [SX_HOOK] <flags> [<extra>] <object>*/ - if (!recursed++) { - PUTMARK(SX_HOOK); - PUTMARK(flags); - if (obj_type == SHT_EXTRA) - PUTMARK(eflags); - } else - PUTMARK(flags); - - if ((ret = store(aTHX_ cxt, xsv))) /* Given by hook for us to store */ - return ret; - -#ifdef USE_PTR_TABLE - fake_tag = (char *)ptr_table_fetch(cxt->pseen, xsv); - if (!sv) - CROAK(("Could not serialize item #%d from hook in %s", i, classname)); -#else - svh = hv_fetch(cxt->hseen, (char *) &xsv, sizeof(xsv), FALSE); - if (!svh) - CROAK(("Could not serialize item #%d from hook in %s", i, classname)); -#endif - /* - * It was the first time we serialized `xsv'. - * - * Keep this SV alive until the end of the serialization: if we - * disposed of it right now by decrementing its refcount, and it was - * a temporary value, some next temporary value allocated during - * another STORABLE_freeze might take its place, and we'd wrongly - * assume that new SV was already serialized, based on its presence - * in cxt->hseen. - * - * Therefore, push it away in cxt->hook_seen. - */ - - av_store(av_hook, AvFILLp(av_hook)+1, SvREFCNT_inc(xsv)); - - sv_seen: - /* - * Dispose of the REF they returned. If we saved the `xsv' away - * in the array of returned SVs, that will not cause the underlying - * referenced SV to be reclaimed. - */ - - ASSERT(SvREFCNT(xsv) > 1, ("SV will survive disposal of its REF")); - SvREFCNT_dec(rsv); /* Dispose of reference */ - - /* - * Replace entry with its tag (not a real SV, so no refcnt increment) - */ - -#ifdef USE_PTR_TABLE - tag = (SV *)--fake_tag; -#else - tag = *svh; -#endif - ary[i] = tag; - TRACEME(("listed object %d at 0x%"UVxf" is tag #%"UVuf, - i-1, PTR2UV(xsv), PTR2UV(tag))); - } - - /* - * Allocate a class ID if not already done. - * - * This needs to be done after the recursion above, since at retrieval - * time, we'll see the inner objects first. Many thanks to - * Salvador Ortiz Garcia <sog@msg.com.mx> who spot that bug and - * proposed the right fix. -- RAM, 15/09/2000 - */ - -check_done: - if (!known_class(aTHX_ cxt, classname, len, &classnum)) { - TRACEME(("first time we see class %s, ID = %d", classname, classnum)); - classnum = -1; /* Mark: we must store classname */ - } else { - TRACEME(("already seen class %s, ID = %d", classname, classnum)); - } - - /* - * Compute leading flags. - */ - - flags = obj_type; - if (((classnum == -1) ? len : classnum) > LG_SCALAR) - flags |= SHF_LARGE_CLASSLEN; - if (classnum != -1) - flags |= SHF_IDX_CLASSNAME; - if (len2 > LG_SCALAR) - flags |= SHF_LARGE_STRLEN; - if (count > 1) - flags |= SHF_HAS_LIST; - if (count > (LG_SCALAR + 1)) - flags |= SHF_LARGE_LISTLEN; - - /* - * We're ready to emit either serialized form: - * - * SX_HOOK <flags> <len> <classname> <len2> <str> [<len3> <object-IDs>] - * SX_HOOK <flags> <index> <len2> <str> [<len3> <object-IDs>] - * - * If we recursed, the SX_HOOK has already been emitted. - */ - - TRACEME(("SX_HOOK (recursed=%d) flags=0x%x " - "class=%"IVdf" len=%"IVdf" len2=%"IVdf" len3=%d", - recursed, flags, (IV)classnum, (IV)len, (IV)len2, count-1)); - - /* SX_HOOK <flags> [<extra>] */ - if (!recursed) { - PUTMARK(SX_HOOK); - PUTMARK(flags); - if (obj_type == SHT_EXTRA) - PUTMARK(eflags); - } else - PUTMARK(flags); - - /* <len> <classname> or <index> */ - if (flags & SHF_IDX_CLASSNAME) { - if (flags & SHF_LARGE_CLASSLEN) - WLEN(classnum); - else { - unsigned char cnum = (unsigned char) classnum; - PUTMARK(cnum); - } - } else { - if (flags & SHF_LARGE_CLASSLEN) - WLEN(len); - else { - unsigned char clen = (unsigned char) len; - PUTMARK(clen); - } - WRITE(classname, len); /* Final \0 is omitted */ - } - - /* <len2> <frozen-str> */ - if (flags & SHF_LARGE_STRLEN) { - I32 wlen2 = len2; /* STRLEN might be 8 bytes */ - WLEN(wlen2); /* Must write an I32 for 64-bit machines */ - } else { - unsigned char clen = (unsigned char) len2; - PUTMARK(clen); - } - if (len2) - WRITE(pv, (SSize_t)len2); /* Final \0 is omitted */ - - /* [<len3> <object-IDs>] */ - if (flags & SHF_HAS_LIST) { - int len3 = count - 1; - if (flags & SHF_LARGE_LISTLEN) - WLEN(len3); - else { - unsigned char clen = (unsigned char) len3; - PUTMARK(clen); - } - - /* - * NOTA BENE, for 64-bit machines: the ary[i] below does not yield a - * real pointer, rather a tag number, well under the 32-bit limit. - */ - - for (i = 1; i < count; i++) { - I32 tagval = htonl(LOW_32BITS(ary[i])); - WRITE_I32(tagval); - TRACEME(("object %d, tag #%d", i-1, ntohl(tagval))); - } - } - - /* - * Free the array. We need extra care for indices after 0, since they - * don't hold real SVs but integers cast. - */ - - if (count > 1) - AvFILLp(av) = 0; /* Cheat, nothing after 0 interests us */ - av_undef(av); - sv_free((SV *) av); - - /* - * If object was tied, need to insert serialization of the magic object. - */ - - if (obj_type == SHT_EXTRA) { - MAGIC *mg; - - if (!(mg = mg_find(sv, mtype))) { - int svt = SvTYPE(sv); - CROAK(("No magic '%c' found while storing ref to tied %s with hook", - mtype, (svt == SVt_PVHV) ? "hash" : - (svt == SVt_PVAV) ? "array" : "scalar")); - } - - TRACEME(("handling the magic object 0x%"UVxf" part of 0x%"UVxf, - PTR2UV(mg->mg_obj), PTR2UV(sv))); - - /* - * [<magic object>] - */ - - if ((ret = store(aTHX_ cxt, mg->mg_obj))) /* Extra () for -Wall, grr... */ - return ret; - } - - return 0; -} - -/* - * store_blessed -- dispatched manually, not via sv_store[] - * - * Check whether there is a STORABLE_xxx hook defined in the class or in one - * of its ancestors. If there is, then redispatch to store_hook(); - * - * Otherwise, the blessed SV is stored using the following layout: - * - * SX_BLESS <flag> <len> <classname> <object> - * - * where <flag> indicates whether <len> is stored on 0 or 4 bytes, depending - * on the high-order bit in flag: if 1, then length follows on 4 bytes. - * Otherwise, the low order bits give the length, thereby giving a compact - * representation for class names less than 127 chars long. - * - * Each <classname> seen is remembered and indexed, so that the next time - * an object in the blessed in the same <classname> is stored, the following - * will be emitted: - * - * SX_IX_BLESS <flag> <index> <object> - * - * where <index> is the classname index, stored on 0 or 4 bytes depending - * on the high-order bit in flag (same encoding as above for <len>). - */ -static int store_blessed( - pTHX_ - stcxt_t *cxt, - SV *sv, - int type, - HV *pkg) -{ - SV *hook; - I32 len; - char *classname; - I32 classnum; - - TRACEME(("store_blessed, type %d, class \"%s\"", type, HvNAME_get(pkg))); - - /* - * Look for a hook for this blessed SV and redirect to store_hook() - * if needed. - */ - - hook = pkg_can(aTHX_ cxt->hook, pkg, "STORABLE_freeze"); - if (hook) - return store_hook(aTHX_ cxt, sv, type, pkg, hook); - - /* - * This is a blessed SV without any serialization hook. - */ - - classname = HvNAME_get(pkg); - len = strlen(classname); - - TRACEME(("blessed 0x%"UVxf" in %s, no hook: tagged #%d", - PTR2UV(sv), classname, cxt->tagnum)); - - /* - * Determine whether it is the first time we see that class name (in which - * case it will be stored in the SX_BLESS form), or whether we already - * saw that class name before (in which case the SX_IX_BLESS form will be - * used). - */ - - if (known_class(aTHX_ cxt, classname, len, &classnum)) { - TRACEME(("already seen class %s, ID = %d", classname, classnum)); - PUTMARK(SX_IX_BLESS); - if (classnum <= LG_BLESS) { - unsigned char cnum = (unsigned char) classnum; - PUTMARK(cnum); - } else { - unsigned char flag = (unsigned char) 0x80; - PUTMARK(flag); - WLEN(classnum); - } - } else { - TRACEME(("first time we see class %s, ID = %d", classname, classnum)); - PUTMARK(SX_BLESS); - if (len <= LG_BLESS) { - unsigned char clen = (unsigned char) len; - PUTMARK(clen); - } else { - unsigned char flag = (unsigned char) 0x80; - PUTMARK(flag); - WLEN(len); /* Don't BER-encode, this should be rare */ - } - WRITE(classname, len); /* Final \0 is omitted */ - } - - /* - * Now emit the <object> part. - */ - - return SV_STORE(type)(aTHX_ cxt, sv); -} - -/* - * store_other - * - * We don't know how to store the item we reached, so return an error condition. - * (it's probably a GLOB, some CODE reference, etc...) - * - * If they defined the `forgive_me' variable at the Perl level to some - * true value, then don't croak, just warn, and store a placeholder string - * instead. - */ -static int store_other(pTHX_ stcxt_t *cxt, SV *sv) -{ - I32 len; - char buf[80]; - - TRACEME(("store_other")); - - /* - * Fetch the value from perl only once per store() operation. - */ - - if ( - cxt->forgive_me == 0 || - (cxt->forgive_me < 0 && !(cxt->forgive_me = - SvTRUE(perl_get_sv("Storable::forgive_me", GV_ADD)) ? 1 : 0)) - ) - CROAK(("Can't store %s items", sv_reftype(sv, FALSE))); - - warn("Can't store item %s(0x%"UVxf")", - sv_reftype(sv, FALSE), PTR2UV(sv)); - - /* - * Store placeholder string as a scalar instead... - */ - - (void) sprintf(buf, "You lost %s(0x%"UVxf")%c", sv_reftype(sv, FALSE), - PTR2UV(sv), (char) 0); - - len = strlen(buf); - STORE_SCALAR(buf, len); - TRACEME(("ok (dummy \"%s\", length = %"IVdf")", buf, (IV) len)); - - return 0; -} - -/*** - *** Store driving routines - ***/ - -/* - * sv_type - * - * WARNING: partially duplicates Perl's sv_reftype for speed. - * - * Returns the type of the SV, identified by an integer. That integer - * may then be used to index the dynamic routine dispatch table. - */ -static int sv_type(pTHX_ SV *sv) -{ - switch (SvTYPE(sv)) { - case SVt_NULL: -#if PERL_VERSION <= 10 - case SVt_IV: -#endif - case SVt_NV: - /* - * No need to check for ROK, that can't be set here since there - * is no field capable of hodling the xrv_rv reference. - */ - return svis_SCALAR; - case SVt_PV: -#if PERL_VERSION <= 10 - case SVt_RV: -#else - case SVt_IV: -#endif - case SVt_PVIV: - case SVt_PVNV: - /* - * Starting from SVt_PV, it is possible to have the ROK flag - * set, the pointer to the other SV being either stored in - * the xrv_rv (in the case of a pure SVt_RV), or as the - * xpv_pv field of an SVt_PV and its heirs. - * - * However, those SV cannot be magical or they would be an - * SVt_PVMG at least. - */ - return SvROK(sv) ? svis_REF : svis_SCALAR; - case SVt_PVMG: - case SVt_PVLV: /* Workaround for perl5.004_04 "LVALUE" bug */ - if (SvRMAGICAL(sv) && (mg_find(sv, 'p'))) - return svis_TIED_ITEM; - /* FALL THROUGH */ -#if PERL_VERSION < 9 - case SVt_PVBM: -#endif - if (SvRMAGICAL(sv) && (mg_find(sv, 'q'))) - return svis_TIED; - return SvROK(sv) ? svis_REF : svis_SCALAR; - case SVt_PVAV: - if (SvRMAGICAL(sv) && (mg_find(sv, 'P'))) - return svis_TIED; - return svis_ARRAY; - case SVt_PVHV: - if (SvRMAGICAL(sv) && (mg_find(sv, 'P'))) - return svis_TIED; - return svis_HASH; - case SVt_PVCV: - return svis_CODE; -#if PERL_VERSION > 8 - /* case SVt_BIND: */ -#endif - default: - break; - } - - return svis_OTHER; -} - -/* - * store - * - * Recursively store objects pointed to by the sv to the specified file. - * - * Layout is <content> or SX_OBJECT <tagnum> if we reach an already stored - * object (one for which storage has started -- it may not be over if we have - * a self-referenced structure). This data set forms a stored <object>. - */ -static int store(pTHX_ stcxt_t *cxt, SV *sv) -{ - SV **svh; - int ret; - int type; -#ifdef USE_PTR_TABLE - struct ptr_tbl *pseen = cxt->pseen; -#else - HV *hseen = cxt->hseen; -#endif - - TRACEME(("store (0x%"UVxf")", PTR2UV(sv))); - - /* - * If object has already been stored, do not duplicate data. - * Simply emit the SX_OBJECT marker followed by its tag data. - * The tag is always written in network order. - * - * NOTA BENE, for 64-bit machines: the "*svh" below does not yield a - * real pointer, rather a tag number (watch the insertion code below). - * That means it probably safe to assume it is well under the 32-bit limit, - * and makes the truncation safe. - * -- RAM, 14/09/1999 - */ - -#ifdef USE_PTR_TABLE - svh = (SV **)ptr_table_fetch(pseen, sv); -#else - svh = hv_fetch(hseen, (char *) &sv, sizeof(sv), FALSE); -#endif - if (svh) { - I32 tagval; - - if (sv == &PL_sv_undef) { - /* We have seen PL_sv_undef before, but fake it as - if we have not. - - Not the simplest solution to making restricted - hashes work on 5.8.0, but it does mean that - repeated references to the one true undef will - take up less space in the output file. - */ - /* Need to jump past the next hv_store, because on the - second store of undef the old hash value will be - SvREFCNT_dec()ed, and as Storable cheats horribly - by storing non-SVs in the hash a SEGV will ensure. - Need to increase the tag number so that the - receiver has no idea what games we're up to. This - special casing doesn't affect hooks that store - undef, as the hook routine does its own lookup into - hseen. Also this means that any references back - to PL_sv_undef (from the pathological case of hooks - storing references to it) will find the seen hash - entry for the first time, as if we didn't have this - hackery here. (That hseen lookup works even on 5.8.0 - because it's a key of &PL_sv_undef and a value - which is a tag number, not a value which is - PL_sv_undef.) */ - cxt->tagnum++; - type = svis_SCALAR; - goto undef_special_case; - } - -#ifdef USE_PTR_TABLE - tagval = htonl(LOW_32BITS(((char *)svh)-1)); -#else - tagval = htonl(LOW_32BITS(*svh)); -#endif - - TRACEME(("object 0x%"UVxf" seen as #%d", PTR2UV(sv), ntohl(tagval))); - - PUTMARK(SX_OBJECT); - WRITE_I32(tagval); - return 0; - } - - /* - * Allocate a new tag and associate it with the address of the sv being - * stored, before recursing... - * - * In order to avoid creating new SvIVs to hold the tagnum we just - * cast the tagnum to an SV pointer and store that in the hash. This - * means that we must clean up the hash manually afterwards, but gives - * us a 15% throughput increase. - * - */ - - cxt->tagnum++; -#ifdef USE_PTR_TABLE - ptr_table_store(pseen, sv, INT2PTR(SV*, 1 + cxt->tagnum)); -#else - if (!hv_store(hseen, - (char *) &sv, sizeof(sv), INT2PTR(SV*, cxt->tagnum), 0)) - return -1; -#endif - - /* - * Store `sv' and everything beneath it, using appropriate routine. - * Abort immediately if we get a non-zero status back. - */ - - type = sv_type(aTHX_ sv); - -undef_special_case: - TRACEME(("storing 0x%"UVxf" tag #%d, type %d...", - PTR2UV(sv), cxt->tagnum, type)); - - if (SvOBJECT(sv)) { - HV *pkg = SvSTASH(sv); - ret = store_blessed(aTHX_ cxt, sv, type, pkg); - } else - ret = SV_STORE(type)(aTHX_ cxt, sv); - - TRACEME(("%s (stored 0x%"UVxf", refcnt=%d, %s)", - ret ? "FAILED" : "ok", PTR2UV(sv), - SvREFCNT(sv), sv_reftype(sv, FALSE))); - - return ret; -} - -/* - * magic_write - * - * Write magic number and system information into the file. - * Layout is <magic> <network> [<len> <byteorder> <sizeof int> <sizeof long> - * <sizeof ptr>] where <len> is the length of the byteorder hexa string. - * All size and lenghts are written as single characters here. - * - * Note that no byte ordering info is emitted when <network> is true, since - * integers will be emitted in network order in that case. - */ -static int magic_write(pTHX_ stcxt_t *cxt) -{ - /* - * Starting with 0.6, the "use_network_order" byte flag is also used to - * indicate the version number of the binary image, encoded in the upper - * bits. The bit 0 is always used to indicate network order. - */ - /* - * Starting with 0.7, a full byte is dedicated to the minor version of - * the binary format, which is incremented only when new markers are - * introduced, for instance, but when backward compatibility is preserved. - */ - - /* Make these at compile time. The WRITE() macro is sufficiently complex - that it saves about 200 bytes doing it this way and only using it - once. */ - static const unsigned char network_file_header[] = { - MAGICSTR_BYTES, - (STORABLE_BIN_MAJOR << 1) | 1, - STORABLE_BIN_WRITE_MINOR - }; - static const unsigned char file_header[] = { - MAGICSTR_BYTES, - (STORABLE_BIN_MAJOR << 1) | 0, - STORABLE_BIN_WRITE_MINOR, - /* sizeof the array includes the 0 byte at the end: */ - (char) sizeof (byteorderstr) - 1, - BYTEORDER_BYTES, - (unsigned char) sizeof(int), - (unsigned char) sizeof(long), - (unsigned char) sizeof(char *), - (unsigned char) sizeof(NV) - }; -#ifdef USE_56_INTERWORK_KLUDGE - static const unsigned char file_header_56[] = { - MAGICSTR_BYTES, - (STORABLE_BIN_MAJOR << 1) | 0, - STORABLE_BIN_WRITE_MINOR, - /* sizeof the array includes the 0 byte at the end: */ - (char) sizeof (byteorderstr_56) - 1, - BYTEORDER_BYTES_56, - (unsigned char) sizeof(int), - (unsigned char) sizeof(long), - (unsigned char) sizeof(char *), - (unsigned char) sizeof(NV) - }; -#endif - const unsigned char *header; - SSize_t length; - - TRACEME(("magic_write on fd=%d", cxt->fio ? PerlIO_fileno(cxt->fio) : -1)); - - if (cxt->netorder) { - header = network_file_header; - length = sizeof (network_file_header); - } else { -#ifdef USE_56_INTERWORK_KLUDGE - if (SvTRUE(perl_get_sv("Storable::interwork_56_64bit", GV_ADD))) { - header = file_header_56; - length = sizeof (file_header_56); - } else -#endif - { - header = file_header; - length = sizeof (file_header); - } - } - - if (!cxt->fio) { - /* sizeof the array includes the 0 byte at the end. */ - header += sizeof (magicstr) - 1; - length -= sizeof (magicstr) - 1; - } - - WRITE( (unsigned char*) header, length); - - if (!cxt->netorder) { - TRACEME(("ok (magic_write byteorder = 0x%lx [%d], I%d L%d P%d D%d)", - (unsigned long) BYTEORDER, (int) sizeof (byteorderstr) - 1, - (int) sizeof(int), (int) sizeof(long), - (int) sizeof(char *), (int) sizeof(NV))); - } - return 0; -} - -/* - * do_store - * - * Common code for store operations. - * - * When memory store is requested (f = NULL) and a non null SV* is given in - * `res', it is filled with a new SV created out of the memory buffer. - * - * It is required to provide a non-null `res' when the operation type is not - * dclone() and store() is performed to memory. - */ -static int do_store( - pTHX_ - PerlIO *f, - SV *sv, - int optype, - int network_order, - SV **res) -{ - dSTCXT; - int status; - - ASSERT(!(f == 0 && !(optype & ST_CLONE)) || res, - ("must supply result SV pointer for real recursion to memory")); - - TRACEME(("do_store (optype=%d, netorder=%d)", - optype, network_order)); - - optype |= ST_STORE; - - /* - * Workaround for CROAK leak: if they enter with a "dirty" context, - * free up memory for them now. - */ - - if (cxt->s_dirty) - clean_context(aTHX_ cxt); - - /* - * Now that STORABLE_xxx hooks exist, it is possible that they try to - * re-enter store() via the hooks. We need to stack contexts. - */ - - if (cxt->entry) - cxt = allocate_context(aTHX_ cxt); - - cxt->entry++; - - ASSERT(cxt->entry == 1, ("starting new recursion")); - ASSERT(!cxt->s_dirty, ("clean context")); - - /* - * Ensure sv is actually a reference. From perl, we called something - * like: - * pstore(aTHX_ FILE, \@array); - * so we must get the scalar value behing that reference. - */ - - if (!SvROK(sv)) - CROAK(("Not a reference")); - sv = SvRV(sv); /* So follow it to know what to store */ - - /* - * If we're going to store to memory, reset the buffer. - */ - - if (!f) - MBUF_INIT(0); - - /* - * Prepare context and emit headers. - */ - - init_store_context(aTHX_ cxt, f, optype, network_order); - - if (-1 == magic_write(aTHX_ cxt)) /* Emit magic and ILP info */ - return 0; /* Error */ - - /* - * Recursively store object... - */ - - ASSERT(is_storing(aTHX), ("within store operation")); - - status = store(aTHX_ cxt, sv); /* Just do it! */ - - /* - * If they asked for a memory store and they provided an SV pointer, - * make an SV string out of the buffer and fill their pointer. - * - * When asking for ST_REAL, it's MANDATORY for the caller to provide - * an SV, since context cleanup might free the buffer if we did recurse. - * (unless caller is dclone(), which is aware of that). - */ - - if (!cxt->fio && res) - *res = mbuf2sv(aTHX); - - /* - * Final cleanup. - * - * The "root" context is never freed, since it is meant to be always - * handy for the common case where no recursion occurs at all (i.e. - * we enter store() outside of any Storable code and leave it, period). - * We know it's the "root" context because there's nothing stacked - * underneath it. - * - * OPTIMIZATION: - * - * When deep cloning, we don't free the context: doing so would force - * us to copy the data in the memory buffer. Sicne we know we're - * about to enter do_retrieve... - */ - - clean_store_context(aTHX_ cxt); - if (cxt->prev && !(cxt->optype & ST_CLONE)) - free_context(aTHX_ cxt); - - TRACEME(("do_store returns %d", status)); - - return status == 0; -} - -/* - * pstore - * - * Store the transitive data closure of given object to disk. - * Returns 0 on error, a true value otherwise. - */ -static int pstore(pTHX_ PerlIO *f, SV *sv) -{ - TRACEME(("pstore")); - return do_store(aTHX_ f, sv, 0, FALSE, (SV**) 0); - -} - -/* - * net_pstore - * - * Same as pstore(), but network order is used for integers and doubles are - * emitted as strings. - */ -static int net_pstore(pTHX_ PerlIO *f, SV *sv) -{ - TRACEME(("net_pstore")); - return do_store(aTHX_ f, sv, 0, TRUE, (SV**) 0); -} - -/*** - *** Memory stores. - ***/ - -/* - * mbuf2sv - * - * Build a new SV out of the content of the internal memory buffer. - */ -static SV *mbuf2sv(pTHX) -{ - dSTCXT; - - return newSVpv(mbase, MBUF_SIZE()); -} - -/* - * mstore - * - * Store the transitive data closure of given object to memory. - * Returns undef on error, a scalar value containing the data otherwise. - */ -static SV *mstore(pTHX_ SV *sv) -{ - SV *out; - - TRACEME(("mstore")); - - if (!do_store(aTHX_ (PerlIO*) 0, sv, 0, FALSE, &out)) - return &PL_sv_undef; - - return out; -} - -/* - * net_mstore - * - * Same as mstore(), but network order is used for integers and doubles are - * emitted as strings. - */ -static SV *net_mstore(pTHX_ SV *sv) -{ - SV *out; - - TRACEME(("net_mstore")); - - if (!do_store(aTHX_ (PerlIO*) 0, sv, 0, TRUE, &out)) - return &PL_sv_undef; - - return out; -} - -/*** - *** Specific retrieve callbacks. - ***/ - -/* - * retrieve_other - * - * Return an error via croak, since it is not possible that we get here - * under normal conditions, when facing a file produced via pstore(). - */ -static SV *retrieve_other(pTHX_ stcxt_t *cxt, const char *cname) -{ - if ( - cxt->ver_major != STORABLE_BIN_MAJOR && - cxt->ver_minor != STORABLE_BIN_MINOR - ) { - CROAK(("Corrupted storable %s (binary v%d.%d), current is v%d.%d", - cxt->fio ? "file" : "string", - cxt->ver_major, cxt->ver_minor, - STORABLE_BIN_MAJOR, STORABLE_BIN_MINOR)); - } else { - CROAK(("Corrupted storable %s (binary v%d.%d)", - cxt->fio ? "file" : "string", - cxt->ver_major, cxt->ver_minor)); - } - - return (SV *) 0; /* Just in case */ -} - -/* - * retrieve_idx_blessed - * - * Layout is SX_IX_BLESS <index> <object> with SX_IX_BLESS already read. - * <index> can be coded on either 1 or 5 bytes. - */ -static SV *retrieve_idx_blessed(pTHX_ stcxt_t *cxt, const char *cname) -{ - I32 idx; - const char *classname; - SV **sva; - SV *sv; - - TRACEME(("retrieve_idx_blessed (#%d)", cxt->tagnum)); - ASSERT(!cname, ("no bless-into class given here, got %s", cname)); - - GETMARK(idx); /* Index coded on a single char? */ - if (idx & 0x80) - RLEN(idx); - - /* - * Fetch classname in `aclass' - */ - - sva = av_fetch(cxt->aclass, idx, FALSE); - if (!sva) - CROAK(("Class name #%"IVdf" should have been seen already", (IV) idx)); - - classname = SvPVX(*sva); /* We know it's a PV, by construction */ - - TRACEME(("class ID %d => %s", idx, classname)); - - /* - * Retrieve object and bless it. - */ - - sv = retrieve(aTHX_ cxt, classname); /* First SV which is SEEN will be blessed */ - - return sv; -} - -/* - * retrieve_blessed - * - * Layout is SX_BLESS <len> <classname> <object> with SX_BLESS already read. - * <len> can be coded on either 1 or 5 bytes. - */ -static SV *retrieve_blessed(pTHX_ stcxt_t *cxt, const char *cname) -{ - I32 len; - SV *sv; - char buf[LG_BLESS + 1]; /* Avoid malloc() if possible */ - char *classname = buf; - char *malloced_classname = NULL; - - TRACEME(("retrieve_blessed (#%d)", cxt->tagnum)); - ASSERT(!cname, ("no bless-into class given here, got %s", cname)); - - /* - * Decode class name length and read that name. - * - * Short classnames have two advantages: their length is stored on one - * single byte, and the string can be read on the stack. - */ - - GETMARK(len); /* Length coded on a single char? */ - if (len & 0x80) { - RLEN(len); - TRACEME(("** allocating %d bytes for class name", len+1)); - New(10003, classname, len+1, char); - malloced_classname = classname; - } - SAFEPVREAD(classname, len, malloced_classname); - classname[len] = '\0'; /* Mark string end */ - - /* - * It's a new classname, otherwise it would have been an SX_IX_BLESS. - */ - - TRACEME(("new class name \"%s\" will bear ID = %d", classname, cxt->classnum)); - - if (!av_store(cxt->aclass, cxt->classnum++, newSVpvn(classname, len))) { - Safefree(malloced_classname); - return (SV *) 0; - } - - /* - * Retrieve object and bless it. - */ - - sv = retrieve(aTHX_ cxt, classname); /* First SV which is SEEN will be blessed */ - if (malloced_classname) - Safefree(malloced_classname); - - return sv; -} - -/* - * retrieve_hook - * - * Layout: SX_HOOK <flags> <len> <classname> <len2> <str> [<len3> <object-IDs>] - * with leading mark already read, as usual. - * - * When recursion was involved during serialization of the object, there - * is an unknown amount of serialized objects after the SX_HOOK mark. Until - * we reach a <flags> marker with the recursion bit cleared. - * - * If the first <flags> byte contains a type of SHT_EXTRA, then the real type - * is held in the <extra> byte, and if the object is tied, the serialized - * magic object comes at the very end: - * - * SX_HOOK <flags> <extra> ... [<len3> <object-IDs>] <magic object> - * - * This means the STORABLE_thaw hook will NOT get a tied variable during its - * processing (since we won't have seen the magic object by the time the hook - * is called). See comments below for why it was done that way. - */ -static SV *retrieve_hook(pTHX_ stcxt_t *cxt, const char *cname) -{ - I32 len; - char buf[LG_BLESS + 1]; /* Avoid malloc() if possible */ - char *classname = buf; - unsigned int flags; - I32 len2; - SV *frozen; - I32 len3 = 0; - AV *av = 0; - SV *hook; - SV *sv; - SV *rv; - GV *attach; - int obj_type; - int clone = cxt->optype & ST_CLONE; - char mtype = '\0'; - unsigned int extra_type = 0; - - TRACEME(("retrieve_hook (#%d)", cxt->tagnum)); - ASSERT(!cname, ("no bless-into class given here, got %s", cname)); - - /* - * Read flags, which tell us about the type, and whether we need to recurse. - */ - - GETMARK(flags); - - /* - * Create the (empty) object, and mark it as seen. - * - * This must be done now, because tags are incremented, and during - * serialization, the object tag was affected before recursion could - * take place. - */ - - obj_type = flags & SHF_TYPE_MASK; - switch (obj_type) { - case SHT_SCALAR: - sv = newSV(0); - break; - case SHT_ARRAY: - sv = (SV *) newAV(); - break; - case SHT_HASH: - sv = (SV *) newHV(); - break; - case SHT_EXTRA: - /* - * Read <extra> flag to know the type of the object. - * Record associated magic type for later. - */ - GETMARK(extra_type); - switch (extra_type) { - case SHT_TSCALAR: - sv = newSV(0); - mtype = 'q'; - break; - case SHT_TARRAY: - sv = (SV *) newAV(); - mtype = 'P'; - break; - case SHT_THASH: - sv = (SV *) newHV(); - mtype = 'P'; - break; - default: - return retrieve_other(aTHX_ cxt, 0); /* Let it croak */ - } - break; - default: - return retrieve_other(aTHX_ cxt, 0); /* Let it croak */ - } - SEEN(sv, 0, 0); /* Don't bless yet */ - - /* - * Whilst flags tell us to recurse, do so. - * - * We don't need to remember the addresses returned by retrieval, because - * all the references will be obtained through indirection via the object - * tags in the object-ID list. - * - * We need to decrement the reference count for these objects - * because, if the user doesn't save a reference to them in the hook, - * they must be freed when this context is cleaned. - */ - - while (flags & SHF_NEED_RECURSE) { - TRACEME(("retrieve_hook recursing...")); - rv = retrieve(aTHX_ cxt, 0); - if (!rv) - return (SV *) 0; - SvREFCNT_dec(rv); - TRACEME(("retrieve_hook back with rv=0x%"UVxf, - PTR2UV(rv))); - GETMARK(flags); - } - - if (flags & SHF_IDX_CLASSNAME) { - SV **sva; - I32 idx; - - /* - * Fetch index from `aclass' - */ - - if (flags & SHF_LARGE_CLASSLEN) - RLEN(idx); - else - GETMARK(idx); - - sva = av_fetch(cxt->aclass, idx, FALSE); - if (!sva) - CROAK(("Class name #%"IVdf" should have been seen already", - (IV) idx)); - - classname = SvPVX(*sva); /* We know it's a PV, by construction */ - TRACEME(("class ID %d => %s", idx, classname)); - - } else { - /* - * Decode class name length and read that name. - * - * NOTA BENE: even if the length is stored on one byte, we don't read - * on the stack. Just like retrieve_blessed(), we limit the name to - * LG_BLESS bytes. This is an arbitrary decision. - */ - char *malloced_classname = NULL; - - if (flags & SHF_LARGE_CLASSLEN) - RLEN(len); - else - GETMARK(len); - - if (len > LG_BLESS) { - TRACEME(("** allocating %d bytes for class name", len+1)); - New(10003, classname, len+1, char); - malloced_classname = classname; - } - - SAFEPVREAD(classname, len, malloced_classname); - classname[len] = '\0'; /* Mark string end */ - - /* - * Record new classname. - */ - - if (!av_store(cxt->aclass, cxt->classnum++, newSVpvn(classname, len))) { - Safefree(malloced_classname); - return (SV *) 0; - } - } - - TRACEME(("class name: %s", classname)); - - /* - * Decode user-frozen string length and read it in an SV. - * - * For efficiency reasons, we read data directly into the SV buffer. - * To understand that code, read retrieve_scalar() - */ - - if (flags & SHF_LARGE_STRLEN) - RLEN(len2); - else - GETMARK(len2); - - frozen = NEWSV(10002, len2); - if (len2) { - SAFEREAD(SvPVX(frozen), len2, frozen); - SvCUR_set(frozen, len2); - *SvEND(frozen) = '\0'; - } - (void) SvPOK_only(frozen); /* Validates string pointer */ - if (cxt->s_tainted) /* Is input source tainted? */ - SvTAINT(frozen); - - TRACEME(("frozen string: %d bytes", len2)); - - /* - * Decode object-ID list length, if present. - */ - - if (flags & SHF_HAS_LIST) { - if (flags & SHF_LARGE_LISTLEN) - RLEN(len3); - else - GETMARK(len3); - if (len3) { - av = newAV(); - av_extend(av, len3 + 1); /* Leave room for [0] */ - AvFILLp(av) = len3; /* About to be filled anyway */ - } - } - - TRACEME(("has %d object IDs to link", len3)); - - /* - * Read object-ID list into array. - * Because we pre-extended it, we can cheat and fill it manually. - * - * We read object tags and we can convert them into SV* on the fly - * because we know all the references listed in there (as tags) - * have been already serialized, hence we have a valid correspondance - * between each of those tags and the recreated SV. - */ - - if (av) { - SV **ary = AvARRAY(av); - int i; - for (i = 1; i <= len3; i++) { /* We leave [0] alone */ - I32 tag; - SV **svh; - SV *xsv; - - READ_I32(tag); - tag = ntohl(tag); - svh = av_fetch(cxt->aseen, tag, FALSE); - if (!svh) { - if (tag == cxt->where_is_undef) { - /* av_fetch uses PL_sv_undef internally, hence this - somewhat gruesome hack. */ - xsv = &PL_sv_undef; - svh = &xsv; - } else { - CROAK(("Object #%"IVdf" should have been retrieved already", - (IV) tag)); - } - } - xsv = *svh; - ary[i] = SvREFCNT_inc(xsv); - } - } - - /* - * Bless the object and look up the STORABLE_thaw hook. - */ - - BLESS(sv, classname); - - /* Handle attach case; again can't use pkg_can because it only - * caches one method */ - attach = gv_fetchmethod_autoload(SvSTASH(sv), "STORABLE_attach", FALSE); - if (attach && isGV(attach)) { - SV* attached; - SV* attach_hook = newRV((SV*) GvCV(attach)); - - if (av) - CROAK(("STORABLE_attach called with unexpected references")); - av = newAV(); - av_extend(av, 1); - AvFILLp(av) = 0; - AvARRAY(av)[0] = SvREFCNT_inc(frozen); - rv = newSVpv(classname, 0); - attached = scalar_call(aTHX_ rv, attach_hook, clone, av, G_SCALAR); - if (attached && - SvROK(attached) && - sv_derived_from(attached, classname)) - return SvRV(attached); - CROAK(("STORABLE_attach did not return a %s object", classname)); - } - - hook = pkg_can(aTHX_ cxt->hook, SvSTASH(sv), "STORABLE_thaw"); - if (!hook) { - /* - * Hook not found. Maybe they did not require the module where this - * hook is defined yet? - * - * If the load below succeeds, we'll be able to find the hook. - * Still, it only works reliably when each class is defined in a - * file of its own. - */ - - TRACEME(("No STORABLE_thaw defined for objects of class %s", classname)); - TRACEME(("Going to load module '%s'", classname)); - load_module(PERL_LOADMOD_NOIMPORT, newSVpv(classname, 0), Nullsv); - - /* - * We cache results of pkg_can, so we need to uncache before attempting - * the lookup again. - */ - - pkg_uncache(aTHX_ cxt->hook, SvSTASH(sv), "STORABLE_thaw"); - hook = pkg_can(aTHX_ cxt->hook, SvSTASH(sv), "STORABLE_thaw"); - - if (!hook) - CROAK(("No STORABLE_thaw defined for objects of class %s " - "(even after a \"require %s;\")", classname, classname)); - } - - /* - * If we don't have an `av' yet, prepare one. - * Then insert the frozen string as item [0]. - */ - - if (!av) { - av = newAV(); - av_extend(av, 1); - AvFILLp(av) = 0; - } - AvARRAY(av)[0] = SvREFCNT_inc(frozen); - - /* - * Call the hook as: - * - * $object->STORABLE_thaw($cloning, $frozen, @refs); - * - * where $object is our blessed (empty) object, $cloning is a boolean - * telling whether we're running a deep clone, $frozen is the frozen - * string the user gave us in his serializing hook, and @refs, which may - * be empty, is the list of extra references he returned along for us - * to serialize. - * - * In effect, the hook is an alternate creation routine for the class, - * the object itself being already created by the runtime. - */ - - TRACEME(("calling STORABLE_thaw on %s at 0x%"UVxf" (%"IVdf" args)", - classname, PTR2UV(sv), (IV) AvFILLp(av) + 1)); - - rv = newRV(sv); - (void) scalar_call(aTHX_ rv, hook, clone, av, G_SCALAR|G_DISCARD); - SvREFCNT_dec(rv); - - /* - * Final cleanup. - */ - - SvREFCNT_dec(frozen); - av_undef(av); - sv_free((SV *) av); - if (!(flags & SHF_IDX_CLASSNAME) && classname != buf) - Safefree(classname); - - /* - * If we had an <extra> type, then the object was not as simple, and - * we need to restore extra magic now. - */ - - if (!extra_type) - return sv; - - TRACEME(("retrieving magic object for 0x%"UVxf"...", PTR2UV(sv))); - - rv = retrieve(aTHX_ cxt, 0); /* Retrieve <magic object> */ - - TRACEME(("restoring the magic object 0x%"UVxf" part of 0x%"UVxf, - PTR2UV(rv), PTR2UV(sv))); - - switch (extra_type) { - case SHT_TSCALAR: - sv_upgrade(sv, SVt_PVMG); - break; - case SHT_TARRAY: - sv_upgrade(sv, SVt_PVAV); - AvREAL_off((AV *)sv); - break; - case SHT_THASH: - sv_upgrade(sv, SVt_PVHV); - break; - default: - CROAK(("Forgot to deal with extra type %d", extra_type)); - break; - } - - /* - * Adding the magic only now, well after the STORABLE_thaw hook was called - * means the hook cannot know it deals with an object whose variable is - * tied. But this is happening when retrieving $o in the following case: - * - * my %h; - * tie %h, 'FOO'; - * my $o = bless \%h, 'BAR'; - * - * The 'BAR' class is NOT the one where %h is tied into. Therefore, as - * far as the 'BAR' class is concerned, the fact that %h is not a REAL - * hash but a tied one should not matter at all, and remain transparent. - * This means the magic must be restored by Storable AFTER the hook is - * called. - * - * That looks very reasonable to me, but then I've come up with this - * after a bug report from David Nesting, who was trying to store such - * an object and caused Storable to fail. And unfortunately, it was - * also the easiest way to retrofit support for blessed ref to tied objects - * into the existing design. -- RAM, 17/02/2001 - */ - - sv_magic(sv, rv, mtype, (char *)NULL, 0); - SvREFCNT_dec(rv); /* Undo refcnt inc from sv_magic() */ - - return sv; -} - -/* - * retrieve_ref - * - * Retrieve reference to some other scalar. - * Layout is SX_REF <object>, with SX_REF already read. - */ -static SV *retrieve_ref(pTHX_ stcxt_t *cxt, const char *cname) -{ - SV *rv; - SV *sv; - - TRACEME(("retrieve_ref (#%d)", cxt->tagnum)); - - /* - * We need to create the SV that holds the reference to the yet-to-retrieve - * object now, so that we may record the address in the seen table. - * Otherwise, if the object to retrieve references us, we won't be able - * to resolve the SX_OBJECT we'll see at that point! Hence we cannot - * do the retrieve first and use rv = newRV(sv) since it will be too late - * for SEEN() recording. - */ - - rv = NEWSV(10002, 0); - SEEN(rv, cname, 0); /* Will return if rv is null */ - sv = retrieve(aTHX_ cxt, 0); /* Retrieve <object> */ - if (!sv) - return (SV *) 0; /* Failed */ - - /* - * WARNING: breaks RV encapsulation. - * - * Now for the tricky part. We have to upgrade our existing SV, so that - * it is now an RV on sv... Again, we cheat by duplicating the code - * held in newSVrv(), since we already got our SV from retrieve(). - * - * We don't say: - * - * SvRV(rv) = SvREFCNT_inc(sv); - * - * here because the reference count we got from retrieve() above is - * already correct: if the object was retrieved from the file, then - * its reference count is one. Otherwise, if it was retrieved via - * an SX_OBJECT indication, a ref count increment was done. - */ - - if (cname) { - /* No need to do anything, as rv will already be PVMG. */ - assert (SvTYPE(rv) == SVt_RV || SvTYPE(rv) >= SVt_PV); - } else { - sv_upgrade(rv, SVt_RV); - } - - SvRV_set(rv, sv); /* $rv = \$sv */ - SvROK_on(rv); - - TRACEME(("ok (retrieve_ref at 0x%"UVxf")", PTR2UV(rv))); - - return rv; -} - -/* - * retrieve_weakref - * - * Retrieve weak reference to some other scalar. - * Layout is SX_WEAKREF <object>, with SX_WEAKREF already read. - */ -static SV *retrieve_weakref(pTHX_ stcxt_t *cxt, const char *cname) -{ - SV *sv; - - TRACEME(("retrieve_weakref (#%d)", cxt->tagnum)); - - sv = retrieve_ref(aTHX_ cxt, cname); - if (sv) { -#ifdef SvWEAKREF - sv_rvweaken(sv); -#else - WEAKREF_CROAK(); -#endif - } - return sv; -} - -/* - * retrieve_overloaded - * - * Retrieve reference to some other scalar with overloading. - * Layout is SX_OVERLOAD <object>, with SX_OVERLOAD already read. - */ -static SV *retrieve_overloaded(pTHX_ stcxt_t *cxt, const char *cname) -{ - SV *rv; - SV *sv; - HV *stash; - - TRACEME(("retrieve_overloaded (#%d)", cxt->tagnum)); - - /* - * Same code as retrieve_ref(), duplicated to avoid extra call. - */ - - rv = NEWSV(10002, 0); - SEEN(rv, cname, 0); /* Will return if rv is null */ - sv = retrieve(aTHX_ cxt, 0); /* Retrieve <object> */ - if (!sv) - return (SV *) 0; /* Failed */ - - /* - * WARNING: breaks RV encapsulation. - */ - - SvUPGRADE(rv, SVt_RV); - SvRV_set(rv, sv); /* $rv = \$sv */ - SvROK_on(rv); - - /* - * Restore overloading magic. - */ - - stash = SvTYPE(sv) ? (HV *) SvSTASH (sv) : 0; - if (!stash) { - CROAK(("Cannot restore overloading on %s(0x%"UVxf - ") (package <unknown>)", - sv_reftype(sv, FALSE), - PTR2UV(sv))); - } - if (!Gv_AMG(stash)) { - const char *package = HvNAME_get(stash); - TRACEME(("No overloading defined for package %s", package)); - TRACEME(("Going to load module '%s'", package)); - load_module(PERL_LOADMOD_NOIMPORT, newSVpv(package, 0), Nullsv); - if (!Gv_AMG(stash)) { - CROAK(("Cannot restore overloading on %s(0x%"UVxf - ") (package %s) (even after a \"require %s;\")", - sv_reftype(sv, FALSE), - PTR2UV(sv), - package, package)); - } - } - - SvAMAGIC_on(rv); - - TRACEME(("ok (retrieve_overloaded at 0x%"UVxf")", PTR2UV(rv))); - - return rv; -} - -/* - * retrieve_weakoverloaded - * - * Retrieve weak overloaded reference to some other scalar. - * Layout is SX_WEAKOVERLOADED <object>, with SX_WEAKOVERLOADED already read. - */ -static SV *retrieve_weakoverloaded(pTHX_ stcxt_t *cxt, const char *cname) -{ - SV *sv; - - TRACEME(("retrieve_weakoverloaded (#%d)", cxt->tagnum)); - - sv = retrieve_overloaded(aTHX_ cxt, cname); - if (sv) { -#ifdef SvWEAKREF - sv_rvweaken(sv); -#else - WEAKREF_CROAK(); -#endif - } - return sv; -} - -/* - * retrieve_tied_array - * - * Retrieve tied array - * Layout is SX_TIED_ARRAY <object>, with SX_TIED_ARRAY already read. - */ -static SV *retrieve_tied_array(pTHX_ stcxt_t *cxt, const char *cname) -{ - SV *tv; - SV *sv; - - TRACEME(("retrieve_tied_array (#%d)", cxt->tagnum)); - - tv = NEWSV(10002, 0); - SEEN(tv, cname, 0); /* Will return if tv is null */ - sv = retrieve(aTHX_ cxt, 0); /* Retrieve <object> */ - if (!sv) - return (SV *) 0; /* Failed */ - - sv_upgrade(tv, SVt_PVAV); - AvREAL_off((AV *)tv); - sv_magic(tv, sv, 'P', (char *)NULL, 0); - SvREFCNT_dec(sv); /* Undo refcnt inc from sv_magic() */ - - TRACEME(("ok (retrieve_tied_array at 0x%"UVxf")", PTR2UV(tv))); - - return tv; -} - -/* - * retrieve_tied_hash - * - * Retrieve tied hash - * Layout is SX_TIED_HASH <object>, with SX_TIED_HASH already read. - */ -static SV *retrieve_tied_hash(pTHX_ stcxt_t *cxt, const char *cname) -{ - SV *tv; - SV *sv; - - TRACEME(("retrieve_tied_hash (#%d)", cxt->tagnum)); - - tv = NEWSV(10002, 0); - SEEN(tv, cname, 0); /* Will return if tv is null */ - sv = retrieve(aTHX_ cxt, 0); /* Retrieve <object> */ - if (!sv) - return (SV *) 0; /* Failed */ - - sv_upgrade(tv, SVt_PVHV); - sv_magic(tv, sv, 'P', (char *)NULL, 0); - SvREFCNT_dec(sv); /* Undo refcnt inc from sv_magic() */ - - TRACEME(("ok (retrieve_tied_hash at 0x%"UVxf")", PTR2UV(tv))); - - return tv; -} - -/* - * retrieve_tied_scalar - * - * Retrieve tied scalar - * Layout is SX_TIED_SCALAR <object>, with SX_TIED_SCALAR already read. - */ -static SV *retrieve_tied_scalar(pTHX_ stcxt_t *cxt, const char *cname) -{ - SV *tv; - SV *sv, *obj = NULL; - - TRACEME(("retrieve_tied_scalar (#%d)", cxt->tagnum)); - - tv = NEWSV(10002, 0); - SEEN(tv, cname, 0); /* Will return if rv is null */ - sv = retrieve(aTHX_ cxt, 0); /* Retrieve <object> */ - if (!sv) { - return (SV *) 0; /* Failed */ - } - else if (SvTYPE(sv) != SVt_NULL) { - obj = sv; - } - - sv_upgrade(tv, SVt_PVMG); - sv_magic(tv, obj, 'q', (char *)NULL, 0); - - if (obj) { - /* Undo refcnt inc from sv_magic() */ - SvREFCNT_dec(obj); - } - - TRACEME(("ok (retrieve_tied_scalar at 0x%"UVxf")", PTR2UV(tv))); - - return tv; -} - -/* - * retrieve_tied_key - * - * Retrieve reference to value in a tied hash. - * Layout is SX_TIED_KEY <object> <key>, with SX_TIED_KEY already read. - */ -static SV *retrieve_tied_key(pTHX_ stcxt_t *cxt, const char *cname) -{ - SV *tv; - SV *sv; - SV *key; - - TRACEME(("retrieve_tied_key (#%d)", cxt->tagnum)); - - tv = NEWSV(10002, 0); - SEEN(tv, cname, 0); /* Will return if tv is null */ - sv = retrieve(aTHX_ cxt, 0); /* Retrieve <object> */ - if (!sv) - return (SV *) 0; /* Failed */ - - key = retrieve(aTHX_ cxt, 0); /* Retrieve <key> */ - if (!key) - return (SV *) 0; /* Failed */ - - sv_upgrade(tv, SVt_PVMG); - sv_magic(tv, sv, 'p', (char *)key, HEf_SVKEY); - SvREFCNT_dec(key); /* Undo refcnt inc from sv_magic() */ - SvREFCNT_dec(sv); /* Undo refcnt inc from sv_magic() */ - - return tv; -} - -/* - * retrieve_tied_idx - * - * Retrieve reference to value in a tied array. - * Layout is SX_TIED_IDX <object> <idx>, with SX_TIED_IDX already read. - */ -static SV *retrieve_tied_idx(pTHX_ stcxt_t *cxt, const char *cname) -{ - SV *tv; - SV *sv; - I32 idx; - - TRACEME(("retrieve_tied_idx (#%d)", cxt->tagnum)); - - tv = NEWSV(10002, 0); - SEEN(tv, cname, 0); /* Will return if tv is null */ - sv = retrieve(aTHX_ cxt, 0); /* Retrieve <object> */ - if (!sv) - return (SV *) 0; /* Failed */ - - RLEN(idx); /* Retrieve <idx> */ - - sv_upgrade(tv, SVt_PVMG); - sv_magic(tv, sv, 'p', (char *)NULL, idx); - SvREFCNT_dec(sv); /* Undo refcnt inc from sv_magic() */ - - return tv; -} - - -/* - * retrieve_lscalar - * - * Retrieve defined long (string) scalar. - * - * Layout is SX_LSCALAR <length> <data>, with SX_LSCALAR already read. - * The scalar is "long" in that <length> is larger than LG_SCALAR so it - * was not stored on a single byte. - */ -static SV *retrieve_lscalar(pTHX_ stcxt_t *cxt, const char *cname) -{ - I32 len; - SV *sv; - - RLEN(len); - TRACEME(("retrieve_lscalar (#%d), len = %"IVdf, cxt->tagnum, (IV) len)); - - /* - * Allocate an empty scalar of the suitable length. - */ - - sv = NEWSV(10002, len); - SEEN(sv, cname, 0); /* Associate this new scalar with tag "tagnum" */ - - if (len == 0) { - sv_setpvn(sv, "", 0); - return sv; - } - - /* - * WARNING: duplicates parts of sv_setpv and breaks SV data encapsulation. - * - * Now, for efficiency reasons, read data directly inside the SV buffer, - * and perform the SV final settings directly by duplicating the final - * work done by sv_setpv. Since we're going to allocate lots of scalars - * this way, it's worth the hassle and risk. - */ - - SAFEREAD(SvPVX(sv), len, sv); - SvCUR_set(sv, len); /* Record C string length */ - *SvEND(sv) = '\0'; /* Ensure it's null terminated anyway */ - (void) SvPOK_only(sv); /* Validate string pointer */ - if (cxt->s_tainted) /* Is input source tainted? */ - SvTAINT(sv); /* External data cannot be trusted */ - - TRACEME(("large scalar len %"IVdf" '%s'", (IV) len, SvPVX(sv))); - TRACEME(("ok (retrieve_lscalar at 0x%"UVxf")", PTR2UV(sv))); - - return sv; -} - -/* - * retrieve_scalar - * - * Retrieve defined short (string) scalar. - * - * Layout is SX_SCALAR <length> <data>, with SX_SCALAR already read. - * The scalar is "short" so <length> is single byte. If it is 0, there - * is no <data> section. - */ -static SV *retrieve_scalar(pTHX_ stcxt_t *cxt, const char *cname) -{ - int len; - SV *sv; - - GETMARK(len); - TRACEME(("retrieve_scalar (#%d), len = %d", cxt->tagnum, len)); - - /* - * Allocate an empty scalar of the suitable length. - */ - - sv = NEWSV(10002, len); - SEEN(sv, cname, 0); /* Associate this new scalar with tag "tagnum" */ - - /* - * WARNING: duplicates parts of sv_setpv and breaks SV data encapsulation. - */ - - if (len == 0) { - /* - * newSV did not upgrade to SVt_PV so the scalar is undefined. - * To make it defined with an empty length, upgrade it now... - * Don't upgrade to a PV if the original type contains more - * information than a scalar. - */ - if (SvTYPE(sv) <= SVt_PV) { - sv_upgrade(sv, SVt_PV); - } - SvGROW(sv, 1); - *SvEND(sv) = '\0'; /* Ensure it's null terminated anyway */ - TRACEME(("ok (retrieve_scalar empty at 0x%"UVxf")", PTR2UV(sv))); - } else { - /* - * Now, for efficiency reasons, read data directly inside the SV buffer, - * and perform the SV final settings directly by duplicating the final - * work done by sv_setpv. Since we're going to allocate lots of scalars - * this way, it's worth the hassle and risk. - */ - SAFEREAD(SvPVX(sv), len, sv); - SvCUR_set(sv, len); /* Record C string length */ - *SvEND(sv) = '\0'; /* Ensure it's null terminated anyway */ - TRACEME(("small scalar len %d '%s'", len, SvPVX(sv))); - } - - (void) SvPOK_only(sv); /* Validate string pointer */ - if (cxt->s_tainted) /* Is input source tainted? */ - SvTAINT(sv); /* External data cannot be trusted */ - - TRACEME(("ok (retrieve_scalar at 0x%"UVxf")", PTR2UV(sv))); - return sv; -} - -/* - * retrieve_utf8str - * - * Like retrieve_scalar(), but tag result as utf8. - * If we're retrieving UTF8 data in a non-UTF8 perl, croaks. - */ -static SV *retrieve_utf8str(pTHX_ stcxt_t *cxt, const char *cname) -{ - SV *sv; - - TRACEME(("retrieve_utf8str")); - - sv = retrieve_scalar(aTHX_ cxt, cname); - if (sv) { -#ifdef HAS_UTF8_SCALARS - SvUTF8_on(sv); -#else - if (cxt->use_bytes < 0) - cxt->use_bytes - = (SvTRUE(perl_get_sv("Storable::drop_utf8", GV_ADD)) - ? 1 : 0); - if (cxt->use_bytes == 0) - UTF8_CROAK(); -#endif - } - - return sv; -} - -/* - * retrieve_lutf8str - * - * Like retrieve_lscalar(), but tag result as utf8. - * If we're retrieving UTF8 data in a non-UTF8 perl, croaks. - */ -static SV *retrieve_lutf8str(pTHX_ stcxt_t *cxt, const char *cname) -{ - SV *sv; - - TRACEME(("retrieve_lutf8str")); - - sv = retrieve_lscalar(aTHX_ cxt, cname); - if (sv) { -#ifdef HAS_UTF8_SCALARS - SvUTF8_on(sv); -#else - if (cxt->use_bytes < 0) - cxt->use_bytes - = (SvTRUE(perl_get_sv("Storable::drop_utf8", GV_ADD)) - ? 1 : 0); - if (cxt->use_bytes == 0) - UTF8_CROAK(); -#endif - } - return sv; -} - -/* - * retrieve_integer - * - * Retrieve defined integer. - * Layout is SX_INTEGER <data>, whith SX_INTEGER already read. - */ -static SV *retrieve_integer(pTHX_ stcxt_t *cxt, const char *cname) -{ - SV *sv; - IV iv; - - TRACEME(("retrieve_integer (#%d)", cxt->tagnum)); - - READ(&iv, sizeof(iv)); - sv = newSViv(iv); - SEEN(sv, cname, 0); /* Associate this new scalar with tag "tagnum" */ - - TRACEME(("integer %"IVdf, iv)); - TRACEME(("ok (retrieve_integer at 0x%"UVxf")", PTR2UV(sv))); - - return sv; -} - -/* - * retrieve_netint - * - * Retrieve defined integer in network order. - * Layout is SX_NETINT <data>, whith SX_NETINT already read. - */ -static SV *retrieve_netint(pTHX_ stcxt_t *cxt, const char *cname) -{ - SV *sv; - I32 iv; - - TRACEME(("retrieve_netint (#%d)", cxt->tagnum)); - - READ_I32(iv); -#ifdef HAS_NTOHL - sv = newSViv((int) ntohl(iv)); - TRACEME(("network integer %d", (int) ntohl(iv))); -#else - sv = newSViv(iv); - TRACEME(("network integer (as-is) %d", iv)); -#endif - SEEN(sv, cname, 0); /* Associate this new scalar with tag "tagnum" */ - - TRACEME(("ok (retrieve_netint at 0x%"UVxf")", PTR2UV(sv))); - - return sv; -} - -/* - * retrieve_double - * - * Retrieve defined double. - * Layout is SX_DOUBLE <data>, whith SX_DOUBLE already read. - */ -static SV *retrieve_double(pTHX_ stcxt_t *cxt, const char *cname) -{ - SV *sv; - NV nv; - - TRACEME(("retrieve_double (#%d)", cxt->tagnum)); - - READ(&nv, sizeof(nv)); - sv = newSVnv(nv); - SEEN(sv, cname, 0); /* Associate this new scalar with tag "tagnum" */ - - TRACEME(("double %"NVff, nv)); - TRACEME(("ok (retrieve_double at 0x%"UVxf")", PTR2UV(sv))); - - return sv; -} - -/* - * retrieve_byte - * - * Retrieve defined byte (small integer within the [-128, +127] range). - * Layout is SX_BYTE <data>, whith SX_BYTE already read. - */ -static SV *retrieve_byte(pTHX_ stcxt_t *cxt, const char *cname) -{ - SV *sv; - int siv; - signed char tmp; /* Workaround for AIX cc bug --H.Merijn Brand */ - - TRACEME(("retrieve_byte (#%d)", cxt->tagnum)); - - GETMARK(siv); - TRACEME(("small integer read as %d", (unsigned char) siv)); - tmp = (unsigned char) siv - 128; - sv = newSViv(tmp); - SEEN(sv, cname, 0); /* Associate this new scalar with tag "tagnum" */ - - TRACEME(("byte %d", tmp)); - TRACEME(("ok (retrieve_byte at 0x%"UVxf")", PTR2UV(sv))); - - return sv; -} - -/* - * retrieve_undef - * - * Return the undefined value. - */ -static SV *retrieve_undef(pTHX_ stcxt_t *cxt, const char *cname) -{ - SV* sv; - - TRACEME(("retrieve_undef")); - - sv = newSV(0); - SEEN(sv, cname, 0); - - return sv; -} - -/* - * retrieve_sv_undef - * - * Return the immortal undefined value. - */ -static SV *retrieve_sv_undef(pTHX_ stcxt_t *cxt, const char *cname) -{ - SV *sv = &PL_sv_undef; - - TRACEME(("retrieve_sv_undef")); - - /* Special case PL_sv_undef, as av_fetch uses it internally to mark - deleted elements, and will return NULL (fetch failed) whenever it - is fetched. */ - if (cxt->where_is_undef == -1) { - cxt->where_is_undef = cxt->tagnum; - } - SEEN(sv, cname, 1); - return sv; -} - -/* - * retrieve_sv_yes - * - * Return the immortal yes value. - */ -static SV *retrieve_sv_yes(pTHX_ stcxt_t *cxt, const char *cname) -{ - SV *sv = &PL_sv_yes; - - TRACEME(("retrieve_sv_yes")); - - SEEN(sv, cname, 1); - return sv; -} - -/* - * retrieve_sv_no - * - * Return the immortal no value. - */ -static SV *retrieve_sv_no(pTHX_ stcxt_t *cxt, const char *cname) -{ - SV *sv = &PL_sv_no; - - TRACEME(("retrieve_sv_no")); - - SEEN(sv, cname, 1); - return sv; -} - -/* - * retrieve_array - * - * Retrieve a whole array. - * Layout is SX_ARRAY <size> followed by each item, in increading index order. - * Each item is stored as <object>. - * - * When we come here, SX_ARRAY has been read already. - */ -static SV *retrieve_array(pTHX_ stcxt_t *cxt, const char *cname) -{ - I32 len; - I32 i; - AV *av; - SV *sv; - - TRACEME(("retrieve_array (#%d)", cxt->tagnum)); - - /* - * Read length, and allocate array, then pre-extend it. - */ - - RLEN(len); - TRACEME(("size = %d", len)); - av = newAV(); - SEEN(av, cname, 0); /* Will return if array not allocated nicely */ - if (len) - av_extend(av, len); - else - return (SV *) av; /* No data follow if array is empty */ - - /* - * Now get each item in turn... - */ - - for (i = 0; i < len; i++) { - TRACEME(("(#%d) item", i)); - sv = retrieve(aTHX_ cxt, 0); /* Retrieve item */ - if (!sv) - return (SV *) 0; - if (av_store(av, i, sv) == 0) - return (SV *) 0; - } - - TRACEME(("ok (retrieve_array at 0x%"UVxf")", PTR2UV(av))); - - return (SV *) av; -} - -/* - * retrieve_hash - * - * Retrieve a whole hash table. - * Layout is SX_HASH <size> followed by each key/value pair, in random order. - * Keys are stored as <length> <data>, the <data> section being omitted - * if length is 0. - * Values are stored as <object>. - * - * When we come here, SX_HASH has been read already. - */ -static SV *retrieve_hash(pTHX_ stcxt_t *cxt, const char *cname) -{ - I32 len; - I32 size; - I32 i; - HV *hv; - SV *sv; - - TRACEME(("retrieve_hash (#%d)", cxt->tagnum)); - - /* - * Read length, allocate table. - */ - - RLEN(len); - TRACEME(("size = %d", len)); - hv = newHV(); - SEEN(hv, cname, 0); /* Will return if table not allocated properly */ - if (len == 0) - return (SV *) hv; /* No data follow if table empty */ - hv_ksplit(hv, len); /* pre-extend hash to save multiple splits */ - - /* - * Now get each key/value pair in turn... - */ - - for (i = 0; i < len; i++) { - /* - * Get value first. - */ - - TRACEME(("(#%d) value", i)); - sv = retrieve(aTHX_ cxt, 0); - if (!sv) - return (SV *) 0; - - /* - * Get key. - * Since we're reading into kbuf, we must ensure we're not - * recursing between the read and the hv_store() where it's used. - * Hence the key comes after the value. - */ - - RLEN(size); /* Get key size */ - KBUFCHK((STRLEN)size); /* Grow hash key read pool if needed */ - if (size) - READ(kbuf, size); - kbuf[size] = '\0'; /* Mark string end, just in case */ - TRACEME(("(#%d) key '%s'", i, kbuf)); - - /* - * Enter key/value pair into hash table. - */ - - if (hv_store(hv, kbuf, (U32) size, sv, 0) == 0) - return (SV *) 0; - } - - TRACEME(("ok (retrieve_hash at 0x%"UVxf")", PTR2UV(hv))); - - return (SV *) hv; -} - -/* - * retrieve_hash - * - * Retrieve a whole hash table. - * Layout is SX_HASH <size> followed by each key/value pair, in random order. - * Keys are stored as <length> <data>, the <data> section being omitted - * if length is 0. - * Values are stored as <object>. - * - * When we come here, SX_HASH has been read already. - */ -static SV *retrieve_flag_hash(pTHX_ stcxt_t *cxt, const char *cname) -{ - dVAR; - I32 len; - I32 size; - I32 i; - HV *hv; - SV *sv; - int hash_flags; - - GETMARK(hash_flags); - TRACEME(("retrieve_flag_hash (#%d)", cxt->tagnum)); - /* - * Read length, allocate table. - */ - -#ifndef HAS_RESTRICTED_HASHES - if (hash_flags & SHV_RESTRICTED) { - if (cxt->derestrict < 0) - cxt->derestrict - = (SvTRUE(perl_get_sv("Storable::downgrade_restricted", GV_ADD)) - ? 1 : 0); - if (cxt->derestrict == 0) - RESTRICTED_HASH_CROAK(); - } -#endif - - RLEN(len); - TRACEME(("size = %d, flags = %d", len, hash_flags)); - hv = newHV(); - SEEN(hv, cname, 0); /* Will return if table not allocated properly */ - if (len == 0) - return (SV *) hv; /* No data follow if table empty */ - hv_ksplit(hv, len); /* pre-extend hash to save multiple splits */ - - /* - * Now get each key/value pair in turn... - */ - - for (i = 0; i < len; i++) { - int flags; - int store_flags = 0; - /* - * Get value first. - */ - - TRACEME(("(#%d) value", i)); - sv = retrieve(aTHX_ cxt, 0); - if (!sv) - return (SV *) 0; - - GETMARK(flags); -#ifdef HAS_RESTRICTED_HASHES - if ((hash_flags & SHV_RESTRICTED) && (flags & SHV_K_LOCKED)) - SvREADONLY_on(sv); -#endif - - if (flags & SHV_K_ISSV) { - /* XXX you can't set a placeholder with an SV key. - Then again, you can't get an SV key. - Without messing around beyond what the API is supposed to do. - */ - SV *keysv; - TRACEME(("(#%d) keysv, flags=%d", i, flags)); - keysv = retrieve(aTHX_ cxt, 0); - if (!keysv) - return (SV *) 0; - - if (!hv_store_ent(hv, keysv, sv, 0)) - return (SV *) 0; - } else { - /* - * Get key. - * Since we're reading into kbuf, we must ensure we're not - * recursing between the read and the hv_store() where it's used. - * Hence the key comes after the value. - */ - - if (flags & SHV_K_PLACEHOLDER) { - SvREFCNT_dec (sv); - sv = &PL_sv_placeholder; - store_flags |= HVhek_PLACEHOLD; - } - if (flags & SHV_K_UTF8) { -#ifdef HAS_UTF8_HASHES - store_flags |= HVhek_UTF8; -#else - if (cxt->use_bytes < 0) - cxt->use_bytes - = (SvTRUE(perl_get_sv("Storable::drop_utf8", GV_ADD)) - ? 1 : 0); - if (cxt->use_bytes == 0) - UTF8_CROAK(); -#endif - } -#ifdef HAS_UTF8_HASHES - if (flags & SHV_K_WASUTF8) - store_flags |= HVhek_WASUTF8; -#endif - - RLEN(size); /* Get key size */ - KBUFCHK((STRLEN)size); /* Grow hash key read pool if needed */ - if (size) - READ(kbuf, size); - kbuf[size] = '\0'; /* Mark string end, just in case */ - TRACEME(("(#%d) key '%s' flags %X store_flags %X", i, kbuf, - flags, store_flags)); - - /* - * Enter key/value pair into hash table. - */ - -#ifdef HAS_RESTRICTED_HASHES - if (hv_store_flags(hv, kbuf, size, sv, 0, store_flags) == 0) - return (SV *) 0; -#else - if (!(store_flags & HVhek_PLACEHOLD)) - if (hv_store(hv, kbuf, size, sv, 0) == 0) - return (SV *) 0; -#endif - } - } -#ifdef HAS_RESTRICTED_HASHES - if (hash_flags & SHV_RESTRICTED) - SvREADONLY_on(hv); -#endif - - TRACEME(("ok (retrieve_hash at 0x%"UVxf")", PTR2UV(hv))); - - return (SV *) hv; -} - -/* - * retrieve_code - * - * Return a code reference. - */ -static SV *retrieve_code(pTHX_ stcxt_t *cxt, const char *cname) -{ -#if PERL_VERSION < 6 - CROAK(("retrieve_code does not work with perl 5.005 or less\n")); -#else - dSP; - int type, count, tagnum; - SV *cv; - SV *sv, *text, *sub; - - TRACEME(("retrieve_code (#%d)", cxt->tagnum)); - - /* - * Insert dummy SV in the aseen array so that we don't screw - * up the tag numbers. We would just make the internal - * scalar an untagged item in the stream, but - * retrieve_scalar() calls SEEN(). So we just increase the - * tag number. - */ - tagnum = cxt->tagnum; - sv = newSViv(0); - SEEN(sv, cname, 0); - - /* - * Retrieve the source of the code reference - * as a small or large scalar - */ - - GETMARK(type); - switch (type) { - case SX_SCALAR: - text = retrieve_scalar(aTHX_ cxt, cname); - break; - case SX_LSCALAR: - text = retrieve_lscalar(aTHX_ cxt, cname); - break; - default: - CROAK(("Unexpected type %d in retrieve_code\n", type)); - } - - /* - * prepend "sub " to the source - */ - - sub = newSVpvn("sub ", 4); - sv_catpv(sub, SvPV_nolen(text)); /* XXX no sv_catsv! */ - SvREFCNT_dec(text); - - /* - * evaluate the source to a code reference and use the CV value - */ - - if (cxt->eval == NULL) { - cxt->eval = perl_get_sv("Storable::Eval", GV_ADD); - SvREFCNT_inc(cxt->eval); - } - if (!SvTRUE(cxt->eval)) { - if ( - cxt->forgive_me == 0 || - (cxt->forgive_me < 0 && !(cxt->forgive_me = - SvTRUE(perl_get_sv("Storable::forgive_me", GV_ADD)) ? 1 : 0)) - ) { - CROAK(("Can't eval, please set $Storable::Eval to a true value")); - } else { - sv = newSVsv(sub); - /* fix up the dummy entry... */ - av_store(cxt->aseen, tagnum, SvREFCNT_inc(sv)); - return sv; - } - } - - ENTER; - SAVETMPS; - - if (SvROK(cxt->eval) && SvTYPE(SvRV(cxt->eval)) == SVt_PVCV) { - SV* errsv = get_sv("@", GV_ADD); - sv_setpvn(errsv, "", 0); /* clear $@ */ - PUSHMARK(sp); - XPUSHs(sv_2mortal(newSVsv(sub))); - PUTBACK; - count = call_sv(cxt->eval, G_SCALAR); - SPAGAIN; - if (count != 1) - CROAK(("Unexpected return value from $Storable::Eval callback\n")); - cv = POPs; - if (SvTRUE(errsv)) { - CROAK(("code %s caused an error: %s", - SvPV_nolen(sub), SvPV_nolen(errsv))); - } - PUTBACK; - } else { - cv = eval_pv(SvPV_nolen(sub), TRUE); - } - if (cv && SvROK(cv) && SvTYPE(SvRV(cv)) == SVt_PVCV) { - sv = SvRV(cv); - } else { - CROAK(("code %s did not evaluate to a subroutine reference\n", SvPV_nolen(sub))); - } - - SvREFCNT_inc(sv); /* XXX seems to be necessary */ - SvREFCNT_dec(sub); - - FREETMPS; - LEAVE; - /* fix up the dummy entry... */ - av_store(cxt->aseen, tagnum, SvREFCNT_inc(sv)); - - return sv; -#endif -} - -/* - * old_retrieve_array - * - * Retrieve a whole array in pre-0.6 binary format. - * - * Layout is SX_ARRAY <size> followed by each item, in increading index order. - * Each item is stored as SX_ITEM <object> or SX_IT_UNDEF for "holes". - * - * When we come here, SX_ARRAY has been read already. - */ -static SV *old_retrieve_array(pTHX_ stcxt_t *cxt, const char *cname) -{ - I32 len; - I32 i; - AV *av; - SV *sv; - int c; - - TRACEME(("old_retrieve_array (#%d)", cxt->tagnum)); - - /* - * Read length, and allocate array, then pre-extend it. - */ - - RLEN(len); - TRACEME(("size = %d", len)); - av = newAV(); - SEEN(av, 0, 0); /* Will return if array not allocated nicely */ - if (len) - av_extend(av, len); - else - return (SV *) av; /* No data follow if array is empty */ - - /* - * Now get each item in turn... - */ - - for (i = 0; i < len; i++) { - GETMARK(c); - if (c == SX_IT_UNDEF) { - TRACEME(("(#%d) undef item", i)); - continue; /* av_extend() already filled us with undef */ - } - if (c != SX_ITEM) - (void) retrieve_other(aTHX_ (stcxt_t *) 0, 0); /* Will croak out */ - TRACEME(("(#%d) item", i)); - sv = retrieve(aTHX_ cxt, 0); /* Retrieve item */ - if (!sv) - return (SV *) 0; - if (av_store(av, i, sv) == 0) - return (SV *) 0; - } - - TRACEME(("ok (old_retrieve_array at 0x%"UVxf")", PTR2UV(av))); - - return (SV *) av; -} - -/* - * old_retrieve_hash - * - * Retrieve a whole hash table in pre-0.6 binary format. - * - * Layout is SX_HASH <size> followed by each key/value pair, in random order. - * Keys are stored as SX_KEY <length> <data>, the <data> section being omitted - * if length is 0. - * Values are stored as SX_VALUE <object> or SX_VL_UNDEF for "holes". - * - * When we come here, SX_HASH has been read already. - */ -static SV *old_retrieve_hash(pTHX_ stcxt_t *cxt, const char *cname) -{ - I32 len; - I32 size; - I32 i; - HV *hv; - SV *sv = (SV *) 0; - int c; - SV *sv_h_undef = (SV *) 0; /* hv_store() bug */ - - TRACEME(("old_retrieve_hash (#%d)", cxt->tagnum)); - - /* - * Read length, allocate table. - */ - - RLEN(len); - TRACEME(("size = %d", len)); - hv = newHV(); - SEEN(hv, 0, 0); /* Will return if table not allocated properly */ - if (len == 0) - return (SV *) hv; /* No data follow if table empty */ - hv_ksplit(hv, len); /* pre-extend hash to save multiple splits */ - - /* - * Now get each key/value pair in turn... - */ - - for (i = 0; i < len; i++) { - /* - * Get value first. - */ - - GETMARK(c); - if (c == SX_VL_UNDEF) { - TRACEME(("(#%d) undef value", i)); - /* - * Due to a bug in hv_store(), it's not possible to pass - * &PL_sv_undef to hv_store() as a value, otherwise the - * associated key will not be creatable any more. -- RAM, 14/01/97 - */ - if (!sv_h_undef) - sv_h_undef = newSVsv(&PL_sv_undef); - sv = SvREFCNT_inc(sv_h_undef); - } else if (c == SX_VALUE) { - TRACEME(("(#%d) value", i)); - sv = retrieve(aTHX_ cxt, 0); - if (!sv) - return (SV *) 0; - } else - (void) retrieve_other(aTHX_ (stcxt_t *) 0, 0); /* Will croak out */ - - /* - * Get key. - * Since we're reading into kbuf, we must ensure we're not - * recursing between the read and the hv_store() where it's used. - * Hence the key comes after the value. - */ - - GETMARK(c); - if (c != SX_KEY) - (void) retrieve_other(aTHX_ (stcxt_t *) 0, 0); /* Will croak out */ - RLEN(size); /* Get key size */ - KBUFCHK((STRLEN)size); /* Grow hash key read pool if needed */ - if (size) - READ(kbuf, size); - kbuf[size] = '\0'; /* Mark string end, just in case */ - TRACEME(("(#%d) key '%s'", i, kbuf)); - - /* - * Enter key/value pair into hash table. - */ - - if (hv_store(hv, kbuf, (U32) size, sv, 0) == 0) - return (SV *) 0; - } - - TRACEME(("ok (retrieve_hash at 0x%"UVxf")", PTR2UV(hv))); - - return (SV *) hv; -} - -/*** - *** Retrieval engine. - ***/ - -/* - * magic_check - * - * Make sure the stored data we're trying to retrieve has been produced - * on an ILP compatible system with the same byteorder. It croaks out in - * case an error is detected. [ILP = integer-long-pointer sizes] - * Returns null if error is detected, &PL_sv_undef otherwise. - * - * Note that there's no byte ordering info emitted when network order was - * used at store time. - */ -static SV *magic_check(pTHX_ stcxt_t *cxt) -{ - /* The worst case for a malicious header would be old magic (which is - longer), major, minor, byteorder length byte of 255, 255 bytes of - garbage, sizeof int, long, pointer, NV. - So the worse of that we can read is 255 bytes of garbage plus 4. - Err, I am assuming 8 bit bytes here. Please file a bug report if you're - compiling perl on a system with chars that are larger than 8 bits. - (Even Crays aren't *that* perverse). - */ - unsigned char buf[4 + 255]; - unsigned char *current; - int c; - int length; - int use_network_order; - int use_NV_size; - int old_magic = 0; - int version_major; - int version_minor = 0; - - TRACEME(("magic_check")); - - /* - * The "magic number" is only for files, not when freezing in memory. - */ - - if (cxt->fio) { - /* This includes the '\0' at the end. I want to read the extra byte, - which is usually going to be the major version number. */ - STRLEN len = sizeof(magicstr); - STRLEN old_len; - - READ(buf, (SSize_t)(len)); /* Not null-terminated */ - - /* Point at the byte after the byte we read. */ - current = buf + --len; /* Do the -- outside of macros. */ - - if (memNE(buf, magicstr, len)) { - /* - * Try to read more bytes to check for the old magic number, which - * was longer. - */ - - TRACEME(("trying for old magic number")); - - old_len = sizeof(old_magicstr) - 1; - READ(current + 1, (SSize_t)(old_len - len)); - - if (memNE(buf, old_magicstr, old_len)) - CROAK(("File is not a perl storable")); - old_magic++; - current = buf + old_len; - } - use_network_order = *current; - } else - GETMARK(use_network_order); - - /* - * Starting with 0.6, the "use_network_order" byte flag is also used to - * indicate the version number of the binary, and therefore governs the - * setting of sv_retrieve_vtbl. See magic_write(). - */ - if (old_magic && use_network_order > 1) { - /* 0.1 dump - use_network_order is really byte order length */ - version_major = -1; - } - else { - version_major = use_network_order >> 1; - } - cxt->retrieve_vtbl = (SV*(**)(pTHX_ stcxt_t *cxt, const char *cname)) (version_major > 0 ? sv_retrieve : sv_old_retrieve); - - TRACEME(("magic_check: netorder = 0x%x", use_network_order)); - - - /* - * Starting with 0.7 (binary major 2), a full byte is dedicated to the - * minor version of the protocol. See magic_write(). - */ - - if (version_major > 1) - GETMARK(version_minor); - - cxt->ver_major = version_major; - cxt->ver_minor = version_minor; - - TRACEME(("binary image version is %d.%d", version_major, version_minor)); - - /* - * Inter-operability sanity check: we can't retrieve something stored - * using a format more recent than ours, because we have no way to - * know what has changed, and letting retrieval go would mean a probable - * failure reporting a "corrupted" storable file. - */ - - if ( - version_major > STORABLE_BIN_MAJOR || - (version_major == STORABLE_BIN_MAJOR && - version_minor > STORABLE_BIN_MINOR) - ) { - int croak_now = 1; - TRACEME(("but I am version is %d.%d", STORABLE_BIN_MAJOR, - STORABLE_BIN_MINOR)); - - if (version_major == STORABLE_BIN_MAJOR) { - TRACEME(("cxt->accept_future_minor is %d", - cxt->accept_future_minor)); - if (cxt->accept_future_minor < 0) - cxt->accept_future_minor - = (SvTRUE(perl_get_sv("Storable::accept_future_minor", - GV_ADD)) - ? 1 : 0); - if (cxt->accept_future_minor == 1) - croak_now = 0; /* Don't croak yet. */ - } - if (croak_now) { - CROAK(("Storable binary image v%d.%d more recent than I am (v%d.%d)", - version_major, version_minor, - STORABLE_BIN_MAJOR, STORABLE_BIN_MINOR)); - } - } - - /* - * If they stored using network order, there's no byte ordering - * information to check. - */ - - if ((cxt->netorder = (use_network_order & 0x1))) /* Extra () for -Wall */ - return &PL_sv_undef; /* No byte ordering info */ - - /* In C truth is 1, falsehood is 0. Very convienient. */ - use_NV_size = version_major >= 2 && version_minor >= 2; - - if (version_major >= 0) { - GETMARK(c); - } - else { - c = use_network_order; - } - length = c + 3 + use_NV_size; - READ(buf, length); /* Not null-terminated */ - - TRACEME(("byte order '%.*s' %d", c, buf, c)); - -#ifdef USE_56_INTERWORK_KLUDGE - /* No point in caching this in the context as we only need it once per - retrieve, and we need to recheck it each read. */ - if (SvTRUE(perl_get_sv("Storable::interwork_56_64bit", GV_ADD))) { - if ((c != (sizeof (byteorderstr_56) - 1)) - || memNE(buf, byteorderstr_56, c)) - CROAK(("Byte order is not compatible")); - } else -#endif - { - if ((c != (sizeof (byteorderstr) - 1)) || memNE(buf, byteorderstr, c)) - CROAK(("Byte order is not compatible")); - } - - current = buf + c; - - /* sizeof(int) */ - if ((int) *current++ != sizeof(int)) - CROAK(("Integer size is not compatible")); - - /* sizeof(long) */ - if ((int) *current++ != sizeof(long)) - CROAK(("Long integer size is not compatible")); - - /* sizeof(char *) */ - if ((int) *current != sizeof(char *)) - CROAK(("Pointer size is not compatible")); - - if (use_NV_size) { - /* sizeof(NV) */ - if ((int) *++current != sizeof(NV)) - CROAK(("Double size is not compatible")); - } - - return &PL_sv_undef; /* OK */ -} - -/* - * retrieve - * - * Recursively retrieve objects from the specified file and return their - * root SV (which may be an AV or an HV for what we care). - * Returns null if there is a problem. - */ -static SV *retrieve(pTHX_ stcxt_t *cxt, const char *cname) -{ - int type; - SV **svh; - SV *sv; - - TRACEME(("retrieve")); - - /* - * Grab address tag which identifies the object if we are retrieving - * an older format. Since the new binary format counts objects and no - * longer explicitely tags them, we must keep track of the correspondance - * ourselves. - * - * The following section will disappear one day when the old format is - * no longer supported, hence the final "goto" in the "if" block. - */ - - if (cxt->hseen) { /* Retrieving old binary */ - stag_t tag; - if (cxt->netorder) { - I32 nettag; - READ(&nettag, sizeof(I32)); /* Ordered sequence of I32 */ - tag = (stag_t) nettag; - } else - READ(&tag, sizeof(stag_t)); /* Original address of the SV */ - - GETMARK(type); - if (type == SX_OBJECT) { - I32 tagn; - svh = hv_fetch(cxt->hseen, (char *) &tag, sizeof(tag), FALSE); - if (!svh) - CROAK(("Old tag 0x%"UVxf" should have been mapped already", - (UV) tag)); - tagn = SvIV(*svh); /* Mapped tag number computed earlier below */ - - /* - * The following code is common with the SX_OBJECT case below. - */ - - svh = av_fetch(cxt->aseen, tagn, FALSE); - if (!svh) - CROAK(("Object #%"IVdf" should have been retrieved already", - (IV) tagn)); - sv = *svh; - TRACEME(("has retrieved #%d at 0x%"UVxf, tagn, PTR2UV(sv))); - SvREFCNT_inc(sv); /* One more reference to this same sv */ - return sv; /* The SV pointer where object was retrieved */ - } - - /* - * Map new object, but don't increase tagnum. This will be done - * by each of the retrieve_* functions when they call SEEN(). - * - * The mapping associates the "tag" initially present with a unique - * tag number. See test for SX_OBJECT above to see how this is perused. - */ - - if (!hv_store(cxt->hseen, (char *) &tag, sizeof(tag), - newSViv(cxt->tagnum), 0)) - return (SV *) 0; - - goto first_time; - } - - /* - * Regular post-0.6 binary format. - */ - - GETMARK(type); - - TRACEME(("retrieve type = %d", type)); - - /* - * Are we dealing with an object we should have already retrieved? - */ - - if (type == SX_OBJECT) { - I32 tag; - READ_I32(tag); - tag = ntohl(tag); - svh = av_fetch(cxt->aseen, tag, FALSE); - if (!svh) - CROAK(("Object #%"IVdf" should have been retrieved already", - (IV) tag)); - sv = *svh; - TRACEME(("had retrieved #%d at 0x%"UVxf, tag, PTR2UV(sv))); - SvREFCNT_inc(sv); /* One more reference to this same sv */ - return sv; /* The SV pointer where object was retrieved */ - } else if (type >= SX_ERROR && cxt->ver_minor > STORABLE_BIN_MINOR) { - if (cxt->accept_future_minor < 0) - cxt->accept_future_minor - = (SvTRUE(perl_get_sv("Storable::accept_future_minor", - GV_ADD)) - ? 1 : 0); - if (cxt->accept_future_minor == 1) { - CROAK(("Storable binary image v%d.%d contains data of type %d. " - "This Storable is v%d.%d and can only handle data types up to %d", - cxt->ver_major, cxt->ver_minor, type, - STORABLE_BIN_MAJOR, STORABLE_BIN_MINOR, SX_ERROR - 1)); - } - } - -first_time: /* Will disappear when support for old format is dropped */ - - /* - * Okay, first time through for this one. - */ - - sv = RETRIEVE(cxt, type)(aTHX_ cxt, cname); - if (!sv) - return (SV *) 0; /* Failed */ - - /* - * Old binary formats (pre-0.7). - * - * Final notifications, ended by SX_STORED may now follow. - * Currently, the only pertinent notification to apply on the - * freshly retrieved object is either: - * SX_CLASS <char-len> <classname> for short classnames. - * SX_LG_CLASS <int-len> <classname> for larger one (rare!). - * Class name is then read into the key buffer pool used by - * hash table key retrieval. - */ - - if (cxt->ver_major < 2) { - while ((type = GETCHAR()) != SX_STORED) { - I32 len; - switch (type) { - case SX_CLASS: - GETMARK(len); /* Length coded on a single char */ - break; - case SX_LG_CLASS: /* Length coded on a regular integer */ - RLEN(len); - break; - case EOF: - default: - return (SV *) 0; /* Failed */ - } - KBUFCHK((STRLEN)len); /* Grow buffer as necessary */ - if (len) - READ(kbuf, len); - kbuf[len] = '\0'; /* Mark string end */ - BLESS(sv, kbuf); - } - } - - TRACEME(("ok (retrieved 0x%"UVxf", refcnt=%d, %s)", PTR2UV(sv), - SvREFCNT(sv) - 1, sv_reftype(sv, FALSE))); - - return sv; /* Ok */ -} - -/* - * do_retrieve - * - * Retrieve data held in file and return the root object. - * Common routine for pretrieve and mretrieve. - */ -static SV *do_retrieve( - pTHX_ - PerlIO *f, - SV *in, - int optype) -{ - dSTCXT; - SV *sv; - int is_tainted; /* Is input source tainted? */ - int pre_06_fmt = 0; /* True with pre Storable 0.6 formats */ - - TRACEME(("do_retrieve (optype = 0x%x)", optype)); - - optype |= ST_RETRIEVE; - - /* - * Sanity assertions for retrieve dispatch tables. - */ - - ASSERT(sizeof(sv_old_retrieve) == sizeof(sv_retrieve), - ("old and new retrieve dispatch table have same size")); - ASSERT(sv_old_retrieve[SX_ERROR] == retrieve_other, - ("SX_ERROR entry correctly initialized in old dispatch table")); - ASSERT(sv_retrieve[SX_ERROR] == retrieve_other, - ("SX_ERROR entry correctly initialized in new dispatch table")); - - /* - * Workaround for CROAK leak: if they enter with a "dirty" context, - * free up memory for them now. - */ - - if (cxt->s_dirty) - clean_context(aTHX_ cxt); - - /* - * Now that STORABLE_xxx hooks exist, it is possible that they try to - * re-enter retrieve() via the hooks. - */ - - if (cxt->entry) - cxt = allocate_context(aTHX_ cxt); - - cxt->entry++; - - ASSERT(cxt->entry == 1, ("starting new recursion")); - ASSERT(!cxt->s_dirty, ("clean context")); - - /* - * Prepare context. - * - * Data is loaded into the memory buffer when f is NULL, unless `in' is - * also NULL, in which case we're expecting the data to already lie - * in the buffer (dclone case). - */ - - KBUFINIT(); /* Allocate hash key reading pool once */ - - if (!f && in) { -#ifdef SvUTF8_on - if (SvUTF8(in)) { - STRLEN length; - const char *orig = SvPV(in, length); - char *asbytes; - /* This is quite deliberate. I want the UTF8 routines - to encounter the '\0' which perl adds at the end - of all scalars, so that any new string also has - this. - */ - STRLEN klen_tmp = length + 1; - bool is_utf8 = TRUE; - - /* Just casting the &klen to (STRLEN) won't work - well if STRLEN and I32 are of different widths. - --jhi */ - asbytes = (char*)bytes_from_utf8((U8*)orig, - &klen_tmp, - &is_utf8); - if (is_utf8) { - CROAK(("Frozen string corrupt - contains characters outside 0-255")); - } - if (asbytes != orig) { - /* String has been converted. - There is no need to keep any reference to - the old string. */ - in = sv_newmortal(); - /* We donate the SV the malloc()ed string - bytes_from_utf8 returned us. */ - SvUPGRADE(in, SVt_PV); - SvPOK_on(in); - SvPV_set(in, asbytes); - SvLEN_set(in, klen_tmp); - SvCUR_set(in, klen_tmp - 1); - } - } -#endif - MBUF_SAVE_AND_LOAD(in); - } - - /* - * Magic number verifications. - * - * This needs to be done before calling init_retrieve_context() - * since the format indication in the file are necessary to conduct - * some of the initializations. - */ - - cxt->fio = f; /* Where I/O are performed */ - - if (!magic_check(aTHX_ cxt)) - CROAK(("Magic number checking on storable %s failed", - cxt->fio ? "file" : "string")); - - TRACEME(("data stored in %s format", - cxt->netorder ? "net order" : "native")); - - /* - * Check whether input source is tainted, so that we don't wrongly - * taint perfectly good values... - * - * We assume file input is always tainted. If both `f' and `in' are - * NULL, then we come from dclone, and tainted is already filled in - * the context. That's a kludge, but the whole dclone() thing is - * already quite a kludge anyway! -- RAM, 15/09/2000. - */ - - is_tainted = f ? 1 : (in ? SvTAINTED(in) : cxt->s_tainted); - TRACEME(("input source is %s", is_tainted ? "tainted" : "trusted")); - init_retrieve_context(aTHX_ cxt, optype, is_tainted); - - ASSERT(is_retrieving(aTHX), ("within retrieve operation")); - - sv = retrieve(aTHX_ cxt, 0); /* Recursively retrieve object, get root SV */ - - /* - * Final cleanup. - */ - - if (!f && in) - MBUF_RESTORE(); - - pre_06_fmt = cxt->hseen != NULL; /* Before we clean context */ - - /* - * The "root" context is never freed. - */ - - clean_retrieve_context(aTHX_ cxt); - if (cxt->prev) /* This context was stacked */ - free_context(aTHX_ cxt); /* It was not the "root" context */ - - /* - * Prepare returned value. - */ - - if (!sv) { - TRACEME(("retrieve ERROR")); -#if (PATCHLEVEL <= 4) - /* perl 5.00405 seems to screw up at this point with an - 'attempt to modify a read only value' error reported in the - eval { $self = pretrieve(*FILE) } in _retrieve. - I can't see what the cause of this error is, but I suspect a - bug in 5.004, as it seems to be capable of issuing spurious - errors or core dumping with matches on $@. I'm not going to - spend time on what could be a fruitless search for the cause, - so here's a bodge. If you're running 5.004 and don't like - this inefficiency, either upgrade to a newer perl, or you are - welcome to find the problem and send in a patch. - */ - return newSV(0); -#else - return &PL_sv_undef; /* Something went wrong, return undef */ -#endif - } - - TRACEME(("retrieve got %s(0x%"UVxf")", - sv_reftype(sv, FALSE), PTR2UV(sv))); - - /* - * Backward compatibility with Storable-0.5@9 (which we know we - * are retrieving if hseen is non-null): don't create an extra RV - * for objects since we special-cased it at store time. - * - * Build a reference to the SV returned by pretrieve even if it is - * already one and not a scalar, for consistency reasons. - */ - - if (pre_06_fmt) { /* Was not handling overloading by then */ - SV *rv; - TRACEME(("fixing for old formats -- pre 0.6")); - if (sv_type(aTHX_ sv) == svis_REF && (rv = SvRV(sv)) && SvOBJECT(rv)) { - TRACEME(("ended do_retrieve() with an object -- pre 0.6")); - return sv; - } - } - - /* - * If reference is overloaded, restore behaviour. - * - * NB: minor glitch here: normally, overloaded refs are stored specially - * so that we can croak when behaviour cannot be re-installed, and also - * avoid testing for overloading magic at each reference retrieval. - * - * Unfortunately, the root reference is implicitely stored, so we must - * check for possible overloading now. Furthermore, if we don't restore - * overloading, we cannot croak as if the original ref was, because we - * have no way to determine whether it was an overloaded ref or not in - * the first place. - * - * It's a pity that overloading magic is attached to the rv, and not to - * the underlying sv as blessing is. - */ - - if (SvOBJECT(sv)) { - HV *stash = (HV *) SvSTASH(sv); - SV *rv = newRV_noinc(sv); - if (stash && Gv_AMG(stash)) { - SvAMAGIC_on(rv); - TRACEME(("restored overloading on root reference")); - } - TRACEME(("ended do_retrieve() with an object")); - return rv; - } - - TRACEME(("regular do_retrieve() end")); - - return newRV_noinc(sv); -} - -/* - * pretrieve - * - * Retrieve data held in file and return the root object, undef on error. - */ -static SV *pretrieve(pTHX_ PerlIO *f) -{ - TRACEME(("pretrieve")); - return do_retrieve(aTHX_ f, Nullsv, 0); -} - -/* - * mretrieve - * - * Retrieve data held in scalar and return the root object, undef on error. - */ -static SV *mretrieve(pTHX_ SV *sv) -{ - TRACEME(("mretrieve")); - return do_retrieve(aTHX_ (PerlIO*) 0, sv, 0); -} - -/*** - *** Deep cloning - ***/ - -/* - * dclone - * - * Deep clone: returns a fresh copy of the original referenced SV tree. - * - * This is achieved by storing the object in memory and restoring from - * there. Not that efficient, but it should be faster than doing it from - * pure perl anyway. - */ -static SV *dclone(pTHX_ SV *sv) -{ - dSTCXT; - int size; - stcxt_t *real_context; - SV *out; - - TRACEME(("dclone")); - - /* - * Workaround for CROAK leak: if they enter with a "dirty" context, - * free up memory for them now. - */ - - if (cxt->s_dirty) - clean_context(aTHX_ cxt); - - /* - * Tied elements seem to need special handling. - */ - - if ((SvTYPE(sv) == SVt_PVLV -#if PERL_VERSION < 8 - || SvTYPE(sv) == SVt_PVMG -#endif - ) && SvRMAGICAL(sv) && mg_find(sv, 'p')) { - mg_get(sv); - } - - /* - * do_store() optimizes for dclone by not freeing its context, should - * we need to allocate one because we're deep cloning from a hook. - */ - - if (!do_store(aTHX_ (PerlIO*) 0, sv, ST_CLONE, FALSE, (SV**) 0)) - return &PL_sv_undef; /* Error during store */ - - /* - * Because of the above optimization, we have to refresh the context, - * since a new one could have been allocated and stacked by do_store(). - */ - - { dSTCXT; real_context = cxt; } /* Sub-block needed for macro */ - cxt = real_context; /* And we need this temporary... */ - - /* - * Now, `cxt' may refer to a new context. - */ - - ASSERT(!cxt->s_dirty, ("clean context")); - ASSERT(!cxt->entry, ("entry will not cause new context allocation")); - - size = MBUF_SIZE(); - TRACEME(("dclone stored %d bytes", size)); - MBUF_INIT(size); - - /* - * Since we're passing do_retrieve() both a NULL file and sv, we need - * to pre-compute the taintedness of the input by setting cxt->tainted - * to whatever state our own input string was. -- RAM, 15/09/2000 - * - * do_retrieve() will free non-root context. - */ - - cxt->s_tainted = SvTAINTED(sv); - out = do_retrieve(aTHX_ (PerlIO*) 0, Nullsv, ST_CLONE); - - TRACEME(("dclone returns 0x%"UVxf, PTR2UV(out))); - - return out; -} - -/*** - *** Glue with perl. - ***/ - -/* - * The Perl IO GV object distinguishes between input and output for sockets - * but not for plain files. To allow Storable to transparently work on - * plain files and sockets transparently, we have to ask xsubpp to fetch the - * right object for us. Hence the OutputStream and InputStream declarations. - * - * Before perl 5.004_05, those entries in the standard typemap are not - * defined in perl include files, so we do that here. - */ - -#ifndef OutputStream -#define OutputStream PerlIO * -#define InputStream PerlIO * -#endif /* !OutputStream */ - -MODULE = Storable PACKAGE = Storable::Cxt - -void -DESTROY(self) - SV *self -PREINIT: - stcxt_t *cxt = (stcxt_t *)SvPVX(SvRV(self)); -PPCODE: - if (kbuf) - Safefree(kbuf); - if (!cxt->membuf_ro && mbase) - Safefree(mbase); - if (cxt->membuf_ro && (cxt->msaved).arena) - Safefree((cxt->msaved).arena); - - -MODULE = Storable PACKAGE = Storable - -PROTOTYPES: ENABLE - -BOOT: -{ - HV *stash = gv_stashpvn("Storable", 8, GV_ADD); - newCONSTSUB(stash, "BIN_MAJOR", newSViv(STORABLE_BIN_MAJOR)); - newCONSTSUB(stash, "BIN_MINOR", newSViv(STORABLE_BIN_MINOR)); - newCONSTSUB(stash, "BIN_WRITE_MINOR", newSViv(STORABLE_BIN_WRITE_MINOR)); - - init_perinterp(aTHX); - gv_fetchpv("Storable::drop_utf8", GV_ADDMULTI, SVt_PV); -#ifdef DEBUGME - /* Only disable the used only once warning if we are in debugging mode. */ - gv_fetchpv("Storable::DEBUGME", GV_ADDMULTI, SVt_PV); -#endif -#ifdef USE_56_INTERWORK_KLUDGE - gv_fetchpv("Storable::interwork_56_64bit", GV_ADDMULTI, SVt_PV); -#endif -} - -void -init_perinterp() - CODE: - init_perinterp(aTHX); - -int -pstore(f,obj) -OutputStream f -SV * obj - CODE: - RETVAL = pstore(aTHX_ f, obj); - OUTPUT: - RETVAL - -int -net_pstore(f,obj) -OutputStream f -SV * obj - CODE: - RETVAL = net_pstore(aTHX_ f, obj); - OUTPUT: - RETVAL - -SV * -mstore(obj) -SV * obj - CODE: - RETVAL = mstore(aTHX_ obj); - OUTPUT: - RETVAL - -SV * -net_mstore(obj) -SV * obj - CODE: - RETVAL = net_mstore(aTHX_ obj); - OUTPUT: - RETVAL - -SV * -pretrieve(f) -InputStream f - CODE: - RETVAL = pretrieve(aTHX_ f); - OUTPUT: - RETVAL - -SV * -mretrieve(sv) -SV * sv - CODE: - RETVAL = mretrieve(aTHX_ sv); - OUTPUT: - RETVAL - -SV * -dclone(sv) -SV * sv - CODE: - RETVAL = dclone(aTHX_ sv); - OUTPUT: - RETVAL - -int -last_op_in_netorder() - CODE: - RETVAL = last_op_in_netorder(aTHX); - OUTPUT: - RETVAL - -int -is_storing() - CODE: - RETVAL = is_storing(aTHX); - OUTPUT: - RETVAL - -int -is_retrieving() - CODE: - RETVAL = is_retrieving(aTHX); - OUTPUT: - RETVAL diff --git a/ext/Storable/hints/gnukfreebsd.pl b/ext/Storable/hints/gnukfreebsd.pl deleted file mode 100644 index db63567966..0000000000 --- a/ext/Storable/hints/gnukfreebsd.pl +++ /dev/null @@ -1 +0,0 @@ -do './hints/linux.pl' or die $@; diff --git a/ext/Storable/hints/gnuknetbsd.pl b/ext/Storable/hints/gnuknetbsd.pl deleted file mode 100644 index db63567966..0000000000 --- a/ext/Storable/hints/gnuknetbsd.pl +++ /dev/null @@ -1 +0,0 @@ -do './hints/linux.pl' or die $@; diff --git a/ext/Storable/hints/hpux.pl b/ext/Storable/hints/hpux.pl deleted file mode 100644 index 959d6fedf0..0000000000 --- a/ext/Storable/hints/hpux.pl +++ /dev/null @@ -1,10 +0,0 @@ -# HP C-ANSI-C has problems in the optimizer for 5.8.x (not for 5.11.x) -# So drop to -O1 for Storable - -use Config; - -unless ($Config{gccversion}) { - my $optimize = $Config{optimize}; - $optimize =~ s/(^| )[-+]O[2-9]( |$)/$1+O1$2/ and - $self->{OPTIMIZE} = $optimize; - } diff --git a/ext/Storable/hints/linux.pl b/ext/Storable/hints/linux.pl deleted file mode 100644 index 0c7d5e35a9..0000000000 --- a/ext/Storable/hints/linux.pl +++ /dev/null @@ -1,15 +0,0 @@ -# gcc -O3 (and higher) can cause code produced from Storable.xs that -# dumps core immediately in recurse.t and retrieve.t, in is_storing() -# and last_op_in_netorder(), respectively. In both cases the cxt is -# full of junk (and according to valgrind the cxt was never stack'd, -# malloc'd or free'd). Observed in Debian 3.0 x86, with gccs 2.95.4 -# 20011002 and 3.3, and in Redhat 7.1 with gcc 3.3.1. The failures -# happen only for unthreaded builds, threaded builds work okay. -use Config; -if ($Config{gccversion}) { - my $optimize = $Config{optimize}; - if ($optimize =~ s/(^| )-O[3-9]( |$)/$1-O2$2/) { - $self->{OPTIMIZE} = $optimize; - } -} - diff --git a/ext/Storable/t/HAS_ATTACH.pm b/ext/Storable/t/HAS_ATTACH.pm deleted file mode 100644 index 72855aa101..0000000000 --- a/ext/Storable/t/HAS_ATTACH.pm +++ /dev/null @@ -1,10 +0,0 @@ -package HAS_ATTACH; - -sub STORABLE_attach { - ++$attached_count; - return bless [], 'HAS_ATTACH'; -} - -++$loaded_count; - -1; diff --git a/ext/Storable/t/HAS_HOOK.pm b/ext/Storable/t/HAS_HOOK.pm deleted file mode 100644 index 979a6a207d..0000000000 --- a/ext/Storable/t/HAS_HOOK.pm +++ /dev/null @@ -1,9 +0,0 @@ -package HAS_HOOK; - -sub STORABLE_thaw { - ++$thawed_count; -} - -++$loaded_count; - -1; diff --git a/ext/Storable/t/HAS_OVERLOAD.pm b/ext/Storable/t/HAS_OVERLOAD.pm deleted file mode 100644 index 8a622a4bbe..0000000000 --- a/ext/Storable/t/HAS_OVERLOAD.pm +++ /dev/null @@ -1,14 +0,0 @@ -package HAS_OVERLOAD; - -use overload - '""' => sub { ${$_[0]} }, fallback => 1; - -sub make { - my $package = shift; - my $value = shift; - bless \$value, $package; -} - -++$loaded_count; - -1; diff --git a/ext/Storable/t/attach_errors.t b/ext/Storable/t/attach_errors.t deleted file mode 100644 index ffa41f9138..0000000000 --- a/ext/Storable/t/attach_errors.t +++ /dev/null @@ -1,264 +0,0 @@ -#!./perl -w -# -# Copyright 2005, Adam Kennedy. -# -# You may redistribute only under the same terms as Perl 5, as specified -# in the README file that comes with the distribution. -# - -# Man, blessed.t scared the hell out of me. For a second there I thought -# I'd lose Test::More... - -# This file tests several known-error cases relating to STORABLE_attach, in -# which Storable should (correctly) throw errors. - -sub BEGIN { - unshift @INC, 't'; - require Config; import Config; - if ($ENV{PERL_CORE} and $Config{'extensions'} !~ /\bStorable\b/) { - print "1..0 # Skip: Storable was not built\n"; - exit 0; - } -} - -use Test::More tests => 35; -use Storable (); - - - - - -##################################################################### -# Error 1 -# -# Classes that implement STORABLE_thaw _cannot_ have references -# returned by their STORABLE_freeze method. When they do, Storable -# should throw an exception - - - -# Good Case - should not die -{ - my $goodfreeze = bless {}, 'My::GoodFreeze'; - my $frozen = undef; - eval { - $frozen = Storable::freeze( $goodfreeze ); - }; - ok( ! $@, 'Storable does not die when STORABLE_freeze does not return references' ); - ok( $frozen, 'Storable freezes to a string successfully' ); - - package My::GoodFreeze; - - sub STORABLE_freeze { - my ($self, $clone) = @_; - - # Illegally include a reference in this return - return (''); - } - - sub STORABLE_attach { - my ($class, $clone, $string) = @_; - return bless { }, 'My::GoodFreeze'; - } -} - - - -# Error Case - should die on freeze -{ - my $badfreeze = bless {}, 'My::BadFreeze'; - eval { - Storable::freeze( $badfreeze ); - }; - ok( $@, 'Storable dies correctly when STORABLE_freeze returns a referece' ); - # Check for a unique substring of the error message - ok( $@ =~ /cannot return references/, 'Storable dies with the expected error' ); - - package My::BadFreeze; - - sub STORABLE_freeze { - my ($self, $clone) = @_; - - # Illegally include a reference in this return - return ('', []); - } - - sub STORABLE_attach { - my ($class, $clone, $string) = @_; - return bless { }, 'My::BadFreeze'; - } -} - - - - - -##################################################################### -# Error 2 -# -# If, for some reason, a STORABLE_attach object is accidentally stored -# with references, this should be checked and and error should be throw. - - - -# Good Case - should not die -{ - my $goodthaw = bless {}, 'My::GoodThaw'; - my $frozen = undef; - eval { - $frozen = Storable::freeze( $goodthaw ); - }; - ok( $frozen, 'Storable freezes to a string as expected' ); - my $thawed = eval { - Storable::thaw( $frozen ); - }; - isa_ok( $thawed, 'My::GoodThaw' ); - is( $thawed->{foo}, 'bar', 'My::GoodThaw thawed correctly as expected' ); - - package My::GoodThaw; - - sub STORABLE_freeze { - my ($self, $clone) = @_; - - return (''); - } - - sub STORABLE_attach { - my ($class, $clone, $string) = @_; - return bless { 'foo' => 'bar' }, 'My::GoodThaw'; - } -} - - - -# Bad Case - should die on thaw -{ - # Create the frozen string normally - my $badthaw = bless { }, 'My::BadThaw'; - my $frozen = undef; - eval { - $frozen = Storable::freeze( $badthaw ); - }; - ok( $frozen, 'BadThaw was frozen with references correctly' ); - - # Set up the error condition by deleting the normal STORABLE_thaw, - # and creating a STORABLE_attach. - *My::BadThaw::STORABLE_attach = *My::BadThaw::STORABLE_thaw; - *My::BadThaw::STORABLE_attach = *My::BadThaw::STORABLE_thaw; # Suppress a warning - delete ${'My::BadThaw::'}{STORABLE_thaw}; - - # Trigger the error condition - my $thawed = undef; - eval { - $thawed = Storable::thaw( $frozen ); - }; - ok( $@, 'My::BadThaw object dies when thawing as expected' ); - # Check for a snippet from the error message - ok( $@ =~ /unexpected references/, 'Dies with the expected error message' ); - - package My::BadThaw; - - sub STORABLE_freeze { - my ($self, $clone) = @_; - - return ('', []); - } - - # Start with no STORABLE_attach method so we can get a - # frozen object-containing-a-reference into the freeze string. - sub STORABLE_thaw { - my ($class, $clone, $string) = @_; - return bless { 'foo' => 'bar' }, 'My::BadThaw'; - } -} - - - - -##################################################################### -# Error 3 -# -# Die if what is returned by STORABLE_attach is not something of that class - - - -# Good Case - should not die -{ - my $goodattach = bless { }, 'My::GoodAttach'; - my $frozen = Storable::freeze( $goodattach ); - ok( $frozen, 'My::GoodAttach return as expected' ); - my $thawed = eval { - Storable::thaw( $frozen ); - }; - isa_ok( $thawed, 'My::GoodAttach' ); - is( ref($thawed), 'My::GoodAttach::Subclass', - 'The slightly-tricky good "returns a subclass" case returns as expected' ); - - package My::GoodAttach; - - sub STORABLE_freeze { - my ($self, $cloning) = @_; - return (''); - } - - sub STORABLE_attach { - my ($class, $cloning, $string) = @_; - - return bless { }, 'My::GoodAttach::Subclass'; - } - - package My::GoodAttach::Subclass; - - BEGIN { - @ISA = 'My::GoodAttach'; - } -} - - - -# Bad Cases - die on thaw -{ - my $returnvalue = undef; - - # Create and freeze the object - my $badattach = bless { }, 'My::BadAttach'; - my $frozen = Storable::freeze( $badattach ); - ok( $frozen, 'BadAttach freezes as expected' ); - - # Try a number of different return values, all of which - # should cause Storable to die. - my @badthings = ( - undef, - '', - 1, - [], - {}, - \"foo", - (bless { }, 'Foo'), - ); - foreach ( @badthings ) { - $returnvalue = $_; - - my $thawed = undef; - eval { - $thawed = Storable::thaw( $frozen ); - }; - ok( $@, 'BadAttach dies on thaw' ); - ok( $@ =~ /STORABLE_attach did not return a My::BadAttach object/, - 'BadAttach dies on thaw with the expected error message' ); - is( $thawed, undef, 'Double checking $thawed was not set' ); - } - - package My::BadAttach; - - sub STORABLE_freeze { - my ($self, $cloning) = @_; - return (''); - } - - sub STORABLE_attach { - my ($class, $cloning, $string) = @_; - - return $returnvalue; - } -} diff --git a/ext/Storable/t/attach_singleton.t b/ext/Storable/t/attach_singleton.t deleted file mode 100644 index e850a16f9c..0000000000 --- a/ext/Storable/t/attach_singleton.t +++ /dev/null @@ -1,84 +0,0 @@ -#!./perl -w -# -# Copyright 2005, Adam Kennedy. -# -# You may redistribute only under the same terms as Perl 5, as specified -# in the README file that comes with the distribution. -# - -# Tests freezing/thawing structures containing Singleton objects, -# which should see both structs pointing to the same object. - -sub BEGIN { - unshift @INC, 't'; - require Config; import Config; - if ($ENV{PERL_CORE} and $Config{'extensions'} !~ /\bStorable\b/) { - print "1..0 # Skip: Storable was not built\n"; - exit 0; - } -} - -use Test::More tests => 11; -use Storable (); - -# Get the singleton -my $object = My::Singleton->new; -isa_ok( $object, 'My::Singleton' ); - -# Confirm (for the record) that the class is actually a Singleton -my $object2 = My::Singleton->new; -isa_ok( $object2, 'My::Singleton' ); -is( "$object", "$object2", 'Class is a singleton' ); - -############ -# Main Tests - -my $struct = [ 1, $object, 3 ]; - -# Freeze the struct -my $frozen = Storable::freeze( $struct ); -ok( (defined($frozen) and ! ref($frozen) and length($frozen)), 'freeze returns a string' ); - -# Thaw the struct -my $thawed = Storable::thaw( $frozen ); - -# Now it should look exactly like the original -is_deeply( $struct, $thawed, 'Struct superficially looks like the original' ); - -# ... EXCEPT that the Singleton should be the same instance of the object -is( "$struct->[1]", "$thawed->[1]", 'Singleton thaws correctly' ); - -# We can also test this empirically -$struct->[1]->{value} = 'Goodbye cruel world!'; -is_deeply( $struct, $thawed, 'Empiric testing corfirms correct behaviour' ); - -# End Tests -########### - -package My::Singleton; - -my $SINGLETON = undef; - -sub new { - $SINGLETON or - $SINGLETON = bless { value => 'Hello World!' }, $_[0]; -} - -sub STORABLE_freeze { - my $self = shift; - - # We don't actually need to return anything, but provide a null string - # to avoid the null-list-return behaviour. - return ('foo'); -} - -sub STORABLE_attach { - my ($class, $clone, $string) = @_; - Test::More::ok( ! ref $class, 'STORABLE_attach passed class, and not an object' ); - Test::More::is( $class, 'My::Singleton', 'STORABLE_attach is passed the correct class name' ); - Test::More::is( $clone, 0, 'We are not in a dclone' ); - Test::More::is( $string, 'foo', 'STORABLE_attach gets the string back' ); - - # Get the Singleton object and return it - return $class->new; -} diff --git a/ext/Storable/t/blessed.t b/ext/Storable/t/blessed.t deleted file mode 100644 index 7c0494c840..0000000000 --- a/ext/Storable/t/blessed.t +++ /dev/null @@ -1,195 +0,0 @@ -#!./perl -# -# Copyright (c) 1995-2000, Raphael Manfredi -# -# You may redistribute only under the same terms as Perl 5, as specified -# in the README file that comes with the distribution. -# - -sub BEGIN { - unshift @INC, 't'; - require Config; import Config; - if ($ENV{PERL_CORE} and $Config{'extensions'} !~ /\bStorable\b/) { - print "1..0 # Skip: Storable was not built\n"; - exit 0; - } - require 'st-dump.pl'; -} - -sub ok; - -use Storable qw(freeze thaw); - -%::immortals - = (u => \undef, - 'y' => \(1 == 1), - n => \(1 == 0) -); - -my $test = 12; -my $tests = $test + 6 + 2 * 6 * keys %::immortals; -print "1..$tests\n"; - -package SHORT_NAME; - -sub make { bless [], shift } - -package SHORT_NAME_WITH_HOOK; - -sub make { bless [], shift } - -sub STORABLE_freeze { - my $self = shift; - return ("", $self); -} - -sub STORABLE_thaw { - my $self = shift; - my $cloning = shift; - my ($x, $obj) = @_; - die "STORABLE_thaw" unless $obj eq $self; -} - -package main; - -# Still less than 256 bytes, so long classname logic not fully exercised -# Wait until Perl removes the restriction on identifier lengths. -my $name = "LONG_NAME_" . 'xxxxxxxxxxxxx::' x 14 . "final"; - -eval <<EOC; -package $name; - -\@ISA = ("SHORT_NAME"); -EOC -die $@ if $@; -ok 1, $@ eq ''; - -eval <<EOC; -package ${name}_WITH_HOOK; - -\@ISA = ("SHORT_NAME_WITH_HOOK"); -EOC -ok 2, $@ eq ''; - -# Construct a pool of objects -my @pool; - -for (my $i = 0; $i < 10; $i++) { - push(@pool, SHORT_NAME->make); - push(@pool, SHORT_NAME_WITH_HOOK->make); - push(@pool, $name->make); - push(@pool, "${name}_WITH_HOOK"->make); -} - -my $x = freeze \@pool; -ok 3, 1; - -my $y = thaw $x; -ok 4, ref $y eq 'ARRAY'; -ok 5, @{$y} == @pool; - -ok 6, ref $y->[0] eq 'SHORT_NAME'; -ok 7, ref $y->[1] eq 'SHORT_NAME_WITH_HOOK'; -ok 8, ref $y->[2] eq $name; -ok 9, ref $y->[3] eq "${name}_WITH_HOOK"; - -my $good = 1; -for (my $i = 0; $i < 10; $i++) { - do { $good = 0; last } unless ref $y->[4*$i] eq 'SHORT_NAME'; - do { $good = 0; last } unless ref $y->[4*$i+1] eq 'SHORT_NAME_WITH_HOOK'; - do { $good = 0; last } unless ref $y->[4*$i+2] eq $name; - do { $good = 0; last } unless ref $y->[4*$i+3] eq "${name}_WITH_HOOK"; -} -ok 10, $good; - -{ - my $blessed_ref = bless \\[1,2,3], 'Foobar'; - my $x = freeze $blessed_ref; - my $y = thaw $x; - ok 11, ref $y eq 'Foobar'; - ok 12, $$$y->[0] == 1; -} - -package RETURNS_IMMORTALS; - -sub make { my $self = shift; bless [@_], $self } - -sub STORABLE_freeze { - # Some reference some number of times. - my $self = shift; - my ($what, $times) = @$self; - return ("$what$times", ($::immortals{$what}) x $times); -} - -sub STORABLE_thaw { - my $self = shift; - my $cloning = shift; - my ($x, @refs) = @_; - my ($what, $times) = $x =~ /(.)(\d+)/; - die "'$x' didn't match" unless defined $times; - main::ok ++$test, @refs == $times; - my $expect = $::immortals{$what}; - die "'$x' did not give a reference" unless ref $expect; - my $fail; - foreach (@refs) { - $fail++ if $_ != $expect; - } - main::ok ++$test, !$fail; -} - -package main; - -# $Storable::DEBUGME = 1; -my $count; -foreach $count (1..3) { - my $immortal; - foreach $immortal (keys %::immortals) { - print "# $immortal x $count\n"; - my $i = RETURNS_IMMORTALS->make ($immortal, $count); - - my $f = freeze ($i); - ok ++$test, $f; - my $t = thaw $f; - ok ++$test, 1; - } -} - -# Test automatic require of packages to find thaw hook. - -package HAS_HOOK; - -$loaded_count = 0; -$thawed_count = 0; - -sub make { - bless []; -} - -sub STORABLE_freeze { - my $self = shift; - return ''; -} - -package main; - -my $f = freeze (HAS_HOOK->make); - -ok ++$test, $HAS_HOOK::loaded_count == 0; -ok ++$test, $HAS_HOOK::thawed_count == 0; - -my $t = thaw $f; -ok ++$test, $HAS_HOOK::loaded_count == 1; -ok ++$test, $HAS_HOOK::thawed_count == 1; -ok ++$test, $t; -ok ++$test, ref $t eq 'HAS_HOOK'; - -# Can't do this because the method is still cached by UNIVERSAL::can -# delete $INC{"HAS_HOOK.pm"}; -# undef &HAS_HOOK::STORABLE_thaw; -# -# warn HAS_HOOK->can('STORABLE_thaw'); -# $t = thaw $f; -# ok ++$test, $HAS_HOOK::loaded_count == 2; -# ok ++$test, $HAS_HOOK::thawed_count == 2; -# ok ++$test, $t; -# ok ++$test, ref $t eq 'HAS_HOOK'; diff --git a/ext/Storable/t/canonical.t b/ext/Storable/t/canonical.t deleted file mode 100644 index 204a2359b6..0000000000 --- a/ext/Storable/t/canonical.t +++ /dev/null @@ -1,144 +0,0 @@ -#!./perl -# -# Copyright (c) 1995-2000, Raphael Manfredi -# -# You may redistribute only under the same terms as Perl 5, as specified -# in the README file that comes with the distribution. -# - -sub BEGIN { - unshift @INC, 't'; - require Config; import Config; - if ($ENV{PERL_CORE} and $Config{'extensions'} !~ /\bStorable\b/) { - print "1..0 # Skip: Storable was not built\n"; - exit 0; - } -} - - -use Storable qw(freeze thaw dclone); -use vars qw($debugging $verbose); - -print "1..8\n"; - -sub ok { - my($testno, $ok) = @_; - print "not " unless $ok; - print "ok $testno\n"; -} - - -# Uncomment the folowing line to get a dump of the constructed data structure -# (you may want to reduce the size of the hashes too) -# $debugging = 1; - -$hashsize = 100; -$maxhash2size = 100; -$maxarraysize = 100; - -# Use MD5 if its available to make random string keys - -eval { require "MD5.pm" }; -$gotmd5 = !$@; - -# Use Data::Dumper if debugging and it is available to create an ASCII dump - -if ($debugging) { - eval { require "Data/Dumper.pm" }; - $gotdd = !$@; -} - -@fixed_strings = ("January", "February", "March", "April", "May", "June", - "July", "August", "September", "October", "November", "December" ); - -# Build some arbitrarily complex data structure starting with a top level hash -# (deeper levels contain scalars, references to hashes or references to arrays); - -for (my $i = 0; $i < $hashsize; $i++) { - my($k) = int(rand(1_000_000)); - $k = MD5->hexhash($k) if $gotmd5 and int(rand(2)); - $a1{$k} = { key => "$k", "value" => $i }; - - # A third of the elements are references to further hashes - - if (int(rand(1.5))) { - my($hash2) = {}; - my($hash2size) = int(rand($maxhash2size)); - while ($hash2size--) { - my($k2) = $k . $i . int(rand(100)); - $hash2->{$k2} = $fixed_strings[rand(int(@fixed_strings))]; - } - $a1{$k}->{value} = $hash2; - } - - # A further third are references to arrays - - elsif (int(rand(2))) { - my($arr_ref) = []; - my($arraysize) = int(rand($maxarraysize)); - while ($arraysize--) { - push(@$arr_ref, $fixed_strings[rand(int(@fixed_strings))]); - } - $a1{$k}->{value} = $arr_ref; - } -} - - -print STDERR Data::Dumper::Dumper(\%a1) if ($verbose and $gotdd); - - -# Copy the hash, element by element in order of the keys - -foreach $k (sort keys %a1) { - $a2{$k} = { key => "$k", "value" => $a1{$k}->{value} }; -} - -# Deep clone the hash - -$a3 = dclone(\%a1); - -# In canonical mode the frozen representation of each of the hashes -# should be identical - -$Storable::canonical = 1; - -$x1 = freeze(\%a1); -$x2 = freeze(\%a2); -$x3 = freeze($a3); - -ok 1, (length($x1) > $hashsize); # sanity check -ok 2, length($x1) == length($x2); # idem -ok 3, $x1 eq $x2; -ok 4, $x1 eq $x3; - -# In normal mode it is exceedingly unlikely that the frozen -# representaions of all the hashes will be the same (normally the hash -# elements are frozen in the order they are stored internally, -# i.e. pseudo-randomly). - -$Storable::canonical = 0; - -$x1 = freeze(\%a1); -$x2 = freeze(\%a2); -$x3 = freeze($a3); - - -# Two out of three the same may be a coincidence, all three the same -# is much, much more unlikely. Still it could happen, so this test -# may report a false negative. - -ok 5, ($x1 ne $x2) || ($x1 ne $x3); - - -# Ensure refs to "undef" values are properly shared -# Same test as in t/dclone.t to ensure the "canonical" code is also correct - -my $hash; -push @{$$hash{''}}, \$$hash{a}; -ok 6, $$hash{''}[0] == \$$hash{a}; - -my $cloned = dclone(dclone($hash)); -ok 7, $$cloned{''}[0] == \$$cloned{a}; - -$$cloned{a} = "blah"; -ok 8, $$cloned{''}[0] == \$$cloned{a}; diff --git a/ext/Storable/t/circular_hook.t b/ext/Storable/t/circular_hook.t deleted file mode 100644 index 48f4be403d..0000000000 --- a/ext/Storable/t/circular_hook.t +++ /dev/null @@ -1,86 +0,0 @@ -#!./perl -w -# -# Copyright 2005, Adam Kennedy. -# -# You may redistribute only under the same terms as Perl 5, as specified -# in the README file that comes with the distribution. -# - -# Man, blessed.t scared the hell out of me. For a second there I thought -# I'd lose Test::More... - -# This file tests several known-error cases relating to STORABLE_attach, in -# which Storable should (correctly) throw errors. - -sub BEGIN { - unshift @INC, 't'; - require Config; import Config; - if ($ENV{PERL_CORE} and $Config{'extensions'} !~ /\bStorable\b/) { - print "1..0 # Skip: Storable was not built\n"; - exit 0; - } -} - -use Storable (); -use Test::More tests => 9; - -my $ddd = bless { }, 'Foo'; -my $eee = bless { Bar => $ddd }, 'Bar'; -$ddd->{Foo} = $eee; - -my $array = [ $ddd ]; - -my $string = Storable::freeze( $array ); -my $thawed = Storable::thaw( $string ); - -# is_deeply infinite loops in ciculars, so do it manually -# is_deeply( $array, $thawed, 'Circular hooked objects work' ); -is( ref($thawed), 'ARRAY', 'Top level ARRAY' ); -is( scalar(@$thawed), 1, 'ARRAY contains one element' ); -isa_ok( $thawed->[0], 'Foo' ); -is( scalar(keys %{$thawed->[0]}), 1, 'Foo contains one element' ); -isa_ok( $thawed->[0]->{Foo}, 'Bar' ); -is( scalar(keys %{$thawed->[0]->{Foo}}), 1, 'Bar contains one element' ); -isa_ok( $thawed->[0]->{Foo}->{Bar}, 'Foo' ); -is( $thawed->[0], $thawed->[0]->{Foo}->{Bar}, 'Circular is... well... circular' ); - -# Make sure the thawing went the way we expected -is_deeply( \@Foo::order, [ 'Bar', 'Foo' ], 'thaw order is correct (depth first)' ); - - - - - -package Foo; - -@order = (); - -sub STORABLE_freeze { - my ($self, $clone) = @_; - my $class = ref $self; - - # print "# Freezing $class\n"; - - return ($class, $self->{$class}); -} - -sub STORABLE_thaw { - my ($self, $clone, $string, @refs) = @_; - my $class = ref $self; - - # print "# Thawing $class\n"; - - $self->{$class} = shift @refs; - - push @order, $class; - - return; -} - -package Bar; - -BEGIN { -@ISA = 'Foo'; -} - -1; diff --git a/ext/Storable/t/code.t b/ext/Storable/t/code.t deleted file mode 100644 index dd2a96ec1b..0000000000 --- a/ext/Storable/t/code.t +++ /dev/null @@ -1,307 +0,0 @@ -#!./perl -# -# Copyright (c) 2002 Slaven Rezic -# -# You may redistribute only under the same terms as Perl 5, as specified -# in the README file that comes with the distribution. -# - -sub BEGIN { - unshift @INC, 't'; - require Config; import Config; - if ($ENV{PERL_CORE} and $Config{'extensions'} !~ /\bStorable\b/) { - print "1..0 # Skip: Storable was not built\n"; - exit 0; - } -} - -use strict; -BEGIN { - if (!eval q{ - use Test; - use B::Deparse 0.61; - use 5.006; - 1; - }) { - print "1..0 # skip: tests only work with B::Deparse 0.61 and at least perl 5.6.0\n"; - exit; - } - require File::Spec; - if ($File::Spec::VERSION < 0.8) { - print "1..0 # Skip: newer File::Spec needed\n"; - exit 0; - } -} - -BEGIN { plan tests => 59 } - -use Storable qw(retrieve store nstore freeze nfreeze thaw dclone); -use Safe; - -#$Storable::DEBUGME = 1; - -use vars qw($freezed $thawed @obj @res $blessed_code); - -$blessed_code = bless sub { "blessed" }, "Some::Package"; -{ package Another::Package; sub foo { __PACKAGE__ } } - -{ - no strict; # to make the life for Safe->reval easier - sub code { "JAPH" } -} - -local *FOO; - -@obj = - ([\&code, # code reference - sub { 6*7 }, - $blessed_code, # blessed code reference - \&Another::Package::foo, # code in another package - sub ($$;$) { 0 }, # prototypes - sub { print "test\n" }, - \&Test::ok, # large scalar - ], - - {"a" => sub { "srt" }, "b" => \&code}, - - sub { ord("a")-ord("7") }, - - \&code, - - \&dclone, # XS function - - sub { open FOO, "/" }, - ); - -$Storable::Deparse = 1; -$Storable::Eval = 1; - -###################################################################### -# Test freeze & thaw - -$freezed = freeze $obj[0]; -$thawed = thaw $freezed; - -ok($thawed->[0]->(), "JAPH"); -ok($thawed->[1]->(), 42); -ok($thawed->[2]->(), "blessed"); -ok($thawed->[3]->(), "Another::Package"); -ok(prototype($thawed->[4]), prototype($obj[0]->[4])); - -###################################################################### - -$freezed = freeze $obj[1]; -$thawed = thaw $freezed; - -ok($thawed->{"a"}->(), "srt"); -ok($thawed->{"b"}->(), "JAPH"); - -###################################################################### - -$freezed = freeze $obj[2]; -$thawed = thaw $freezed; - -ok($thawed->(), 42); - -###################################################################### - -$freezed = freeze $obj[3]; -$thawed = thaw $freezed; - -ok($thawed->(), "JAPH"); - -###################################################################### - -eval { $freezed = freeze $obj[4] }; -ok($@, qr/The result of B::Deparse::coderef2text was empty/); - -###################################################################### -# Test dclone - -my $new_sub = dclone($obj[2]); -ok($new_sub->(), $obj[2]->()); - -###################################################################### -# Test retrieve & store - -store $obj[0], 'store'; -$thawed = retrieve 'store'; - -ok($thawed->[0]->(), "JAPH"); -ok($thawed->[1]->(), 42); -ok($thawed->[2]->(), "blessed"); -ok($thawed->[3]->(), "Another::Package"); -ok(prototype($thawed->[4]), prototype($obj[0]->[4])); - -###################################################################### - -nstore $obj[0], 'store'; -$thawed = retrieve 'store'; -unlink 'store'; - -ok($thawed->[0]->(), "JAPH"); -ok($thawed->[1]->(), 42); -ok($thawed->[2]->(), "blessed"); -ok($thawed->[3]->(), "Another::Package"); -ok(prototype($thawed->[4]), prototype($obj[0]->[4])); - -###################################################################### -# Security with -# $Storable::Eval -# $Storable::Deparse - -{ - local $Storable::Eval = 0; - - for my $i (0 .. 1) { - $freezed = freeze $obj[$i]; - $@ = ""; - eval { $thawed = thaw $freezed }; - ok($@, qr/Can\'t eval/); - } -} - -{ - - local $Storable::Deparse = 0; - for my $i (0 .. 1) { - $@ = ""; - eval { $freezed = freeze $obj[$i] }; - ok($@, qr/Can\'t store CODE items/); - } -} - -{ - local $Storable::Eval = 0; - local $Storable::forgive_me = 1; - for my $i (0 .. 4) { - $freezed = freeze $obj[0]->[$i]; - $@ = ""; - eval { $thawed = thaw $freezed }; - ok($@, ""); - ok($$thawed, qr/^sub/); - } -} - -{ - local $Storable::Deparse = 0; - local $Storable::forgive_me = 1; - - my $devnull = File::Spec->devnull; - - open(SAVEERR, ">&STDERR"); - open(STDERR, ">$devnull") or - ( print SAVEERR "Unable to redirect STDERR: $!\n" and exit(1) ); - - eval { $freezed = freeze $obj[0]->[0] }; - - open(STDERR, ">&SAVEERR"); - - ok($@, ""); - ok($freezed ne ''); -} - -{ - my $safe = new Safe; - local $Storable::Eval = sub { $safe->reval(shift) }; - - $freezed = freeze $obj[0]->[0]; - $@ = ""; - eval { $thawed = thaw $freezed }; - ok($@, ""); - ok($thawed->(), "JAPH"); - - $freezed = freeze $obj[0]->[6]; - eval { $thawed = thaw $freezed }; - # The "Code sub ..." error message only appears if Log::Agent is installed - ok($@, qr/(trapped|Code sub)/); - - if (0) { - # Disable or fix this test if the internal representation of Storable - # changes. - skip("no malicious storable file check", 1); - } else { - # Construct malicious storable code - $freezed = nfreeze $obj[0]->[0]; - my $bad_code = ';open FOO, "/badfile"'; - # 5th byte is (short) length of scalar - my $len = ord(substr($freezed, 4, 1)); - substr($freezed, 4, 1, chr($len+length($bad_code))); - substr($freezed, -1, 0, $bad_code); - $@ = ""; - eval { $thawed = thaw $freezed }; - ok($@, qr/(trapped|Code sub)/); - } -} - -{ - my $safe = new Safe; - # because of opcodes used in "use strict": - $safe->permit(qw(:default require caller)); - local $Storable::Eval = sub { $safe->reval(shift) }; - - $freezed = freeze $obj[0]->[1]; - $@ = ""; - eval { $thawed = thaw $freezed }; - ok($@, ""); - ok($thawed->(), 42); -} - -{ - { - package MySafe; - sub new { bless {}, shift } - sub reval { - my $source = $_[1]; - # Here you can apply some nifty regexpes to ensure the - # safeness of the source code. - my $coderef = eval $source; - $coderef; - } - } - - my $safe = new MySafe; - local $Storable::Eval = sub { $safe->reval($_[0]) }; - - $freezed = freeze $obj[0]; - eval { $thawed = thaw $freezed }; - ok($@, ""); - - if ($@ ne "") { - ok(0) for (1..5); - } else { - ok($thawed->[0]->(), "JAPH"); - ok($thawed->[1]->(), 42); - ok($thawed->[2]->(), "blessed"); - ok($thawed->[3]->(), "Another::Package"); - ok(prototype($thawed->[4]), prototype($obj[0]->[4])); - } -} - -{ - # Check internal "seen" code - my $short_sub = sub { "short sub" }; # for SX_SCALAR - # for SX_LSCALAR - my $long_sub_code = 'sub { "' . "x"x255 . '" }'; - my $long_sub = eval $long_sub_code; die $@ if $@; - my $sclr = \1; - - local $Storable::Deparse = 1; - local $Storable::Eval = 1; - - for my $sub ($short_sub, $long_sub) { - my $res; - - $res = thaw freeze [$sub, $sub]; - ok(int($res->[0]), int($res->[1])); - - $res = thaw freeze [$sclr, $sub, $sub, $sclr]; - ok(int($res->[0]), int($res->[3])); - ok(int($res->[1]), int($res->[2])); - - $res = thaw freeze [$sub, $sub, $sclr, $sclr]; - ok(int($res->[0]), int($res->[1])); - ok(int($res->[2]), int($res->[3])); - } - -} diff --git a/ext/Storable/t/compat01.t b/ext/Storable/t/compat01.t deleted file mode 100644 index 9b472128ce..0000000000 --- a/ext/Storable/t/compat01.t +++ /dev/null @@ -1,51 +0,0 @@ -#!perl -w - -BEGIN { - unshift @INC, 't'; - require Config; import Config; - if ($ENV{PERL_CORE} and $Config{'extensions'} !~ /\bStorable\b/) { - print "1..0 # Skip: Storable was not built\n"; - exit 0; - } - - use Config; - if ($Config{byteorder} ne "1234") { - print "1..0 # Skip: Test only works for 32 bit little-ending machines\n"; - exit 0; - } -} - -use strict; -use Storable qw(retrieve); - -my $file = "xx-$$.pst"; -my @dumps = ( - # some sample dumps of the hash { one => 1 } - "perl-store\x041234\4\4\4\x94y\22\b\3\1\0\0\0vxz\22\b\1\1\0\0\x001Xk\3\0\0\0oneX", # 0.1 - "perl-store\0\x041234\4\4\4\x94y\22\b\3\1\0\0\0vxz\22\b\b\x81Xk\3\0\0\0oneX", # 0.4@7 -); - -print "1.." . @dumps . "\n"; - -my $testno; -for my $dump (@dumps) { - $testno++; - - open(FH, ">$file") || die "Can't create $file: $!"; - binmode(FH); - print FH $dump; - close(FH) || die "Can't write $file: $!"; - - eval { - my $data = retrieve($file); - if (ref($data) eq "HASH" && $data->{one} eq "1") { - print "ok $testno\n"; - } - else { - print "not ok $testno\n"; - } - }; - warn $@ if $@; - - unlink($file); -} diff --git a/ext/Storable/t/compat06.t b/ext/Storable/t/compat06.t deleted file mode 100644 index 6d8ade3dbf..0000000000 --- a/ext/Storable/t/compat06.t +++ /dev/null @@ -1,146 +0,0 @@ -#!./perl -# -# Copyright (c) 1995-2000, Raphael Manfredi -# -# You may redistribute only under the same terms as Perl 5, as specified -# in the README file that comes with the distribution. -# - -BEGIN { - unshift @INC, 't'; - require Config; import Config; - if ($ENV{PERL_CORE} and $Config{'extensions'} !~ /\bStorable\b/) { - print "1..0 # Skip: Storable was not built\n"; - exit 0; - } - require 'st-dump.pl'; -} - -sub ok; - -print "1..8\n"; - -use Storable qw(freeze nfreeze thaw); - -package TIED_HASH; - -sub TIEHASH { - my $self = bless {}, shift; - return $self; -} - -sub FETCH { - my $self = shift; - my ($key) = @_; - $main::hash_fetch++; - return $self->{$key}; -} - -sub STORE { - my $self = shift; - my ($key, $val) = @_; - $self->{$key} = $val; -} - -package SIMPLE; - -sub make { - my $self = bless [], shift; - my ($x) = @_; - $self->[0] = $x; - return $self; -} - -package ROOT; - -sub make { - my $self = bless {}, shift; - my $h = tie %hash, TIED_HASH; - $self->{h} = $h; - $self->{ref} = \%hash; - my @pool; - for (my $i = 0; $i < 5; $i++) { - push(@pool, SIMPLE->make($i)); - } - $self->{obj} = \@pool; - my @a = ('string', $h, $self); - $self->{a} = \@a; - $self->{num} = [1, 0, -3, -3.14159, 456, 4.5]; - $h->{key1} = 'val1'; - $h->{key2} = 'val2'; - return $self; -}; - -sub num { $_[0]->{num} } -sub h { $_[0]->{h} } -sub ref { $_[0]->{ref} } -sub obj { $_[0]->{obj} } - -package main; - -my $is_EBCDIC = (ord('A') == 193) ? 1 : 0; - -my $r = ROOT->make; - -my $data = ''; -if (!$is_EBCDIC) { # ASCII machine - while (<DATA>) { - next if /^#/; - $data .= unpack("u", $_); - } -} else { - while (<DATA>) { - next if /^#$/; # skip comments - next if /^#\s+/; # skip comments - next if /^[^#]/; # skip uuencoding for ASCII machines - s/^#//; # prepare uuencoded data for EBCDIC machines - $data .= unpack("u", $_); - } -} - -my $expected_length = $is_EBCDIC ? 217 : 278; -ok 1, length $data == $expected_length; - -my $y = thaw($data); -ok 2, 1; -ok 3, ref $y eq 'ROOT'; - -$Storable::canonical = 1; # Prevent "used once" warning -$Storable::canonical = 1; -# Allow for long double string conversions. -$y->{num}->[3] += 0; -$r->{num}->[3] += 0; -ok 4, nfreeze($y) eq nfreeze($r); - -ok 5, $y->ref->{key1} eq 'val1'; -ok 6, $y->ref->{key2} eq 'val2'; -ok 7, $hash_fetch == 2; - -my $num = $r->num; -my $ok = 1; -for (my $i = 0; $i < @$num; $i++) { - do { $ok = 0; last } unless $num->[$i] == $y->num->[$i]; -} -ok 8, $ok; - -__END__ -# -# using Storable-0.6@11, output of: print pack("u", nfreeze(ROOT->make)); -# original size: 278 bytes -# -M`P,````%!`(````&"(%8"(!8"'U8"@@M,RXQ-#$U.5@)```!R%@*`S0N-5A8 -M6`````-N=6T$`P````(*!'9A;#%8````!&ME>3$*!'9A;#)8````!&ME>3)B -M"51)141?2$%32%A8`````6@$`@````,*!G-T<FEN9U@$``````I8!``````` -M6%A8`````6$$`@````4$`@````$(@%AB!E-)35!,15A8!`(````!"(%88@93 -M24U03$586`0"`````0B"6&(&4TE-4$Q%6%@$`@````$(@UAB!E-)35!,15A8 -M!`(````!"(188@9324U03$586%A8`````V]B:@0,!``````*6%A8`````W)E -(9F($4D]/5%@` -# -# using Storable-0.6@11, output of: print '#' . pack("u", nfreeze(ROOT->make)); -# on OS/390 (cp 1047) original size: 217 bytes -# -#M!0,1!-G6UN,#````!00,!!$)X\G%Q&W(P>+(`P````(*!*6!D_$````$DH6H -#M\0H$I8&3\@````22A:CR`````YF%A@0"````!@B!"(`(?0H(8/-+\?3Q]?D) -#M```!R`H#]$OU`````Y6DE`0"````!001!N+)U-?3Q0(````!"(`$$@("```` -#M`0B!!!("`@````$(@@02`@(````!"(,$$@("`````0B$`````Y:"D00````` -#E!`````&(!`(````#"@:BHYF)E8<$``````0$```````````!@0`` diff --git a/ext/Storable/t/croak.t b/ext/Storable/t/croak.t deleted file mode 100644 index ecd2bf831b..0000000000 --- a/ext/Storable/t/croak.t +++ /dev/null @@ -1,38 +0,0 @@ -#!./perl -w - -# Please keep this test this simple. (ie just one test.) -# There's some sort of not-croaking properly problem in Storable when built -# with 5.005_03. This test shows it up, whereas malice.t does not. -# In particular, don't use Test; as this covers up the problem. - -sub BEGIN { - if ($ENV{PERL_CORE}) { - require Config; import Config; - %Config=%Config if 0; # cease -w - if ($Config{'extensions'} !~ /\bStorable\b/) { - print "1..0 # Skip: Storable was not built\n"; - exit 0; - } - } -} - -use strict; - -BEGIN { - die "Oi! No! Don't change this test so that Carp is used before Storable" - if defined &Carp::carp; -} -use Storable qw(freeze thaw); - -print "1..2\n"; - -for my $test (1,2) { - eval {thaw "\xFF\xFF"}; - if ($@ =~ /Storable binary image v127.255 more recent than I am \(v2\.\d+\)/) - { - print "ok $test\n"; - } else { - chomp $@; - print "not ok $test # Expected a meaningful croak. Got '$@'\n"; - } -} diff --git a/ext/Storable/t/dclone.t b/ext/Storable/t/dclone.t deleted file mode 100644 index 078cd81f82..0000000000 --- a/ext/Storable/t/dclone.t +++ /dev/null @@ -1,106 +0,0 @@ -#!./perl -# -# Copyright (c) 1995-2000, Raphael Manfredi -# -# You may redistribute only under the same terms as Perl 5, as specified -# in the README file that comes with the distribution. -# - -sub BEGIN { - unshift @INC, 't'; - require Config; import Config; - if ($ENV{PERL_CORE} and $Config{'extensions'} !~ /\bStorable\b/) { - print "1..0 # Skip: Storable was not built\n"; - exit 0; - } - require 'st-dump.pl'; -} - - -use Storable qw(dclone); - -print "1..12\n"; - -$a = 'toto'; -$b = \$a; -$c = bless {}, CLASS; -$c->{attribute} = 'attrval'; -%a = ('key', 'value', 1, 0, $a, $b, 'cvar', \$c); -@a = ('first', undef, 3, -4, -3.14159, 456, 4.5, - $b, \$a, $a, $c, \$c, \%a); - -print "not " unless defined ($aref = dclone(\@a)); -print "ok 1\n"; - -$dumped = &dump(\@a); -print "ok 2\n"; - -$got = &dump($aref); -print "ok 3\n"; - -print "not " unless $got eq $dumped; -print "ok 4\n"; - -package FOO; @ISA = qw(Storable); - -sub make { - my $self = bless {}; - $self->{key} = \%main::a; - return $self; -}; - -package main; - -$foo = FOO->make; -print "not " unless defined($r = $foo->dclone); -print "ok 5\n"; - -print "not " unless &dump($foo) eq &dump($r); -print "ok 6\n"; - -# Ensure refs to "undef" values are properly shared during cloning -my $hash; -push @{$$hash{''}}, \$$hash{a}; -print "not " unless $$hash{''}[0] == \$$hash{a}; -print "ok 7\n"; - -my $cloned = dclone(dclone($hash)); -print "not " unless $$cloned{''}[0] == \$$cloned{a}; -print "ok 8\n"; - -$$cloned{a} = "blah"; -print "not " unless $$cloned{''}[0] == \$$cloned{a}; -print "ok 9\n"; - -# [ID 20020221.007] SEGV in Storable with empty string scalar object -package TestString; -sub new { - my ($type, $string) = @_; - return bless(\$string, $type); -} -package main; -my $empty_string_obj = TestString->new(''); -my $clone = dclone($empty_string_obj); -# If still here after the dclone the fix (#17543) worked. -print ref $clone eq ref $empty_string_obj && - $$clone eq $$empty_string_obj && - $$clone eq '' ? "ok 10\n" : "not ok 10\n"; - - -# Do not fail if Tie::Hash and/or Tie::StdHash is not available -if (eval { require Tie::Hash; scalar keys %Tie::StdHash:: }) { - tie my %tie, "Tie::StdHash" or die $!; - $tie{array} = [1,2,3,4]; - $tie{hash} = {1,2,3,4}; - my $clone_array = dclone $tie{array}; - print "not " unless "@$clone_array" eq "@{$tie{array}}"; - print "ok 11\n"; - my $clone_hash = dclone $tie{hash}; - print "not " unless $clone_hash->{1} eq $tie{hash}{1}; - print "ok 12\n"; -} else { - print <<EOF; -ok 11 # skip No Tie::StdHash available -ok 12 # skip No Tie::StdHash available -EOF -} diff --git a/ext/Storable/t/downgrade.t b/ext/Storable/t/downgrade.t deleted file mode 100644 index 76bd05ae2a..0000000000 --- a/ext/Storable/t/downgrade.t +++ /dev/null @@ -1,506 +0,0 @@ -#!./perl -w -# -# Copyright 2002, Larry Wall. -# -# You may redistribute only under the same terms as Perl 5, as specified -# in the README file that comes with the distribution. -# - -# I ought to keep this test easily backwards compatible to 5.004, so no -# qr//; - -# This test checks downgrade behaviour on pre-5.8 perls when new 5.8 features -# are encountered. - -sub BEGIN { - unshift @INC, 't'; - require Config; import Config; - if ($ENV{PERL_CORE} and $Config{'extensions'} !~ /\bStorable\b/) { - print "1..0 # Skip: Storable was not built\n"; - exit 0; - } -} - -use Test::More; -use Storable 'thaw'; - -use strict; -use vars qw(@RESTRICT_TESTS %R_HASH %U_HASH $UTF8_CROAK $RESTRICTED_CROAK); - -@RESTRICT_TESTS = ('Locked hash', 'Locked hash placeholder', - 'Locked keys', 'Locked keys placeholder', - ); -%R_HASH = (perl => 'rules'); - -if ($] > 5.007002) { - # This is cheating. "\xdf" in Latin 1 is beta S, so will match \w if it - # is stored in utf8, not bytes. - # "\xdf" is y diaresis in EBCDIC (except for cp875, but so far no-one seems - # to use that) which has exactly the same properties for \w - # So the tests happen to pass. - my $utf8 = "Schlo\xdf" . chr 256; - chop $utf8; - - # \xe5 is V in EBCDIC. That doesn't have the same properties w.r.t. \w as - # an a circumflex, so we need to be explicit. - - # and its these very properties we're trying to test - an edge case - # involving whether scalars are being stored in bytes or in utf8. - my $a_circumflex = (ord ('A') == 193 ? "\x47" : "\xe5"); - %U_HASH = (map {$_, $_} 'castle', "ch${a_circumflex}teau", $utf8, chr 0x57CE); - plan tests => 169; -} elsif ($] >= 5.006) { - plan tests => 59; -} else { - plan tests => 67; -} - -$UTF8_CROAK = "/^Cannot retrieve UTF8 data in non-UTF8 perl/"; -$RESTRICTED_CROAK = "/^Cannot retrieve restricted hash/"; - -my %tests; -{ - local $/ = "\n\nend\n"; - while (<DATA>) { - next unless /\S/s; - unless (/begin ([0-7]{3}) ([^\n]*)\n(.*)$/s) { - s/\n.*//s; - warn "Dodgy data in section starting '$_'"; - next; - } - next unless oct $1 == ord 'A'; # Skip ASCII on EBCDIC, and vice versa - my $data = unpack 'u', $3; - $tests{$2} = $data; - } -} - -# use Data::Dumper; $Data::Dumper::Useqq = 1; print Dumper \%tests; -sub thaw_hash { - my ($name, $expected) = @_; - my $hash = eval {thaw $tests{$name}}; - is ($@, '', "Thawed $name without error?"); - isa_ok ($hash, 'HASH'); - ok (defined $hash && eq_hash($hash, $expected), - "And it is the hash we expected?"); - $hash; -} - -sub thaw_scalar { - my ($name, $expected, $bug) = @_; - my $scalar = eval {thaw $tests{$name}}; - is ($@, '', "Thawed $name without error?"); - isa_ok ($scalar, 'SCALAR', "Thawed $name?"); - if ($bug and $] == 5.006) { - # Aargh. <expletive> <expletive> 5.6.0's harness doesn't even honour - # TODO tests. - warn "# Test skipped because eq is buggy for certain Unicode cases in 5.6.0"; - warn "# Please upgrade to 5.6.1\n"; - ok ("I'd really like to fail this test on 5.6.0 but I'm told that CPAN auto-dependancies mess up, and certain vendors only ship 5.6.0. Get your vendor to ugrade. Else upgrade your vendor."); - # One such vendor being the folks who brought you LONG_MIN as a positive - # integer. - } else { - is ($$scalar, $expected, "And it is the data we expected?"); - } - $scalar; -} - -sub thaw_fail { - my ($name, $expected) = @_; - my $thing = eval {thaw $tests{$name}}; - is ($thing, undef, "Thawed $name failed as expected?"); - like ($@, $expected, "Error as predicted?"); -} - -sub test_locked_hash { - my $hash = shift; - my @keys = keys %$hash; - my ($key, $value) = each %$hash; - eval {$hash->{$key} = reverse $value}; - like( $@, "/^Modification of a read-only value attempted/", - 'trying to change a locked key' ); - is ($hash->{$key}, $value, "hash should not change?"); - eval {$hash->{use} = 'perl'}; - like( $@, "/^Attempt to access disallowed key 'use' in a restricted hash/", - 'trying to add another key' ); - ok (eq_array([keys %$hash], \@keys), "Still the same keys?"); -} - -sub test_restricted_hash { - my $hash = shift; - my @keys = keys %$hash; - my ($key, $value) = each %$hash; - eval {$hash->{$key} = reverse $value}; - is( $@, '', - 'trying to change a restricted key' ); - is ($hash->{$key}, reverse ($value), "hash should change"); - eval {$hash->{use} = 'perl'}; - like( $@, "/^Attempt to access disallowed key 'use' in a restricted hash/", - 'trying to add another key' ); - ok (eq_array([keys %$hash], \@keys), "Still the same keys?"); -} - -sub test_placeholder { - my $hash = shift; - eval {$hash->{rules} = 42}; - is ($@, '', 'No errors'); - is ($hash->{rules}, 42, "New value added"); -} - -sub test_newkey { - my $hash = shift; - eval {$hash->{nms} = "http://nms-cgi.sourceforge.net/"}; - is ($@, '', 'No errors'); - is ($hash->{nms}, "http://nms-cgi.sourceforge.net/", "New value added"); -} - -# $Storable::DEBUGME = 1; -thaw_hash ('Hash with utf8 flag but no utf8 keys', \%R_HASH); - -if (eval "use Hash::Util; 1") { - print "# We have Hash::Util, so test that the restricted hashes in <DATA> are valid\n"; - for $Storable::downgrade_restricted (0, 1, undef, "cheese") { - my $hash = thaw_hash ('Locked hash', \%R_HASH); - test_locked_hash ($hash); - $hash = thaw_hash ('Locked hash placeholder', \%R_HASH); - test_locked_hash ($hash); - test_placeholder ($hash); - - $hash = thaw_hash ('Locked keys', \%R_HASH); - test_restricted_hash ($hash); - $hash = thaw_hash ('Locked keys placeholder', \%R_HASH); - test_restricted_hash ($hash); - test_placeholder ($hash); - } -} else { - print "# We don't have Hash::Util, so test that the restricted hashes downgrade\n"; - my $hash = thaw_hash ('Locked hash', \%R_HASH); - test_newkey ($hash); - $hash = thaw_hash ('Locked hash placeholder', \%R_HASH); - test_newkey ($hash); - $hash = thaw_hash ('Locked keys', \%R_HASH); - test_newkey ($hash); - $hash = thaw_hash ('Locked keys placeholder', \%R_HASH); - test_newkey ($hash); - local $Storable::downgrade_restricted = 0; - thaw_fail ('Locked hash', $RESTRICTED_CROAK); - thaw_fail ('Locked hash placeholder', $RESTRICTED_CROAK); - thaw_fail ('Locked keys', $RESTRICTED_CROAK); - thaw_fail ('Locked keys placeholder', $RESTRICTED_CROAK); -} - -if ($] >= 5.006) { - print "# We have utf8 scalars, so test that the utf8 scalars in <DATA> are valid\n"; - thaw_scalar ('Short 8 bit utf8 data', "\xDF", 1); - thaw_scalar ('Long 8 bit utf8 data', "\xDF" x 256, 1); - thaw_scalar ('Short 24 bit utf8 data', chr 0xC0FFEE); - thaw_scalar ('Long 24 bit utf8 data', chr (0xC0FFEE) x 256); -} else { - print "# We don't have utf8 scalars, so test that the utf8 scalars downgrade\n"; - thaw_fail ('Short 8 bit utf8 data', $UTF8_CROAK); - thaw_fail ('Long 8 bit utf8 data', $UTF8_CROAK); - thaw_fail ('Short 24 bit utf8 data', $UTF8_CROAK); - thaw_fail ('Long 24 bit utf8 data', $UTF8_CROAK); - local $Storable::drop_utf8 = 1; - my $bytes = thaw $tests{'Short 8 bit utf8 data as bytes'}; - thaw_scalar ('Short 8 bit utf8 data', $$bytes); - thaw_scalar ('Long 8 bit utf8 data', $$bytes x 256); - $bytes = thaw $tests{'Short 24 bit utf8 data as bytes'}; - thaw_scalar ('Short 24 bit utf8 data', $$bytes); - thaw_scalar ('Long 24 bit utf8 data', $$bytes x 256); -} - -if ($] > 5.007002) { - print "# We have utf8 hashes, so test that the utf8 hashes in <DATA> are valid\n"; - my $hash = thaw_hash ('Hash with utf8 keys', \%U_HASH); - my $a_circumflex = (ord ('A') == 193 ? "\x47" : "\xe5"); - for (keys %$hash) { - my $l = 0 + /^\w+$/; - my $r = 0 + $hash->{$_} =~ /^\w+$/; - cmp_ok ($l, '==', $r, sprintf "key length %d", length $_); - cmp_ok ($l, '==', $_ eq "ch${a_circumflex}teau" ? 0 : 1); - } - if (eval "use Hash::Util; 1") { - print "# We have Hash::Util, so test that the restricted utf8 hash is valid\n"; - my $hash = thaw_hash ('Locked hash with utf8 keys', \%U_HASH); - for (keys %$hash) { - my $l = 0 + /^\w+$/; - my $r = 0 + $hash->{$_} =~ /^\w+$/; - cmp_ok ($l, '==', $r, sprintf "key length %d", length $_); - cmp_ok ($l, '==', $_ eq "ch${a_circumflex}teau" ? 0 : 1); - } - test_locked_hash ($hash); - } else { - print "# We don't have Hash::Util, so test that the utf8 hash downgrades\n"; - fail ("You can't get here [perl version $]]. This is a bug in the test. -# Please send the output of perl -V to perlbug\@perl.org"); - } -} else { - print "# We don't have utf8 hashes, so test that the utf8 hashes downgrade\n"; - thaw_fail ('Hash with utf8 keys', $UTF8_CROAK); - thaw_fail ('Locked hash with utf8 keys', $UTF8_CROAK); - local $Storable::drop_utf8 = 1; - my $what = $] < 5.006 ? 'pre 5.6' : '5.6'; - my $expect = thaw $tests{"Hash with utf8 keys for $what"}; - thaw_hash ('Hash with utf8 keys', $expect); - #foreach (keys %$expect) { print "'$_':\t'$expect->{$_}'\n"; } - #foreach (keys %$got) { print "'$_':\t'$got->{$_}'\n"; } - if (eval "use Hash::Util; 1") { - print "# We have Hash::Util, so test that the restricted hashes in <DATA> are valid\n"; - fail ("You can't get here [perl version $]]. This is a bug in the test. -# Please send the output of perl -V to perlbug\@perl.org"); - } else { - print "# We don't have Hash::Util, so test that the restricted hashes downgrade\n"; - my $hash = thaw_hash ('Locked hash with utf8 keys', $expect); - test_newkey ($hash); - local $Storable::downgrade_restricted = 0; - thaw_fail ('Locked hash with utf8 keys', $RESTRICTED_CROAK); - # Which croak comes first is a bit of an implementation issue :-) - local $Storable::drop_utf8 = 0; - thaw_fail ('Locked hash with utf8 keys', $RESTRICTED_CROAK); - } -} -__END__ -# A whole run of 2.x nfreeze data, uuencoded. The "mode bits" are the octal -# value of 'A', the "file name" is the test name. Use make_downgrade.pl to -# generate these. -begin 101 Locked hash -8!049`0````$*!7)U;&5S!`````1P97)L - -end - -begin 101 Locked hash placeholder -C!049`0````(*!7)U;&5S!`````1P97)L#A0````%<G5L97,` - -end - -begin 101 Locked keys -8!049`0````$*!7)U;&5S``````1P97)L - -end - -begin 101 Locked keys placeholder -C!049`0````(*!7)U;&5S``````1P97)L#A0````%<G5L97,` - -end - -begin 101 Short 8 bit utf8 data -&!047`L.? - -end - -begin 101 Short 8 bit utf8 data as bytes -&!04*`L.? - -end - -begin 101 Long 8 bit utf8 data -M!048```"`,.?PY_#G\.?PY_#G\.?PY_#G\.?PY_#G\.?PY_#G\.?PY_#G\.? -MPY_#G\.?PY_#G\.?PY_#G\.?PY_#G\.?PY_#G\.?PY_#G\.?PY_#G\.?PY_# -MG\.?PY_#G\.?PY_#G\.?PY_#G\.?PY_#G\.?PY_#G\.?PY_#G\.?PY_#G\.? -MPY_#G\.?PY_#G\.?PY_#G\.?PY_#G\.?PY_#G\.?PY_#G\.?PY_#G\.?PY_# -MG\.?PY_#G\.?PY_#G\.?PY_#G\.?PY_#G\.?PY_#G\.?PY_#G\.?PY_#G\.? -MPY_#G\.?PY_#G\.?PY_#G\.?PY_#G\.?PY_#G\.?PY_#G\.?PY_#G\.?PY_# -MG\.?PY_#G\.?PY_#G\.?PY_#G\.?PY_#G\.?PY_#G\.?PY_#G\.?PY_#G\.? -MPY_#G\.?PY_#G\.?PY_#G\.?PY_#G\.?PY_#G\.?PY_#G\.?PY_#G\.?PY_# -MG\.?PY_#G\.?PY_#G\.?PY_#G\.?PY_#G\.?PY_#G\.?PY_#G\.?PY_#G\.? -MPY_#G\.?PY_#G\.?PY_#G\.?PY_#G\.?PY_#G\.?PY_#G\.?PY_#G\.?PY_# -MG\.?PY_#G\.?PY_#G\.?PY_#G\.?PY_#G\.?PY_#G\.?PY_#G\.?PY_#G\.? -8PY_#G\.?PY_#G\.?PY_#G\.?PY_#G\.? - -end - -begin 101 Short 24 bit utf8 data -)!047!?BPC[^N - -end - -begin 101 Short 24 bit utf8 data as bytes -)!04*!?BPC[^N - -end - -begin 101 Long 24 bit utf8 data -M!048```%`/BPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/ -MOZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/ -MOZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/ -MOZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/ -MOZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/ -MOZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/ -MOZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/ -MOZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/ -MOZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/ -MOZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/ -MOZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/ -MOZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/ -MOZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/ -MOZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/ -MOZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/ -MOZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/ -MOZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/ -MOZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/ -MOZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/ -MOZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/ -MOZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/ -MOZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/ -MOZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/ -MOZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/ -MOZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/ -MOZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/ -MOZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/ -MOZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N^+"/ -;OZ[XL(^_KOBPC[^N^+"/OZ[XL(^_KOBPC[^N - -end - -begin 101 Hash with utf8 flag but no utf8 keys -8!049``````$*!7)U;&5S``````1P97)L - -end - -begin 101 Hash with utf8 keys -M!049``````0*!F-A<W1L90`````&8V%S=&QE"@=C:.5T96%U``````=C:.5T -D96%U%P/EGXX!`````^6?CA<'4V-H;&_#GP(````&4V-H;&_? - -end - -begin 101 Locked hash with utf8 keys -M!049`0````0*!F-A<W1L900````&8V%S=&QE"@=C:.5T96%U!`````=C:.5T -D96%U%P/EGXX%`````^6?CA<'4V-H;&_#GP8````&4V-H;&_? - -end - -begin 101 Hash with utf8 keys for pre 5.6 -M!049``````0*!F-A<W1L90`````&8V%S=&QE"@=C:.5T96%U``````=C:.5T -D96%U"@/EGXX``````^6?C@H'4V-H;&_#GP(````&4V-H;&_? - -end - -begin 101 Hash with utf8 keys for 5.6 -M!049``````0*!F-A<W1L90`````&8V%S=&QE"@=C:.5T96%U``````=C:.5T -D96%U%P/EGXX``````^6?CA<'4V-H;&_#GP(````&4V-H;&_? - -end - -begin 301 Locked hash -8!049`0````$*!9FDDX6B!`````27A9F3 - -end - -begin 301 Locked hash placeholder -C!049`0````(.%`````69I).%H@H%F:23A:($````!)>%F9,` - -end - -begin 301 Locked keys -8!049`0````$*!9FDDX6B``````27A9F3 - -end - -begin 301 Locked keys placeholder -C!049`0````(.%`````69I).%H@H%F:23A:(`````!)>%F9,` - -end - -begin 301 Short 8 bit utf8 data -&!047`HMS - -end - -begin 301 Short 8 bit utf8 data as bytes -&!04*`HMS - -end - -begin 301 Long 8 bit utf8 data -M!048```"`(MSBW.+<XMSBW.+<XMSBW.+<XMSBW.+<XMSBW.+<XMSBW.+<XMS -MBW.+<XMSBW.+<XMSBW.+<XMSBW.+<XMSBW.+<XMSBW.+<XMSBW.+<XMSBW.+ -M<XMSBW.+<XMSBW.+<XMSBW.+<XMSBW.+<XMSBW.+<XMSBW.+<XMSBW.+<XMS -MBW.+<XMSBW.+<XMSBW.+<XMSBW.+<XMSBW.+<XMSBW.+<XMSBW.+<XMSBW.+ -M<XMSBW.+<XMSBW.+<XMSBW.+<XMSBW.+<XMSBW.+<XMSBW.+<XMSBW.+<XMS -MBW.+<XMSBW.+<XMSBW.+<XMSBW.+<XMSBW.+<XMSBW.+<XMSBW.+<XMSBW.+ -M<XMSBW.+<XMSBW.+<XMSBW.+<XMSBW.+<XMSBW.+<XMSBW.+<XMSBW.+<XMS -MBW.+<XMSBW.+<XMSBW.+<XMSBW.+<XMSBW.+<XMSBW.+<XMSBW.+<XMSBW.+ -M<XMSBW.+<XMSBW.+<XMSBW.+<XMSBW.+<XMSBW.+<XMSBW.+<XMSBW.+<XMS -MBW.+<XMSBW.+<XMSBW.+<XMSBW.+<XMSBW.+<XMSBW.+<XMSBW.+<XMSBW.+ -M<XMSBW.+<XMSBW.+<XMSBW.+<XMSBW.+<XMSBW.+<XMSBW.+<XMSBW.+<XMS -8BW.+<XMSBW.+<XMSBW.+<XMSBW.+<XMS - -end - -begin 301 Short 24 bit utf8 data -*!047!OM30G-S50`` - -end - -begin 301 Short 24 bit utf8 data as bytes -*!04*!OM30G-S50`` - -end - -begin 301 Long 24 bit utf8 data -M!048```&`/M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M3 -M0G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S -M5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M3 -M0G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S -M5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M3 -M0G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S -M5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M3 -M0G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S -M5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M3 -M0G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S -M5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M3 -M0G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S -M5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M3 -M0G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S -M5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M3 -M0G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S -M5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M3 -M0G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S -M5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M3 -M0G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S -M5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M3 -M0G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S -M5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M3 -M0G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S -M5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M3 -M0G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S -M5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M3 -M0G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S -M5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M3 -M0G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S -M5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M3 -M0G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S -M5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M3 -M0G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S5?M30G-S --5?M30G-S5?M30G-S50`` - -end - -begin 301 Hash with utf8 flag but no utf8 keys -8!049``````$*!9FDDX6B``````27A9F3 - -end - -begin 301 Hash with utf8 keys -M!049``````0*!X.(1Z.%@:0`````!X.(1Z.%@:0*!H.!HJ.3A0`````&@X&B -FHY.%%P3<9')5`0````3<9')5%P?B@XB3EHMS`@````;B@XB3EM\` - -end - -begin 301 Locked hash with utf8 keys -M!049`0````0*!X.(1Z.%@:0$````!X.(1Z.%@:0*!H.!HJ.3A00````&@X&B -FHY.%%P3<9')5!0````3<9')5%P?B@XB3EHMS!@````;B@XB3EM\` - -end - -begin 301 Hash with utf8 keys for pre 5.6 -M!049``````0*!H.!HJ.3A0`````&@X&BHY.%"@B#B(M&HX6!I``````'@XA' -GHX6!I`H'XH.(DY:+<P(````&XH.(DY;?"@3<9')5``````3<9')5 - -end - -begin 301 Hash with utf8 keys for 5.6 -M!049``````0*!H.!HJ.3A0`````&@X&BHY.%"@>#B$>CA8&D``````>#B$>C -FA8&D%P?B@XB3EHMS`@````;B@XB3EM\7!-QD<E4`````!-QD<E4` - -end diff --git a/ext/Storable/t/file_magic.t b/ext/Storable/t/file_magic.t deleted file mode 100644 index f834510287..0000000000 --- a/ext/Storable/t/file_magic.t +++ /dev/null @@ -1,455 +0,0 @@ -#!perl -w - -use strict; -use Test::More; -use Storable qw(store nstore); -use Config qw(%Config); - -# The @tests array below was create by the following program -my $dummy = <<'EOT'; -use Storable; -use Data::Dump qw(dump); - -print "my \@tests = (\n"; -for my $f (<data_*>) { - print " [\n"; - print " " . dump(substr(`cat $f`, 0, 32) . "...") , ",\n"; - - my $x = dump(Storable::file_magic($f)); - $x =~ s/^/ /gm; - print "$x,\n"; - - print " ],\n"; -} -print ");\n"; -EOT - -my @tests = ( - [ - "perl-store\x041234\4\4\4\xD4\xC2\32\b\3\13\0\0\0v\b\xC5\32\b...", - { - byteorder => 1234, - file => "data_perl-5.006001_i686-linux-thread-multi_Storable-0.1.le32", - hdrsize => 18, - intsize => 4, - longsize => 4, - netorder => 0, - ptrsize => 4, - version => -1, - version_nv => -1, - }, - ], - [ - "perl-store\0\x041234\4\4\4\x8Co\34\b\3\13\0\0\0v\x94v\34...", - { - byteorder => 1234, - file => "data_perl-5.006001_i686-linux-thread-multi_Storable-0.4_07.le32", - hdrsize => 19, - intsize => 4, - longsize => 4, - major => 0, - netorder => 0, - ptrsize => 4, - version => 0, - version_nv => 0, - }, - ], - [ - "perl-store\1\x8Co\34\b\3\0\0\0\13v\x94v\34\b\1\0\0\4\0\0\0...", - { - file => "data_perl-5.006001_i686-linux-thread-multi_Storable-0.4_07.neutral", - hdrsize => 11, - major => 0, - netorder => 1, - version => 0, - version_nv => 0, - }, - ], - [ - "pst0\2\x041234\4\4\4\3\13\0\0\0\1\0\4\0\0\0\0\0\0\0\0\0\0\0...", - { - byteorder => 1234, - file => "data_perl-5.006001_i686-linux-thread-multi_Storable-0.604.le32", - hdrsize => 13, - intsize => 4, - longsize => 4, - major => 1, - netorder => 0, - ptrsize => 4, - version => 1, - version_nv => 1, - }, - ], - [ - "pst0\3\3\0\0\0\13\1\0\0\4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0...", - { - file => "data_perl-5.006001_i686-linux-thread-multi_Storable-0.604.neutral", - hdrsize => 5, - major => 1, - netorder => 1, - version => 1, - version_nv => 1, - }, - ], - [ - "pst0\4\0\x041234\4\4\4\3\13\0\0\0\1\0\4\0\0\0\0\0\0\0\0\0\0...", - { - byteorder => 1234, - file => "data_perl-5.006001_i686-linux-thread-multi_Storable-0.700.le32", - hdrsize => 14, - intsize => 4, - longsize => 4, - major => 2, - minor => 0, - netorder => 0, - ptrsize => 4, - version => "2.0", - version_nv => "2.000", - }, - ], - [ - "pst0\5\0\3\0\0\0\13\1\0\0\4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0...", - { - file => "data_perl-5.006001_i686-linux-thread-multi_Storable-0.700.neutral", - hdrsize => 6, - major => 2, - minor => 0, - netorder => 1, - version => "2.0", - version_nv => "2.000", - }, - ], - [ - "pst0\4\4\x041234\4\4\4\x08\3\13\0\0\0\1\0\4\0\0\0\0\0\0\0\0\0...", - { - byteorder => 1234, - file => "data_perl-5.006001_i686-linux-thread-multi_Storable-1.012.le32", - hdrsize => 15, - intsize => 4, - longsize => 4, - major => 2, - minor => 4, - netorder => 0, - nvsize => 8, - ptrsize => 4, - version => "2.4", - version_nv => "2.004", - }, - ], - [ - "pst0\4\3\x044321\4\4\4\x08\3\0\0\0\13\1\0\0\4\0\0\0\0\0\0\0\0...", - { - byteorder => 4321, - file => "data_perl-5.006001_IA64.ARCHREV_0-thread-multi_Storable-1.006.be32", - hdrsize => 15, - intsize => 4, - longsize => 4, - major => 2, - minor => 3, - netorder => 0, - nvsize => 8, - ptrsize => 4, - version => "2.3", - version_nv => "2.003", - }, - ], - [ - "pst0\5\3\3\0\0\0\13\1\0\0\4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0...", - { - file => "data_perl-5.006001_IA64.ARCHREV_0-thread-multi_Storable-1.006.neutral", - hdrsize => 6, - major => 2, - minor => 3, - netorder => 1, - version => "2.3", - version_nv => "2.003", - }, - ], - [ - "pst0\4\4\x044321\4\4\4\x08\3\0\0\0\13\1\0\0\4\0\0\0\0\0\0\0\0...", - { - byteorder => 4321, - file => "data_perl-5.006001_IA64.ARCHREV_0-thread-multi_Storable-1.012.be32", - hdrsize => 15, - intsize => 4, - longsize => 4, - major => 2, - minor => 4, - netorder => 0, - nvsize => 8, - ptrsize => 4, - version => "2.4", - version_nv => "2.004", - }, - ], - [ - "pst0\5\4\3\0\0\0\13\1\0\0\4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0...", - { - file => "data_perl-5.006001_IA64.ARCHREV_0-thread-multi_Storable-1.012.neutral", - hdrsize => 6, - major => 2, - minor => 4, - netorder => 1, - version => "2.4", - version_nv => "2.004", - }, - ], - [ - "pst0\4\6\x044321\4\4\4\x08\3\0\0\0\13\n\n4294967296...", - { - byteorder => 4321, - file => "data_perl-5.008001_darwin-thread-multi-2level_Storable-2.08.be32", - hdrsize => 15, - intsize => 4, - longsize => 4, - major => 2, - minor => 6, - netorder => 0, - nvsize => 8, - ptrsize => 4, - version => "2.6", - version_nv => "2.006", - }, - ], - [ - "pst0\5\6\3\0\0\0\13\n\n4294967296\0\0\0\bfour_...", - { - file => "data_perl-5.008001_darwin-thread-multi-2level_Storable-2.08.neutral", - hdrsize => 6, - major => 2, - minor => 6, - netorder => 1, - version => "2.6", - version_nv => "2.006", - }, - ], - [ - "pst0\4\6\x044321\4\4\4\x08\3\0\0\0\13\4\3\0\0\0\0\0\0\0\nem...", - { - byteorder => 4321, - file => "data_perl-5.008003_PA-RISC1.1-thread-multi_Storable-2.09.be32", - hdrsize => 15, - intsize => 4, - longsize => 4, - major => 2, - minor => 6, - netorder => 0, - nvsize => 8, - ptrsize => 4, - version => "2.6", - version_nv => "2.006", - }, - ], - [ - "pst0\5\6\3\0\0\0\13\4\3\0\0\0\0\0\0\0\nempty_hash\n...", - { - file => "data_perl-5.008003_PA-RISC1.1-thread-multi_Storable-2.09.neutral", - hdrsize => 6, - major => 2, - minor => 6, - netorder => 1, - version => "2.6", - version_nv => "2.006", - }, - ], - [ - "pst0\4\6\x0812345678\4\4\4\x08\3\13\0\0\0\4\3\0\0\0\0\n\0...", - { - byteorder => 12_345_678, - file => "data_perl-5.008004_i86pc-solaris-64int_Storable-2.12.le64", - hdrsize => 19, - intsize => 4, - longsize => 4, - major => 2, - minor => 6, - netorder => 0, - nvsize => 8, - ptrsize => 4, - version => "2.6", - version_nv => "2.006", - }, - ], - [ - "pst0\4\6\x041234\4\4\4\x08\3\13\0\0\0\4\3\0\0\0\0\n\0\0\0em...", - { - byteorder => 1234, - file => "data_perl-5.008006_i686-linux-thread-multi_Storable-2.13.le32", - hdrsize => 15, - intsize => 4, - longsize => 4, - major => 2, - minor => 6, - netorder => 0, - nvsize => 8, - ptrsize => 4, - version => "2.6", - version_nv => "2.006", - }, - ], - [ - "pst0\4\6\x0887654321\4\x08\x08\x08\3\0\0\0\13\4\3\0\0\0\0\0\0...", - { - byteorder => 87_654_321, - file => "data_perl-5.008007_IA64.ARCHREV_0-thread-multi-LP64_Storable-2.13.be64", - hdrsize => 19, - intsize => 4, - longsize => 8, - major => 2, - minor => 6, - netorder => 0, - nvsize => 8, - ptrsize => 8, - version => "2.6", - version_nv => "2.006", - }, - ], - [ - "pst0\4\x07\x0812345678\4\x08\x08\x08\3\13\0\0\0\4\3\0\0\0\0\n\0...", - { - byteorder => 12_345_678, - file => "data_perl-5.008007_x86-solaris-thread-multi-64_Storable-2.15.le64", - hdrsize => 19, - intsize => 4, - longsize => 8, - major => 2, - minor => 7, - netorder => 0, - nvsize => 8, - ptrsize => 8, - version => "2.7", - version_nv => "2.007", - }, - ], - [ - "pst0\5\x07\3\0\0\0\13\4\3\0\0\0\0\0\0\0\nempty_hash\n...", - { - file => "data_perl-5.008007_x86-solaris-thread-multi-64_Storable-2.15.neutral", - hdrsize => 6, - major => 2, - minor => 7, - netorder => 1, - version => "2.7", - version_nv => "2.007", - }, - ], - [ - "pst0\4\5\x041234\4\4\4\x08\3\13\0\0\0\4\3\0\0\0\0\n\0\0\0em...", - { - byteorder => 1234, - file => "data_perl-5.008_i686-linux-thread-multi_Storable-2.04.le32", - hdrsize => 15, - intsize => 4, - longsize => 4, - major => 2, - minor => 5, - netorder => 0, - nvsize => 8, - ptrsize => 4, - version => "2.5", - version_nv => "2.005", - }, - ], - [ - "pst0\5\5\3\0\0\0\13\4\3\0\0\0\0\0\0\0\nempty_hash\n...", - { - file => "data_perl-5.008_i686-linux-thread-multi_Storable-2.04.neutral", - hdrsize => 6, - major => 2, - minor => 5, - netorder => 1, - version => "2.5", - version_nv => "2.005", - }, - ], - [ - "pst0\4\x07\x041234\4\4\4\x08\3\13\0\0\0\4\3\0\0\0\0\n\0\0\0em...", - { - byteorder => 1234, - file => "data_perl-5.009003_i686-linux_Storable-2.15.le32", - hdrsize => 15, - intsize => 4, - longsize => 4, - major => 2, - minor => 7, - netorder => 0, - nvsize => 8, - ptrsize => 4, - version => "2.7", - version_nv => "2.007", - }, - ], -); - -plan tests => 31 + 2 * @tests; - -my $file = "xx-$$.pst"; - -is(eval { Storable::file_magic($file) }, undef, "empty file give undef"); -like($@, qq{/^Can't open '\Q$file\E':/}, "...and croaks"); -is(Storable::file_magic(__FILE__), undef, "not an image"); - -store({}, $file); -{ - my $info = Storable::file_magic($file); - unlink($file); - ok($info, "got info"); - is($info->{file}, $file, "file set"); - is($info->{hdrsize}, 11 + length($Config{byteorder}), "hdrsize"); - like($info->{version}, q{/^2\.\d+$/}, "sane version"); - is($info->{version_nv}, Storable::BIN_WRITE_VERSION_NV, "version_nv match"); - is($info->{major}, 2, "sane major"); - ok($info->{minor}, "have minor"); - ok($info->{minor} >= Storable::BIN_WRITE_MINOR, "large enough minor"); - - ok(!$info->{netorder}, "no netorder"); - - my %attrs = ( - nvsize => 5.006, - ptrsize => 5.005, - map {$_ => 5.004} qw(byteorder intsize longsize) - ); - for my $attr (keys %attrs) { - SKIP: { - skip "attribute $attr not available on this version of Perl", 1 if $attrs{$attr} > $]; - is($info->{$attr}, $Config{$attr}, "$attr match Config"); - } - } -} - -nstore({}, $file); -{ - my $info = Storable::file_magic($file); - unlink($file); - ok($info, "got info"); - is($info->{file}, $file, "file set"); - is($info->{hdrsize}, 6, "hdrsize"); - like($info->{version}, q{/^2\.\d+$/}, "sane version"); - is($info->{version_nv}, Storable::BIN_WRITE_VERSION_NV, "version_nv match"); - is($info->{major}, 2, "sane major"); - ok($info->{minor}, "have minor"); - ok($info->{minor} >= Storable::BIN_WRITE_MINOR, "large enough minor"); - - ok($info->{netorder}, "no netorder"); - for (qw(byteorder intsize longsize ptrsize nvsize)) { - ok(!exists $info->{$_}, "no $_"); - } -} - -for my $test (@tests) { - my($data, $expected) = @$test; - open(FH, ">$file") || die "Can't create $file: $!"; - binmode(FH); - print FH $data; - close(FH) || die "Can't write $file: $!"; - - my $name = $expected->{file}; - $expected->{file} = $file; - - my $info = Storable::file_magic($file); - unlink($file); - - is_deeply($info, $expected, "file_magic $name"); - - $expected->{file} = 1; - is_deeply(Storable::read_magic($data), $expected, "read magic $name"); -} diff --git a/ext/Storable/t/forgive.t b/ext/Storable/t/forgive.t deleted file mode 100644 index 495edc339a..0000000000 --- a/ext/Storable/t/forgive.t +++ /dev/null @@ -1,65 +0,0 @@ -#!./perl -# -# Copyright (c) 1995-2000, Raphael Manfredi -# -# You may redistribute only under the same terms as Perl 5, as specified -# in the README file that comes with the distribution. -# -# Original Author: Ulrich Pfeifer -# (C) Copyright 1997, Universitat Dortmund, all rights reserved. -# - -sub BEGIN { - unshift @INC, 't'; - require Config; import Config; - if ($ENV{PERL_CORE} and $Config{'extensions'} !~ /\bStorable\b/) { - print "1..0 # Skip: Storable was not built\n"; - exit 0; - } -} - -use Storable qw(store retrieve); - -# problems with 5.00404 when in an BEGIN block, so this is defined here -if (!eval { require File::Spec; 1 } || $File::Spec::VERSION < 0.8) { - print "1..0 # Skip: File::Spec 0.8 needed\n"; - exit 0; - # Mention $File::Spec::VERSION again, as 5.00503's harness seems to have - # warnings on. - exit $File::Spec::VERSION; -} - -print "1..8\n"; - -my $test = 1; -*GLOB = *GLOB; # peacify -w -my $bad = ['foo', \*GLOB, 'bar']; -my $result; - -eval {$result = store ($bad , 'store')}; -print ((!defined $result)?"ok $test\n":"not ok $test\n"); $test++; -print (($@ ne '')?"ok $test\n":"not ok $test\n"); $test++; - -$Storable::forgive_me=1; - -my $devnull = File::Spec->devnull; - -open(SAVEERR, ">&STDERR"); -open(STDERR, ">$devnull") or - ( print SAVEERR "Unable to redirect STDERR: $!\n" and exit(1) ); - -eval {$result = store ($bad , 'store')}; - -open(STDERR, ">&SAVEERR"); - -print ((defined $result)?"ok $test\n":"not ok $test\n"); $test++; -print (($@ eq '')?"ok $test\n":"not ok $test\n"); $test++; - -my $ret = retrieve('store'); -print ((defined $ret)?"ok $test\n":"not ok $test\n"); $test++; -print (($ret->[0] eq 'foo')?"ok $test\n":"not ok $test\n"); $test++; -print (($ret->[2] eq 'bar')?"ok $test\n":"not ok $test\n"); $test++; -print ((ref $ret->[1] eq 'SCALAR')?"ok $test\n":"not ok $test\n"); $test++; - - -END { 1 while unlink 'store' } diff --git a/ext/Storable/t/freeze.t b/ext/Storable/t/freeze.t deleted file mode 100644 index e76b669820..0000000000 --- a/ext/Storable/t/freeze.t +++ /dev/null @@ -1,146 +0,0 @@ -#!./perl -# -# Copyright (c) 1995-2000, Raphael Manfredi -# -# You may redistribute only under the same terms as Perl 5, as specified -# in the README file that comes with the distribution. -# - -sub BEGIN { - unshift @INC, 't'; - require Config; import Config; - if ($ENV{PERL_CORE} and $Config{'extensions'} !~ /\bStorable\b/) { - print "1..0 # Skip: Storable was not built\n"; - exit 0; - } - require 'st-dump.pl'; - sub ok; -} - -use Storable qw(freeze nfreeze thaw); - -print "1..20\n"; - -$a = 'toto'; -$b = \$a; -$c = bless {}, CLASS; -$c->{attribute} = $b; -$d = {}; -$e = []; -$d->{'a'} = $e; -$e->[0] = $d; -%a = ('key', 'value', 1, 0, $a, $b, 'cvar', \$c); -@a = ('first', undef, 3, -4, -3.14159, 456, 4.5, $d, \$d, \$e, $e, - $b, \$a, $a, $c, \$c, \%a); - -print "not " unless defined ($f1 = freeze(\@a)); -print "ok 1\n"; - -$dumped = &dump(\@a); -print "ok 2\n"; - -$root = thaw($f1); -print "not " unless defined $root; -print "ok 3\n"; - -$got = &dump($root); -print "ok 4\n"; - -print "not " unless $got eq $dumped; -print "ok 5\n"; - -package FOO; @ISA = qw(Storable); - -sub make { - my $self = bless {}; - $self->{key} = \%main::a; - return $self; -}; - -package main; - -$foo = FOO->make; -print "not " unless $f2 = $foo->freeze; -print "ok 6\n"; - -print "not " unless $f3 = $foo->nfreeze; -print "ok 7\n"; - -$root3 = thaw($f3); -print "not " unless defined $root3; -print "ok 8\n"; - -print "not " unless &dump($foo) eq &dump($root3); -print "ok 9\n"; - -$root = thaw($f2); -print "not " unless &dump($foo) eq &dump($root); -print "ok 10\n"; - -print "not " unless &dump($root3) eq &dump($root); -print "ok 11\n"; - -$other = freeze($root); -print "not " unless length($other) == length($f2); -print "ok 12\n"; - -$root2 = thaw($other); -print "not " unless &dump($root2) eq &dump($root); -print "ok 13\n"; - -$VAR1 = [ - 'method', - 1, - 'prepare', - 'SELECT table_name, table_owner, num_rows FROM iitables - where table_owner != \'$ingres\' and table_owner != \'DBA\'' -]; - -$x = nfreeze($VAR1); -$VAR2 = thaw($x); -print "not " unless $VAR2->[3] eq $VAR1->[3]; -print "ok 14\n"; - -# Test the workaround for LVALUE bug in perl 5.004_04 -- from Gisle Aas -sub foo { $_[0] = 1 } -$foo = []; -foo($foo->[1]); -eval { freeze($foo) }; -print "not " if $@; -print "ok 15\n"; - -# Test cleanup bug found by Claudio Garcia -- RAM, 08/06/2001 -my $thaw_me = 'asdasdasdasd'; - -eval { - my $thawed = thaw $thaw_me; -}; -ok 16, $@; - -my %to_be_frozen = (foo => 'bar'); -my $frozen; -eval { - $frozen = freeze \%to_be_frozen; -}; -ok 17, !$@; - -freeze {}; -eval { thaw $thaw_me }; -eval { $frozen = freeze { foo => {} } }; -ok 18, !$@; - -thaw $frozen; # used to segfault here -ok 19, 1; - -if ($] >= 5.006) { - eval ' - $a = []; $#$a = 2; $a->[1] = undef; - $b = thaw freeze $a; - @a = map { ~~ exists $a->[$_] } 0 .. $#$a; - @b = map { ~~ exists $b->[$_] } 0 .. $#$b; - ok 20, "@a" eq "@b"; - '; -} -else { - print "ok 20 # skipped (no av_exists)\n"; -} diff --git a/ext/Storable/t/integer.t b/ext/Storable/t/integer.t deleted file mode 100644 index 2c22235a83..0000000000 --- a/ext/Storable/t/integer.t +++ /dev/null @@ -1,177 +0,0 @@ -#!./perl -w -# -# Copyright 2002, Larry Wall. -# -# You may redistribute only under the same terms as Perl 5, as specified -# in the README file that comes with the distribution. -# - -# I ought to keep this test easily backwards compatible to 5.004, so no -# qr//; - -# This test checks downgrade behaviour on pre-5.8 perls when new 5.8 features -# are encountered. - -sub BEGIN { - unshift @INC, 't'; - require Config; import Config; - if ($ENV{PERL_CORE} and $Config{'extensions'} !~ /\bStorable\b/) { - print "1..0 # Skip: Storable was not built\n"; - exit 0; - } -} - -use Test::More; -use Storable qw (dclone store retrieve freeze thaw nstore nfreeze); -use strict; - -my $max_uv = ~0; -my $max_uv_m1 = ~0 ^ 1; -# Express it in this way so as not to use any addition, as 5.6 maths would -# do this in NVs on 64 bit machines, and we're overflowing IVs so can't use -# use integer. -my $max_iv_p1 = $max_uv ^ ($max_uv >> 1); -my $lots_of_9C = do { - my $temp = sprintf "%#x", ~0; - $temp =~ s/ff/9c/g; - local $^W; - eval $temp; -}; - -my $max_iv = ~0 >> 1; -my $min_iv = do {use integer; -$max_iv-1}; # 2s complement assumption - -my @processes = (["dclone", \&do_clone], - ["freeze/thaw", \&freeze_and_thaw], - ["nfreeze/thaw", \&nfreeze_and_thaw], - ["store/retrieve", \&store_and_retrieve], - ["nstore/retrieve", \&nstore_and_retrieve], - ); -my @numbers = - (# IV bounds of 8 bits - -1, 0, 1, -127, -128, -129, 42, 126, 127, 128, 129, 254, 255, 256, 257, - # IV bounds of 32 bits - -2147483647, -2147483648, -2147483649, 2147483646, 2147483647, 2147483648, - # IV bounds - $min_iv, do {use integer; $min_iv + 1}, do {use integer; $max_iv - 1}, - $max_iv, - # UV bounds at 32 bits - 0x7FFFFFFF, 0x80000000, 0x80000001, 0xFFFFFFFF, 0xDEADBEEF, - # UV bounds - $max_iv_p1, $max_uv_m1, $max_uv, $lots_of_9C, - # NV-UV conversion - 2559831922.0, - ); - -plan tests => @processes * @numbers * 5; - -my $file = "integer.$$"; -die "Temporary file '$file' already exists" if -e $file; - -END { while (-f $file) {unlink $file or die "Can't unlink '$file': $!" }} - -sub do_clone { - my $data = shift; - my $copy = eval {dclone $data}; - is ($@, '', 'Should be no error dcloning'); - ok (1, "dlcone is only 1 process, not 2"); - return $copy; -} - -sub freeze_and_thaw { - my $data = shift; - my $frozen = eval {freeze $data}; - is ($@, '', 'Should be no error freezing'); - my $copy = eval {thaw $frozen}; - is ($@, '', 'Should be no error thawing'); - return $copy; -} - -sub nfreeze_and_thaw { - my $data = shift; - my $frozen = eval {nfreeze $data}; - is ($@, '', 'Should be no error nfreezing'); - my $copy = eval {thaw $frozen}; - is ($@, '', 'Should be no error thawing'); - return $copy; -} - -sub store_and_retrieve { - my $data = shift; - my $frozen = eval {store $data, $file}; - is ($@, '', 'Should be no error storing'); - my $copy = eval {retrieve $file}; - is ($@, '', 'Should be no error retrieving'); - return $copy; -} - -sub nstore_and_retrieve { - my $data = shift; - my $frozen = eval {nstore $data, $file}; - is ($@, '', 'Should be no error storing'); - my $copy = eval {retrieve $file}; - is ($@, '', 'Should be no error retrieving'); - return $copy; -} - -foreach (@processes) { - my ($process, $sub) = @$_; - foreach my $number (@numbers) { - # as $number is an alias into @numbers, we don't want any side effects of - # conversion macros affecting later runs, so pass a copy to Storable: - my $copy1 = my $copy2 = my $copy0 = $number; - my $copy_s = &$sub (\$copy0); - if (is (ref $copy_s, "SCALAR", "got back a scalar ref?")) { - # Test inside use integer to see if the bit pattern is identical - # and outside to see if the sign is right. - # On 5.8 we don't need this trickery anymore. - # We really do need 2 copies here, as conversion may have side effect - # bugs. In particular, I know that this happens: - # perl5.00503 -le '$a = "-2147483649"; $a & 0; print $a; print $a+1' - # -2147483649 - # 2147483648 - - my $copy_s1 = my $copy_s2 = $$copy_s; - # On 5.8 can do this with a straight ==, due to the integer/float maths - # on 5.6 can't do this with - # my $eq = do {use integer; $copy_s1 == $copy1} && $copy_s1 == $copy1; - # because on builds with IV as long long it tickles bugs. - # (Uncomment it and the Devel::Peek line below to see the messed up - # state of the scalar, with PV showing the correct string for the - # number, and IV holding a bogus value which has been truncated to 32 bits - - # So, check the bit patterns are identical, and check that the sign is the - # same. This works on all the versions in all the sizes. - # $eq = && (($copy_s1 <=> 0) == ($copy1 <=> 0)); - # Split this into 2 tests, to cater for 5.005_03 - - # Aargh. Even this doesn't work because 5.6.x sends values with (same - # number of decimal digits as ~0 + 1) via atof. So ^ is getting strings - # cast to doubles cast to integers. And that truncates low order bits. - # my $bit = ok (($copy_s1 ^ $copy1) == 0, "$process $copy1 (bitpattern)"); - - # Oh well; at least the parser gets it right. :-) - my $copy_s3 = eval $copy_s1; - die "Was supposed to have number $copy_s3, got error $@" - unless defined $copy_s3; - my $bit = ok (($copy_s3 ^ $copy1) == 0, "$process $copy1 (bitpattern)"); - # This is sick. 5.005_03 survives without the IV/UV flag, and somehow - # gets it right, providing you don't have side effects of conversion. -# local $TODO; -# $TODO = "pre 5.6 doesn't have flag to distinguish IV/UV" -# if $[ < 5.005_56 and $copy1 > $max_iv; - my $sign = ok (($copy_s2 <=> 0) == ($copy2 <=> 0), - "$process $copy1 (sign)"); - - unless ($bit and $sign) { - printf "# Passed in %s (%#x, %i)\n# got back '%s' (%#x, %i)\n", - $copy1, $copy1, $copy1, $copy_s1, $copy_s1, $copy_s1; - # use Devel::Peek; Dump $number; Dump $copy1; Dump $copy_s1; - } - # unless ($bit) { use Devel::Peek; Dump $copy_s1; Dump $$copy_s; } - } else { - fail ("$process $copy1"); - fail ("$process $copy1"); - } - } -} diff --git a/ext/Storable/t/interwork56.t b/ext/Storable/t/interwork56.t deleted file mode 100644 index 4e9b414e54..0000000000 --- a/ext/Storable/t/interwork56.t +++ /dev/null @@ -1,195 +0,0 @@ -#!./perl -w -# -# Copyright 2002, Larry Wall. -# -# You may redistribute only under the same terms as Perl 5, as specified -# in the README file that comes with the distribution. -# - -# I ought to keep this test easily backwards compatible to 5.004, so no -# qr//; - -# This test checks whether the kludge to interwork with 5.6 Storables compiled -# on Unix systems with IV as long long works. - -sub BEGIN { - unshift @INC, 't'; - require Config; import Config; - if ($ENV{PERL_CORE} and $Config{'extensions'} !~ /\bStorable\b/) { - print "1..0 # Skip: Storable was not built\n"; - exit 0; - } - unless ($Config{ivsize} and $Config{ivsize} > $Config{longsize}) { - print "1..0 # Skip: Your IVs are no larger than your longs\n"; - exit 0; - } -} - -use Storable qw(freeze thaw); -use strict; -use Test::More tests=>30; - -use vars qw(%tests); - -{ - local $/ = "\n\nend\n"; - while (<DATA>) { - next unless /\S/s; - unless (/begin ([0-7]{3}) ([^\n]*)\n(.*)$/s) { - s/\n.*//s; - warn "Dodgy data in section starting '$_'"; - next; - } - next unless oct $1 == ord 'A'; # Skip ASCII on EBCDIC, and vice versa - my $data = unpack 'u', $3; - $tests{$2} = $data; - } -} - -# perl makes easy things easy, and hard things possible: -my $test = freeze \'Hell'; - -my $header = Storable::read_magic ($test); - -is ($header->{byteorder}, $Config{byteorder}, - "header's byteorder and Config.pm's should agree"); - -my $result = eval {thaw $test}; -isa_ok ($result, 'SCALAR', "Check thawing test data"); -is ($@, '', "causes no errors"); -is ($$result, 'Hell', 'and gives the expected data'); - -my $kingdom = $Config{byteorder} =~ /23/ ? "Lillput" : "Belfuscu"; - -my $name = join ',', $kingdom, @$header{qw(intsize longsize ptrsize nvsize)}; - -SKIP: { - my $real_thing = $tests{$name}; - if (!defined $real_thing) { - print << "EOM"; -# No test data for Storable 1.x for: -# -# byteorder '$Config{byteorder}' -# sizeof(int) $$header{intsize} -# sizeof(long) $$header{longsize} -# sizeof(char *) $$header{ptrsize} -# sizeof(NV) $$header{nvsize} - -# If you have Storable 1.x built with perl 5.6.x on this platform, please -# make_56_interwork.pl to generate test data, and append the test data to -# this test. -# You may find that make_56_interwork.pl reports that your platform has no -# interworking problems, in which case you need do nothing. -EOM - skip "# No 1.x test file", 9; - } - my $result = eval {thaw $real_thing}; - is ($result, undef, "By default should not be able to thaw"); - like ($@, qr/Byte order is not compatible/, - "because the header byte order strings differ"); - local $Storable::interwork_56_64bit = 1; - $result = eval {thaw $real_thing}; - isa_ok ($result, 'ARRAY', "With flag should now thaw"); - is ($@, '', "with no errors"); - - # However, as the file is written with Storable pre 2.01, it's a known - # bug that large (positive) UVs become IVs - my $value = (~0 ^ (~0 >> 1) ^ 2); - - is (@$result, 4, "4 elements in array"); - like ($$result[0], - qr/^This file was written with [0-9.]+ on perl [0-9.]+\z/, - "1st element"); - is ($$result[1], "$kingdom was correct", "2nd element"); - cmp_ok ($$result[2] ^ $value, '==', 0, "3rd element") or - printf "# expected %#X, got %#X\n", $value, $$result[2]; - is ($$result[3], "The End", "4th element"); -} - -$result = eval {thaw $test}; -isa_ok ($result, 'SCALAR', "CHORUS: check thawing test data"); -is ($@, '', " causes no errors"); -is ($$result, 'Hell', " and gives the expected data"); - -my $test_kludge; -{ - local $Storable::interwork_56_64bit = 1; - $test_kludge = freeze \'Heck'; -} - -my $header_kludge = Storable::read_magic ($test_kludge); - -cmp_ok (length ($header_kludge->{byteorder}), '==', $Config{longsize}, - "With 5.6 interwork kludge byteorder string should be same size as long" - ); -$result = eval {thaw $test_kludge}; -is ($result, undef, "By default should not be able to thaw"); -like ($@, qr/Byte order is not compatible/, - "because the header byte order strings differ"); - -$result = eval {thaw $test}; -isa_ok ($result, 'SCALAR', "CHORUS: check thawing test data"); -is ($@, '', " causes no errors"); -is ($$result, 'Hell', " and gives the expected data"); - -{ - local $Storable::interwork_56_64bit = 1; - - $result = eval {thaw $test_kludge}; - isa_ok ($result, 'SCALAR', "should be able to thaw kludge data"); - is ($@, '', "with no errors"); - is ($$result, 'Heck', "and gives expected data"); - - $result = eval {thaw $test}; - is ($result, undef, "But now can't thaw real data"); - like ($@, qr/Byte order is not compatible/, - "because the header byte order strings differ"); -} - -# All together now: -$result = eval {thaw $test}; -isa_ok ($result, 'SCALAR', "CHORUS: check thawing test data"); -is ($@, '', " causes no errors"); -is ($$result, 'Hell', " and gives the expected data"); - -__END__ -# A whole run of 1.1.14 freeze data, uuencoded. The "mode bits" are the octal -# value of 'A', the "file name" is the test name. Use make_56_interwork.pl -# with a copy of Storable 1.X generate these. - -# byteorder '1234' -# sizeof(int) 4 -# sizeof(long) 4 -# sizeof(char *) 4 -# sizeof(NV) 8 -begin 101 Lillput,4,4,4,8 -M!`0$,3(S-`0$!`@"!`````HQ5&AI<R!F:6QE('=A<R!W<FET=&5N('=I=&@@ -M,2XP,30@;VX@<&5R;"`U+C`P-C`P,0H33&EL;'!U="!W87,@8V]R<F5C=`8" -0````````@`H'5&AE($5N9``` - -end - -# byteorder '4321' -# sizeof(int) 4 -# sizeof(long) 4 -# sizeof(char *) 4 -# sizeof(NV) 8 -begin 101 Belfuscu,4,4,4,8 -M!`0$-#,R,00$!`@"````!`HQ5&AI<R!F:6QE('=A<R!W<FET=&5N('=I=&@@ -M,2XP,30@;VX@<&5R;"`U+C`P-C`P,0H40F5L9G5S8W4@=V%S(&-O<G)E8W0& -1@`````````(*!U1H92!%;F0` - -end - -# byteorder '1234' -# sizeof(int) 4 -# sizeof(long) 4 -# sizeof(char *) 4 -# sizeof(NV) 12 -begin 101 Lillput,4,4,4,12 -M!`0$,3(S-`0$!`P"!`````HQ5&AI<R!F:6QE('=A<R!W<FET=&5N('=I=&@@ -M,2XP,30@;VX@<&5R;"`U+C`P-C`P,0H33&EL;'!U="!W87,@8V]R<F5C=`8" -0````````@`H'5&AE($5N9``` - -end - diff --git a/ext/Storable/t/just_plain_nasty.t b/ext/Storable/t/just_plain_nasty.t deleted file mode 100644 index 85eaa901cf..0000000000 --- a/ext/Storable/t/just_plain_nasty.t +++ /dev/null @@ -1,146 +0,0 @@ -#!/usr/bin/perl - -# This is a test suite to cover all the nasty and horrible data -# structures that cause bizarre corner cases. - -# Everyone's invited! :-D - -sub BEGIN { - unshift @INC, 't'; - require Config; import Config; - if ($ENV{PERL_CORE} and $Config{'extensions'} !~ /\bStorable\b/) { - print "1..0 # Skip: Storable was not built\n"; - exit 0; - } -} - -use strict; -BEGIN { - if (!eval q{ - use Test; - use B::Deparse 0.61; - use 5.006; - 1; - }) { - print "1..0 # skip: tests only work with B::Deparse 0.61 and at least perl 5.6.0\n"; - exit; - } - require File::Spec; - if ($File::Spec::VERSION < 0.8) { - print "1..0 # Skip: newer File::Spec needed\n"; - exit 0; - } -} - -use Storable qw(freeze thaw); - -#$Storable::DEBUGME = 1; -BEGIN { - plan tests => 34; -} - -{ - package Banana; - use overload - '<=>' => \&compare, - '==' => \&equal, - '""' => \&real, - fallback => 1; - sub compare { return int(rand(3))-1 }; - sub equal { return 1 if rand(1) > 0.5 } - sub real { return "keep it so" } -} - -my (@a); - -for my $dbun (1, 0) { # dbun - don't be utterly nasty - being utterly - # nasty means having a reference to the object - # directly within itself. otherwise it's in the - # second array. - my $nasty = [ - ($a[0] = bless [ ], "Banana"), - ($a[1] = [ ]), - ]; - - $a[$dbun]->[0] = $a[0]; - - ok(ref($nasty), "ARRAY", "Sanity found (now to play with it :->)"); - - $Storable::Deparse = $Storable::Deparse = 1; - $Storable::Eval = $Storable::Eval = 1; - - headit("circular overload 1 - freeze"); - my $icicle = freeze $nasty; - #print $icicle; # cat -ve recommended :) - headit("circular overload 1 - thaw"); - my $oh_dear = thaw $icicle; - ok(ref($oh_dear), "ARRAY", "dclone - circular overload"); - ok($oh_dear->[0], "keep it so", "amagic ok 1"); - ok($oh_dear->[$dbun]->[0], "keep it so", "amagic ok 2"); - - headit("closure dclone - freeze"); - $icicle = freeze sub { "two" }; - #print $icicle; - headit("closure dclone - thaw"); - my $sub2 = thaw $icicle; - ok($sub2->(), "two", "closures getting dcloned OK"); - - headit("circular overload, after closure - freeze"); - #use Data::Dumper; - #print Dumper $nasty; - $icicle = freeze $nasty; - #print $icicle; - headit("circular overload, after closure - thaw"); - $oh_dear = thaw $icicle; - ok(ref($oh_dear), "ARRAY", "dclone - after a closure dclone"); - ok($oh_dear->[0], "keep it so", "amagic ok 1"); - ok($oh_dear->[$dbun]->[0], "keep it so", "amagic ok 2"); - - push @{$nasty}, sub { print "Goodbye, cruel world.\n" }; - headit("closure freeze AFTER circular overload"); - #print Dumper $nasty; - $icicle = freeze $nasty; - #print $icicle; - headit("circular thaw AFTER circular overload"); - $oh_dear = thaw $icicle; - ok(ref($oh_dear), "ARRAY", "dclone - before a closure dclone"); - ok($oh_dear->[0], "keep it so", "amagic ok 1"); - ok($oh_dear->[$dbun]->[0], "keep it so", "amagic ok 2"); - - @{$nasty} = @{$nasty}[0, 2, 1]; - headit("closure freeze BETWEEN circular overload"); - #print Dumper $nasty; - $icicle = freeze $nasty; - #print $icicle; - headit("circular thaw BETWEEN circular overload"); - $oh_dear = thaw $icicle; - ok(ref($oh_dear), "ARRAY", "dclone - between a closure dclone"); - ok($oh_dear->[0], "keep it so", "amagic ok 1"); - ok($oh_dear->[$dbun?2:0]->[0], "keep it so", "amagic ok 2"); - - @{$nasty} = @{$nasty}[1, 0, 2]; - headit("closure freeze BEFORE circular overload"); - #print Dumper $nasty; - $icicle = freeze $nasty; - #print $icicle; - headit("circular thaw BEFORE circular overload"); - $oh_dear = thaw $icicle; - ok(ref($oh_dear), "ARRAY", "dclone - after a closure dclone"); - ok($oh_dear->[1], "keep it so", "amagic ok 1"); - ok($oh_dear->[$dbun+1]->[0], "keep it so", "amagic ok 2"); -} - -sub headit { - - return; # comment out to get headings - useful for scanning - # output with $Storable::DEBUGME = 1 - - my $title = shift; - - my $size_left = (66 - length($title)) >> 1; - my $size_right = (67 - length($title)) >> 1; - - print "# ".("-" x $size_left). " $title " - .("-" x $size_right)."\n"; -} - diff --git a/ext/Storable/t/lock.t b/ext/Storable/t/lock.t deleted file mode 100644 index 14b5f4261a..0000000000 --- a/ext/Storable/t/lock.t +++ /dev/null @@ -1,46 +0,0 @@ -#!./perl -# -# Copyright (c) 1995-2000, Raphael Manfredi -# -# You may redistribute only under the same terms as Perl 5, as specified -# in the README file that comes with the distribution. -# - -sub BEGIN { - unshift @INC, 't'; - require Config; import Config; - if ($ENV{PERL_CORE} and $Config{'extensions'} !~ /\bStorable\b/) { - print "1..0 # Skip: Storable was not built\n"; - exit 0; - } - - require 'st-dump.pl'; -} - -sub ok; - -use Storable qw(lock_store lock_retrieve); - -unless (&Storable::CAN_FLOCK) { - print "1..0 # Skip: fcntl/flock emulation broken on this platform\n"; - exit 0; -} - -print "1..5\n"; - -@a = ('first', undef, 3, -4, -3.14159, 456, 4.5); - -# -# We're just ensuring things work, we're not validating locking. -# - -ok 1, defined lock_store(\@a, 'store'); -ok 2, $dumped = &dump(\@a); - -$root = lock_retrieve('store'); -ok 3, ref $root eq 'ARRAY'; -ok 4, @a == @$root; -ok 5, &dump($root) eq $dumped; - -unlink 't/store'; - diff --git a/ext/Storable/t/make_56_interwork.pl b/ext/Storable/t/make_56_interwork.pl deleted file mode 100644 index c73e9b6d90..0000000000 --- a/ext/Storable/t/make_56_interwork.pl +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/perl -w -use strict; - -use Config; -use Storable qw(freeze thaw); - -# Lilliput decreed that eggs should be eaten small end first. -# Belfuscu welcomed the rebels who wanted to eat big end first. -my $kingdom = $Config{byteorder} =~ /23/ ? "Lillput" : "Belfuscu"; - -my $frozen = freeze - ["This file was written with $Storable::VERSION on perl $]", - "$kingdom was correct", (~0 ^ (~0 >> 1) ^ 2), - "The End"]; - -my $ivsize = $Config{ivsize} || $Config{longsize}; - -my $storesize = unpack 'xxC', $frozen; -my $storebyteorder = unpack "xxxA$storesize", $frozen; - -if ($Config{byteorder} eq $storebyteorder) { - my $ivtype = $Config{ivtype} || 'long'; - print <<"EOM"; -You only need to run this generator program where Config.pm's byteorder string -is not the same length as the size of IVs. - -This length difference should only happen on perl 5.6.x configured with IVs as -long long on Unix, OS/2 or any platform that runs the Configure stript (ie not -MS Windows) - -This is perl $], sizeof(long) is $Config{longsize}, IVs are '$ivtype', sizeof(IV) is $ivsize, -byteorder is '$Config{byteorder}', Storable $Storable::VERSION writes a byteorder of '$storebyteorder' -EOM - exit; # Grr ' -} - -my ($i, $l, $p, $n) = unpack "xxxx${storesize}CCCC", $frozen; - -print <<"EOM"; -# byteorder '$storebyteorder' -# sizeof(int) $i -# sizeof(long) $l -# sizeof(char *) $p -# sizeof(NV) $n -EOM - -my $uu = pack 'u', $frozen; - -printf "begin %3o $kingdom,$i,$l,$p,$n\n", ord 'A'; -print $uu; -print "\nend\n\n"; diff --git a/ext/Storable/t/make_downgrade.pl b/ext/Storable/t/make_downgrade.pl deleted file mode 100644 index fc801a4953..0000000000 --- a/ext/Storable/t/make_downgrade.pl +++ /dev/null @@ -1,106 +0,0 @@ -#!/usr/local/bin/perl -w -use strict; - -use 5.007003; -use Hash::Util qw(lock_hash unlock_hash lock_keys); -use Storable qw(nfreeze); - -# If this looks like a hack, it's probably because it is :-) -sub uuencode_it { - my ($data, $name) = @_; - my $frozen = nfreeze $data; - - my $uu = pack 'u', $frozen; - - printf "begin %3o $name\n", ord 'A'; - print $uu; - print "\nend\n\n"; -} - - -my %hash = (perl=>"rules"); - -lock_hash %hash; - -uuencode_it (\%hash, "Locked hash"); - -unlock_hash %hash; - -lock_keys %hash, 'perl', 'rules'; -lock_hash %hash; - -uuencode_it (\%hash, "Locked hash placeholder"); - -unlock_hash %hash; - -lock_keys %hash, 'perl'; - -uuencode_it (\%hash, "Locked keys"); - -unlock_hash %hash; - -lock_keys %hash, 'perl', 'rules'; - -uuencode_it (\%hash, "Locked keys placeholder"); - -unlock_hash %hash; - -my $utf8 = "\x{DF}\x{100}"; -chop $utf8; - -uuencode_it (\$utf8, "Short 8 bit utf8 data"); - -my $utf8b = $utf8; -utf8::encode ($utf8b); - -uuencode_it (\$utf8b, "Short 8 bit utf8 data as bytes"); - -$utf8 x= 256; - -uuencode_it (\$utf8, "Long 8 bit utf8 data"); - -$utf8 = "\x{C0FFEE}"; - -uuencode_it (\$utf8, "Short 24 bit utf8 data"); - -$utf8b = $utf8; -utf8::encode ($utf8b); - -uuencode_it (\$utf8b, "Short 24 bit utf8 data as bytes"); - -$utf8 x= 256; - -uuencode_it (\$utf8, "Long 24 bit utf8 data"); - -# Hash which has the utf8 bit set, but no longer has any utf8 keys -my %uhash = ("\x{100}", "gone", "perl", "rules"); -delete $uhash{"\x{100}"}; - -# use Devel::Peek; Dump \%uhash; -uuencode_it (\%uhash, "Hash with utf8 flag but no utf8 keys"); - -$utf8 = "Schlo\xdf" . chr 256; -chop $utf8; -my $a_circumflex = (ord ('A') == 193 ? "\x47" : "\xe5"); -%uhash = (map {$_, $_} 'castle', "ch${a_circumflex}teau", $utf8, "\x{57CE}"); - -uuencode_it (\%uhash, "Hash with utf8 keys"); - -lock_hash %uhash; - -uuencode_it (\%uhash, "Locked hash with utf8 keys"); - -my (%pre56, %pre58); - -while (my ($key, $val) = each %uhash) { - # hash keys are always stored downgraded to bytes if possible, with a flag - # to say "promote back to utf8" - # Whereas scalars are stored as is. - utf8::encode ($key) if ord $key > 256; - $pre58{$key} = $val; - utf8::encode ($val) unless $val eq "ch\xe5teau"; - $pre56{$key} = $val; - -} -uuencode_it (\%pre56, "Hash with utf8 keys for pre 5.6"); -uuencode_it (\%pre58, "Hash with utf8 keys for 5.6"); diff --git a/ext/Storable/t/make_overload.pl b/ext/Storable/t/make_overload.pl deleted file mode 100644 index bd224f5373..0000000000 --- a/ext/Storable/t/make_overload.pl +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/local/bin/perl -w -use strict; - -use Storable qw(nfreeze); -use HAS_OVERLOAD; - -my $o = HAS_OVERLOAD->make("snow"); -my $f = nfreeze \$o; - -my $uu = pack 'u', $f; - -print $uu; - diff --git a/ext/Storable/t/malice.t b/ext/Storable/t/malice.t deleted file mode 100644 index ddb65ddf2c..0000000000 --- a/ext/Storable/t/malice.t +++ /dev/null @@ -1,309 +0,0 @@ -#!./perl -w -# -# Copyright 2002, Larry Wall. -# -# You may redistribute only under the same terms as Perl 5, as specified -# in the README file that comes with the distribution. -# - -# I'm trying to keep this test easily backwards compatible to 5.004, so no -# qr//; - -# This test tries to craft malicious data to test out as many different -# error traps in Storable as possible -# It also acts as a test for read_header - -sub BEGIN { - # This lets us distribute Test::More in t/ - unshift @INC, 't'; - require Config; import Config; - if ($ENV{PERL_CORE} and $Config{'extensions'} !~ /\bStorable\b/) { - print "1..0 # Skip: Storable was not built\n"; - exit 0; - } -} - -use strict; -use vars qw($file_magic_str $other_magic $network_magic $byteorder - $major $minor $minor_write $fancy); - -$byteorder = $Config{byteorder}; - -$file_magic_str = 'pst0'; -$other_magic = 7 + length $byteorder; -$network_magic = 2; -$major = 2; -$minor = 7; -$minor_write = $] > 5.005_50 ? 7 : 4; - -use Test::More; - -# If it's 5.7.3 or later the hash will be stored with flags, which is -# 2 extra bytes. There are 2 * 2 * 2 tests per byte in the body and header -# common to normal and network order serialised objects (hence the 8) -# There are only 2 * 2 tests per byte in the parts of the header not present -# for network order, and 2 tests per byte on the 'pst0' "magic number" only -# present in files, but not in things store()ed to memory -$fancy = ($] > 5.007 ? 2 : 0); - -plan tests => 372 + length ($byteorder) * 4 + $fancy * 8; - -use Storable qw (store retrieve freeze thaw nstore nfreeze); -require 'testlib.pl'; -use vars '$file'; - -# The chr 256 is a hack to force the hash to always have the utf8 keys flag -# set on 5.7.3 and later. Otherwise the test fails if run with -Mutf8 because -# only there does the hash has the flag on, and hence only there is it stored -# as a flagged hash, which is 2 bytes longer -my %hash = (perl => 'rules', chr 256, ''); -delete $hash{chr 256}; - -sub test_hash { - my $clone = shift; - is (ref $clone, "HASH", "Get hash back"); - is (scalar keys %$clone, 1, "with 1 key"); - is ((keys %$clone)[0], "perl", "which is correct"); - is ($clone->{perl}, "rules"); -} - -sub test_header { - my ($header, $isfile, $isnetorder) = @_; - is (!!$header->{file}, !!$isfile, "is file"); - is ($header->{major}, $major, "major number"); - is ($header->{minor}, $minor_write, "minor number"); - is (!!$header->{netorder}, !!$isnetorder, "is network order"); - if ($isnetorder) { - # Network order header has no sizes - } else { - is ($header->{byteorder}, $byteorder, "byte order"); - is ($header->{intsize}, $Config{intsize}, "int size"); - is ($header->{longsize}, $Config{longsize}, "long size"); - SKIP: { - skip ("No \$Config{prtsize} on this perl version ($])", 1) - unless defined $Config{ptrsize}; - is ($header->{ptrsize}, $Config{ptrsize}, "long size"); - } - is ($header->{nvsize}, $Config{nvsize} || $Config{doublesize} || 8, - "nv size"); # 5.00405 doesn't even have doublesize in config. - } -} - -sub test_truncated { - my ($data, $sub, $magic_len, $what) = @_; - for my $i (0 .. length ($data) - 1) { - my $short = substr $data, 0, $i; - - # local $Storable::DEBUGME = 1; - my $clone = &$sub($short); - is (defined ($clone), '', "truncated $what to $i should fail"); - if ($i < $magic_len) { - like ($@, "/^Magic number checking on storable $what failed/", - "Should croak with magic number warning"); - } else { - is ($@, "", "Should not set \$\@"); - } - } -} - -sub test_corrupt { - my ($data, $sub, $what, $name) = @_; - - my $clone = &$sub($data); - is (defined ($clone), '', "$name $what should fail"); - like ($@, $what, $name); -} - -sub test_things { - my ($contents, $sub, $what, $isnetwork) = @_; - my $isfile = $what eq 'file'; - my $file_magic = $isfile ? length $file_magic_str : 0; - - my $header = Storable::read_magic ($contents); - test_header ($header, $isfile, $isnetwork); - - # Test that if we re-write it, everything still works: - my $clone = &$sub ($contents); - - is ($@, "", "There should be no error"); - - test_hash ($clone); - - # Now lets check the short version: - test_truncated ($contents, $sub, $file_magic - + ($isnetwork ? $network_magic : $other_magic), $what); - - my $copy; - if ($isfile) { - $copy = $contents; - substr ($copy, 0, 4) = 'iron'; - test_corrupt ($copy, $sub, "/^File is not a perl storable/", - "magic number"); - } - - $copy = $contents; - # Needs to be more than 1, as we're already coding a spread of 1 minor version - # number on writes (2.5, 2.4). May increase to 2 if we figure we can do 2.3 - # on 5.005_03 (No utf8). - # 4 allows for a small safety margin - # (Joke: - # Question: What is the value of pi? - # Mathematician answers "It's pi, isn't it" - # Physicist answers "3.1, within experimental error" - # Engineer answers "Well, allowing for a small safety margin, 18" - # ) - my $minor4 = $header->{minor} + 4; - substr ($copy, $file_magic + 1, 1) = chr $minor4; - { - # Now by default newer minor version numbers are not a pain. - $clone = &$sub($copy); - is ($@, "", "by default no error on higher minor"); - test_hash ($clone); - - local $Storable::accept_future_minor = 0; - test_corrupt ($copy, $sub, - "/^Storable binary image v$header->{major}\.$minor4 more recent than I am \\(v$header->{major}\.$minor\\)/", - "higher minor"); - } - - $copy = $contents; - my $major1 = $header->{major} + 1; - substr ($copy, $file_magic, 1) = chr 2*$major1; - test_corrupt ($copy, $sub, - "/^Storable binary image v$major1\.$header->{minor} more recent than I am \\(v$header->{major}\.$minor\\)/", - "higher major"); - - # Continue messing with the previous copy - my $minor1 = $header->{minor} - 1; - substr ($copy, $file_magic + 1, 1) = chr $minor1; - test_corrupt ($copy, $sub, - "/^Storable binary image v$major1\.$minor1 more recent than I am \\(v$header->{major}\.$minor\\)/", - "higher major, lower minor"); - - my $where; - if (!$isnetwork) { - # All these are omitted from the network order header. - # I'm not sure if it's correct to omit the byte size stuff. - $copy = $contents; - substr ($copy, $file_magic + 3, length $header->{byteorder}) - = reverse $header->{byteorder}; - - test_corrupt ($copy, $sub, "/^Byte order is not compatible/", - "byte order"); - $where = $file_magic + 3 + length $header->{byteorder}; - foreach (['intsize', "Integer"], - ['longsize', "Long integer"], - ['ptrsize', "Pointer"], - ['nvsize', "Double"]) { - my ($key, $name) = @$_; - $copy = $contents; - substr ($copy, $where++, 1) = chr 0; - test_corrupt ($copy, $sub, "/^$name size is not compatible/", - "$name size"); - } - } else { - $where = $file_magic + $network_magic; - } - - # Just the header and a tag 255. As 28 is currently the highest tag, this - # is "unexpected" - $copy = substr ($contents, 0, $where) . chr 255; - - test_corrupt ($copy, $sub, - "/^Corrupted storable $what \\(binary v$header->{major}.$header->{minor}\\)/", - "bogus tag"); - - # Now drop the minor version number - substr ($copy, $file_magic + 1, 1) = chr $minor1; - - test_corrupt ($copy, $sub, - "/^Corrupted storable $what \\(binary v$header->{major}.$minor1\\)/", - "bogus tag, minor less 1"); - # Now increase the minor version number - substr ($copy, $file_magic + 1, 1) = chr $minor4; - - # local $Storable::DEBUGME = 1; - # This is the delayed croak - test_corrupt ($copy, $sub, - "/^Storable binary image v$header->{major}.$minor4 contains data of type 255. This Storable is v$header->{major}.$minor and can only handle data types up to 28/", - "bogus tag, minor plus 4"); - # And check again that this croak is not delayed: - { - # local $Storable::DEBUGME = 1; - local $Storable::accept_future_minor = 0; - test_corrupt ($copy, $sub, - "/^Storable binary image v$header->{major}\.$minor4 more recent than I am \\(v$header->{major}\.$minor\\)/", - "higher minor"); - } -} - -ok (defined store(\%hash, $file)); - -my $expected = 20 + length ($file_magic_str) + $other_magic + $fancy; -my $length = -s $file; - -die "Don't seem to have written file '$file' as I can't get its length: $!" - unless defined $file; - -die "Expected file to be $expected bytes (sizeof long is $Config{longsize}) but it is $length" - unless $length == $expected; - -# Read the contents into memory: -my $contents = slurp ($file); - -# Test the original direct from disk -my $clone = retrieve $file; -test_hash ($clone); - -# Then test it. -test_things($contents, \&store_and_retrieve, 'file'); - -# And now try almost everything again with a Storable string -my $stored = freeze \%hash; -test_things($stored, \&freeze_and_thaw, 'string'); - -# Network order. -unlink $file or die "Can't unlink '$file': $!"; - -ok (defined nstore(\%hash, $file)); - -$expected = 20 + length ($file_magic_str) + $network_magic + $fancy; -$length = -s $file; - -die "Don't seem to have written file '$file' as I can't get its length: $!" - unless defined $file; - -die "Expected file to be $expected bytes (sizeof long is $Config{longsize}) but it is $length" - unless $length == $expected; - -# Read the contents into memory: -$contents = slurp ($file); - -# Test the original direct from disk -$clone = retrieve $file; -test_hash ($clone); - -# Then test it. -test_things($contents, \&store_and_retrieve, 'file', 1); - -# And now try almost everything again with a Storable string -$stored = nfreeze \%hash; -test_things($stored, \&freeze_and_thaw, 'string', 1); - -# Test that the bug fixed by #20587 doesn't affect us under some older -# Perl. AMS 20030901 -{ - chop(my $a = chr(0xDF).chr(256)); - my %a = (chr(0xDF) => 1); - $a{$a}++; - freeze \%a; - # If we were built with -DDEBUGGING, the assert() should have killed - # us, which will probably alert the user that something went wrong. - ok(1); -} - -# Unusual in that the empty string is stored with an SX_LSCALAR marker -my $hash = store_and_retrieve("pst0\5\6\3\0\0\0\1\1\0\0\0\0\0\0\0\5empty"); -ok(!$@, "no exception"); -is(ref($hash), "HASH", "got a hash"); -is($hash->{empty}, "", "got empty element"); diff --git a/ext/Storable/t/overload.t b/ext/Storable/t/overload.t deleted file mode 100644 index 22fccfb61b..0000000000 --- a/ext/Storable/t/overload.t +++ /dev/null @@ -1,114 +0,0 @@ -#!./perl -# -# Copyright (c) 1995-2000, Raphael Manfredi -# -# You may redistribute only under the same terms as Perl 5, as specified -# in the README file that comes with the distribution. -# - -sub BEGIN { - unshift @INC, 't'; - require Config; import Config; - if ($ENV{PERL_CORE} and $Config{'extensions'} !~ /\bStorable\b/) { - print "1..0 # Skip: Storable was not built\n"; - exit 0; - } - require 'st-dump.pl'; -} - -sub ok; - -use Storable qw(freeze thaw); - -print "1..19\n"; - -package OVERLOADED; - -use overload - '""' => sub { $_[0][0] }; - -package main; - -$a = bless [77], OVERLOADED; - -$b = thaw freeze $a; -ok 1, ref $b eq 'OVERLOADED'; -ok 2, "$b" eq "77"; - -$c = thaw freeze \$a; -ok 3, ref $c eq 'REF'; -ok 4, ref $$c eq 'OVERLOADED'; -ok 5, "$$c" eq "77"; - -$d = thaw freeze [$a, $a]; -ok 6, "$d->[0]" eq "77"; -$d->[0][0]++; -ok 7, "$d->[1]" eq "78"; - -package REF_TO_OVER; - -sub make { - my $self = bless {}, shift; - my ($over) = @_; - $self->{over} = $over; - return $self; -} - -package OVER; - -use overload - '+' => \&plus, - '""' => sub { ref $_[0] }; - -sub plus { - return 314; -} - -sub make { - my $self = bless {}, shift; - my $ref = REF_TO_OVER->make($self); - $self->{ref} = $ref; - return $self; -} - -package main; - -$a = OVER->make(); -$b = thaw freeze $a; - -ok 8, ref $b eq 'OVER'; -ok 9, $a + $a == 314; -ok 10, ref $b->{ref} eq 'REF_TO_OVER'; -ok 11, "$b->{ref}->{over}" eq "$b"; -ok 12, $b + $b == 314; - -# nfreeze data generated by make_overload.pl -my $f = ''; -if (ord ('A') == 193) { # EBCDIC. - $f = unpack 'u', q{7!084$0S(P>)MUN7%V=/6P<0*!**5EJ8`}; -}else { - $f = unpack 'u', q{7!084$0Q(05-?3U9%4DQ/040*!'-N;W<`}; -} - -# see note at the end of do_retrieve in Storable.xs about why this test has to -# use a reference to an overloaded reference, rather than just a reference. -my $t = eval {thaw $f}; -print "# $@" if $@; -ok 13, $@ eq ""; -ok 14, ref ($t) eq 'REF'; -ok 15, ref ($$t) eq 'HAS_OVERLOAD'; -ok 16, $$$t eq 'snow'; - - -#--- -# blessed reference to overloded object. -{ - my $a = bless [88], 'OVERLOADED'; - my $c = thaw freeze bless \$a, 'main'; - ok 17, ref $c eq 'main'; - ok 18, ref $$c eq 'OVERLOADED'; - ok 19, "$$c" eq "88"; - -} - -1; diff --git a/ext/Storable/t/recurse.t b/ext/Storable/t/recurse.t deleted file mode 100644 index d7dcb0e010..0000000000 --- a/ext/Storable/t/recurse.t +++ /dev/null @@ -1,315 +0,0 @@ -#!./perl -# -# Copyright (c) 1995-2000, Raphael Manfredi -# -# You may redistribute only under the same terms as Perl 5, as specified -# in the README file that comes with the distribution. -# - -sub BEGIN { - unshift @INC, 't'; - require Config; import Config; - if ($ENV{PERL_CORE} and $Config{'extensions'} !~ /\bStorable\b/) { - print "1..0 # Skip: Storable was not built\n"; - exit 0; - } - require 'st-dump.pl'; -} - -sub ok; - -use Storable qw(freeze thaw dclone); - -print "1..33\n"; - -package OBJ_REAL; - -use Storable qw(freeze thaw); - -@x = ('a', 1); - -sub make { bless [], shift } - -sub STORABLE_freeze { - my $self = shift; - my $cloning = shift; - die "STORABLE_freeze" unless Storable::is_storing; - return (freeze(\@x), $self); -} - -sub STORABLE_thaw { - my $self = shift; - my $cloning = shift; - my ($x, $obj) = @_; - die "STORABLE_thaw #1" unless $obj eq $self; - my $len = length $x; - my $a = thaw $x; - die "STORABLE_thaw #2" unless ref $a eq 'ARRAY'; - die "STORABLE_thaw #3" unless @$a == 2 && $a->[0] eq 'a' && $a->[1] == 1; - @$self = @$a; - die "STORABLE_thaw #4" unless Storable::is_retrieving; -} - -package OBJ_SYNC; - -@x = ('a', 1); - -sub make { bless {}, shift } - -sub STORABLE_freeze { - my $self = shift; - my ($cloning) = @_; - return if $cloning; - return ("", \@x, $self); -} - -sub STORABLE_thaw { - my $self = shift; - my ($cloning, $undef, $a, $obj) = @_; - die "STORABLE_thaw #1" unless $obj eq $self; - die "STORABLE_thaw #2" unless ref $a eq 'ARRAY' || @$a != 2; - $self->{ok} = $self; -} - -package OBJ_SYNC2; - -use Storable qw(dclone); - -sub make { - my $self = bless {}, shift; - my ($ext) = @_; - $self->{sync} = OBJ_SYNC->make; - $self->{ext} = $ext; - return $self; -} - -sub STORABLE_freeze { - my $self = shift; - my %copy = %$self; - my $r = \%copy; - my $t = dclone($r->{sync}); - return ("", [$t, $self->{ext}], $r, $self, $r->{ext}); -} - -sub STORABLE_thaw { - my $self = shift; - my ($cloning, $undef, $a, $r, $obj, $ext) = @_; - die "STORABLE_thaw #1" unless $obj eq $self; - die "STORABLE_thaw #2" unless ref $a eq 'ARRAY'; - die "STORABLE_thaw #3" unless ref $r eq 'HASH'; - die "STORABLE_thaw #4" unless $a->[1] == $r->{ext}; - $self->{ok} = $self; - ($self->{sync}, $self->{ext}) = @$a; -} - -package OBJ_REAL2; - -use Storable qw(freeze thaw); - -$MAX = 20; -$recursed = 0; -$hook_called = 0; - -sub make { bless [], shift } - -sub STORABLE_freeze { - my $self = shift; - $hook_called++; - return (freeze($self), $self) if ++$recursed < $MAX; - return ("no", $self); -} - -sub STORABLE_thaw { - my $self = shift; - my $cloning = shift; - my ($x, $obj) = @_; - die "STORABLE_thaw #1" unless $obj eq $self; - $self->[0] = thaw($x) if $x ne "no"; - $recursed--; -} - -package main; - -my $real = OBJ_REAL->make; -my $x = freeze $real; -ok 1, 1; - -my $y = thaw $x; -ok 2, ref $y eq 'OBJ_REAL'; -ok 3, $y->[0] eq 'a'; -ok 4, $y->[1] == 1; - -my $sync = OBJ_SYNC->make; -$x = freeze $sync; -ok 5, 1; - -$y = thaw $x; -ok 6, 1; -ok 7, $y->{ok} == $y; - -my $ext = [1, 2]; -$sync = OBJ_SYNC2->make($ext); -$x = freeze [$sync, $ext]; -ok 8, 1; - -my $z = thaw $x; -$y = $z->[0]; -ok 9, 1; -ok 10, $y->{ok} == $y; -ok 11, ref $y->{sync} eq 'OBJ_SYNC'; -ok 12, $y->{ext} == $z->[1]; - -$real = OBJ_REAL2->make; -$x = freeze $real; -ok 13, 1; -ok 14, $OBJ_REAL2::recursed == $OBJ_REAL2::MAX; -ok 15, $OBJ_REAL2::hook_called == $OBJ_REAL2::MAX; - -$y = thaw $x; -ok 16, 1; -ok 17, $OBJ_REAL2::recursed == 0; - -$x = dclone $real; -ok 18, 1; -ok 19, ref $x eq 'OBJ_REAL2'; -ok 20, $OBJ_REAL2::recursed == 0; -ok 21, $OBJ_REAL2::hook_called == 2 * $OBJ_REAL2::MAX; - -ok 22, !Storable::is_storing; -ok 23, !Storable::is_retrieving; - -# -# The following was a test-case that Salvador Ortiz Garcia <sog@msg.com.mx> -# sent me, along with a proposed fix. -# - -package Foo; - -sub new { - my $class = shift; - my $dat = shift; - return bless {dat => $dat}, $class; -} - -package Bar; -sub new { - my $class = shift; - return bless { - a => 'dummy', - b => [ - Foo->new(1), - Foo->new(2), # Second instance of a Foo - ] - }, $class; -} - -sub STORABLE_freeze { - my($self,$clonning) = @_; - return "$self->{a}", $self->{b}; -} - -sub STORABLE_thaw { - my($self,$clonning,$dummy,$o) = @_; - $self->{a} = $dummy; - $self->{b} = $o; -} - -package main; - -my $bar = new Bar; -my $bar2 = thaw freeze $bar; - -ok 24, ref($bar2) eq 'Bar'; -ok 25, ref($bar->{b}[0]) eq 'Foo'; -ok 26, ref($bar->{b}[1]) eq 'Foo'; -ok 27, ref($bar2->{b}[0]) eq 'Foo'; -ok 28, ref($bar2->{b}[1]) eq 'Foo'; - -# -# The following attempts to make sure blessed objects are blessed ASAP -# at retrieve time. -# - -package CLASS_1; - -sub make { - my $self = bless {}, shift; - return $self; -} - -package CLASS_2; - -sub make { - my $self = bless {}, shift; - my ($o) = @_; - $self->{c1} = CLASS_1->make(); - $self->{o} = $o; - $self->{c3} = bless CLASS_1->make(), "CLASS_3"; - $o->set_c2($self); - return $self; -} - -sub STORABLE_freeze { - my($self, $clonning) = @_; - return "", $self->{c1}, $self->{c3}, $self->{o}; -} - -sub STORABLE_thaw { - my($self, $clonning, $frozen, $c1, $c3, $o) = @_; - main::ok 29, ref $self eq "CLASS_2"; - main::ok 30, ref $c1 eq "CLASS_1"; - main::ok 31, ref $c3 eq "CLASS_3"; - main::ok 32, ref $o eq "CLASS_OTHER"; - $self->{c1} = $c1; - $self->{c3} = $c3; -} - -package CLASS_OTHER; - -sub make { - my $self = bless {}, shift; - return $self; -} - -sub set_c2 { $_[0]->{c2} = $_[1] } - -# -# Is the reference count of the extra references returned from a -# STORABLE_freeze hook correct? [ID 20020601.005] -# -package Foo2; - -sub new { - my $self = bless {}, $_[0]; - $self->{freezed} = "$self"; - return $self; -} - -sub DESTROY { - my $self = shift; - $::refcount_ok = 1 unless "$self" eq $self->{freezed}; -} - -package Foo3; - -sub new { - bless {}, $_[0]; -} - -sub STORABLE_freeze { - my $obj = shift; - return ("", $obj, Foo2->new); -} - -sub STORABLE_thaw { } # Not really used - -package main; -use vars qw($refcount_ok); - -my $o = CLASS_OTHER->make(); -my $c2 = CLASS_2->make($o); -my $so = thaw freeze $o; - -$refcount_ok = 0; -thaw freeze(Foo3->new); -ok 33, $refcount_ok == 1; diff --git a/ext/Storable/t/restrict.t b/ext/Storable/t/restrict.t deleted file mode 100644 index be7f4087f6..0000000000 --- a/ext/Storable/t/restrict.t +++ /dev/null @@ -1,129 +0,0 @@ -#!./perl -w -# -# Copyright 2002, Larry Wall. -# -# You may redistribute only under the same terms as Perl 5, as specified -# in the README file that comes with the distribution. -# - -sub BEGIN { - unshift @INC, 't'; - if ($ENV{PERL_CORE}){ - require Config; - if ($Config::Config{'extensions'} !~ /\bStorable\b/) { - print "1..0 # Skip: Storable was not built\n"; - exit 0; - } - } else { - if ($] < 5.005) { - print "1..0 # Skip: No Hash::Util pre 5.005\n"; - exit 0; - # And doing this seems on 5.004 seems to create bogus warnings about - # unitialized variables, or coredumps in Perl_pp_padsv - } elsif (!eval "require Hash::Util") { - if ($@ =~ /Can\'t locate Hash\/Util\.pm in \@INC/s) { - print "1..0 # Skip: No Hash::Util:\n"; - exit 0; - } else { - die; - } - } - unshift @INC, 't'; - } - require 'st-dump.pl'; -} - - -use Storable qw(dclone freeze thaw); -use Hash::Util qw(lock_hash unlock_value); - -print "1..100\n"; - -my %hash = (question => '?', answer => 42, extra => 'junk', undef => undef); -lock_hash %hash; -unlock_value %hash, 'answer'; -unlock_value %hash, 'extra'; -delete $hash{'extra'}; - -my $test; - -package Restrict_Test; - -sub me_second { - return (undef, $_[0]); -} - -package main; - -sub freeze_thaw { - my $temp = freeze $_[0]; - return thaw $temp; -} - -sub testit { - my $hash = shift; - my $cloner = shift; - my $copy = &$cloner($hash); - - my @in_keys = sort keys %$hash; - my @out_keys = sort keys %$copy; - unless (ok ++$test, "@in_keys" eq "@out_keys") { - print "# Failed: keys mis-match after deep clone.\n"; - print "# Original keys: @in_keys\n"; - print "# Copy's keys: @out_keys\n"; - } - - # $copy = $hash; # used in initial debug of the tests - - ok ++$test, Internals::SvREADONLY(%$copy), "cloned hash restricted?"; - - ok ++$test, Internals::SvREADONLY($copy->{question}), - "key 'question' not locked in copy?"; - - ok ++$test, !Internals::SvREADONLY($copy->{answer}), - "key 'answer' not locked in copy?"; - - eval { $copy->{extra} = 15 } ; - unless (ok ++$test, !$@, "Can assign to reserved key 'extra'?") { - my $diag = $@; - $diag =~ s/\n.*\z//s; - print "# \$\@: $diag\n"; - } - - eval { $copy->{nono} = 7 } ; - ok ++$test, $@, "Can not assign to invalid key 'nono'?"; - - ok ++$test, exists $copy->{undef}, - "key 'undef' exists"; - - ok ++$test, !defined $copy->{undef}, - "value for key 'undef' is undefined"; -} - -for $Storable::canonical (0, 1) { - for my $cloner (\&dclone, \&freeze_thaw) { - print "# \$Storable::canonical = $Storable::canonical\n"; - testit (\%hash, $cloner); - my $object = \%hash; - # bless {}, "Restrict_Test"; - - my %hash2; - $hash2{"k$_"} = "v$_" for 0..16; - lock_hash %hash2; - for (0..16) { - unlock_value %hash2, "k$_"; - delete $hash2{"k$_"}; - } - my $copy = &$cloner(\%hash2); - - for (0..16) { - my $k = "k$_"; - eval { $copy->{$k} = undef } ; - unless (ok ++$test, !$@, "Can assign to reserved key '$k'?") { - my $diag = $@; - $diag =~ s/\n.*\z//s; - print "# \$\@: $diag\n"; - } - } - } -} diff --git a/ext/Storable/t/retrieve.t b/ext/Storable/t/retrieve.t deleted file mode 100644 index 2e44d5d7cb..0000000000 --- a/ext/Storable/t/retrieve.t +++ /dev/null @@ -1,69 +0,0 @@ -#!./perl -# -# Copyright (c) 1995-2000, Raphael Manfredi -# -# You may redistribute only under the same terms as Perl 5, as specified -# in the README file that comes with the distribution. -# - -sub BEGIN { - unshift @INC, 't'; - require Config; import Config; - if ($ENV{PERL_CORE} and $Config{'extensions'} !~ /\bStorable\b/) { - print "1..0 # Skip: Storable was not built\n"; - exit 0; - } - require 'st-dump.pl'; -} - - -use Storable qw(store retrieve nstore); - -print "1..14\n"; - -$a = 'toto'; -$b = \$a; -$c = bless {}, CLASS; -$c->{attribute} = 'attrval'; -%a = ('key', 'value', 1, 0, $a, $b, 'cvar', \$c); -@a = ('first', '', undef, 3, -4, -3.14159, 456, 4.5, - $b, \$a, $a, $c, \$c, \%a); - -print "not " unless defined store(\@a, 'store'); -print "ok 1\n"; -print "not " if Storable::last_op_in_netorder(); -print "ok 2\n"; -print "not " unless defined nstore(\@a, 'nstore'); -print "ok 3\n"; -print "not " unless Storable::last_op_in_netorder(); -print "ok 4\n"; -print "not " unless Storable::last_op_in_netorder(); -print "ok 5\n"; - -$root = retrieve('store'); -print "not " unless defined $root; -print "ok 6\n"; -print "not " if Storable::last_op_in_netorder(); -print "ok 7\n"; - -$nroot = retrieve('nstore'); -print "not " unless defined $nroot; -print "ok 8\n"; -print "not " unless Storable::last_op_in_netorder(); -print "ok 9\n"; - -$d1 = &dump($root); -print "ok 10\n"; -$d2 = &dump($nroot); -print "ok 11\n"; - -print "not " unless $d1 eq $d2; -print "ok 12\n"; - -# Make sure empty string is defined at retrieval time -print "not " unless defined $root->[1]; -print "ok 13\n"; -print "not " if length $root->[1]; -print "ok 14\n"; - -END { 1 while unlink('store', 'nstore') } diff --git a/ext/Storable/t/sig_die.t b/ext/Storable/t/sig_die.t deleted file mode 100644 index d2390a7621..0000000000 --- a/ext/Storable/t/sig_die.t +++ /dev/null @@ -1,39 +0,0 @@ -#!./perl -# -# Copyright (c) 2002 Slaven Rezic -# -# You may redistribute only under the same terms as Perl 5, as specified -# in the README file that comes with the distribution. -# - -sub BEGIN { - unshift @INC, 't'; - require Config; import Config; - if ($ENV{PERL_CORE} and $Config{'extensions'} !~ /\bStorable\b/) { - print "1..0 # Skip: Storable was not built\n"; - exit 0; - } -} - -use strict; -BEGIN { - if (!eval q{ - use Test::More; - 1; - }) { - print "1..0 # skip: tests only work with Test::More\n"; - exit; - } -} - -BEGIN { plan tests => 1 } - -my @warns; -$SIG{__WARN__} = sub { push @warns, shift }; -$SIG{__DIE__} = sub { require Carp; warn Carp::longmess(); warn "Evil die!" }; - -require Storable; - -Storable::dclone({foo => "bar"}); - -is(join("", @warns), "", "__DIE__ is not evil here"); diff --git a/ext/Storable/t/st-dump.pl b/ext/Storable/t/st-dump.pl deleted file mode 100644 index 152b85a101..0000000000 --- a/ext/Storable/t/st-dump.pl +++ /dev/null @@ -1,165 +0,0 @@ -# -# Copyright (c) 1995-2000, Raphael Manfredi -# -# You may redistribute only under the same terms as Perl 5, as specified -# in the README file that comes with the distribution. -# - -# NOTE THAT THIS FILE IS COPIED FROM ext/Storable/t/st-dump.pl -# TO t/lib/st-dump.pl. One could also play games with -# File::Spec->updir and catdir to get the st-dump.pl in -# ext/Storable into @INC. - -sub ok { - my ($num, $ok, $name) = @_; - $num .= " - $name" if defined $name and length $name; - print $ok ? "ok $num\n" : "not ok $num\n"; - $ok; -} - -sub num_equal { - my ($num, $left, $right, $name) = @_; - my $ok = ((defined $left) ? $left == $right : undef); - unless (ok ($num, $ok, $name)) { - print "# Expected $right\n"; - if (!defined $left) { - print "# Got undef\n"; - } elsif ($left !~ tr/0-9//c) { - print "# Got $left\n"; - } else { - $left =~ s/([^-a-zA-Z0-9_+])/sprintf "\\%03o", ord $1/ge; - print "# Got \"$left\"\n"; - } - } - $ok; -} - -package dump; -use Carp; - -%dump = ( - 'SCALAR' => 'dump_scalar', - 'LVALUE' => 'dump_scalar', - 'ARRAY' => 'dump_array', - 'HASH' => 'dump_hash', - 'REF' => 'dump_ref', -); - -# Given an object, dump its transitive data closure -sub main'dump { - my ($object) = @_; - croak "Not a reference!" unless ref($object); - local %dumped; - local %object; - local $count = 0; - local $dumped = ''; - &recursive_dump($object, 1); - return $dumped; -} - -# This is the root recursive dumping routine that may indirectly be -# called by one of the routine it calls... -# The link parameter is set to false when the reference passed to -# the routine is an internal temporay variable, implying the object's -# address is not to be dumped in the %dumped table since it's not a -# user-visible object. -sub recursive_dump { - my ($object, $link) = @_; - - # Get something like SCALAR(0x...) or TYPE=SCALAR(0x...). - # Then extract the bless, ref and address parts of that string. - - my $what = "$object"; # Stringify - my ($bless, $ref, $addr) = $what =~ /^(\w+)=(\w+)\((0x.*)\)$/; - ($ref, $addr) = $what =~ /^(\w+)\((0x.*)\)$/ unless $bless; - - # Special case for references to references. When stringified, - # they appear as being scalars. However, ref() correctly pinpoints - # them as being references indirections. And that's it. - - $ref = 'REF' if ref($object) eq 'REF'; - - # Make sure the object has not been already dumped before. - # We don't want to duplicate data. Retrieval will know how to - # relink from the previously seen object. - - if ($link && $dumped{$addr}++) { - my $num = $object{$addr}; - $dumped .= "OBJECT #$num seen\n"; - return; - } - - my $objcount = $count++; - $object{$addr} = $objcount; - - # Call the appropriate dumping routine based on the reference type. - # If the referenced was blessed, we bless it once the object is dumped. - # The retrieval code will perform the same on the last object retrieved. - - croak "Unknown simple type '$ref'" unless defined $dump{$ref}; - - &{$dump{$ref}}($object); # Dump object - &bless($bless) if $bless; # Mark it as blessed, if necessary - - $dumped .= "OBJECT $objcount\n"; -} - -# Indicate that current object is blessed -sub bless { - my ($class) = @_; - $dumped .= "BLESS $class\n"; -} - -# Dump single scalar -sub dump_scalar { - my ($sref) = @_; - my $scalar = $$sref; - unless (defined $scalar) { - $dumped .= "UNDEF\n"; - return; - } - my $len = length($scalar); - $dumped .= "SCALAR len=$len $scalar\n"; -} - -# Dump array -sub dump_array { - my ($aref) = @_; - my $items = 0 + @{$aref}; - $dumped .= "ARRAY items=$items\n"; - foreach $item (@{$aref}) { - unless (defined $item) { - $dumped .= 'ITEM_UNDEF' . "\n"; - next; - } - $dumped .= 'ITEM '; - &recursive_dump(\$item, 1); - } -} - -# Dump hash table -sub dump_hash { - my ($href) = @_; - my $items = scalar(keys %{$href}); - $dumped .= "HASH items=$items\n"; - foreach $key (sort keys %{$href}) { - $dumped .= 'KEY '; - &recursive_dump(\$key, undef); - unless (defined $href->{$key}) { - $dumped .= 'VALUE_UNDEF' . "\n"; - next; - } - $dumped .= 'VALUE '; - &recursive_dump(\$href->{$key}, 1); - } -} - -# Dump reference to reference -sub dump_ref { - my ($rref) = @_; - my $deref = $$rref; # Follow reference to reference - $dumped .= 'REF '; - &recursive_dump($deref, 1); # $dref is a reference -} - -1; diff --git a/ext/Storable/t/store.t b/ext/Storable/t/store.t deleted file mode 100644 index 61bcf7d49b..0000000000 --- a/ext/Storable/t/store.t +++ /dev/null @@ -1,109 +0,0 @@ -#!./perl -# -# Copyright (c) 1995-2000, Raphael Manfredi -# -# You may redistribute only under the same terms as Perl 5, as specified -# in the README file that comes with the distribution. -# - -sub BEGIN { - unshift @INC, 't'; - require Config; import Config; - if ($ENV{PERL_CORE} and $Config{'extensions'} !~ /\bStorable\b/) { - print "1..0 # Skip: Storable was not built\n"; - exit 0; - } - require 'st-dump.pl'; -} - -use Storable qw(store retrieve store_fd nstore_fd fd_retrieve); - -print "1..20\n"; - -$a = 'toto'; -$b = \$a; -$c = bless {}, CLASS; -$c->{attribute} = 'attrval'; -%a = ('key', 'value', 1, 0, $a, $b, 'cvar', \$c); -@a = ('first', undef, 3, -4, -3.14159, 456, 4.5, - $b, \$a, $a, $c, \$c, \%a); - -print "not " unless defined store(\@a, 'store'); -print "ok 1\n"; - -$dumped = &dump(\@a); -print "ok 2\n"; - -$root = retrieve('store'); -print "not " unless defined $root; -print "ok 3\n"; - -$got = &dump($root); -print "ok 4\n"; - -print "not " unless $got eq $dumped; -print "ok 5\n"; - -1 while unlink 'store'; - -package FOO; @ISA = qw(Storable); - -sub make { - my $self = bless {}; - $self->{key} = \%main::a; - return $self; -}; - -package main; - -$foo = FOO->make; -print "not " unless $foo->store('store'); -print "ok 6\n"; - -print "not " unless open(OUT, '>>store'); -print "ok 7\n"; -binmode OUT; - -print "not " unless defined store_fd(\@a, ::OUT); -print "ok 8\n"; -print "not " unless defined nstore_fd($foo, ::OUT); -print "ok 9\n"; -print "not " unless defined nstore_fd(\%a, ::OUT); -print "ok 10\n"; - -print "not " unless close(OUT); -print "ok 11\n"; - -print "not " unless open(OUT, 'store'); -binmode OUT; - -$r = fd_retrieve(::OUT); -print "not " unless defined $r; -print "ok 12\n"; -print "not " unless &dump($foo) eq &dump($r); -print "ok 13\n"; - -$r = fd_retrieve(::OUT); -print "not " unless defined $r; -print "ok 14\n"; -print "not " unless &dump(\@a) eq &dump($r); -print "ok 15\n"; - -$r = fd_retrieve(main::OUT); -print "not " unless defined $r; -print "ok 16\n"; -print "not " unless &dump($foo) eq &dump($r); -print "ok 17\n"; - -$r = fd_retrieve(::OUT); -print "not " unless defined $r; -print "ok 18\n"; -print "not " unless &dump(\%a) eq &dump($r); -print "ok 19\n"; - -eval { $r = fd_retrieve(::OUT); }; -print "not " unless $@; -print "ok 20\n"; - -close OUT or die "Could not close: $!"; -END { 1 while unlink 'store' } diff --git a/ext/Storable/t/testlib.pl b/ext/Storable/t/testlib.pl deleted file mode 100644 index 6d885d7f68..0000000000 --- a/ext/Storable/t/testlib.pl +++ /dev/null @@ -1,38 +0,0 @@ -#!perl -w -use strict; -use vars '$file'; - -$file = "storable-testfile.$$"; -die "Temporary file '$file' already exists" if -e $file; - -END { while (-f $file) {unlink $file or die "Can't unlink '$file': $!" }} - -use Storable qw (store retrieve freeze thaw nstore nfreeze); - -sub slurp { - my $file = shift; - local (*FH, $/); - open FH, "<$file" or die "Can't open '$file': $!"; - binmode FH; - my $contents = <FH>; - die "Can't read $file: $!" unless defined $contents; - return $contents; -} - -sub store_and_retrieve { - my $data = shift; - unlink $file or die "Can't unlink '$file': $!"; - open FH, ">$file" or die "Can't open '$file': $!"; - binmode FH; - print FH $data or die "Can't print to '$file': $!"; - close FH or die "Can't close '$file': $!"; - - return eval {retrieve $file}; -} - -sub freeze_and_thaw { - my $data = shift; - return eval {thaw $data}; -} - -$file; diff --git a/ext/Storable/t/threads.t b/ext/Storable/t/threads.t deleted file mode 100644 index 72efdda334..0000000000 --- a/ext/Storable/t/threads.t +++ /dev/null @@ -1,57 +0,0 @@ - -# as of 2.09 on win32 Storable w/threads dies with "free to wrong -# pool" since it uses the same context for different threads. since -# win32 perl implementation allocates a different memory pool for each -# thread using the a memory pool from one thread to allocate memory -# for another thread makes win32 perl very unhappy -# -# but the problem exists everywhere, not only on win32 perl , it's -# just hard to catch it deterministically - since the same context is -# used if two or more threads happen to change the state of the -# context in the middle of the operation, and those operations aren't -# atomic per thread, bad things including data loss and corrupted data -# can happen. -# -# this has been solved in 2.10 by adding a Storable::CLONE which calls -# Storable::init_perinterp() to create a new context for each new -# thread when it starts - -sub BEGIN { - unshift @INC, 't'; - require Config; import Config; - if ($ENV{PERL_CORE} and $Config{'extensions'} !~ /\bStorable\b/) { - print "1..0 # Skip: Storable was not built\n"; - exit 0; - } - unless ($Config{'useithreads'} and eval { require threads; 1 }) { - print "1..0 # Skip: no threads\n"; - exit 0; - } - # - is \W, so can't use \b at start. Negative look ahead and look behind - # works at start/end of string, or where preceded/followed by spaces - if ($] == 5.008002 and eval q{ $Config{'ccflags'} =~ /(?<!\S)-DDEBUGGING(?!\S)/ }) { - # Bug caused by change 21610, fixed by change 21849 - print "1..0 # Skip: tickles bug in threads combined with -DDEBUGGING on 5.8.2\n"; - exit 0; - } -} - -use Test::More; - -use strict; - -use threads; -use Storable qw(nfreeze); - -plan tests => 2; - -threads->new(\&sub1); - -$_->join() for threads->list(); - -ok 1; - -sub sub1 { - nfreeze {}; - ok 1; -} diff --git a/ext/Storable/t/tied.t b/ext/Storable/t/tied.t deleted file mode 100644 index 9a7f5711da..0000000000 --- a/ext/Storable/t/tied.t +++ /dev/null @@ -1,215 +0,0 @@ -#!./perl -# -# Copyright (c) 1995-2000, Raphael Manfredi -# -# You may redistribute only under the same terms as Perl 5, as specified -# in the README file that comes with the distribution. -# - -sub BEGIN { - unshift @INC, 't'; - require Config; import Config; - if ($ENV{PERL_CORE} and $Config{'extensions'} !~ /\bStorable\b/) { - print "1..0 # Skip: Storable was not built\n"; - exit 0; - } - require 'st-dump.pl'; -} - -sub ok; - -use Storable qw(freeze thaw); - -print "1..23\n"; - -($scalar_fetch, $array_fetch, $hash_fetch) = (0, 0, 0); - -package TIED_HASH; - -sub TIEHASH { - my $self = bless {}, shift; - return $self; -} - -sub FETCH { - my $self = shift; - my ($key) = @_; - $main::hash_fetch++; - return $self->{$key}; -} - -sub STORE { - my $self = shift; - my ($key, $value) = @_; - $self->{$key} = $value; -} - -sub FIRSTKEY { - my $self = shift; - scalar keys %{$self}; - return each %{$self}; -} - -sub NEXTKEY { - my $self = shift; - return each %{$self}; -} - -package TIED_ARRAY; - -sub TIEARRAY { - my $self = bless [], shift; - return $self; -} - -sub FETCH { - my $self = shift; - my ($idx) = @_; - $main::array_fetch++; - return $self->[$idx]; -} - -sub STORE { - my $self = shift; - my ($idx, $value) = @_; - $self->[$idx] = $value; -} - -sub FETCHSIZE { - my $self = shift; - return @{$self}; -} - -package TIED_SCALAR; - -sub TIESCALAR { - my $scalar; - my $self = bless \$scalar, shift; - return $self; -} - -sub FETCH { - my $self = shift; - $main::scalar_fetch++; - return $$self; -} - -sub STORE { - my $self = shift; - my ($value) = @_; - $$self = $value; -} - -package FAULT; - -$fault = 0; - -sub TIESCALAR { - my $pkg = shift; - return bless [@_], $pkg; -} - -sub FETCH { - my $self = shift; - my ($href, $key) = @$self; - $fault++; - untie $href->{$key}; - return $href->{$key} = 1; -} - -package main; - -$a = 'toto'; -$b = \$a; - -$c = tie %hash, TIED_HASH; -$d = tie @array, TIED_ARRAY; -tie $scalar, TIED_SCALAR; - -#$scalar = 'foo'; -#$hash{'attribute'} = \$d; -#$array[0] = $c; -#$array[1] = \$scalar; - -### If I say -### $hash{'attribute'} = $d; -### below, then dump() incorectly dumps the hash value as a string the second -### time it is reached. I have not investigated enough to tell whether it's -### a bug in my dump() routine or in the Perl tieing mechanism. -$scalar = 'foo'; -$hash{'attribute'} = 'plain value'; -$array[0] = \$scalar; -$array[1] = $c; -$array[2] = \@array; - -@tied = (\$scalar, \@array, \%hash); -%a = ('key', 'value', 1, 0, $a, $b, 'cvar', \$a, 'scalarref', \$scalar); -@a = ('first', 3, -4, -3.14159, 456, 4.5, $d, \$d, - $b, \$a, $a, $c, \$c, \%a, \@array, \%hash, \@tied); - -ok 1, defined($f = freeze(\@a)); - -$dumped = &dump(\@a); -ok 2, 1; - -$root = thaw($f); -ok 3, defined $root; - -$got = &dump($root); -ok 4, 1; - -### Used to see the manifestation of the bug documented above. -### print "original: $dumped"; -### print "--------\n"; -### print "got: $got"; -### print "--------\n"; - -ok 5, $got eq $dumped; - -$g = freeze($root); -ok 6, length($f) == length($g); - -# Ensure the tied items in the retrieved image work -@old = ($scalar_fetch, $array_fetch, $hash_fetch); -@tied = ($tscalar, $tarray, $thash) = @{$root->[$#{$root}]}; -@type = qw(SCALAR ARRAY HASH); - -ok 7, tied $$tscalar; -ok 8, tied @{$tarray}; -ok 9, tied %{$thash}; - -@new = ($$tscalar, $tarray->[0], $thash->{'attribute'}); -@new = ($scalar_fetch, $array_fetch, $hash_fetch); - -# Tests 10..15 -for ($i = 0; $i < @new; $i++) { - print "not " unless $new[$i] == $old[$i] + 1; - printf "ok %d\n", 10 + 2*$i; # Tests 10,12,14 - print "not " unless ref $tied[$i] eq $type[$i]; - printf "ok %d\n", 11 + 2*$i; # Tests 11,13,15 -} - -# Check undef ties -my $h = {}; -tie $h->{'x'}, 'FAULT', $h, 'x'; -my $hf = freeze($h); -ok 16, defined $hf; -ok 17, $FAULT::fault == 0; -ok 18, $h->{'x'} == 1; -ok 19, $FAULT::fault == 1; - -my $ht = thaw($hf); -ok 20, defined $ht; -ok 21, $ht->{'x'} == 1; -ok 22, $FAULT::fault == 2; - -{ - package P; - use Storable qw(freeze thaw); - use vars qw($a $b); - $b = "not ok "; - sub TIESCALAR { bless \$a } sub FETCH { "ok " } - tie $a, P; my $r = thaw freeze \$a; $b = $$r; - print $b , 23, "\n"; -} - diff --git a/ext/Storable/t/tied_hook.t b/ext/Storable/t/tied_hook.t deleted file mode 100644 index 8f2846ed6e..0000000000 --- a/ext/Storable/t/tied_hook.t +++ /dev/null @@ -1,242 +0,0 @@ -#!./perl -# -# Copyright (c) 1995-2000, Raphael Manfredi -# -# You may redistribute only under the same terms as Perl 5, as specified -# in the README file that comes with the distribution. -# - -sub BEGIN { - unshift @INC, 't'; - require Config; import Config; - if ($ENV{PERL_CORE} and $Config{'extensions'} !~ /\bStorable\b/) { - print "1..0 # Skip: Storable was not built\n"; - exit 0; - } - require 'st-dump.pl'; -} - -sub ok; - -use Storable qw(freeze thaw); - -print "1..25\n"; - -($scalar_fetch, $array_fetch, $hash_fetch) = (0, 0, 0); - -package TIED_HASH; - -sub TIEHASH { - my $self = bless {}, shift; - return $self; -} - -sub FETCH { - my $self = shift; - my ($key) = @_; - $main::hash_fetch++; - return $self->{$key}; -} - -sub STORE { - my $self = shift; - my ($key, $value) = @_; - $self->{$key} = $value; -} - -sub FIRSTKEY { - my $self = shift; - scalar keys %{$self}; - return each %{$self}; -} - -sub NEXTKEY { - my $self = shift; - return each %{$self}; -} - -sub STORABLE_freeze { - my $self = shift; - $main::hash_hook1++; - return join(":", keys %$self) . ";" . join(":", values %$self); -} - -sub STORABLE_thaw { - my ($self, $cloning, $frozen) = @_; - my ($keys, $values) = split(/;/, $frozen); - my @keys = split(/:/, $keys); - my @values = split(/:/, $values); - for (my $i = 0; $i < @keys; $i++) { - $self->{$keys[$i]} = $values[$i]; - } - $main::hash_hook2++; -} - -package TIED_ARRAY; - -sub TIEARRAY { - my $self = bless [], shift; - return $self; -} - -sub FETCH { - my $self = shift; - my ($idx) = @_; - $main::array_fetch++; - return $self->[$idx]; -} - -sub STORE { - my $self = shift; - my ($idx, $value) = @_; - $self->[$idx] = $value; -} - -sub FETCHSIZE { - my $self = shift; - return @{$self}; -} - -sub STORABLE_freeze { - my $self = shift; - $main::array_hook1++; - return join(":", @$self); -} - -sub STORABLE_thaw { - my ($self, $cloning, $frozen) = @_; - @$self = split(/:/, $frozen); - $main::array_hook2++; -} - -package TIED_SCALAR; - -sub TIESCALAR { - my $scalar; - my $self = bless \$scalar, shift; - return $self; -} - -sub FETCH { - my $self = shift; - $main::scalar_fetch++; - return $$self; -} - -sub STORE { - my $self = shift; - my ($value) = @_; - $$self = $value; -} - -sub STORABLE_freeze { - my $self = shift; - $main::scalar_hook1++; - return $$self; -} - -sub STORABLE_thaw { - my ($self, $cloning, $frozen) = @_; - $$self = $frozen; - $main::scalar_hook2++; -} - -package main; - -$a = 'toto'; -$b = \$a; - -$c = tie %hash, TIED_HASH; -$d = tie @array, TIED_ARRAY; -tie $scalar, TIED_SCALAR; - -$scalar = 'foo'; -$hash{'attribute'} = 'plain value'; -$array[0] = \$scalar; -$array[1] = $c; -$array[2] = \@array; -$array[3] = "plaine scalaire"; - -@tied = (\$scalar, \@array, \%hash); -%a = ('key', 'value', 1, 0, $a, $b, 'cvar', \$a, 'scalarref', \$scalar); -@a = ('first', 3, -4, -3.14159, 456, 4.5, $d, \$d, - $b, \$a, $a, $c, \$c, \%a, \@array, \%hash, \@tied); - -ok 1, defined($f = freeze(\@a)); - -$dumped = &dump(\@a); -ok 2, 1; - -$root = thaw($f); -ok 3, defined $root; - -$got = &dump($root); -ok 4, 1; - -ok 5, $got ne $dumped; # our hooks did not handle refs in array - -$g = freeze($root); -ok 6, length($f) == length($g); - -# Ensure the tied items in the retrieved image work -@old = ($scalar_fetch, $array_fetch, $hash_fetch); -@tied = ($tscalar, $tarray, $thash) = @{$root->[$#{$root}]}; -@type = qw(SCALAR ARRAY HASH); - -ok 7, tied $$tscalar; -ok 8, tied @{$tarray}; -ok 9, tied %{$thash}; - -@new = ($$tscalar, $tarray->[0], $thash->{'attribute'}); -@new = ($scalar_fetch, $array_fetch, $hash_fetch); - -# Tests 10..15 -for ($i = 0; $i < @new; $i++) { - ok 10 + 2*$i, $new[$i] == $old[$i] + 1; # Tests 10,12,14 - ok 11 + 2*$i, ref $tied[$i] eq $type[$i]; # Tests 11,13,15 -} - -ok 16, $$tscalar eq 'foo'; -ok 17, $tarray->[3] eq 'plaine scalaire'; -ok 18, $thash->{'attribute'} eq 'plain value'; - -# Ensure hooks were called -ok 19, ($scalar_hook1 && $scalar_hook2); -ok 20, ($array_hook1 && $array_hook2); -ok 21, ($hash_hook1 && $hash_hook2); - -# -# And now for the "blessed ref to tied hash" with "store hook" test... -# - -my $bc = bless \%hash, 'FOO'; # FOO does not exist -> no hook -my $bx = thaw freeze $bc; - -ok 22, ref $bx eq 'FOO'; -my $old_hash_fetch = $hash_fetch; -my $v = $bx->{attribute}; -ok 23, $hash_fetch == $old_hash_fetch + 1; # Still tied - -package TIED_HASH_REF; - - -sub STORABLE_freeze { - my ($self, $cloning) = @_; - return if $cloning; - return('ref lost'); -} - -sub STORABLE_thaw { - my ($self, $cloning, $data) = @_; - return if $cloning; -} - -package main; - -$bc = bless \%hash, 'TIED_HASH_REF'; -$bx = thaw freeze $bc; - -ok 24, ref $bx eq 'TIED_HASH_REF'; -$old_hash_fetch = $hash_fetch; -$v = $bx->{attribute}; -ok 25, $hash_fetch == $old_hash_fetch + 1; # Still tied diff --git a/ext/Storable/t/tied_items.t b/ext/Storable/t/tied_items.t deleted file mode 100644 index bd15e5cc4f..0000000000 --- a/ext/Storable/t/tied_items.t +++ /dev/null @@ -1,59 +0,0 @@ -#!./perl -# -# Copyright (c) 1995-2000, Raphael Manfredi -# -# You may redistribute only under the same terms as Perl 5, as specified -# in the README file that comes with the distribution. -# - -# -# Tests ref to items in tied hash/array structures. -# - -sub BEGIN { - unshift @INC, 't'; - require Config; import Config; - if ($ENV{PERL_CORE} and $Config{'extensions'} !~ /\bStorable\b/) { - print "1..0 # Skip: Storable was not built\n"; - exit 0; - } - require 'st-dump.pl'; -} - -sub ok; -$^W = 0; - -print "1..8\n"; - -use Storable qw(dclone); - -$h_fetches = 0; - -sub H::TIEHASH { bless \(my $x), "H" } -sub H::FETCH { $h_fetches++; $_[1] - 70 } - -tie %h, "H"; - -$ref = \$h{77}; -$ref2 = dclone $ref; - -ok 1, $h_fetches == 0; -ok 2, $$ref2 eq $$ref; -ok 3, $$ref2 == 7; -ok 4, $h_fetches == 2; - -$a_fetches = 0; - -sub A::TIEARRAY { bless \(my $x), "A" } -sub A::FETCH { $a_fetches++; $_[1] - 70 } - -tie @a, "A"; - -$ref = \$a[78]; -$ref2 = dclone $ref; - -ok 5, $a_fetches == 0; -ok 6, $$ref2 eq $$ref; -ok 7, $$ref2 == 8; -# I don't understand why it's 3 and not 2 -ok 8, $a_fetches == 3; diff --git a/ext/Storable/t/utf8.t b/ext/Storable/t/utf8.t deleted file mode 100644 index 67b79170e5..0000000000 --- a/ext/Storable/t/utf8.t +++ /dev/null @@ -1,58 +0,0 @@ - -#!./perl -w -# -# Copyright (c) 1995-2000, Raphael Manfredi -# -# You may redistribute only under the same terms as Perl 5, as specified -# in the README file that comes with the distribution. -# - -sub BEGIN { - if ($] < 5.006) { - print "1..0 # Skip: no utf8 support\n"; - exit 0; - } - unshift @INC, 't'; - require Config; import Config; - if ($ENV{PERL_CORE} and $Config{'extensions'} !~ /\bStorable\b/) { - print "1..0 # Skip: Storable was not built\n"; - exit 0; - } - require 'st-dump.pl'; -} - -use strict; -sub ok; - -use Storable qw(thaw freeze); - -print "1..6\n"; - -my $x = chr(1234); -ok 1, $x eq ${thaw freeze \$x}; - -# Long scalar -$x = join '', map {chr $_} (0..1023); -ok 2, $x eq ${thaw freeze \$x}; - -# Char in the range 127-255 (probably) in utf8 -$x = chr (175) . chr (256); -chop $x; -ok 3, $x eq ${thaw freeze \$x}; - -# Storable needs to cope if a frozen string happens to be internall utf8 -# encoded - -$x = chr 256; -my $data = freeze \$x; -ok 4, $x eq ${thaw $data}; - -$data .= chr 256; -chop $data; -ok 5, $x eq ${thaw $data}; - - -$data .= chr 256; -# This definately isn't valid -eval {thaw $data}; -ok 6, $@ =~ /corrupt.*characters outside/; diff --git a/ext/Storable/t/utf8hash.t b/ext/Storable/t/utf8hash.t deleted file mode 100644 index 7eac651c6e..0000000000 --- a/ext/Storable/t/utf8hash.t +++ /dev/null @@ -1,197 +0,0 @@ -#!./perl - -sub BEGIN { - if ($] < 5.007) { - print "1..0 # Skip: no utf8 hash key support\n"; - exit 0; - } - unshift @INC, 't'; - require Config; import Config; - if ($ENV{PERL_CORE}){ - if($Config{'extensions'} !~ /\bStorable\b/) { - print "1..0 # Skip: Storable was not built\n"; - exit 0; - } - } -} - -use strict; -our $DEBUGME = shift || 0; -use Storable qw(store nstore retrieve thaw freeze); -{ - no warnings; - $Storable::DEBUGME = ($DEBUGME > 1); -} -# Better than no plan, because I was getting out of memory errors, at which -# point Test::More tidily prints up 1..79 as if I meant to finish there. -use Test::More tests=>144; -use bytes (); -my %utf8hash; - -$Storable::canonical = $Storable::canonical; # Shut up a used only once warning. - -for $Storable::canonical (0, 1) { - -# first we generate a nasty hash which keys include both utf8 -# on and off with identical PVs - -no utf8; # we have a naked 8-bit byte below (in Latin 1, anyway) - -# In Latin 1 -ese the below ord() should end up 0xc0 (192), -# in EBCDIC 0x64 (100). Both should end up being UTF-8/UTF-EBCDIC. -my @ords = ( - ord("Á"), # LATIN CAPITAL LETTER A WITH GRAVE - 0x3000, #IDEOGRAPHIC SPACE - ); - -foreach my $i (@ords){ - my $u = chr($i); utf8::upgrade($u); - # warn sprintf "%d,%d", bytes::length($u), is_utf8($u); - my $b = chr($i); utf8::encode($b); - # warn sprintf "%d,%d" ,bytes::length($b), is_utf8($b); - - isnt($u, $b, "equivalence - with utf8flag"); - - $utf8hash{$u} = $utf8hash{$b} = $i; -} - -sub nkeys($){ - my $href = shift; - return scalar keys %$href; -} - -my $nk; -is($nk = nkeys(\%utf8hash), scalar(@ords)*2, - "nasty hash generated (nkeys=$nk)"); - -# now let the show begin! - -my $thawed = thaw(freeze(\%utf8hash)); - -is($nk = nkeys($thawed), - nkeys(\%utf8hash), - "scalar keys \%{\$thawed} (nkeys=$nk)"); -for my $k (sort keys %$thawed){ - is($utf8hash{$k}, $thawed->{$k}, "frozen item chr($utf8hash{$k})"); -} - -my $storage = "utfhash.po"; # po = perl object! -my $retrieved; - -ok((nstore \%utf8hash, $storage), "nstore to $storage"); -ok(($retrieved = retrieve($storage)), "retrieve from $storage"); - -is($nk = nkeys($retrieved), - nkeys(\%utf8hash), - "scalar keys \%{\$retrieved} (nkeys=$nk)"); -for my $k (sort keys %$retrieved){ - is($utf8hash{$k}, $retrieved->{$k}, "nstored item chr($utf8hash{$k})"); -} -unlink $storage; - - -ok((store \%utf8hash, $storage), "store to $storage"); -ok(($retrieved = retrieve($storage)), "retrieve from $storage"); -is($nk = nkeys($retrieved), - nkeys(\%utf8hash), - "scalar keys \%{\$retrieved} (nkeys=$nk)"); -for my $k (sort keys %$retrieved){ - is($utf8hash{$k}, $retrieved->{$k}, "stored item chr($utf8hash{$k})"); -} -$DEBUGME or unlink $storage; - -# On the premis that more tests are good, here are NWC's tests: - -package Hash_Test; - -sub me_second { - return (undef, $_[0]); -} - -package main; - -my $utf8 = "Schlo\xdf" . chr 256; -chop $utf8; - -# Set this to 1 to test the test by bypassing Storable. -my $bypass = 0; - -sub class_test { - my ($object, $package) = @_; - unless ($package) { - is ref $object, 'HASH', "$object is unblessed"; - return; - } - isa_ok ($object, $package); - my ($garbage, $copy) = eval {$object->me_second}; - is $@, "", "check it has correct method"; - cmp_ok $copy, '==', $object, "and that it returns the same object"; -} - -# Thanks to Dan Kogai for the Kanji for "castle" (which he informs me also -# means 'a city' in Mandarin). -my %hash = (map {$_, $_} 'castle', "ch\xe5teau", $utf8, "\x{57CE}"); - -for my $package ('', 'Hash_Test') { - # Run through and sanity check these. - if ($package) { - bless \%hash, $package; - } - for (keys %hash) { - my $l = 0 + /^\w+$/; - my $r = 0 + $hash{$_} =~ /^\w+$/; - cmp_ok ($l, '==', $r); - } - - # Grr. This cperl mode thinks that ${ is a punctuation variable. - # I presume it's punishment for using xemacs rather than emacs. Or OS/2 :-) - my $copy = $bypass ? \%hash : ${thaw freeze \\%hash}; - class_test ($copy, $package); - - for (keys %$copy) { - my $l = 0 + /^\w+$/; - my $r = 0 + $copy->{$_} =~ /^\w+$/; - cmp_ok ($l, '==', $r, sprintf "key length %d", length $_); - } - - - my $bytes = my $char = chr 27182; - utf8::encode ($bytes); - - my $orig = {$char => 1}; - if ($package) { - bless $orig, $package; - } - my $just_utf8 = $bypass ? $orig : ${thaw freeze \$orig}; - class_test ($just_utf8, $package); - cmp_ok (scalar keys %$just_utf8, '==', 1, "1 key in utf8?"); - cmp_ok ($just_utf8->{$char}, '==', 1, "utf8 key present?"); - ok (!exists $just_utf8->{$bytes}, "bytes key absent?"); - - $orig = {$bytes => 1}; - if ($package) { - bless $orig, $package; - } - my $just_bytes = $bypass ? $orig : ${thaw freeze \$orig}; - class_test ($just_bytes, $package); - - cmp_ok (scalar keys %$just_bytes, '==', 1, "1 key in bytes?"); - cmp_ok ($just_bytes->{$bytes}, '==', 1, "bytes key present?"); - ok (!exists $just_bytes->{$char}, "utf8 key absent?"); - - die sprintf "Both have length %d, which is crazy", length $char - if length $char == length $bytes; - - $orig = {$bytes => length $bytes, $char => length $char}; - if ($package) { - bless $orig, $package; - } - my $both = $bypass ? $orig : ${thaw freeze \$orig}; - class_test ($both, $package); - - cmp_ok (scalar keys %$both, '==', 2, "2 keys?"); - cmp_ok ($both->{$bytes}, '==', length $bytes, "bytes key present?"); - cmp_ok ($both->{$char}, '==', length $char, "utf8 key present?"); -} - -} diff --git a/ext/Storable/t/weak.t b/ext/Storable/t/weak.t deleted file mode 100644 index a2be7a10b0..0000000000 --- a/ext/Storable/t/weak.t +++ /dev/null @@ -1,142 +0,0 @@ -#!./perl -w -# -# Copyright 2004, Larry Wall. -# -# You may redistribute only under the same terms as Perl 5, as specified -# in the README file that comes with the distribution. -# - -sub BEGIN { - # This lets us distribute Test::More in t/ - unshift @INC, 't'; - require Config; import Config; - if ($ENV{PERL_CORE} and $Config{'extensions'} !~ /\bStorable\b/) { - print "1..0 # Skip: Storable was not built\n"; - exit 0; - } - if ($Config{extensions} !~ /\bList\/Util\b/) { - print "1..0 # Skip: List::Util was not built\n"; - exit 0; - } - - require Scalar::Util; - Scalar::Util->import(qw(weaken isweak)); - if (grep { /weaken/ } @Scalar::Util::EXPORT_FAIL) { - print("1..0 # Skip: No support for weaken in Scalar::Util\n"); - exit 0; - } -} - -use Test::More 'no_plan'; -use Storable qw (store retrieve freeze thaw nstore nfreeze); -require 'testlib.pl'; -use vars '$file'; -use strict; - -sub tester { - my ($contents, $sub, $testersub, $what) = @_; - # Test that if we re-write it, everything still works: - my $clone = &$sub ($contents); - is ($@, "", "There should be no error extracting for $what"); - &$testersub ($clone, $what); -} - -my $r = {}; -my $s1 = [$r, $r]; -weaken $s1->[1]; -ok (isweak($s1->[1]), "element 1 is a weak reference"); - -my $s0 = [$r, $r]; -weaken $s0->[0]; -ok (isweak($s0->[0]), "element 0 is a weak reference"); - -my $w = [$r]; -weaken $w->[0]; -ok (isweak($w->[0]), "element 0 is a weak reference"); - -package OVERLOADED; - -use overload - '""' => sub { $_[0][0] }; - -package main; - -$a = bless [77], 'OVERLOADED'; - -my $o = [$a, $a]; -weaken $o->[0]; -ok (isweak($o->[0]), "element 0 is a weak reference"); - -my @tests = ( -[$s1, - sub { - my ($clone, $what) = @_; - isa_ok($clone,'ARRAY'); - isa_ok($clone->[0],'HASH'); - isa_ok($clone->[1],'HASH'); - ok(!isweak $clone->[0], "Element 0 isn't weak"); - ok(isweak $clone->[1], "Element 1 is weak"); -} -], -# The weak reference needs to hang around long enough for other stuff to -# be able to make references to it. So try it second. -[$s0, - sub { - my ($clone, $what) = @_; - isa_ok($clone,'ARRAY'); - isa_ok($clone->[0],'HASH'); - isa_ok($clone->[1],'HASH'); - ok(isweak $clone->[0], "Element 0 is weak"); - ok(!isweak $clone->[1], "Element 1 isn't weak"); -} -], -[$w, - sub { - my ($clone, $what) = @_; - isa_ok($clone,'ARRAY'); - if ($what eq 'nothing') { - # We're the original, so we're still a weakref to a hash - isa_ok($clone->[0],'HASH'); - ok(isweak $clone->[0], "Element 0 is weak"); - } else { - is($clone->[0],undef); - } -} -], -[$o, -sub { - my ($clone, $what) = @_; - isa_ok($clone,'ARRAY'); - isa_ok($clone->[0],'OVERLOADED'); - isa_ok($clone->[1],'OVERLOADED'); - ok(isweak $clone->[0], "Element 0 is weak"); - ok(!isweak $clone->[1], "Element 1 isn't weak"); - is ("$clone->[0]", 77, "Element 0 stringifies to 77"); - is ("$clone->[1]", 77, "Element 1 stringifies to 77"); -} -], -); - -foreach (@tests) { - my ($input, $testsub) = @$_; - - tester($input, sub {return shift}, $testsub, 'nothing'); - - ok (defined store($input, $file)); - - # Read the contents into memory: - my $contents = slurp ($file); - - tester($contents, \&store_and_retrieve, $testsub, 'file'); - - # And now try almost everything again with a Storable string - my $stored = freeze $input; - tester($stored, \&freeze_and_thaw, $testsub, 'string'); - - ok (defined nstore($input, $file)); - - tester($contents, \&store_and_retrieve, $testsub, 'network file'); - - $stored = nfreeze $input; - tester($stored, \&freeze_and_thaw, $testsub, 'network string'); -} |