Re: Clock skew failures in Memoize test suite
[p5sagit/p5-mst-13.2.git] / ext / Storable / Storable.xs
index 1ab7a4c..98e3059 100644 (file)
@@ -1,56 +1,11 @@
 /*
- * Store and retrieve mechanism.
- */
-
-/*
- * $Id: Storable.xs,v 1.0.1.10 2001/08/28 21:52:14 ram Exp $
+ *  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.
  *
- * $Log: Storable.xs,v $
- * Revision 1.0.1.10  2001/08/28 21:52:14  ram
- * patch13: removed spurious debugging messages
- *
- * Revision 1.0.1.9  2001/07/01 11:25:02  ram
- * patch12: fixed memory corruption on croaks during thaw()
- * patch12: made code compile cleanly with -Wall (Jarkko Hietaniemi)
- * patch12: changed tagnum and classnum from I32 to IV in context
- *
- * Revision 1.0.1.8  2001/03/15 00:20:55  ram
- * patch11: last version was wrongly compiling with assertions on
- *
- * Revision 1.0.1.7  2001/02/17 12:25:26  ram
- * patch8: now bless objects ASAP at retrieve time
- * patch8: added support for blessed ref to tied structures
- *
- * Revision 1.0.1.6  2001/01/03 09:40:40  ram
- * patch7: prototype and casting cleanup
- * patch7: trace offending package when overloading cannot be restored
- * patch7: made context cleanup safer to avoid dup freeing
- *
- * Revision 1.0.1.5  2000/11/05 17:21:24  ram
- * patch6: fixed severe "object lost" bug for STORABLE_freeze returns
- *
- * Revision 1.0.1.4  2000/10/26 17:11:04  ram
- * patch5: auto requires module of blessed ref when STORABLE_thaw misses
- *
- * Revision 1.0.1.3  2000/09/29 19:49:57  ram
- * patch3: avoid using "tainted" and "dirty" since Perl remaps them via cpp
- *
- * Revision 1.0.1.2  2000/09/28 21:43:10  ram
- * patch2: perls before 5.004_04 lack newSVpvn
- *
- * Revision 1.0.1.1  2000/09/17 16:47:49  ram
- * patch1: now only taint retrieved data when source was tainted
- * patch1: added support for UTF-8 strings
- * patch1: fixed store hook bug: was allocating class id too soon
- *
- * Revision 1.0  2000/09/01 19:40:41  ram
- * Baseline for first official release.
- *
  */
 
 #include <EXTERN.h>
@@ -396,14 +351,14 @@ typedef struct stcxt {
 
 #else /* !MULTIPLICITY && !PERL_OBJECT && !PERL_CAPI */
 
-static stcxt_t Context;
-static stcxt_t *Context_ptr = &Context;
+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)
+       NEW_STORABLE_CXT_OBJ(cxt);                      \
+       SET_STCXT(cxt)
 
-#define SET_STCXT(x)           Context_ptr = x
 
 #endif /* MULTIPLICITY || PERL_OBJECT || PERL_CAPI */
 
@@ -736,9 +691,69 @@ static stcxt_t *Context_ptr = &Context;
  * a "minor" version, to better track this kind of evolution from now on.
  * 
  */
-static char old_magicstr[] = "perl-store";     /* Magic number before 0.6 */
-static char magicstr[] = "pst0";                       /* Used as a magic number */
+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 byteoder. 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     5               /* Binary minor "version" */
@@ -1836,89 +1851,134 @@ static int store_scalar(stcxt_t *cxt, SV *sv)
                        pv = SvPV(sv, len);                     /* We know it's SvPOK */
                        goto string;                            /* Share code below */
                }
-       } else if (flags & SVp_POK) {           /* SvPOKp(sv) => string */
-               I32 wlen;                                               /* For 64-bit machines */
-               pv = SvPV(sv, len);
-
-               /*
-                * Will come here from below with pv and len set if double & netorder,
-                * or from above if it was readonly, POK and NOK but neither &PL_sv_yes
-                * nor &PL_sv_no.
-                */
-       string:
+       } 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:
 
-               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));
+            /* 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).
+             */
 
-       } else if (flags & SVp_NOK) {           /* SvNOKp(sv) => double */
-               NV nv = SvNV(sv);
+            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;
 
-               /*
-                * 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 below */
-               }
 
-               if (cxt->netorder) {
-                       TRACEME(("double %"NVff" stored as string", nv));
-                       pv = SvPV(sv, len);
-                       goto string;            /* Share code above */
-               }
+#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
 
-               PUTMARK(SX_DOUBLE);
-               WRITE(&nv, sizeof(nv));
+                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
 
-               TRACEME(("ok (double 0x%"UVxf", value = %"NVff")", PTR2UV(sv), nv));
+            SvIV_please(sv);
+            if (SvIOK(sv)) {
+                iv = SvIV(sv);
+                goto integer;          /* Share code above */
+            }
+            nv = SvNV(sv);
+#endif
 
