Upgrade to Storable 1.0.5, from Raphael Manfredi.
[p5sagit/p5-mst-13.2.git] / ext / Storable / Storable.xs
index 1b580cf..b4066dc 100644 (file)
@@ -3,29 +3,23 @@
  */
 
 /*
- * $Id: Storable.xs,v 0.7.1.3 2000/08/23 23:00:41 ram Exp $
+ * $Id: Storable.xs,v 1.0.1.4 2000/10/26 17:11:04 ram Exp ram $
  *
  *  Copyright (c) 1995-2000, Raphael Manfredi
  *  
- *  You may redistribute only under the terms of the Artistic License,
- *  as specified in the README file that comes with the distribution.
+ *  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 0.7.1.3  2000/08/23 23:00:41  ram
- * patch3: ANSI-fied most of the code, preparing for Perl core integration
- * patch3: dispatch tables moved upfront to relieve some compilers
- * patch3: merged 64-bit fixes from perl5-porters
+ * Revision 1.0.1.4  2000/10/26 17:11:04  ram
+ * patch5: auto requires module of blessed ref when STORABLE_thaw misses
  *
- * Revision 0.7.1.2  2000/08/14 07:19:27  ram
- * patch2: added a refcnt dec in retrieve_tied_key()
+ * 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 0.7.1.1  2000/08/13 20:10:06  ram
- * patch1: was wrongly optimizing for "undef" values in hashes
- * patch1: added support for ref to tied items in hash/array
- * patch1: added overloading support
- *
- * Revision 0.7  2000/08/03 22:04:44  ram
- * Baseline for second beta release.
+ * $Log: Storable.xs,v $
+ * Revision 1.0  2000/09/01 19:40:41  ram
+ * Baseline for first official release.
  *
  */
 
 #include <patchlevel.h>                /* Perl's one, needed since 5.6 */
 #include <XSUB.h>
 
-/*#define DEBUGME /* Debug mode, turns assertions on as well */
-/*#define DASSERT /* Assertion mode */
+#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
 #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 INT2PTR
-#define INT2PTR(t,v)   (t)(IV)(v)
-#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
+
 #ifdef DEBUGME
 #ifndef DASSERT
 #define DASSERT
@@ -106,7 +121,7 @@ typedef double NV;                  /* Older perls lack the NV type */
 #define C(x) ((char) (x))      /* For markers with dynamic retrieval handling */
 
 #define SX_OBJECT      C(0)    /* Already stored object */
-#define SX_LSCALAR     C(1)    /* Scalar (string) forthcoming (length, data) */
+#define SX_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 */
@@ -115,7 +130,7 @@ typedef double NV;                  /* Older perls lack the NV type */
 #define SX_DOUBLE      C(7)    /* Double forthcoming */
 #define SX_BYTE                C(8)    /* (signed) byte forthcoming */
 #define SX_NETINT      C(9)    /* Integer in network order forthcoming */
-#define SX_SCALAR      C(10)   /* Scalar (small) forthcoming (length, data) */
+#define SX_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 */
@@ -128,7 +143,9 @@ typedef double NV;                  /* Older perls lack the NV type */
 #define SX_OVERLOAD    C(20)   /* Overloaded reference */
 #define SX_TIED_KEY C(21)   /* Tied magic key forthcoming */
 #define SX_TIED_IDX C(22)   /* Tied magic index forthcoming */
-#define SX_ERROR       C(23)   /* Error */
+#define SX_UTF8STR     C(23)   /* UTF-8 string forthcoming (small) */
+#define SX_LUTF8STR    C(24)   /* UTF-8 string forthcoming (large) */
+#define SX_ERROR       C(25)   /* Error */
 
 /*
  * Those are only used to retrieve "old" pre-0.6 binary images.
@@ -215,6 +232,12 @@ typedef unsigned long stag_t;      /* Used by pre-0.6 binary format */
 
 #define MY_VERSION "Storable(" XS_VERSION ")"
 
