*/
/*
- * $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
#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
-#endif
-#ifndef NVef
+#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"
#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 */
#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 */
#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.
#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 */
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 */
#endif /* < perl5.004_68 */
#define dSTCXT_PTR(T,name) \
- T name = (T)(perinterp_sv && SvIOK(perinterp_sv)\
- ? INT2PTR(T, 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)
* 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
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)) \
} else \
return (SV *) 0; \
} while (0)
+#endif
#define MBUF_READ(x,s) do { \
if ((mptr + (s)) <= mend) { \
} \
} 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)) \
memcpy(mptr, &i, sizeof(int)); \
mptr += sizeof(int); \
} while (0)
+#endif
#define MBUF_WRITE(x,s) do { \
MBUF_CHK(s); \
} while (0)
/*
- * LOW_32BITS
- *
- * Keep only the low 32 bits of a pointer (used for tags, which are not
- * really pointers).
- */
-
-#if PTRSIZE <= 4
-#define LOW_32BITS(x) ((I32) (x))
-#else
-#define LOW_32BITS(x) ((I32) ((unsigned long) (x) & 0xffffffffUL))
-#endif
-
-/*
* Possible return values for sv_type().
*/
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...
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 { \
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().
*/
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 { \
*/
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);
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);
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 */
};
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 */
};
sv_free((SV *) cxt->hook);
cxt->entry = 0;
- cxt->dirty = 0;
+ cxt->s_dirty = 0;
}
/*
*
* 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"));
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 */
}
*
* 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"));
sv_free((SV *) cxt->hseen); /* optional HV, for backward compat. */
cxt->entry = 0;
- cxt->dirty = 0;
+ cxt->s_dirty = 0;
}
/*
{
TRACEME(("clean_context"));
- ASSERT(cxt->dirty, ("dirty context"));
+ ASSERT(cxt->s_dirty, ("dirty context"));
if (cxt->optype & ST_RETRIEVE)
clean_retrieve_context(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;
TRACEME(("free_context"));
- ASSERT(!cxt->dirty, ("clean context"));
+ ASSERT(!cxt->s_dirty, ("clean context"));
ASSERT(prev, ("not freeing root context"));
if (kbuf)
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));
}
/*
+ * 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.
return (SV *) 0;
} else {
TRACEME(("cached %s->%s: 0x%"UVxf,
- HvNAME(pkg), method,
- PTR2UV(sv)));
+ HvNAME(pkg), method, PTR2UV(sv)));
return 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);
*/
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));
* 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 */
}
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);
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")",
* 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;
* 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;
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;
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;
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.
*
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
}
/*
+ * 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.
*/
* 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> */
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)));
}
}
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;
}
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;
}
* free up memory for them now.
*/
- if (cxt->dirty)
+ if (cxt->s_dirty)
clean_context(cxt);
/*
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
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 */
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));
*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));
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);
}
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.
*/
static SV *retrieve_lscalar(stcxt_t *cxt)
{
- STRLEN len;
+ I32 len;
SV *sv;
RLEN(len);
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)));
}
(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.
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)));
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 */
}
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 */
/*
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 */
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 */
{
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));
* free up memory for them now.
*/
- if (cxt->dirty)
+ if (cxt->s_dirty)
clean_context(cxt);
/*
cxt->entry++;
ASSERT(cxt->entry == 1, ("starting new recursion"));
- ASSERT(!cxt->dirty, ("clean context"));
+ ASSERT(!cxt->s_dirty, ("clean context"));
/*
* Prepare context.
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"));
* free up memory for them now.
*/
- if (cxt->dirty)
+ if (cxt->s_dirty)
clean_context(cxt);
/*
* 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)));