-       } else if (flags & SVp_IOK) {           /* SvIOKp(sv) => integer */
-               iv = SvIV(sv);
+            if (cxt->netorder) {
+                TRACEME(("double %"NVff" stored as string", nv));
+                goto string_readlen;           /* Share code below */
+            }
 
-               /*
-                * Will come here from above with iv set if double is an integer.
-                */
-       integer:
+            PUTMARK(SX_DOUBLE);
+            WRITE(&nv, sizeof(nv));
 
-               /*
-                * Optimize small integers into a single byte, otherwise store as
-                * a real integer (converted into network order if they asked).
-                */
+            TRACEME(("ok (double 0x%"UVxf", value = %"NVff")", PTR2UV(sv), nv));
 
-               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) {
-                       I32 niv;
-#ifdef HAS_HTONL
-                       niv = (I32) htonl(iv);
-                       TRACEME(("using network order"));
-#else
-                       niv = (I32) iv;
-                       TRACEME(("as-is for network order"));
-#endif
-                       PUTMARK(SX_NETINT);
-                       WRITE_I32(niv);
-               } else {
-                       PUTMARK(SX_INTEGER);
-                       WRITE(&iv, sizeof(iv));
-               }
+       } else if (flags & (SVp_POK | SVp_NOK | SVp_IOK)) {
+            I32 wlen; /* For 64-bit machines */
 
-               TRACEME(("ok (integer 0x%"UVxf", value = %"IVdf")", PTR2UV(sv), iv));
+          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 */
+            CROAK(("Can't determine type of %s(0x%"UVxf")",
+                   sv_reftype(sv, FALSE),
+                   PTR2UV(sv)));
+        return 0;              /* Ok, no recursion on scalars */
 }
 
 /*
@@ -2585,7 +2645,7 @@ static int store_hook(
         * If they returned more than one item, we need to serialize some
         * extra references if not already done.
         *
-        * Loop over the array, starting at postion #1, and for each item,
+        * 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.
@@ -3113,52 +3173,87 @@ static int store(stcxt_t *cxt, SV *sv)
  */
 static int magic_write(stcxt_t *cxt)
 {
-       char buf[256];  /* Enough room for 256 hexa digits */
-       unsigned char c;
-       int use_network_order = cxt->netorder;
-
-       TRACEME(("magic_write on fd=%d", cxt->fio ? PerlIO_fileno(cxt->fio)
-                 : -1));
-
-       if (cxt->fio)
-               WRITE(magicstr, (SSize_t)strlen(magicstr));     /* Don't write final \0 */
-
-       /*
-        * 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.
-        */
-
-       c = (unsigned char)
-               ((use_network_order ? 0x1 : 0x0) | (STORABLE_BIN_MAJOR << 1));
-       PUTMARK(c);
-
-       /*
-        * 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.
-        */
+    /*
+     * 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.
+     */
 
-       PUTMARK((unsigned char) STORABLE_BIN_WRITE_MINOR);
+    /* 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", TRUE))) {
+            header = file_header_56;
+            length = sizeof (file_header_56);
+        } else
+#endif
+        {
+            header = file_header;
+            length = sizeof (file_header);
+        }
+    }        
 
-       if (use_network_order)
-               return 0;                                               /* Don't bother with byte ordering */
+    if (!cxt->fio) {
+        /* sizeof the array includes the 0 byte at the end.  */
+        header += sizeof (magicstr) - 1;
+        length -= sizeof (magicstr) - 1;
+    }        
 
-       sprintf(buf, "%lx", (unsigned long) BYTEORDER);
-       c = (unsigned char) strlen(buf);
-       PUTMARK(c);
-       WRITE(buf, (SSize_t)c);         /* Don't write final \0 */
-       PUTMARK((unsigned char) sizeof(int));
-       PUTMARK((unsigned char) sizeof(long));
-       PUTMARK((unsigned char) sizeof(char *));
-       PUTMARK((unsigned char) sizeof(NV));
+    WRITE(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) c,
+                (unsigned long) BYTEORDER, (int) sizeof (byteorderstr) - 1,
                 (int) sizeof(int), (int) sizeof(long),
                 (int) sizeof(char *), (int) sizeof(NV)));