+/*
+ * 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
+ */
+
 typedef struct stcxt {
        int entry;                      /* flags recursion */
        int optype;                     /* type of traversal operation */
@@ -226,9 +249,10 @@ typedef struct stcxt {
     I32 tagnum;                        /* incremented at store time for each seen object */
     I32 classnum;              /* incremented at store time for each seen classname */
     int netorder;              /* true if network order used */
+    int s_tainted;             /* true if input source is tainted, at retrieve time */
     int forgive_me;            /* whether to be forgiving... */
     int canonical;             /* whether to store hashes sorted by key */
-       int dirty;                      /* context is dirty due to CROAK() -- can be cleaned */
+       int s_dirty;            /* context is dirty due to CROAK() -- can be cleaned */
     struct extendable keybuf;  /* for hash key retrieval */
     struct extendable membuf;  /* for memory store/retrieve operations */
        PerlIO *fio;            /* where I/O are performed, NULL for memory */
@@ -250,8 +274,8 @@ typedef struct stcxt {
 #endif /* < perl5.004_68 */
 
 #define dSTCXT_PTR(T,name)                                                     \
-       T name = (T)(perinterp_sv && SvIOK(perinterp_sv)\
-                               ? SvIVX(perinterp_sv) : NULL)
+       T name = (perinterp_sv && SvIOK(perinterp_sv)   \
+                               ? INT2PTR(T, SvIVX(perinterp_sv)) : (T) 0)
 #define dSTCXT                                                                         \
        dSTCXT_SV;                                                                              \
        dSTCXT_PTR(stcxt_t *, cxt)
@@ -293,13 +317,44 @@ static stcxt_t *Context_ptr = &Context;
  * but the topmost context stacked.
  */
 
-#define CROAK(x)       do { cxt->dirty = 1; croak x; } while (0)
+#define CROAK(x)       do { cxt->s_dirty = 1; croak x; } while (0)
 
 /*
  * 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
@@ -386,6 +441,16 @@ static stcxt_t *Context_ptr = &Context;
                return (SV *) 0;                        \
 } while (0)
 
+#ifdef CRAY_HACK
+#define MBUF_GETINT(x) do {                            \
+       oC(x);                                                          \
+       if ((mptr + 4) <= mend) {                       \
+               memcpy(oI(&x), mptr, 4);                \
+               mptr += 4;                                              \
+       } else                                                          \
+               return (SV *) 0;                                \
+} while (0)
+#else
 #define MBUF_GETINT(x) do {                            \
        if ((mptr + sizeof(int)) <= mend) {     \
                if (int_aligned(mptr))                  \
@@ -396,6 +461,7 @@ static stcxt_t *Context_ptr = &Context;
        } else                                                          \
                return (SV *) 0;                                \
 } while (0)
+#endif
 
 #define MBUF_READ(x,s) do {                    \
        if ((mptr + (s)) <= mend) {             \
@@ -424,6 +490,13 @@ static stcxt_t *Context_ptr = &Context;
        }                                                               \
 } while (0)
 
+#ifdef CRAY_HACK
+#define MBUF_PUTINT(i) do {                    \
+       MBUF_CHK(4);                                    \
+       memcpy(mptr, oI(&i), 4);                \
+       mptr += 4;                                              \
+} while (0)
+#else
 #define MBUF_PUTINT(i) do {                    \
        MBUF_CHK(sizeof(int));                  \
        if (int_aligned(mptr))                  \
@@ -432,6 +505,7 @@ static stcxt_t *Context_ptr = &Context;
                memcpy(mptr, &i, sizeof(int));  \
        mptr += sizeof(int);                    \
 } while (0)
+#endif
 
 #define MBUF_WRITE(x,s) do {           \
        MBUF_CHK(s);                                    \
@@ -440,19 +514,6 @@ static stcxt_t *Context_ptr = &Context;
 } while (0)
 
 /*
- * LOW_32BITS
- *
- * Keep only the low 32 bits of a pointer (used for tags, which are not
- * really pointers).
- */
-
-#if PTRSIZE <= 4
-#define LOW_32BITS(x)  ((I32) (x))
-#else
-#define LOW_32BITS(x)  ((I32) ((unsigned long) (x) & 0xffffffffUL))
-#endif
-
-/*
  * Possible return values for sv_type().
  */
 
@@ -504,7 +565,7 @@ static char old_magicstr[] = "perl-store";  /* Magic number before 0.6 */
 static char magicstr[] = "pst0";                       /* Used as a magic number */
 
 #define STORABLE_BIN_MAJOR     2                               /* Binary major "version" */
-#define STORABLE_BIN_MINOR     1                               /* Binary minor "version" */
+#define STORABLE_BIN_MINOR     3                               /* Binary minor "version" */
 
 /*
  * Useful store shortcuts...
@@ -517,28 +578,31 @@ static char magicstr[] = "pst0";                  /* Used as a magic number */
                return -1;                                                      \
 } while (0)
 
+#define WRITE_I32(x)   do {                    \
+       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;                                      \
+       } while (0)
+
 #ifdef HAS_HTONL
 #define WLEN(x)        do {                            \
        if (cxt->netorder) {                    \
                int y = (int) htonl(x);         \
                if (!cxt->fio)                          \
                        MBUF_PUTINT(y);                 \
-               else if (PerlIO_write(cxt->fio, &y, sizeof(y)) != sizeof(y))    \
+               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, &x, sizeof(x)) != sizeof(x))    \
+               else if (PerlIO_write(cxt->fio,oI(&x),oS(sizeof(x))) != oS(sizeof(x))) \
                        return -1;                              \
        }                                                               \
 } while (0)
 #else
-#define WLEN(x)        do {                            \
-       if (!cxt->fio)                                  \
-               MBUF_PUTINT(x);                         \
-       else if (PerlIO_write(cxt->fio, &x, sizeof(x)) != sizeof(x))    \
-               return -1;                                      \
-       } while (0)
+#define WLEN(x)        WRITE_I32(x)
 #endif
 
 #define WRITE(x,y) do {                                                \
@@ -548,20 +612,35 @@ static char magicstr[] = "pst0";                  /* Used as a magic number */
                return -1;                                                      \
        } while (0)
 
-#define STORE_SCALAR(pv, len) do {             \
+#define STORE_PV_LEN(pv, len, small, large) do {       \
        if (len <= LG_SCALAR) {                         \
                unsigned char clen = (unsigned char) len;       \
-               PUTMARK(SX_SCALAR);                             \
+               PUTMARK(small);                                 \
                PUTMARK(clen);                                  \
                if (len)                                                \
                        WRITE(pv, len);                         \
        } else {                                                        \
-               PUTMARK(SX_LSCALAR);                    \
+               PUTMARK(large);                                 \
                WLEN(len);                                              \
                WRITE(pv, len);                                 \
        }                                                                       \
 } while (0)
 
+#define STORE_SCALAR(pv, len)  STORE_PV_LEN(pv, len, SX_SCALAR, SX_LSCALAR)
+
+/*
+ * Conditional UTF8 support.
+ * On non-UTF8 perls, UTF8 strings are returned as normal strings.
+ *
+ */
+#ifdef SvUTF8_on
+#define STORE_UTF8STR(pv, len) STORE_PV_LEN(pv, len, SX_UTF8STR, SX_LUTF8STR)
+#else
+#define SvUTF8(sv) 0
+#define STORE_UTF8STR(pv, len) CROAK(("panic: storing UTF8 in non-UTF8 perl"))
+#define SvUTF8_on(sv) CROAK(("Cannot retrieve UTF8 data in non-UTF8 perl"))
+#endif
+
 /*
  * Store undef in arrays and hashes without recursing through store().
  */
@@ -584,22 +663,27 @@ static char magicstr[] = "pst0";                  /* Used as a magic number */
                return (SV *) 0;                                                \
 } while (0)
 
-#ifdef HAS_NTOHL
-#define RLEN(x)        do {                                    \
+#define READ_I32(x)    do {                            \
+       ASSERT(sizeof(x) == sizeof(I32), ("reading an I32"));   \
+       oC(x);                                                          \
        if (!cxt->fio)                                          \
                MBUF_GETINT(x);                                 \
-       else if (PerlIO_read(cxt->fio, &x, sizeof(x)) != sizeof(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);                             \
 } while (0)
-#else
+
+#ifdef HAS_NTOHL
 #define RLEN(x)        do {                                    \
+       oC(x);                                                          \
        if (!cxt->fio)                                          \
                MBUF_GETINT(x);                                 \
-       else if (PerlIO_read(cxt->fio, &x, sizeof(x)) != sizeof(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);                             \
 } while (0)
+#else
+#define RLEN(x) READ_I32(x)
 #endif
 
 #define READ(x,y) do {                                         \
@@ -680,6 +764,7 @@ static int (*sv_store[])() = {
  */
 
 static SV *retrieve_lscalar(stcxt_t *cxt);
+static SV *retrieve_lutf8str(stcxt_t *cxt);
 static SV *old_retrieve_array(stcxt_t *cxt);
 static SV *old_retrieve_hash(stcxt_t *cxt);
 static SV *retrieve_ref(stcxt_t *cxt);
@@ -689,6 +774,7 @@ static SV *retrieve_double(stcxt_t *cxt);
 static SV *retrieve_byte(stcxt_t *cxt);
 static SV *retrieve_netint(stcxt_t *cxt);
 static SV *retrieve_scalar(stcxt_t *cxt);
+static SV *retrieve_utf8str(stcxt_t *cxt);
 static SV *retrieve_tied_array(stcxt_t *cxt);
 static SV *retrieve_tied_hash(stcxt_t *cxt);
 static SV *retrieve_tied_scalar(stcxt_t *cxt);
@@ -718,6 +804,8 @@ static SV *(*sv_old_retrieve[])() = {
        retrieve_other,                 /* SX_OVERLOADED not supported */
        retrieve_other,                 /* SX_TIED_KEY not supported */
        retrieve_other,                 /* SX_TIED_IDX not supported */
+       retrieve_other,                 /* SX_UTF8STR not supported */
+       retrieve_other,                 /* SX_LUTF8STR not supported */
        retrieve_other,                 /* SX_ERROR */
 };
 
@@ -757,6 +845,8 @@ static SV *(*sv_retrieve[])() = {
        retrieve_overloaded,    /* SX_OVERLOAD */
        retrieve_tied_key,              /* SX_TIED_KEY */
        retrieve_tied_idx,              /* SX_TIED_IDX */
+       retrieve_utf8str,               /* SX_UTF8STR  */
+       retrieve_lutf8str,              /* SX_LUTF8STR */
        retrieve_other,                 /* SX_ERROR */
 };
 
@@ -904,7 +994,7 @@ static void clean_store_context(stcxt_t *cxt)
        sv_free((SV *) cxt->hook);
 
        cxt->entry = 0;
-       cxt->dirty = 0;
+       cxt->s_dirty = 0;
 }
 
 /*
@@ -912,9 +1002,7 @@ static void clean_store_context(stcxt_t *cxt)
  *
  * Initialize a new retrieve context for real recursion.
  */