-
-       return 0;
+    }
+    return 0;
 }
 
 /*
@@ -3582,6 +3677,10 @@ static SV *retrieve_hook(stcxt_t *cxt, char *cname)
         * 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) {
@@ -3589,6 +3688,7 @@ static SV *retrieve_hook(stcxt_t *cxt, char *cname)
                rv = retrieve(cxt, 0);
                if (!rv)
                        return (SV *) 0;
+               SvREFCNT_dec(rv);
                TRACEME(("retrieve_hook back with rv=0x%"UVxf,
                         PTR2UV(rv)));
                GETMARK(flags);
@@ -4871,140 +4971,170 @@ static SV *old_retrieve_hash(stcxt_t *cxt, char *cname)
  */
 static SV *magic_check(stcxt_t *cxt)
 {
-       char buf[256];
-       char byteorder[256];
-       int c;
-       int use_network_order;
-       int version_major;
-       int version_minor = 0;
+    /* 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 version_major;
+    int version_minor = 0;
+
+    TRACEME(("magic_check"));
 
-       TRACEME(("magic_check"));
+    /*
+     * The "magic number" is only for files, not when freezing in memory.
+     */
 
-       /*
-        * 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;
 
-       if (cxt->fio) {
-               STRLEN len = sizeof(magicstr) - 1;
-               STRLEN old_len;
+        READ(buf, (SSize_t)(len));     /* Not null-terminated */
 
-               READ(buf, (SSize_t)len);                        /* Not null-terminated */
-               buf[len] = '\0';                                /* Is now */
+        /* Point at the byte after the byte we read.  */
+        current = buf + --len; /* Do the -- outside of macros.  */
 
-               if (0 == strcmp(buf, magicstr))
-                       goto magic_ok;
+        if (memNE(buf, magicstr, len)) {
+            /*
+             * Try to read more bytes to check for the old magic number, which
+             * was longer.
+             */
 
-               /*
-                * 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(&buf[len], (SSize_t)(old_len - len));
-               buf[old_len] = '\0';                    /* Is now null-terminated */
+            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"));
+            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 (strcmp(buf, old_magicstr))
-                       CROAK(("File is not a perl storable"));
-       }
+    version_major = use_network_order >> 1;
+    cxt->retrieve_vtbl = version_major ? sv_retrieve : sv_old_retrieve;
 
-magic_ok:
-       /*
-        * 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().
-        */
+    TRACEME(("magic_check: netorder = 0x%x", use_network_order));
 
-       GETMARK(use_network_order);
-       version_major = use_network_order >> 1;
-       cxt->retrieve_vtbl = version_major ? 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);
 
-       /*
-        * Starting with 0.7 (binary major 2), a full byte is dedicated to the
-        * minor version of the protocol.  See magic_write().
-        */
+    cxt->ver_major = version_major;
+    cxt->ver_minor = version_minor;
 
-       if (version_major > 1)
-               GETMARK(version_minor);
+    TRACEME(("binary image version is %d.%d", version_major, version_minor));
 
-       cxt->ver_major = version_major;
-       cxt->ver_minor = 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.
+     */
 
-       TRACEME(("binary image version is %d.%d", version_major, version_minor));
+    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",
+                                          TRUE))
+                       ? 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));
+        }
+    }
 
-       /*
-        * 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 they stored using network order, there's no byte ordering
+     * information to check.
+     */
 
-       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",
-                                              TRUE))
-                           ? 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 ((cxt->netorder = (use_network_order & 0x1)))   /* Extra () for -Wall */
+        return &PL_sv_undef;                   /* No byte ordering info */
 
-       /*
-        * If they stored using network order, there's no byte ordering
-        * information to check.
-        */
+    /* In C truth is 1, falsehood is 0. Very convienient.  */
+    use_NV_size = version_major >= 2 && version_minor >= 2;
 
-       if ((cxt->netorder = (use_network_order & 0x1)))        /* Extra () for -Wall */
-               return &PL_sv_undef;                    /* No byte ordering info */
+    GETMARK(c);
+    length = c + 3 + use_NV_size;
+    READ(buf, length); /* Not null-terminated */
 
-       sprintf(byteorder, "%lx", (unsigned long) BYTEORDER);
-       GETMARK(c);
-       READ(buf, c);                                           /* Not null-terminated */
-       buf[c] = '\0';                                          /* Is now */
+    TRACEME(("byte order '%.*s' %d", c, buf, c));
 
-       TRACEME(("byte order '%s'", buf));
+#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", TRUE))) {
+        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"));
+    }
 
-       if (strcmp(buf, byteorder))
-               CROAK(("Byte order is not compatible"));
-       
-       GETMARK(c);             /* sizeof(int) */
-       if ((int) c != sizeof(int))
-               CROAK(("Integer size is not compatible"));
-
-       GETMARK(c);             /* sizeof(long) */
-       if ((int) c != sizeof(long))
-               CROAK(("Long integer size is not compatible"));
-
-       GETMARK(c);             /* sizeof(char *) */
-       if ((int) c != sizeof(char *))
-               CROAK(("Pointer integer size is not compatible"));
-
-       if (version_major >= 2 && version_minor >= 2) {
-               GETMARK(c);             /* sizeof(NV) */
-               if ((int) c != sizeof(NV))
-                       CROAK(("Double size 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 integer 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 */
+    return &PL_sv_undef;       /* OK */
 }
 
 /*
@@ -5483,6 +5613,14 @@ PROTOTYPES: ENABLE
 
 BOOT:
     init_perinterp();
+    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
 
 int
 pstore(f,obj)
@@ -5522,4 +5660,3 @@ is_storing()
 
 int
 is_retrieving()
-