-static void init_retrieve_context(cxt, optype)
-stcxt_t *cxt;
-int optype;
+static void init_retrieve_context(stcxt_t *cxt, int optype, int is_tainted)
 {
        TRACEME(("init_retrieve_context"));
 
@@ -943,6 +1031,7 @@ int optype;
        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 */
 }
 
@@ -951,8 +1040,7 @@ int optype;
  *
  * Clean retrieve context by
  */
-static void clean_retrieve_context(cxt)
-stcxt_t *cxt;
+static void clean_retrieve_context(stcxt_t *cxt)
 {
        TRACEME(("clean_retrieve_context"));
 
@@ -971,7 +1059,7 @@ stcxt_t *cxt;
                sv_free((SV *) cxt->hseen);             /* optional HV, for backward compat. */
 
        cxt->entry = 0;
-       cxt->dirty = 0;
+       cxt->s_dirty = 0;
 }
 
 /*
@@ -984,7 +1072,7 @@ stcxt_t *cxt;
 {
        TRACEME(("clean_context"));
 
-       ASSERT(cxt->dirty, ("dirty context"));
+       ASSERT(cxt->s_dirty, ("dirty context"));
 
        if (cxt->optype & ST_RETRIEVE)
                clean_retrieve_context(cxt);
@@ -1005,7 +1093,7 @@ stcxt_t *parent_cxt;
 
        TRACEME(("allocate_context"));
 
-       ASSERT(!parent_cxt->dirty, ("parent context clean"));
+       ASSERT(!parent_cxt->s_dirty, ("parent context clean"));
 
        Newz(0, cxt, 1, stcxt_t);
        cxt->prev = parent_cxt;
@@ -1027,7 +1115,7 @@ stcxt_t *cxt;
 
        TRACEME(("free_context"));
 
-       ASSERT(!cxt->dirty, ("clean context"));
+       ASSERT(!cxt->s_dirty, ("clean context"));
        ASSERT(prev, ("not freeing root context"));
 
        if (kbuf)
@@ -1111,9 +1199,7 @@ static SV *pkg_fetchmeth(
        gv = gv_fetchmethod_autoload(pkg, method, FALSE);
        if (gv && isGV(gv)) {
                sv = newRV((SV*) GvCV(gv));
-               TRACEME(("%s->%s: 0x%"UVxf,
-                        HvNAME(pkg), method,
-                        PTR2UV(sv)));
+               TRACEME(("%s->%s: 0x%"UVxf, HvNAME(pkg), method, PTR2UV(sv)));
        } else {
                sv = newSVsv(&PL_sv_undef);
                TRACEME(("%s->%s: not found", HvNAME(pkg), method));
@@ -1144,6 +1230,19 @@ static void pkg_hide(
 }
 
 /*
+ * pkg_uncache
+ *
+ * Discard cached value: a whole fetch loop will be retried at next lookup.
+ */
+static void pkg_uncache(
+       HV *cache,
+       HV *pkg,
+       char *method)
+{
+       (void) hv_delete(cache, HvNAME(pkg), strlen(HvNAME(pkg)), G_DISCARD);
+}
+
+/*
  * pkg_can
  *
  * Our own "UNIVERSAL::can", which caches results.
@@ -1177,8 +1276,7 @@ static SV *pkg_can(
                        return (SV *) 0;
                } else {
                        TRACEME(("cached %s->%s: 0x%"UVxf,
-                                HvNAME(pkg), method,
-                                PTR2UV(sv)));
+                               HvNAME(pkg), method, PTR2UV(sv)));
                        return sv;
                }
        }
@@ -1351,8 +1449,7 @@ static int store_ref(stcxt_t *cxt, SV *sv)
        if (SvOBJECT(sv)) {
                HV *stash = (HV *) SvSTASH(sv);
                if (stash && Gv_AMG(stash)) {
-                       TRACEME(("ref (0x%"UVxf") is overloaded",
-                                PTR2UV(sv)));
+                       TRACEME(("ref (0x%"UVxf") is overloaded", PTR2UV(sv)));
                        PUTMARK(SX_OVERLOAD);
                } else
                        PUTMARK(SX_REF);
@@ -1442,6 +1539,7 @@ static int store_scalar(stcxt_t *cxt, SV *sv)
                        goto string;                            /* Share code below */
                }
        } else if (flags & SVp_POK) {           /* SvPOKp(sv) => string */
+               I32 wlen;                                               /* For 64-bit machines */
                pv = SvPV(sv, len);
 
                /*
@@ -1451,7 +1549,11 @@ static int store_scalar(stcxt_t *cxt, SV *sv)
                 */
        string:
 
-               STORE_SCALAR(pv, len);
+               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));
 
@@ -1462,8 +1564,7 @@ static int store_scalar(stcxt_t *cxt, SV *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));
+                       TRACEME(("double %"NVff" is actually integer %"IVdf, nv, iv));
                        goto integer;           /* Share code below */
                }
 
@@ -1476,8 +1577,7 @@ static int store_scalar(stcxt_t *cxt, SV *sv)
                PUTMARK(SX_DOUBLE);
                WRITE(&nv, sizeof(nv));
 
-               TRACEME(("ok (double 0x%"UVxf", value = %"NVff")",
-                        PTR2UV(sv), nv));
+               TRACEME(("ok (double 0x%"UVxf", value = %"NVff")", PTR2UV(sv), nv));
 
        } else if (flags & SVp_IOK) {           /* SvIOKp(sv) => integer */
                iv = SvIV(sv);
@@ -1498,23 +1598,22 @@ static int store_scalar(stcxt_t *cxt, SV *sv)
                        PUTMARK(siv);
                        TRACEME(("small integer stored as %d", siv));
                } else if (cxt->netorder) {
-                       int niv;
+                       I32 niv;
 #ifdef HAS_HTONL
-                       niv = (int) htonl(iv);
+                       niv = (I32) htonl(iv);
                        TRACEME(("using network order"));
 #else
-                       niv = (int) iv;
+                       niv = (I32) iv;
                        TRACEME(("as-is for network order"));
 #endif
                        PUTMARK(SX_NETINT);
-                       WRITE(&niv, sizeof(niv));
+                       WRITE_I32(niv);
                } else {
                        PUTMARK(SX_INTEGER);
                        WRITE(&iv, sizeof(iv));
                }
 
-               TRACEME(("ok (integer 0x%"UVxf", value = %"IVdf")",
-                        PTR2UV(sv), iv));
+               TRACEME(("ok (integer 0x%"UVxf", value = %"IVdf")", PTR2UV(sv), iv));
 
        } else
                CROAK(("Can't determine type of %s(0x%"UVxf")",
@@ -1667,8 +1766,7 @@ static int store_hash(stcxt_t *cxt, HV *hv)
                         * Store value first.
                         */
                        
-                       TRACEME(("(#%d) value 0x%"UVxf,
-                                i, PTR2UV(val)));
+                       TRACEME(("(#%d) value 0x%"UVxf, i, PTR2UV(val)));
 
                        if (ret = store(cxt, val))
                                goto out;
@@ -1714,8 +1812,7 @@ static int store_hash(stcxt_t *cxt, HV *hv)
                         * Store value first.
                         */
 
-                       TRACEME(("(#%d) value 0x%"UVxf,
-                                i, PTR2UV(val)));
+                       TRACEME(("(#%d) value 0x%"UVxf, i, PTR2UV(val)));
 
                        if (ret = store(cxt, val))
                                goto out;
@@ -1837,14 +1934,12 @@ static int store_tied_item(stcxt_t *cxt, SV *sv)
        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)));
+               TRACEME(("store_tied_item: storing OBJ 0x%"UVxf, PTR2UV(mg->mg_obj)));
 
                if (ret = store(cxt, mg->mg_obj))
                        return ret;
 
-               TRACEME(("store_tied_item: storing PTR 0x%"UVxf,
-                        PTR2UV(mg->mg_ptr)));
+               TRACEME(("store_tied_item: storing PTR 0x%"UVxf, PTR2UV(mg->mg_ptr)));
 
                if (ret = store(cxt, (SV *) mg->mg_ptr))
                        return ret;
@@ -1853,8 +1948,7 @@ static int store_tied_item(stcxt_t *cxt, SV *sv)
 
                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)));
+               TRACEME(("store_tied_item: storing OBJ 0x%"UVxf, PTR2UV(mg->mg_obj)));
 
                if (ret = store(cxt, mg->mg_obj))
                        return ret;
@@ -1995,7 +2089,7 @@ static int store_hook(
                pkg_hide(cxt->hook, pkg, "STORABLE_freeze");
 
                ASSERT(!pkg_can(cxt->hook, pkg, "STORABLE_freeze"), ("hook invisible"));
-               TRACEME(("Ignoring STORABLE_freeze in class \"%s\"", class));
+               TRACEME(("ignoring STORABLE_freeze in class \"%s\"", class));
 
                return store_blessed(cxt, sv, type, pkg);
        }
@@ -2008,17 +2102,6 @@ static int store_hook(
        pv = SvPV(ary[0], len2);
 
        /*
-        * Allocate a class ID if not already done.
-        */
-
-       if (!known_class(cxt, class, len, &classnum)) {
-               TRACEME(("first time we see class %s, ID = %d", class, classnum));
-               classnum = -1;                          /* Mark: we must store classname */
-       } else {
-               TRACEME(("already seen class %s, ID = %d", class, classnum));
-       }
-
-       /*
         * If they returned more than one item, we need to serialize some
         * extra references if not already done.
         *
@@ -2047,8 +2130,7 @@ static int store_hook(
                if (svh = hv_fetch(cxt->hseen, (char *) &xsv, sizeof(xsv), FALSE))
                        goto sv_seen;           /* Avoid moving code too far to the right */
 
-               TRACEME(("listed object %d at 0x%"UVxf" is unknown",
-                       i-1, PTR2UV(xsv)));
+               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
@@ -2085,6 +2167,22 @@ static int store_hook(
        }
 
        /*
+        * 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
+        */
+
+       if (!known_class(cxt, class, len, &classnum)) {
+               TRACEME(("first time we see class %s, ID = %d", class, classnum));
+               classnum = -1;                          /* Mark: we must store classname */
+       } else {
+               TRACEME(("already seen class %s, ID = %d", class, classnum));
+       }
+
+       /*
         * Compute leading flags.
         */
 
@@ -2109,7 +2207,8 @@ static int store_hook(
         * 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",
+       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> */
@@ -2136,9 +2235,10 @@ static int store_hook(
        }
 
        /* <len2> <frozen-str> */
-       if (flags & SHF_LARGE_STRLEN)
-               WLEN(len2);
-       else {
+       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);
        }
@@ -2162,7 +2262,7 @@ static int store_hook(
 
                for (i = 1; i < count; i++) {
                        I32 tagval = htonl(LOW_32BITS(ary[i]));
-                       WRITE(&tagval, sizeof(I32));
+                       WRITE_I32(tagval);
                        TRACEME(("object %d, tag #%d", i-1, ntohl(tagval)));
                }
        }
@@ -2287,7 +2387,7 @@ static int store_blessed(
  */
 static int store_other(stcxt_t *cxt, SV *sv)
 {
-       STRLEN len;
+       I32 len;
        static char buf[80];
 
        TRACEME(("store_other"));
@@ -2416,11 +2516,10 @@ static int store(stcxt_t *cxt, SV *sv)
        if (svh) {
                I32 tagval = htonl(LOW_32BITS(*svh));
 
-               TRACEME(("object 0x%"UVxf" seen as #%d",
-                        PTR2UV(sv), ntohl(tagval)));
+               TRACEME(("object 0x%"UVxf" seen as #%d", PTR2UV(sv), ntohl(tagval)));
 
                PUTMARK(SX_OBJECT);
-               WRITE(&tagval, sizeof(I32));
+               WRITE_I32(tagval);
                return 0;
        }
 
@@ -2513,10 +2612,12 @@ static int magic_write(stcxt_t *cxt)
        PUTMARK((unsigned char) sizeof(int));
        PUTMARK((unsigned char) sizeof(long));
        PUTMARK((unsigned char) sizeof(char *));
+       PUTMARK((unsigned char) sizeof(NV));
 
-       TRACEME(("ok (magic_write byteorder = 0x%lx [%d], I%d L%d P%d)",
+       TRACEME(("ok (magic_write byteorder = 0x%lx [%d], I%d L%d P%d D%d)",
                 (unsigned long) BYTEORDER, (int) c,
-                (int) sizeof(int), (int) sizeof(long), (int) sizeof(char *)));
+                (int) sizeof(int), (int) sizeof(long),
+                (int) sizeof(char *), (int) sizeof(NV)));
 
        return 0;
 }
@@ -2555,7 +2656,7 @@ static int do_store(
         * free up memory for them now.
         */
 
-       if (cxt->dirty)
+       if (cxt->s_dirty)
                clean_context(cxt);
 
        /*
@@ -2569,7 +2670,7 @@ static int do_store(
        cxt->entry++;
 
        ASSERT(cxt->entry == 1, ("starting new recursion"));
-       ASSERT(!cxt->dirty, ("clean context"));
+       ASSERT(!cxt->s_dirty, ("clean context"));
 
        /*
         * Ensure sv is actually a reference. From perl, we called something
@@ -2776,7 +2877,7 @@ static SV *retrieve_idx_blessed(stcxt_t *cxt)
 
        sva = av_fetch(cxt->aclass, idx, FALSE);
        if (!sva)
-               CROAK(("Class name #%d should have been seen already", idx));
+               CROAK(("Class name #%d should have been seen already", (int)idx));
 
        class = SvPVX(*sva);    /* We know it's a PV, by construction */
 
@@ -2937,7 +3038,7 @@ static SV *retrieve_hook(stcxt_t *cxt)
 
                sva = av_fetch(cxt->aclass, idx, FALSE);
                if (!sva)
-                       CROAK(("Class name #%d should have been seen already", idx));
+                       CROAK(("Class name #%d should have been seen already", (int)idx));
 
                class = SvPVX(*sva);    /* We know it's a PV, by construction */
                TRACEME(("class ID %d => %s", idx, class));
@@ -2993,7 +3094,8 @@ static SV *retrieve_hook(stcxt_t *cxt)
                *SvEND(frozen) = '\0';
        }
        (void) SvPOK_only(frozen);              /* Validates string pointer */
-       SvTAINT(frozen);
+       if (cxt->s_tainted)                             /* Is input source tainted? */
+               SvTAINT(frozen);
 
        TRACEME(("frozen string: %d bytes", len2));
 
@@ -3033,11 +3135,11 @@ static SV *retrieve_hook(stcxt_t *cxt)
                        SV **svh;
                        SV *xsv;
 
-                       READ(&tag, sizeof(I32));
+                       READ_I32(tag);
                        tag = ntohl(tag);
                        svh = av_fetch(cxt->aseen, tag, FALSE);
                        if (!svh)
-                               CROAK(("Object #%d should have been retrieved already", tag));
+                               CROAK(("Object #%d should have been retrieved already", (int)tag));
                        xsv = *svh;
                        ary[i] = SvREFCNT_inc(xsv);
                }
@@ -3049,8 +3151,37 @@ static SV *retrieve_hook(stcxt_t *cxt)
 
        BLESS(sv, class);
        hook = pkg_can(cxt->hook, SvSTASH(sv), "STORABLE_thaw");
-       if (!hook)
-               CROAK(("No STORABLE_thaw defined for objects of class %s", class));
+       if (!hook) {
+               /*
+                * Hook not found.  Maybe they did not require the module where this
+                * hook is defined yet?
+                *
+                * If the require 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.
+                */
+
+               SV *psv = newSVpvn("require ", 8);
+               sv_catpv(psv, class);
+
+               TRACEME(("No STORABLE_thaw defined for objects of class %s", class));
+               TRACEME(("Going to require module '%s' with '%s'", class, SvPVX(psv)));
+
+               perl_eval_sv(psv, G_DISCARD);
+               sv_free(psv);
+
+               /*
+                * We cache results of pkg_can, so we need to uncache before attempting
+                * the lookup again.
+                */
+
+               pkg_uncache(cxt->hook, SvSTASH(sv), "STORABLE_thaw");
+               hook = pkg_can(cxt->hook, SvSTASH(sv), "STORABLE_thaw");
+
+               if (!hook)
+                       CROAK(("No STORABLE_thaw defined for objects of class %s "
+                                       "(even after a \"require %s;\")", class, class));
+       }
 
        /*
         * If we don't have an `av' yet, prepare one.
@@ -3361,7 +3492,7 @@ static SV *retrieve_tied_idx(stcxt_t *cxt)
  */
 static SV *retrieve_lscalar(stcxt_t *cxt)
 {
-       STRLEN len;
+       I32 len;
        SV *sv;
 
        RLEN(len);
@@ -3387,7 +3518,8 @@ static SV *retrieve_lscalar(stcxt_t *cxt)
        SvCUR_set(sv, len);                             /* Record C string length */
        *SvEND(sv) = '\0';                              /* Ensure it's null terminated anyway */
        (void) SvPOK_only(sv);                  /* Validate string pointer */
-       SvTAINT(sv);                                    /* External data cannot be trusted */
+       if (cxt->s_tainted)                             /* Is input source tainted? */
+               SvTAINT(sv);                            /* External data cannot be trusted */
 
        TRACEME(("large scalar len %"IVdf" '%s'", len, SvPVX(sv)));
        TRACEME(("ok (retrieve_lscalar at 0x%"UVxf")", PTR2UV(sv)));
@@ -3446,13 +3578,52 @@ static SV *retrieve_scalar(stcxt_t *cxt)
        }
 
        (void) SvPOK_only(sv);                  /* Validate string pointer */
-       SvTAINT(sv);                                    /* External data cannot be trusted */
+       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(stcxt_t *cxt)
+{
+       SV *sv;
+
+       TRACEME(("retrieve_utf8str"));
+
+       sv = retrieve_scalar(cxt);
+       if (sv)
+               SvUTF8_on(sv);
+
+       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(stcxt_t *cxt)
+{
+       SV *sv;
+
+       TRACEME(("retrieve_lutf8str"));
+
+       sv = retrieve_lscalar(cxt);
+       if (sv)
+               SvUTF8_on(sv);
+
+       return sv;
+}
+
+/*
  * retrieve_integer
  *
  * Retrieve defined integer.
@@ -3484,11 +3655,11 @@ static SV *retrieve_integer(stcxt_t *cxt)
 static SV *retrieve_netint(stcxt_t *cxt)
 {
        SV *sv;
-       int iv;
+       I32 iv;
 
        TRACEME(("retrieve_netint (#%d)", cxt->tagnum));
 
-       READ(&iv, sizeof(iv));
+       READ_I32(iv);
 #ifdef HAS_NTOHL
        sv = newSViv((int) ntohl(iv));
        TRACEME(("network integer %d", (int) ntohl(iv)));
@@ -4010,6 +4181,12 @@ magic_ok:
        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"));
+       }
+
        return &PL_sv_undef;    /* OK */
 }
 
@@ -4052,7 +4229,7 @@ static SV *retrieve(stcxt_t *cxt)
                        I32 tagn;
                        svh = hv_fetch(cxt->hseen, (char *) &tag, sizeof(tag), FALSE);
                        if (!svh)
-                               CROAK(("Old tag 0x%x should have been mapped already", tag));
+                               CROAK(("Old tag 0x%x should have been mapped already", (unsigned)tag));
                        tagn = SvIV(*svh);      /* Mapped tag number computed earlier below */
 
                        /*
@@ -4061,7 +4238,7 @@ static SV *retrieve(stcxt_t *cxt)
 
                        svh = av_fetch(cxt->aseen, tagn, FALSE);
                        if (!svh)
-                               CROAK(("Object #%d should have been retrieved already", tagn));
+                               CROAK(("Object #%d should have been retrieved already", (int)tagn));
                        sv = *svh;
                        TRACEME(("has retrieved #%d at 0x%"UVxf, tagn, PTR2UV(sv)));
                        SvREFCNT_inc(sv);       /* One more reference to this same sv */
@@ -4098,11 +4275,11 @@ again:
 
        if (type == SX_OBJECT) {
                I32 tag;
-               READ(&tag, sizeof(I32));
+               READ_I32(tag);
                tag = ntohl(tag);
                svh = av_fetch(cxt->aseen, tag, FALSE);
                if (!svh)
-                       CROAK(("Object #%d should have been retrieved already", tag));
+                       CROAK(("Object #%d should have been retrieved already", (int)tag));
                sv = *svh;
                TRACEME(("had retrieved #%d at 0x%"UVxf, tag, PTR2UV(sv)));
                SvREFCNT_inc(sv);       /* One more reference to this same sv */
@@ -4172,6 +4349,7 @@ static SV *do_retrieve(
 {
        dSTCXT;
        SV *sv;
+       int is_tainted;                         /* Is input source tainted? */
        struct extendable msave;        /* Where potentially valid mbuf is saved */
 
        TRACEME(("do_retrieve (optype = 0x%x)", optype));
@@ -4194,7 +4372,7 @@ static SV *do_retrieve(
         * free up memory for them now.
         */
 
-       if (cxt->dirty)
+       if (cxt->s_dirty)
                clean_context(cxt);
 
        /*
@@ -4208,7 +4386,7 @@ static SV *do_retrieve(
        cxt->entry++;
 
        ASSERT(cxt->entry == 1, ("starting new recursion"));
-       ASSERT(!cxt->dirty, ("clean context"));
+       ASSERT(!cxt->s_dirty, ("clean context"));
 
        /*
         * Prepare context.
@@ -4243,7 +4421,19 @@ static SV *do_retrieve(
        TRACEME(("data stored in %s format",
                cxt->netorder ? "net order" : "native"));
 
-       init_retrieve_context(cxt, optype);
+       /*
+        * 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(cxt, optype, is_tainted);
 
        ASSERT(is_retrieving(), ("within retrieve operation"));
 
@@ -4373,7 +4563,7 @@ SV *dclone(SV *sv)
         * free up memory for them now.
         */
 
-       if (cxt->dirty)
+       if (cxt->s_dirty)
                clean_context(cxt);
 
        /*
@@ -4396,14 +4586,23 @@ SV *dclone(SV *sv)
         * Now, `cxt' may refer to a new context.
         */
 
-       ASSERT(!cxt->dirty, ("clean 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);
-       out = do_retrieve((PerlIO*) 0, Nullsv, ST_CLONE);       /* Will free non-root context */
+
+       /*
+        * 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((PerlIO*) 0, Nullsv, ST_CLONE);
 
        TRACEME(("dclone returns 0x%"UVxf, PTR2UV(out)));