This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
pp_hot.c: Add -Dr messages
[perl5.git] / pp_pack.c
index 204b17c..909eb45 100644 (file)
--- a/pp_pack.c
+++ b/pp_pack.c
@@ -1,7 +1,7 @@
 /*    pp_pack.c
  *
 /*    pp_pack.c
  *
- *    Copyright (C) 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
- *    2000, 2001, 2002, 2003, 2004, 2005, by Larry Wall and others
+ *    Copyright (C) 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
+ *    2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 by Larry Wall and others
  *
  *    You may distribute under the terms of either the GNU General Public
  *    License or the Artistic License, as specified in the README file.
  *
  *    You may distribute under the terms of either the GNU General Public
  *    License or the Artistic License, as specified in the README file.
@@ -14,6 +14,8 @@
  * wooden spoon, a short two-pronged fork and some skewers were stowed; and
  * hidden at the bottom of the pack in a flat wooden box a dwindling treasure,
  * some salt.
  * wooden spoon, a short two-pronged fork and some skewers were stowed; and
  * hidden at the bottom of the pack in a flat wooden box a dwindling treasure,
  * some salt.
+ *
+ *     [p.653 of _The Lord of the Rings_, IV/iv: "Of Herbs and Stewed Rabbit"]
  */
 
 /* This file contains pp ("push/pop") functions that
  */
 
 /* This file contains pp ("push/pop") functions that
  * other pp*.c files for the rest of the pp_ functions.
  */
 
  * other pp*.c files for the rest of the pp_ functions.
  */
 
-
 #include "EXTERN.h"
 #define PERL_IN_PP_PACK_C
 #include "perl.h"
 
 #include "EXTERN.h"
 #define PERL_IN_PP_PACK_C
 #include "perl.h"
 
+/* Types used by pack/unpack */ 
+typedef enum {
+  e_no_len,     /* no length  */
+  e_number,     /* number, [] */
+  e_star        /* asterisk   */
+} howlen_t;
+
+typedef struct tempsym {
+  const char*    patptr;   /* current template char */
+  const char*    patend;   /* one after last char   */
+  const char*    grpbeg;   /* 1st char of ()-group  */
+  const char*    grpend;   /* end of ()-group       */
+  I32      code;     /* template code (!<>)   */
+  I32      length;   /* length/repeat count   */
+  howlen_t howlen;   /* how length is given   */ 
+  int      level;    /* () nesting level      */
+  U32      flags;    /* /=4, comma=2, pack=1  */
+                     /*   and group modifiers */
+  STRLEN   strbeg;   /* offset of group start */
+  struct tempsym *previous; /* previous group */
+} tempsym_t;
+
+#define TEMPSYM_INIT(symptr, p, e, f) \
+    STMT_START {       \
+       (symptr)->patptr   = (p);       \
+       (symptr)->patend   = (e);       \
+       (symptr)->grpbeg   = NULL;      \
+       (symptr)->grpend   = NULL;      \
+       (symptr)->grpend   = NULL;      \
+       (symptr)->code     = 0;         \
+       (symptr)->length   = 0;         \
+       (symptr)->howlen   = e_no_len;  \
+       (symptr)->level    = 0;         \
+       (symptr)->flags    = (f);       \
+       (symptr)->strbeg   = 0;         \
+       (symptr)->previous = NULL;      \
+   } STMT_END
+
+typedef union {
+    NV nv;
+    U8 bytes[sizeof(NV)];
+} NV_bytes;
+
+#if defined(HAS_LONG_DOUBLE) && defined(USE_LONG_DOUBLE)
+typedef union {
+    long double ld;
+    U8 bytes[sizeof(long double)];
+} ld_bytes;
+#endif
+
 #if PERL_VERSION >= 9
 # define PERL_PACK_CAN_BYTEORDER
 # define PERL_PACK_CAN_SHRIEKSIGN
 #if PERL_VERSION >= 9
 # define PERL_PACK_CAN_BYTEORDER
 # define PERL_PACK_CAN_SHRIEKSIGN
 #define PUSH32(utf8, cur, p) PUSH_BYTES(utf8, cur, OFF32(p), SIZE32)
 
 /* Only to be used inside a loop (see the break) */
 #define PUSH32(utf8, cur, p) PUSH_BYTES(utf8, cur, OFF32(p), SIZE32)
 
 /* Only to be used inside a loop (see the break) */
-#define SHIFT_VAR(utf8, s, strend, var, datumtype)     \
+#define SHIFT_BYTES(utf8, s, strend, buf, len, datumtype)      \
 STMT_START {                                           \
     if (utf8) {                                                \
         if (!uni_to_bytes(aTHX_ &s, strend,            \
 STMT_START {                                           \
     if (utf8) {                                                \
         if (!uni_to_bytes(aTHX_ &s, strend,            \
-            (char *) &var, sizeof(var), datumtype)) break;\
+         (char *) (buf), len, datumtype)) break;       \
     } else {                                           \
     } else {                                           \
-        Copy(s, (char *) &var, sizeof(var), char);     \
-        s += sizeof(var);                              \
+        Copy(s, (char *) (buf), len, char);            \
+        s += len;                                      \
     }                                                  \
 } STMT_END
 
     }                                                  \
 } STMT_END
 
+#define SHIFT_VAR(utf8, s, strend, var, datumtype)     \
+       SHIFT_BYTES(utf8, s, strend, &(var), sizeof(var), datumtype)
+
 #define PUSH_VAR(utf8, aptr, var)      \
 #define PUSH_VAR(utf8, aptr, var)      \
-       PUSH_BYTES(utf8, aptr, (char *) &(var), sizeof(var))
+       PUSH_BYTES(utf8, aptr, &(var), sizeof(var))
 
 /* Avoid stack overflow due to pathological templates. 100 should be plenty. */
 #define MAX_SUB_TEMPLATE_LEVEL 100
 
 /* Avoid stack overflow due to pathological templates. 100 should be plenty. */
 #define MAX_SUB_TEMPLATE_LEVEL 100
@@ -139,10 +193,11 @@ S_mul128(pTHX_ SV *sv, U8 m)
   STRLEN          len;
   char           *s = SvPV(sv, len);
   char           *t;
   STRLEN          len;
   char           *s = SvPV(sv, len);
   char           *t;
-  U32             i = 0;
+
+  PERL_ARGS_ASSERT_MUL128;
 
   if (!strnEQ(s, "0000", 4)) {  /* need to grow sv */
 
   if (!strnEQ(s, "0000", 4)) {  /* need to grow sv */
-    SV             *tmpNew = newSVpvn("0000000000", 10);
+    SV * const tmpNew = newSVpvs("0000000000");
 
     sv_catsv(tmpNew, sv);
     SvREFCNT_dec(sv);          /* free old sv */
 
     sv_catsv(tmpNew, sv);
     SvREFCNT_dec(sv);          /* free old sv */
@@ -153,7 +208,7 @@ S_mul128(pTHX_ SV *sv, U8 m)
   while (!*t)                   /* trailing '\0'? */
     t--;
   while (t > s) {
   while (!*t)                   /* trailing '\0'? */
     t--;
   while (t > s) {
-    i = ((*t - '0') << 7) + m;
+    const U32 i = ((*t - '0') << 7) + m;
     *(t--) = '0' + (char)(i % 10);
     m = (char)(i / 10);
   }
     *(t--) = '0' + (char)(i % 10);
     m = (char)(i / 10);
   }
@@ -183,9 +238,9 @@ S_mul128(pTHX_ SV *sv, U8 m)
 #define TYPE_NO_MODIFIERS(t)   ((t) & 0xFF)
 
 #ifdef PERL_PACK_CAN_SHRIEKSIGN
 #define TYPE_NO_MODIFIERS(t)   ((t) & 0xFF)
 
 #ifdef PERL_PACK_CAN_SHRIEKSIGN
-#define SHRIEKING_ALLOWED_TYPES "sSiIlLxXnNvV"
+# define SHRIEKING_ALLOWED_TYPES "sSiIlLxXnNvV@."
 #else
 #else
-#define SHRIEKING_ALLOWED_TYPES "sSiIlLxX"
+# define SHRIEKING_ALLOWED_TYPES "sSiIlLxX"
 #endif
 
 #ifndef PERL_PACK_CAN_BYTEORDER
 #endif
 
 #ifndef PERL_PACK_CAN_BYTEORDER
@@ -197,12 +252,14 @@ S_mul128(pTHX_ SV *sv, U8 m)
 
 # define DO_BO_UNPACK(var, type)
 # define DO_BO_PACK(var, type)
 
 # define DO_BO_UNPACK(var, type)
 # define DO_BO_PACK(var, type)
-# define DO_BO_UNPACK_PTR(var, type, pre_cast)
-# define DO_BO_PACK_PTR(var, type, pre_cast)
+# define DO_BO_UNPACK_PTR(var, type, pre_cast, post_cast)
+# define DO_BO_PACK_PTR(var, type, pre_cast, post_cast)
 # define DO_BO_UNPACK_N(var, type)
 # define DO_BO_PACK_N(var, type)
 # define DO_BO_UNPACK_P(var)
 # define DO_BO_PACK_P(var)
 # define DO_BO_UNPACK_N(var, type)
 # define DO_BO_PACK_N(var, type)
 # define DO_BO_UNPACK_P(var)
 # define DO_BO_PACK_P(var)
+# define DO_BO_UNPACK_PC(var)
+# define DO_BO_PACK_PC(var)
 
 #else /* PERL_PACK_CAN_BYTEORDER */
 
 
 #else /* PERL_PACK_CAN_BYTEORDER */
 
@@ -229,28 +286,28 @@ S_mul128(pTHX_ SV *sv, U8 m)
           }                                                                   \
         } STMT_END
 
           }                                                                   \
         } STMT_END
 
-# define DO_BO_UNPACK_PTR(var, type, pre_cast)                                \
+# define DO_BO_UNPACK_PTR(var, type, pre_cast, post_cast)                     \
         STMT_START {                                                          \
           switch (TYPE_ENDIANNESS(datumtype)) {                               \
             case TYPE_IS_BIG_ENDIAN:                                          \
         STMT_START {                                                          \
           switch (TYPE_ENDIANNESS(datumtype)) {                               \
             case TYPE_IS_BIG_ENDIAN:                                          \
-              var = (void *) my_betoh ## type ((pre_cast) var);               \
+              var = (post_cast*) my_betoh ## type ((pre_cast) var);           \
               break;                                                          \
             case TYPE_IS_LITTLE_ENDIAN:                                       \
               break;                                                          \
             case TYPE_IS_LITTLE_ENDIAN:                                       \
-              var = (void *) my_letoh ## type ((pre_cast) var);               \
+              var = (post_cast *) my_letoh ## type ((pre_cast) var);          \
               break;                                                          \
             default:                                                          \
               break;                                                          \
           }                                                                   \
         } STMT_END
 
               break;                                                          \
             default:                                                          \
               break;                                                          \
           }                                                                   \
         } STMT_END
 
-# define DO_BO_PACK_PTR(var, type, pre_cast)                                  \
+# define DO_BO_PACK_PTR(var, type, pre_cast, post_cast)                       \
         STMT_START {                                                          \
           switch (TYPE_ENDIANNESS(datumtype)) {                               \
             case TYPE_IS_BIG_ENDIAN:                                          \
         STMT_START {                                                          \
           switch (TYPE_ENDIANNESS(datumtype)) {                               \
             case TYPE_IS_BIG_ENDIAN:                                          \
-              var = (void *) my_htobe ## type ((pre_cast) var);               \
+              var = (post_cast *) my_htobe ## type ((pre_cast) var);          \
               break;                                                          \
             case TYPE_IS_LITTLE_ENDIAN:                                       \
               break;                                                          \
             case TYPE_IS_LITTLE_ENDIAN:                                       \
-              var = (void *) my_htole ## type ((pre_cast) var);               \
+              var = (post_cast *) my_htole ## type ((pre_cast) var);          \
               break;                                                          \
             default:                                                          \
               break;                                                          \
               break;                                                          \
             default:                                                          \
               break;                                                          \
@@ -274,14 +331,32 @@ S_mul128(pTHX_ SV *sv, U8 m)
          } STMT_END
 
 # if PTRSIZE == INTSIZE
          } STMT_END
 
 # if PTRSIZE == INTSIZE
-#  define DO_BO_UNPACK_P(var)  DO_BO_UNPACK_PTR(var, i, int)
-#  define DO_BO_PACK_P(var)    DO_BO_PACK_PTR(var, i, int)
+#  define DO_BO_UNPACK_P(var)  DO_BO_UNPACK_PTR(var, i, int, void)
+#  define DO_BO_PACK_P(var)    DO_BO_PACK_PTR(var, i, int, void)
+#  define DO_BO_UNPACK_PC(var) DO_BO_UNPACK_PTR(var, i, int, char)
+#  define DO_BO_PACK_PC(var)   DO_BO_PACK_PTR(var, i, int, char)
 # elif PTRSIZE == LONGSIZE
 # elif PTRSIZE == LONGSIZE
-#  define DO_BO_UNPACK_P(var)  DO_BO_UNPACK_PTR(var, l, long)
-#  define DO_BO_PACK_P(var)    DO_BO_PACK_PTR(var, l, long)
+#  if LONGSIZE < IVSIZE && IVSIZE == 8
+#   define DO_BO_UNPACK_P(var) DO_BO_UNPACK_PTR(var, 64, IV, void)
+#   define DO_BO_PACK_P(var)   DO_BO_PACK_PTR(var, 64, IV, void)
+#   define DO_BO_UNPACK_PC(var)        DO_BO_UNPACK_PTR(var, 64, IV, char)
+#   define DO_BO_PACK_PC(var)  DO_BO_PACK_PTR(var, 64, IV, char)
+#  else
+#   define DO_BO_UNPACK_P(var) DO_BO_UNPACK_PTR(var, l, IV, void)
+#   define DO_BO_PACK_P(var)   DO_BO_PACK_PTR(var, l, IV, void)
+#   define DO_BO_UNPACK_PC(var)        DO_BO_UNPACK_PTR(var, l, IV, char)
+#   define DO_BO_PACK_PC(var)  DO_BO_PACK_PTR(var, l, IV, char)
+#  endif
+# elif PTRSIZE == IVSIZE
+#  define DO_BO_UNPACK_P(var)  DO_BO_UNPACK_PTR(var, l, IV, void)
+#  define DO_BO_PACK_P(var)    DO_BO_PACK_PTR(var, l, IV, void)
+#  define DO_BO_UNPACK_PC(var) DO_BO_UNPACK_PTR(var, l, IV, char)
+#  define DO_BO_PACK_PC(var)   DO_BO_PACK_PTR(var, l, IV, char)
 # else
 #  define DO_BO_UNPACK_P(var)  BO_CANT_DOIT(unpack, pointer)
 #  define DO_BO_PACK_P(var)    BO_CANT_DOIT(pack, pointer)
 # else
 #  define DO_BO_UNPACK_P(var)  BO_CANT_DOIT(unpack, pointer)
 #  define DO_BO_PACK_P(var)    BO_CANT_DOIT(pack, pointer)
+#  define DO_BO_UNPACK_PC(var) BO_CANT_DOIT(unpack, pointer)
+#  define DO_BO_PACK_PC(var)   BO_CANT_DOIT(pack, pointer)
 # endif
 
 # if defined(my_htolen) && defined(my_letohn) && \
 # endif
 
 # if defined(my_htolen) && defined(my_letohn) && \
@@ -314,238 +389,276 @@ S_mul128(pTHX_ SV *sv, U8 m)
 #define PACK_SIZE_UNPREDICTABLE                0x40    /* Not a fixed size element */
 #define PACK_SIZE_MASK                 0x3F
 
 #define PACK_SIZE_UNPREDICTABLE                0x40    /* Not a fixed size element */
 #define PACK_SIZE_MASK                 0x3F
 
-
-struct packsize_t {
-    const unsigned char *array;
-    int first;
-    int size;
-};
-
-#define PACK_SIZE_NORMAL 0
-#define PACK_SIZE_SHRIEKING 1
-
 /* These tables are regenerated by genpacksizetables.pl (and then hand pasted
    in).  You're unlikely ever to need to regenerate them.  */
 /* These tables are regenerated by genpacksizetables.pl (and then hand pasted
    in).  You're unlikely ever to need to regenerate them.  */
+
+#if TYPE_IS_SHRIEKING != 0x100
+   ++++shriek offset should be 256
+#endif
+
+typedef U8 packprops_t;
 #if 'J'-'I' == 1
 /* ASCII */
 #if 'J'-'I' == 1
 /* ASCII */
-unsigned char size_normal[53] = {
-  /* C */ sizeof(unsigned char),
+STATIC const packprops_t packprops[512] = {
+    /* normal */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0,
+    /* C */ sizeof(unsigned char),
 #if defined(HAS_LONG_DOUBLE) && defined(USE_LONG_DOUBLE)
 #if defined(HAS_LONG_DOUBLE) && defined(USE_LONG_DOUBLE)
-  /* D */ LONG_DOUBLESIZE,
+    /* D */ LONG_DOUBLESIZE,
 #else
 #else
-  0,
-#endif
-  0,
-  /* F */ NVSIZE,
-  0, 0,
-  /* I */ sizeof(unsigned int),
-  /* J */ UVSIZE,
-  0,
-  /* L */ SIZE32,
-  0,
-  /* N */ SIZE32,
-  0, 0,
+    0,
+#endif
+    0,
+    /* F */ NVSIZE,
+    0, 0,
+    /* I */ sizeof(unsigned int),
+    /* J */ UVSIZE,
+    0,
+    /* L */ SIZE32,
+    0,
+    /* N */ SIZE32,
+    0, 0,
 #if defined(HAS_QUAD)
 #if defined(HAS_QUAD)
-  /* Q */ sizeof(Uquad_t),
+    /* Q */ sizeof(Uquad_t),
 #else
 #else
-  0,
-#endif
-  0,
-  /* S */ SIZE16,
-  0,
-  /* U */ sizeof(char) | PACK_SIZE_UNPREDICTABLE,
-  /* V */ SIZE32,
-  /* W */ sizeof(unsigned char) | PACK_SIZE_UNPREDICTABLE,
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-  /* c */ sizeof(char),
-  /* d */ sizeof(double),
-  0,
-  /* f */ sizeof(float),
-  0, 0,
-  /* i */ sizeof(int),
-  /* j */ IVSIZE,
-  0,
-  /* l */ SIZE32,
-  0,
-  /* n */ SIZE16,
-  0,
-  /* p */ sizeof(char *) | PACK_SIZE_CANNOT_CSUM,
+    0,
+#endif
+    0,
+    /* S */ SIZE16,
+    0,
+    /* U */ sizeof(char) | PACK_SIZE_UNPREDICTABLE,
+    /* V */ SIZE32,
+    /* W */ sizeof(unsigned char) | PACK_SIZE_UNPREDICTABLE,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    /* c */ sizeof(char),
+    /* d */ sizeof(double),
+    0,
+    /* f */ sizeof(float),
+    0, 0,
+    /* i */ sizeof(int),
+    /* j */ IVSIZE,
+    0,
+    /* l */ SIZE32,
+    0,
+    /* n */ SIZE16,
+    0,
+    /* p */ sizeof(char *) | PACK_SIZE_CANNOT_CSUM,
 #if defined(HAS_QUAD)
 #if defined(HAS_QUAD)
-  /* q */ sizeof(Quad_t),
+    /* q */ sizeof(Quad_t),
 #else
 #else
-  0,
+    0,
 #endif
 #endif
-  0,
-  /* s */ SIZE16,
-  0, 0,
-  /* v */ SIZE16,
-  /* w */ sizeof(char) | PACK_SIZE_UNPREDICTABLE | PACK_SIZE_CANNOT_CSUM,
-};
-unsigned char size_shrieking[46] = {
-  /* I */ sizeof(unsigned int),
-  0, 0,
-  /* L */ sizeof(unsigned long),
-  0,
+    0,
+    /* s */ SIZE16,
+    0, 0,
+    /* v */ SIZE16,
+    /* w */ sizeof(char) | PACK_SIZE_UNPREDICTABLE | PACK_SIZE_CANNOT_CSUM,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0,
+    /* shrieking */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0,
+    /* I */ sizeof(unsigned int),
+    0, 0,
+    /* L */ sizeof(unsigned long),
+    0,
 #if defined(PERL_PACK_CAN_SHRIEKSIGN)
 #if defined(PERL_PACK_CAN_SHRIEKSIGN)
-  /* N */ SIZE32,
+    /* N */ SIZE32,
 #else
 #else
-  0,
+    0,
 #endif
 #endif
-  0, 0, 0, 0,
-  /* S */ sizeof(unsigned short),
-  0, 0,
+    0, 0, 0, 0,
+    /* S */ sizeof(unsigned short),
+    0, 0,
 #if defined(PERL_PACK_CAN_SHRIEKSIGN)
 #if defined(PERL_PACK_CAN_SHRIEKSIGN)
-  /* V */ SIZE32,
+    /* V */ SIZE32,
 #else
 #else
-  0,
+    0,
 #endif
 #endif
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-  /* i */ sizeof(int),
-  0, 0,
-  /* l */ sizeof(long),
-  0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0,
+    /* i */ sizeof(int),
+    0, 0,
+    /* l */ sizeof(long),
+    0,
 #if defined(PERL_PACK_CAN_SHRIEKSIGN)
 #if defined(PERL_PACK_CAN_SHRIEKSIGN)
-  /* n */ SIZE16,
+    /* n */ SIZE16,
 #else
 #else
-  0,
+    0,
 #endif
 #endif
-  0, 0, 0, 0,
-  /* s */ sizeof(short),
-  0, 0,
+    0, 0, 0, 0,
+    /* s */ sizeof(short),
+    0, 0,
 #if defined(PERL_PACK_CAN_SHRIEKSIGN)
 #if defined(PERL_PACK_CAN_SHRIEKSIGN)
-  /* v */ SIZE16
+    /* v */ SIZE16,
 #else
 #else
-  0
+    0,
 #endif
 #endif
-};
-struct packsize_t packsize[2] = {
-  {size_normal, 67, 53},
-  {size_shrieking, 73, 46}
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0
 };
 #else
 /* EBCDIC (or bust) */
 };
 #else
 /* EBCDIC (or bust) */
-unsigned char size_normal[100] = {
-  /* c */ sizeof(char),
-  /* d */ sizeof(double),
-  0,
-  /* f */ sizeof(float),
-  0, 0,
-  /* i */ sizeof(int),
-  0, 0, 0, 0, 0, 0, 0,
-  /* j */ IVSIZE,
-  0,
-  /* l */ SIZE32,
-  0,
-  /* n */ SIZE16,
-  0,
-  /* p */ sizeof(char *) | PACK_SIZE_CANNOT_CSUM,
+STATIC const packprops_t packprops[512] = {
+    /* normal */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0,
+    /* c */ sizeof(char),
+    /* d */ sizeof(double),
+    0,
+    /* f */ sizeof(float),
+    0, 0,
+    /* i */ sizeof(int),
+    0, 0, 0, 0, 0, 0, 0,
+    /* j */ IVSIZE,
+    0,
+    /* l */ SIZE32,
+    0,
+    /* n */ SIZE16,
+    0,
+    /* p */ sizeof(char *) | PACK_SIZE_CANNOT_CSUM,
 #if defined(HAS_QUAD)
 #if defined(HAS_QUAD)
-  /* q */ sizeof(Quad_t),
+    /* q */ sizeof(Quad_t),
 #else
 #else
-  0,
-#endif
-  0, 0, 0, 0, 0, 0, 0, 0, 0,
-  /* s */ SIZE16,
-  0, 0,
-  /* v */ SIZE16,
-  /* w */ sizeof(char) | PACK_SIZE_UNPREDICTABLE | PACK_SIZE_CANNOT_CSUM,
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-  0, 0,
-  /* C */ sizeof(unsigned char),
+    0,
+#endif
+    0, 0, 0, 0, 0, 0, 0, 0, 0,
+    /* s */ SIZE16,
+    0, 0,
+    /* v */ SIZE16,
+    /* w */ sizeof(char) | PACK_SIZE_UNPREDICTABLE | PACK_SIZE_CANNOT_CSUM,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    /* C */ sizeof(unsigned char),
 #if defined(HAS_LONG_DOUBLE) && defined(USE_LONG_DOUBLE)
 #if defined(HAS_LONG_DOUBLE) && defined(USE_LONG_DOUBLE)
-  /* D */ LONG_DOUBLESIZE,
+    /* D */ LONG_DOUBLESIZE,
 #else
 #else
-  0,
-#endif
-  0,
-  /* F */ NVSIZE,
-  0, 0,
-  /* I */ sizeof(unsigned int),
-  0, 0, 0, 0, 0, 0, 0,
-  /* J */ UVSIZE,
-  0,
-  /* L */ SIZE32,
-  0,
-  /* N */ SIZE32,
-  0, 0,
+    0,
+#endif
+    0,
+    /* F */ NVSIZE,
+    0, 0,
+    /* I */ sizeof(unsigned int),
+    0, 0, 0, 0, 0, 0, 0,
+    /* J */ UVSIZE,
+    0,
+    /* L */ SIZE32,
+    0,
+    /* N */ SIZE32,
+    0, 0,
 #if defined(HAS_QUAD)
 #if defined(HAS_QUAD)
-  /* Q */ sizeof(Uquad_t),
+    /* Q */ sizeof(Uquad_t),
 #else
 #else
-  0,
-#endif
-  0, 0, 0, 0, 0, 0, 0, 0, 0,
-  /* S */ SIZE16,
-  0,
-  /* U */ sizeof(char) | PACK_SIZE_UNPREDICTABLE,
-  /* V */ SIZE32,
-  /* W */ sizeof(unsigned char) | PACK_SIZE_UNPREDICTABLE,
-};
-unsigned char size_shrieking[93] = {
-  /* i */ sizeof(int),
-  0, 0, 0, 0, 0, 0, 0, 0, 0,
-  /* l */ sizeof(long),
-  0,
+    0,
+#endif
+    0, 0, 0, 0, 0, 0, 0, 0, 0,
+    /* S */ SIZE16,
+    0,
+    /* U */ sizeof(char) | PACK_SIZE_UNPREDICTABLE,
+    /* V */ SIZE32,
+    /* W */ sizeof(unsigned char) | PACK_SIZE_UNPREDICTABLE,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0,
+    /* shrieking */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0,
+    /* i */ sizeof(int),
+    0, 0, 0, 0, 0, 0, 0, 0, 0,
+    /* l */ sizeof(long),
+    0,
 #if defined(PERL_PACK_CAN_SHRIEKSIGN)
 #if defined(PERL_PACK_CAN_SHRIEKSIGN)
-  /* n */ SIZE16,
+    /* n */ SIZE16,
 #else
 #else
-  0,
+    0,
 #endif
 #endif
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-  /* s */ sizeof(short),
-  0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    /* s */ sizeof(short),
+    0, 0,
 #if defined(PERL_PACK_CAN_SHRIEKSIGN)
 #if defined(PERL_PACK_CAN_SHRIEKSIGN)
-  /* v */ SIZE16,
+    /* v */ SIZE16,
 #else
 #else
-  0,
-#endif
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-  0, 0, 0, 0, 0, 0, 0, 0, 0,
-  /* I */ sizeof(unsigned int),
-  0, 0, 0, 0, 0, 0, 0, 0, 0,
-  /* L */ sizeof(unsigned long),
-  0,
+    0,
+#endif
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0,
+    /* I */ sizeof(unsigned int),
+    0, 0, 0, 0, 0, 0, 0, 0, 0,
+    /* L */ sizeof(unsigned long),
+    0,
 #if defined(PERL_PACK_CAN_SHRIEKSIGN)
 #if defined(PERL_PACK_CAN_SHRIEKSIGN)
-  /* N */ SIZE32,
+    /* N */ SIZE32,
 #else
 #else
-  0,
+    0,
 #endif
 #endif
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-  /* S */ sizeof(unsigned short),
-  0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    /* S */ sizeof(unsigned short),
+    0, 0,
 #if defined(PERL_PACK_CAN_SHRIEKSIGN)
 #if defined(PERL_PACK_CAN_SHRIEKSIGN)
-  /* V */ SIZE32
+    /* V */ SIZE32,
 #else
 #else
-  0
+    0,
 #endif
 #endif
-};
-struct packsize_t packsize[2] = {
-  {size_normal, 131, 100},
-  {size_shrieking, 137, 93}
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0
 };
 #endif
 
 STATIC U8
 };
 #endif
 
 STATIC U8
-uni_to_byte(pTHX_ char **s, const char *end, I32 datumtype)
+uni_to_byte(pTHX_ const char **s, const char *end, I32 datumtype)
 {
 {
-    UV val;
     STRLEN retlen;
     STRLEN retlen;
-    val = utf8n_to_uvchr((U8 *) *s, end-*s, &retlen,
+    UV val = utf8n_to_uvchr((U8 *) *s, end-*s, &retlen,
                         ckWARN(WARN_UTF8) ? 0 : UTF8_ALLOW_ANY);
                         ckWARN(WARN_UTF8) ? 0 : UTF8_ALLOW_ANY);
-    /* We try to process malformed UTF-8 as much as possible (preferrably with
+    /* We try to process malformed UTF-8 as much as possible (preferably with
        warnings), but these two mean we make no progress in the string and
        might enter an infinite loop */
     if (retlen == (STRLEN) -1 || retlen == 0)
        Perl_croak(aTHX_ "Malformed UTF-8 string in '%c' format in unpack",
                   (int) TYPE_NO_MODIFIERS(datumtype));
     if (val >= 0x100) {
        warnings), but these two mean we make no progress in the string and
        might enter an infinite loop */
     if (retlen == (STRLEN) -1 || retlen == 0)
        Perl_croak(aTHX_ "Malformed UTF-8 string in '%c' format in unpack",
                   (int) TYPE_NO_MODIFIERS(datumtype));
     if (val >= 0x100) {
-       if (ckWARN(WARN_UNPACK))
-       Perl_warner(aTHX_ packWARN(WARN_UNPACK),
-                   "Character in '%c' format wrapped in unpack",
-                   (int) TYPE_NO_MODIFIERS(datumtype));
+       Perl_ck_warner(aTHX_ packWARN(WARN_UNPACK),
+                      "Character in '%c' format wrapped in unpack",
+                      (int) TYPE_NO_MODIFIERS(datumtype));
        val &= 0xff;
     }
     *s += retlen;
        val &= 0xff;
     }
     *s += retlen;
-    return val;
+    return (U8)val;
 }
 
 #define SHIFT_BYTE(utf8, s, strend, datumtype) ((utf8) ? \
 }
 
 #define SHIFT_BYTE(utf8, s, strend, datumtype) ((utf8) ? \
@@ -553,13 +666,13 @@ uni_to_byte(pTHX_ char **s, const char *end, I32 datumtype)
        *(U8 *)(s)++)
 
 STATIC bool
        *(U8 *)(s)++)
 
 STATIC bool
-uni_to_bytes(pTHX_ char **s, char *end, char *buf, int buf_len, I32 datumtype)
+uni_to_bytes(pTHX_ const char **s, const char *end, const char *buf, int buf_len, I32 datumtype)
 {
     UV val;
     STRLEN retlen;
 {
     UV val;
     STRLEN retlen;
-    char *from = *s;
+    const char *from = *s;
     int bad = 0;
     int bad = 0;
-    U32 flags = ckWARN(WARN_UTF8) ?
+    const U32 flags = ckWARN(WARN_UTF8) ?
        UTF8_CHECK_ONLY : (UTF8_CHECK_ONLY | UTF8_ALLOW_ANY);
     for (;buf_len > 0; buf_len--) {
        if (from >= end) return FALSE;
        UTF8_CHECK_ONLY : (UTF8_CHECK_ONLY | UTF8_ALLOW_ANY);
     for (;buf_len > 0; buf_len--) {
        if (from >= end) return FALSE;
@@ -572,37 +685,37 @@ uni_to_bytes(pTHX_ char **s, char *end, char *buf, int buf_len, I32 datumtype)
            bad |= 2;
            val &= 0xff;
        }
            bad |= 2;
            val &= 0xff;
        }
-       *(U8 *)buf++ = val;
+       *(U8 *)buf++ = (U8)val;
     }
     /* We have enough characters for the buffer. Did we have problems ? */
     if (bad) {
        if (bad & 1) {
            /* Rewalk the string fragment while warning */
     }
     /* We have enough characters for the buffer. Did we have problems ? */
     if (bad) {
        if (bad & 1) {
            /* Rewalk the string fragment while warning */
-           char *ptr;
-           flags = ckWARN(WARN_UTF8) ? 0 : UTF8_ALLOW_ANY;
+           const char *ptr;
+           const int flags = ckWARN(WARN_UTF8) ? 0 : UTF8_ALLOW_ANY;
            for (ptr = *s; ptr < from; ptr += UTF8SKIP(ptr)) {
                if (ptr >= end) break;
                utf8n_to_uvuni((U8 *) ptr, end-ptr, &retlen, flags);
            }
            if (from > end) from = end;
        }
            for (ptr = *s; ptr < from; ptr += UTF8SKIP(ptr)) {
                if (ptr >= end) break;
                utf8n_to_uvuni((U8 *) ptr, end-ptr, &retlen, flags);
            }
            if (from > end) from = end;
        }
-       if ((bad & 2) && ckWARN(WARN_UNPACK))
-           Perl_warner(aTHX_ packWARN(datumtype & TYPE_IS_PACK ? 
+       if ((bad & 2))
+           Perl_ck_warner(aTHX_ packWARN(datumtype & TYPE_IS_PACK ?
                                       WARN_PACK : WARN_UNPACK),
                                       WARN_PACK : WARN_UNPACK),
-                       "Character(s) in '%c' format wrapped in %s",
-                       (int) TYPE_NO_MODIFIERS(datumtype), 
-                       datumtype & TYPE_IS_PACK ? "pack" : "unpack");
+                          "Character(s) in '%c' format wrapped in %s",
+                          (int) TYPE_NO_MODIFIERS(datumtype),
+                          datumtype & TYPE_IS_PACK ? "pack" : "unpack");
     }
     *s = from;
     return TRUE;
 }
 
 STATIC bool
     }
     *s = from;
     return TRUE;
 }
 
 STATIC bool
-next_uni_uu(pTHX_ char **s, const char *end, I32 *out)
+next_uni_uu(pTHX_ const char **s, const char *end, I32 *out)
 {
 {
-    UV val;
+    dVAR;
     STRLEN retlen;
     STRLEN retlen;
-    val = utf8n_to_uvchr((U8 *) *s, end-*s, &retlen, UTF8_CHECK_ONLY);
+    const UV val = utf8n_to_uvchr((U8 *) *s, end-*s, &retlen, UTF8_CHECK_ONLY);
     if (val >= 0x100 || !ISUUCHAR(val) ||
        retlen == (STRLEN) -1 || retlen == 0) {
        *out = 0;
     if (val >= 0x100 || !ISUUCHAR(val) ||
        retlen == (STRLEN) -1 || retlen == 0) {
        *out = 0;
@@ -613,38 +726,33 @@ next_uni_uu(pTHX_ char **s, const char *end, I32 *out)
     return TRUE;
 }
 
     return TRUE;
 }
 
-STATIC void
-bytes_to_uni(pTHX_ U8 *start, STRLEN len, char **dest) {
-    U8 buffer[UTF8_MAXLEN];
-    U8 *end = start + len;
-    char *d = *dest;
+STATIC char *
+S_bytes_to_uni(const U8 *start, STRLEN len, char *dest) {
+    const U8 * const end = start + len;
+
+    PERL_ARGS_ASSERT_BYTES_TO_UNI;
+
     while (start < end) {
     while (start < end) {
-       int length =
-           uvuni_to_utf8_flags(buffer, NATIVE_TO_UNI(*start), 0) - buffer;
-       switch(length) {
-         case 1:
-           *d++ = buffer[0];
-           break;
-         case 2:
-           *d++ = buffer[0];
-           *d++ = buffer[1];
-           break;
-         default:
-           Perl_croak(aTHX_ "Perl bug: value %d UTF-8 expands to %d bytes",
-                      *start, length);
+       const UV uv = NATIVE_TO_ASCII(*start);
+       if (UNI_IS_INVARIANT(uv))
+           *dest++ = (char)(U8)UTF_TO_NATIVE(uv);
+       else {
+           *dest++ = (char)(U8)UTF8_EIGHT_BIT_HI(uv);
+           *dest++ = (char)(U8)UTF8_EIGHT_BIT_LO(uv);
        }
        start++;
     }
        }
        start++;
     }
-    *dest = d;
+    return dest;
 }
 
 }
 
-#define PUSH_BYTES(utf8, cur, buf, len)                        \
-STMT_START {                                           \
-    if (utf8) bytes_to_uni(aTHX_ buf, len, &(cur));    \
-    else {                                             \
-       Copy(buf, cur, len, char);                      \
-       (cur) += (len);                                 \
-    }                                                  \
+#define PUSH_BYTES(utf8, cur, buf, len)                                \
+STMT_START {                                                   \
+    if (utf8)                                                  \
+       (cur) = bytes_to_uni((U8 *) buf, len, (cur));           \
+    else {                                                     \
+       Copy(buf, cur, len, char);                              \
+       (cur) += (len);                                         \
+    }                                                          \
 } STMT_END
 
 #define GROWING(utf8, cat, start, cur, in_len) \
 } STMT_END
 
 #define GROWING(utf8, cat, start, cur, in_len) \
@@ -652,20 +760,20 @@ STMT_START {                                      \
     STRLEN glen = (in_len);                    \
     if (utf8) glen *= UTF8_EXPAND;             \
     if ((cur) + glen >= (start) + SvLEN(cat)) {        \
     STRLEN glen = (in_len);                    \
     if (utf8) glen *= UTF8_EXPAND;             \
     if ((cur) + glen >= (start) + SvLEN(cat)) {        \
-       (start) = sv_exp_grow(aTHX_ cat, glen); \
+       (start) = sv_exp_grow(cat, glen);       \
        (cur) = (start) + SvCUR(cat);           \
     }                                          \
 } STMT_END
 
 #define PUSH_GROWING_BYTES(utf8, cat, start, cur, buf, in_len) \
 STMT_START {                                   \
        (cur) = (start) + SvCUR(cat);           \
     }                                          \
 } STMT_END
 
 #define PUSH_GROWING_BYTES(utf8, cat, start, cur, buf, in_len) \
 STMT_START {                                   \
-    STRLEN glen = (in_len);                    \
+    const STRLEN glen = (in_len);              \
     STRLEN gl = glen;                          \
     if (utf8) gl *= UTF8_EXPAND;               \
     if ((cur) + gl >= (start) + SvLEN(cat)) {  \
         *cur = '\0';                           \
     STRLEN gl = glen;                          \
     if (utf8) gl *= UTF8_EXPAND;               \
     if ((cur) + gl >= (start) + SvLEN(cat)) {  \
         *cur = '\0';                           \
-        SvCUR(cat) = (cur) - (start);          \
-       (start) = sv_exp_grow(aTHX_ cat, gl);   \
+        SvCUR_set((cat), (cur) - (start));     \
+       (start) = sv_exp_grow(cat, gl);         \
        (cur) = (start) + SvCUR(cat);           \
     }                                          \
     PUSH_BYTES(utf8, cur, buf, glen);          \
        (cur) = (start) + SvCUR(cat);           \
     }                                          \
     PUSH_BYTES(utf8, cur, buf, glen);          \
@@ -674,8 +782,8 @@ STMT_START {                                        \
 #define PUSH_BYTE(utf8, s, byte)               \
 STMT_START {                                   \
     if (utf8) {                                        \
 #define PUSH_BYTE(utf8, s, byte)               \
 STMT_START {                                   \
     if (utf8) {                                        \
-       U8 au8 = (byte);                        \
-       bytes_to_uni(aTHX_ &au8, 1, &(s));      \
+       const U8 au8 = (byte);                  \
+       (s) = bytes_to_uni(&au8, 1, (s));       \
     } else *(U8 *)(s)++ = (byte);              \
 } STMT_END
 
     } else *(U8 *)(s)++ = (byte);              \
 } STMT_END
 
@@ -692,23 +800,27 @@ STMT_START {                                                      \
     str += retlen;                                             \
 } STMT_END
 
     str += retlen;                                             \
 } STMT_END
 
+static const char *_action( const tempsym_t* symptr )
+{
+    return (const char *)(( symptr->flags & FLAG_PACK ) ? "pack" : "unpack");
+}
+
 /* Returns the sizeof() struct described by pat */
 STATIC I32
 S_measure_struct(pTHX_ tempsym_t* symptr)
 {
     I32 total = 0;
 
 /* Returns the sizeof() struct described by pat */
 STATIC I32
 S_measure_struct(pTHX_ tempsym_t* symptr)
 {
     I32 total = 0;
 
+    PERL_ARGS_ASSERT_MEASURE_STRUCT;
+
     while (next_symbol(symptr)) {
        I32 len;
     while (next_symbol(symptr)) {
        I32 len;
-       int star, size;
-       int which = (symptr->code & TYPE_IS_SHRIEKING) ?
-           PACK_SIZE_SHRIEKING : PACK_SIZE_NORMAL;
-       int offset = TYPE_NO_MODIFIERS(symptr->code) - packsize[which].first;
+       int size;
 
         switch (symptr->howlen) {
 
         switch (symptr->howlen) {
-        case e_star:
+         case e_star:
            Perl_croak(aTHX_ "Within []-length '*' not allowed in %s",
            Perl_croak(aTHX_ "Within []-length '*' not allowed in %s",
-                       symptr->flags & FLAG_PACK ? "pack" : "unpack" );
+                        _action( symptr ) );
             break;
          default:
            /* e_no_len and e_number */
             break;
          default:
            /* e_no_len and e_number */
@@ -716,42 +828,44 @@ S_measure_struct(pTHX_ tempsym_t* symptr)
            break;
         }
 
            break;
         }
 
-       if ((offset >= 0) && (offset < packsize[which].size))
-           size = packsize[which].array[offset] & PACK_SIZE_MASK;
-       else
-           size = 0;
-
+       size = packprops[TYPE_NO_ENDIANNESS(symptr->code)] & PACK_SIZE_MASK;
        if (!size) {
        if (!size) {
+            int star;
            /* endianness doesn't influence the size of a type */
            switch(TYPE_NO_ENDIANNESS(symptr->code)) {
            default:
                Perl_croak(aTHX_ "Invalid type '%c' in %s",
                           (int)TYPE_NO_MODIFIERS(symptr->code),
            /* endianness doesn't influence the size of a type */
            switch(TYPE_NO_ENDIANNESS(symptr->code)) {
            default:
                Perl_croak(aTHX_ "Invalid type '%c' in %s",
                           (int)TYPE_NO_MODIFIERS(symptr->code),
-                          symptr->flags & FLAG_PACK ? "pack" : "unpack" );
+                           _action( symptr ) );
+#ifdef PERL_PACK_CAN_SHRIEKSIGN
+           case '.' | TYPE_IS_SHRIEKING:
+           case '@' | TYPE_IS_SHRIEKING:
+#endif
            case '@':
            case '@':
+           case '.':
            case '/':
            case 'U':                   /* XXXX Is it correct? */
            case 'w':
            case 'u':
                Perl_croak(aTHX_ "Within []-length '%c' not allowed in %s",
            case '/':
            case 'U':                   /* XXXX Is it correct? */
            case 'w':
            case 'u':
                Perl_croak(aTHX_ "Within []-length '%c' not allowed in %s",
-                          (int)symptr->code,
-                          symptr->flags & FLAG_PACK ? "pack" : "unpack" );
+                          (int) TYPE_NO_MODIFIERS(symptr->code),
+                           _action( symptr ) );
            case '%':
                size = 0;
                break;
            case '(':
            case '%':
                size = 0;
                break;
            case '(':
-               {
-                   tempsym_t savsym = *symptr;
-                   symptr->patptr = savsym.grpbeg;
-                   symptr->patend = savsym.grpend;
-                   /* XXXX Theoretically, we need to measure many times at
-                      different positions, since the subexpression may contain
-                      alignment commands, but be not of aligned length.
-                      Need to detect this and croak().  */
-                   size = measure_struct(symptr);
-                   *symptr = savsym;
-                   break;
-               }
+           {
+               tempsym_t savsym = *symptr;
+               symptr->patptr = savsym.grpbeg;
+               symptr->patend = savsym.grpend;
+               /* XXXX Theoretically, we need to measure many times at
+                  different positions, since the subexpression may contain
+                  alignment commands, but be not of aligned length.
+                  Need to detect this and croak().  */
+               size = measure_struct(symptr);
+               *symptr = savsym;
+               break;
+           }
            case 'X' | TYPE_IS_SHRIEKING:
                /* XXXX Is this useful?  Then need to treat MEASURE_BACKWARDS.
                 */
            case 'X' | TYPE_IS_SHRIEKING:
                /* XXXX Is this useful?  Then need to treat MEASURE_BACKWARDS.
                 */
@@ -762,8 +876,7 @@ S_measure_struct(pTHX_ tempsym_t* symptr)
            case 'X':
                size = -1;
                if (total < len)
            case 'X':
                size = -1;
                if (total < len)
-                   Perl_croak(aTHX_ "'X' outside of string in %s",
-                              symptr->flags & FLAG_PACK ? "pack" : "unpack" );
+                    Perl_croak(aTHX_ "'X' outside of string in %s", _action( symptr ) );
                break;
            case 'x' | TYPE_IS_SHRIEKING:
                if (!len)               /* Avoid division by 0 */
                break;
            case 'x' | TYPE_IS_SHRIEKING:
                if (!len)               /* Avoid division by 0 */
@@ -806,11 +919,13 @@ S_measure_struct(pTHX_ tempsym_t* symptr)
 /* locate matching closing parenthesis or bracket
  * returns char pointer to char after match, or NULL
  */
 /* locate matching closing parenthesis or bracket
  * returns char pointer to char after match, or NULL
  */
-STATIC char *
-S_group_end(pTHX_ register char *patptr, register char *patend, char ender)
+STATIC const char *
+S_group_end(pTHX_ register const char *patptr, register const char *patend, char ender)
 {
 {
+    PERL_ARGS_ASSERT_GROUP_END;
+
     while (patptr < patend) {
     while (patptr < patend) {
-       char c = *patptr++;
+       const char c = *patptr++;
 
        if (isSPACE(c))
            continue;
 
        if (isSPACE(c))
            continue;
@@ -834,11 +949,14 @@ S_group_end(pTHX_ register char *patptr, register char *patend, char ender)
 /* Convert unsigned decimal number to binary.
  * Expects a pointer to the first digit and address of length variable
  * Advances char pointer to 1st non-digit char and returns number
 /* Convert unsigned decimal number to binary.
  * Expects a pointer to the first digit and address of length variable
  * Advances char pointer to 1st non-digit char and returns number
- */ 
-STATIC char *
-S_get_num(pTHX_ register char *patptr, I32 *lenptr )
+ */
+STATIC const char *
+S_get_num(pTHX_ register const char *patptr, I32 *lenptr )
 {
   I32 len = *patptr++ - '0';
 {
   I32 len = *patptr++ - '0';
+
+  PERL_ARGS_ASSERT_GET_NUM;
+
   while (isDIGIT(*patptr)) {
     if (len >= 0x7FFFFFFF/10)
       Perl_croak(aTHX_ "pack/unpack repeat count overflow");
   while (isDIGIT(*patptr)) {
     if (len >= 0x7FFFFFFF/10)
       Perl_croak(aTHX_ "pack/unpack repeat count overflow");
@@ -854,9 +972,10 @@ S_get_num(pTHX_ register char *patptr, I32 *lenptr )
 STATIC bool
 S_next_symbol(pTHX_ tempsym_t* symptr )
 {
 STATIC bool
 S_next_symbol(pTHX_ tempsym_t* symptr )
 {
-  char* patptr = symptr->patptr; 
-  char* patend = symptr->patend; 
-  const char *allowed = "";
+  const char* patptr = symptr->patptr;
+  const char* const patend = symptr->patend;
+
+  PERL_ARGS_ASSERT_NEXT_SYMBOL;
 
   symptr->flags &= ~FLAG_SLASH;
 
 
   symptr->flags &= ~FLAG_SLASH;
 
@@ -870,7 +989,7 @@ S_next_symbol(pTHX_ tempsym_t* symptr )
       if (patptr < patend)
        patptr++;
     } else {
       if (patptr < patend)
        patptr++;
     } else {
-      /* We should have found a template code */ 
+      /* We should have found a template code */
       I32 code = *patptr++ & 0xFF;
       U32 inherited_modifiers = 0;
 
       I32 code = *patptr++ & 0xFF;
       U32 inherited_modifiers = 0;
 
@@ -878,22 +997,21 @@ S_next_symbol(pTHX_ tempsym_t* symptr )
        if (((symptr->flags & FLAG_COMMA) == 0) && ckWARN(WARN_UNPACK)){
           symptr->flags |= FLAG_COMMA;
          Perl_warner(aTHX_ packWARN(WARN_UNPACK),
        if (((symptr->flags & FLAG_COMMA) == 0) && ckWARN(WARN_UNPACK)){
           symptr->flags |= FLAG_COMMA;
          Perl_warner(aTHX_ packWARN(WARN_UNPACK),
-                     "Invalid type ',' in %s",
-                      symptr->flags & FLAG_PACK ? "pack" : "unpack" );
+                     "Invalid type ',' in %s", _action( symptr ) );
         }
        continue;
       }
         }
        continue;
       }
-      
+
       /* for '(', skip to ')' */
       /* for '(', skip to ')' */
-      if (code == '(') {  
+      if (code == '(') {
         if( isDIGIT(*patptr) || *patptr == '*' || *patptr == '[' )
           Perl_croak(aTHX_ "()-group starts with a count in %s",
         if( isDIGIT(*patptr) || *patptr == '*' || *patptr == '[' )
           Perl_croak(aTHX_ "()-group starts with a count in %s",
-                     symptr->flags & FLAG_PACK ? "pack" : "unpack" );
+                        _action( symptr ) );
         symptr->grpbeg = patptr;
         patptr = 1 + ( symptr->grpend = group_end(patptr, patend, ')') );
         if( symptr->level >= MAX_SUB_TEMPLATE_LEVEL )
          Perl_croak(aTHX_ "Too deeply nested ()-groups in %s",
         symptr->grpbeg = patptr;
         patptr = 1 + ( symptr->grpend = group_end(patptr, patend, ')') );
         if( symptr->level >= MAX_SUB_TEMPLATE_LEVEL )
          Perl_croak(aTHX_ "Too deeply nested ()-groups in %s",
-                     symptr->flags & FLAG_PACK ? "pack" : "unpack" );
+                        _action( symptr ) );
       }
 
       /* look for group modifiers to inherit */
       }
 
       /* look for group modifiers to inherit */
@@ -904,7 +1022,8 @@ S_next_symbol(pTHX_ tempsym_t* symptr )
 
       /* look for modifiers */
       while (patptr < patend) {
 
       /* look for modifiers */
       while (patptr < patend) {
-        I32 modifier = 0;
+        const char *allowed;
+        I32 modifier;
         switch (*patptr) {
           case '!':
             modifier = TYPE_IS_SHRIEKING;
         switch (*patptr) {
           case '!':
             modifier = TYPE_IS_SHRIEKING;
@@ -921,6 +1040,8 @@ S_next_symbol(pTHX_ tempsym_t* symptr )
             break;
 #endif /* PERL_PACK_CAN_BYTEORDER */
           default:
             break;
 #endif /* PERL_PACK_CAN_BYTEORDER */
           default:
+            allowed = "";
+            modifier = 0;
             break;
         }
 
             break;
         }
 
@@ -929,23 +1050,21 @@ S_next_symbol(pTHX_ tempsym_t* symptr )
 
         if (!strchr(allowed, TYPE_NO_MODIFIERS(code)))
           Perl_croak(aTHX_ "'%c' allowed only after types %s in %s", *patptr,
 
         if (!strchr(allowed, TYPE_NO_MODIFIERS(code)))
           Perl_croak(aTHX_ "'%c' allowed only after types %s in %s", *patptr,
-                     allowed, symptr->flags & FLAG_PACK ? "pack" : "unpack" );
+                        allowed, _action( symptr ) );
 
         if (TYPE_ENDIANNESS(code | modifier) == TYPE_ENDIANNESS_MASK)
           Perl_croak(aTHX_ "Can't use both '<' and '>' after type '%c' in %s",
 
         if (TYPE_ENDIANNESS(code | modifier) == TYPE_ENDIANNESS_MASK)
           Perl_croak(aTHX_ "Can't use both '<' and '>' after type '%c' in %s",
-                     (int) TYPE_NO_MODIFIERS(code),
-                     symptr->flags & FLAG_PACK ? "pack" : "unpack" );
+                     (int) TYPE_NO_MODIFIERS(code), _action( symptr ) );
         else if (TYPE_ENDIANNESS(code | modifier | inherited_modifiers) ==
                  TYPE_ENDIANNESS_MASK)
           Perl_croak(aTHX_ "Can't use '%c' in a group with different byte-order in %s",
         else if (TYPE_ENDIANNESS(code | modifier | inherited_modifiers) ==
                  TYPE_ENDIANNESS_MASK)
           Perl_croak(aTHX_ "Can't use '%c' in a group with different byte-order in %s",
-                     *patptr, symptr->flags & FLAG_PACK ? "pack" : "unpack" );
-
-        if (ckWARN(WARN_UNPACK)) {
-          if (code & modifier)
-           Perl_warner(aTHX_ packWARN(WARN_UNPACK),
-                        "Duplicate modifier '%c' after '%c' in %s",
-                        *patptr, (int) TYPE_NO_MODIFIERS(code),
-                        symptr->flags & FLAG_PACK ? "pack" : "unpack" );
+                     *patptr, _action( symptr ) );
+
+        if ((code & modifier)) {
+           Perl_ck_warner(aTHX_ packWARN(WARN_UNPACK),
+                          "Duplicate modifier '%c' after '%c' in %s",
+                          *patptr, (int) TYPE_NO_MODIFIERS(code),
+                          _action( symptr ) );
         }
 
         code |= modifier;
         }
 
         code |= modifier;
@@ -955,7 +1074,7 @@ S_next_symbol(pTHX_ tempsym_t* symptr )
       /* inherit modifiers */
       code |= inherited_modifiers;
 
       /* inherit modifiers */
       code |= inherited_modifiers;
 
-      /* look for count and/or / */ 
+      /* look for count and/or / */
       if (patptr < patend) {
        if (isDIGIT(*patptr)) {
          patptr = get_num( patptr, &symptr->length );
       if (patptr < patend) {
        if (isDIGIT(*patptr)) {
          patptr = get_num( patptr, &symptr->length );
@@ -966,7 +1085,7 @@ S_next_symbol(pTHX_ tempsym_t* symptr )
           symptr->howlen = e_star;
 
         } else if (*patptr == '[') {
           symptr->howlen = e_star;
 
         } else if (*patptr == '[') {
-          char* lenptr = ++patptr;            
+          const char* lenptr = ++patptr;
           symptr->howlen = e_number;
           patptr = group_end( patptr, patend, ']' ) + 1;
           /* what kind of [] is it? */
           symptr->howlen = e_number;
           patptr = group_end( patptr, patend, ']' ) + 1;
           /* what kind of [] is it? */
@@ -974,7 +1093,7 @@ S_next_symbol(pTHX_ tempsym_t* symptr )
             lenptr = get_num( lenptr, &symptr->length );
             if( *lenptr != ']' )
               Perl_croak(aTHX_ "Malformed integer in [] in %s",
             lenptr = get_num( lenptr, &symptr->length );
             if( *lenptr != ']' )
               Perl_croak(aTHX_ "Malformed integer in [] in %s",
-                         symptr->flags & FLAG_PACK ? "pack" : "unpack");
+                            _action( symptr ) );
           } else {
             tempsym_t savsym = *symptr;
             symptr->patend = patptr-1;
           } else {
             tempsym_t savsym = *symptr;
             symptr->patend = patptr-1;
@@ -1004,7 +1123,7 @@ S_next_symbol(pTHX_ tempsym_t* symptr )
               if (patptr < patend &&
                   (isDIGIT(*patptr) || *patptr == '*' || *patptr == '['))
                 Perl_croak(aTHX_ "'/' does not take a repeat count in %s",
               if (patptr < patend &&
                   (isDIGIT(*patptr) || *patptr == '*' || *patptr == '['))
                 Perl_croak(aTHX_ "'/' does not take a repeat count in %s",
-                           symptr->flags & FLAG_PACK ? "pack" : "unpack" );
+                            _action( symptr ) );
             }
             break;
          }
             }
             break;
          }
@@ -1016,31 +1135,34 @@ S_next_symbol(pTHX_ tempsym_t* symptr )
       }
 
       symptr->code = code;
       }
 
       symptr->code = code;
-      symptr->patptr = patptr; 
+      symptr->patptr = patptr;
       return TRUE;
     }
   }
       return TRUE;
     }
   }
-  symptr->patptr = patptr; 
+  symptr->patptr = patptr;
   return FALSE;
 }
 
 /*
   return FALSE;
 }
 
 /*
-   There is no way to cleanly handle the case where we should process the 
+   There is no way to cleanly handle the case where we should process the
    string per byte in its upgraded form while it's really in downgraded form
    string per byte in its upgraded form while it's really in downgraded form
-   (e.g. estimates like strend-s as an upper bound for the number of 
-   characters left wouldn't work). So if we foresee the need of this 
-   (pattern starts with U or contains U0), we want to work on the encoded 
-   version of the string. Users are advised to upgrade their pack string 
+   (e.g. estimates like strend-s as an upper bound for the number of
+   characters left wouldn't work). So if we foresee the need of this
+   (pattern starts with U or contains U0), we want to work on the encoded
+   version of the string. Users are advised to upgrade their pack string
    themselves if they need to do a lot of unpacks like this on it
 */
    themselves if they need to do a lot of unpacks like this on it
 */
-STATIC bool 
+STATIC bool
 need_utf8(const char *pat, const char *patend)
 {
     bool first = TRUE;
 need_utf8(const char *pat, const char *patend)
 {
     bool first = TRUE;
+
+    PERL_ARGS_ASSERT_NEED_UTF8;
+
     while (pat < patend) {
        if (pat[0] == '#') {
            pat++;
     while (pat < patend) {
        if (pat[0] == '#') {
            pat++;
-           pat = memchr(pat, '\n', patend-pat);
+           pat = (const char *) memchr(pat, '\n', patend-pat);
            if (!pat) return FALSE;
        } else if (pat[0] == 'U') {
            if (first || pat[1] == '0') return TRUE;
            if (!pat) return FALSE;
        } else if (pat[0] == 'U') {
            if (first || pat[1] == '0') return TRUE;
@@ -1052,10 +1174,12 @@ need_utf8(const char *pat, const char *patend)
 
 STATIC char
 first_symbol(const char *pat, const char *patend) {
 
 STATIC char
 first_symbol(const char *pat, const char *patend) {
+    PERL_ARGS_ASSERT_FIRST_SYMBOL;
+
     while (pat < patend) {
        if (pat[0] != '#') return pat[0];
        pat++;
     while (pat < patend) {
        if (pat[0] != '#') return pat[0];
        pat++;
-       pat = memchr(pat, '\n', patend-pat);
+       pat = (const char *) memchr(pat, '\n', patend-pat);
        if (!pat) return 0;
        pat++;
     }
        if (!pat) return 0;
        pat++;
     }
@@ -1063,40 +1187,6 @@ first_symbol(const char *pat, const char *patend) {
 }
 
 /*
 }
 
 /*
-=for apidoc unpack_str
-
-The engine implementing unpack() Perl function. Note: parameters strbeg, new_s
-and ocnt are not used. This call should not be used, use unpackstring instead.
-
-=cut */
-
-I32
-Perl_unpack_str(pTHX_ char *pat, char *patend, char *s, char *strbeg, char *strend, char **new_s, I32 ocnt, U32 flags)
-{
-    tempsym_t sym = { 0 };
-
-    if (flags & FLAG_DO_UTF8) flags |= FLAG_WAS_UTF8;
-    else if (need_utf8(pat, patend)) {
-       /* We probably should try to avoid this in case a scalar context call
-          wouldn't get to the "U0" */
-       STRLEN len = strend - s;
-       s = (char *) bytes_to_utf8(s, &len);
-       SAVEFREEPV(s);
-       strend = s + len;
-       flags |= FLAG_DO_UTF8;
-    }
-
-    if (first_symbol(pat, patend) != 'U' && (flags & FLAG_DO_UTF8))
-       flags |= FLAG_PARSE_UTF8;
-
-    sym.patptr = pat;
-    sym.patend = patend;
-    sym.flags  = flags;
-
-    return unpack_rec(&sym, s, s, strend, NULL );
-}
-
-/*
 =for apidoc unpackstring
 
 The engine implementing unpack() Perl function. C<unpackstring> puts the
 =for apidoc unpackstring
 
 The engine implementing unpack() Perl function. C<unpackstring> puts the
@@ -1106,16 +1196,18 @@ Issue C<PUTBACK> before and C<SPAGAIN> after the call to this function.
 =cut */
 
 I32
 =cut */
 
 I32
-Perl_unpackstring(pTHX_ char *pat, char *patend, char *s, char *strend, U32 flags)
+Perl_unpackstring(pTHX_ const char *pat, const char *patend, const char *s, const char *strend, U32 flags)
 {
 {
-    tempsym_t sym = { 0 };
+    tempsym_t sym;
+
+    PERL_ARGS_ASSERT_UNPACKSTRING;
 
     if (flags & FLAG_DO_UTF8) flags |= FLAG_WAS_UTF8;
     else if (need_utf8(pat, patend)) {
        /* We probably should try to avoid this in case a scalar context call
           wouldn't get to the "U0" */
        STRLEN len = strend - s;
 
     if (flags & FLAG_DO_UTF8) flags |= FLAG_WAS_UTF8;
     else if (need_utf8(pat, patend)) {
        /* We probably should try to avoid this in case a scalar context call
           wouldn't get to the "U0" */
        STRLEN len = strend - s;
-       s = (char *) bytes_to_utf8(s, &len);
+       s = (char *) bytes_to_utf8((U8 *) s, &len);
        SAVEFREEPV(s);
        strend = s + len;
        flags |= FLAG_DO_UTF8;
        SAVEFREEPV(s);
        strend = s + len;
        flags |= FLAG_DO_UTF8;
@@ -1124,34 +1216,34 @@ Perl_unpackstring(pTHX_ char *pat, char *patend, char *s, char *strend, U32 flag
     if (first_symbol(pat, patend) != 'U' && (flags & FLAG_DO_UTF8))
        flags |= FLAG_PARSE_UTF8;
 
     if (first_symbol(pat, patend) != 'U' && (flags & FLAG_DO_UTF8))
        flags |= FLAG_PARSE_UTF8;
 
-    sym.patptr = pat;
-    sym.patend = patend;
-    sym.flags  = flags;
+    TEMPSYM_INIT(&sym, pat, patend, flags);
 
     return unpack_rec(&sym, s, s, strend, NULL );
 }
 
 
     return unpack_rec(&sym, s, s, strend, NULL );
 }
 
-STATIC
-I32
-S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char **new_s )
+STATIC I32
+S_unpack_rec(pTHX_ tempsym_t* symptr, const char *s, const char *strbeg, const char *strend, const char **new_s )
 {
 {
-    dSP;
-    SV *sv;
-    I32 start_sp_offset = SP - PL_stack_base;
+    dVAR; dSP;
+    SV *sv = NULL;
+    const I32 start_sp_offset = SP - PL_stack_base;
     howlen_t howlen;
     howlen_t howlen;
-
     I32 checksum = 0;
     UV cuv = 0;
     NV cdouble = 0.0;
     const int bits_in_uv = CHAR_BIT * sizeof(cuv);
     I32 checksum = 0;
     UV cuv = 0;
     NV cdouble = 0.0;
     const int bits_in_uv = CHAR_BIT * sizeof(cuv);
-    char* strrelbeg = s;
     bool beyond = FALSE;
     bool explicit_length;
     bool beyond = FALSE;
     bool explicit_length;
-    bool unpack_only_one = (symptr->flags & FLAG_UNPACK_ONLY_ONE) != 0;
+    const bool unpack_only_one = (symptr->flags & FLAG_UNPACK_ONLY_ONE) != 0;
     bool utf8 = (symptr->flags & FLAG_PARSE_UTF8) ? 1 : 0;
 
     bool utf8 = (symptr->flags & FLAG_PARSE_UTF8) ? 1 : 0;
 
+    PERL_ARGS_ASSERT_UNPACK_REC;
+
+    symptr->strbeg = s - strbeg;
+
     while (next_symbol(symptr)) {
     while (next_symbol(symptr)) {
-       I32 len, ai32;
+       packprops_t props;
+       I32 len;
         I32 datumtype = symptr->code;
        /* do first one only unless in list context
           / is implemented by unpacking the count, then popping it from the
         I32 datumtype = symptr->code;
        /* do first one only unless in list context
           / is implemented by unpacking the count, then popping it from the
@@ -1162,8 +1254,8 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
             break;
 
         switch (howlen = symptr->howlen) {
             break;
 
         switch (howlen = symptr->howlen) {
-        case e_star:
-           len = strend - strbeg;      /* long enough */          
+         case e_star:
+           len = strend - strbeg;      /* long enough */
            break;
          default:
            /* e_no_len and e_number */
            break;
          default:
            /* e_no_len and e_number */
@@ -1174,32 +1266,22 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
         explicit_length = TRUE;
       redo_switch:
         beyond = s >= strend;
         explicit_length = TRUE;
       redo_switch:
         beyond = s >= strend;
-       {
-           struct packsize_t *pack_props =
-               &packsize[(symptr->code & TYPE_IS_SHRIEKING) ?
-                         PACK_SIZE_SHRIEKING : PACK_SIZE_NORMAL];
-           const int rawtype = TYPE_NO_MODIFIERS(datumtype);
-           int offset = rawtype - pack_props->first;
-
-           if (offset >= 0 && offset < pack_props->size) {
-               /* Data about this template letter  */
-               unsigned char data = pack_props->array[offset];
-
-               if (data) {
-                   /* data nonzero means we can process this letter.  */
-                   long size = data & PACK_SIZE_MASK;
-                   long howmany = (strend - s) / size;
-                   if (len > howmany)
-                       len = howmany;
-
-                   if (!checksum || (data & PACK_SIZE_CANNOT_CSUM)) {
-                       if (len && unpack_only_one) len = 1;
-                       EXTEND(SP, len);
-                       EXTEND_MORTAL(len);
-                   }
-               }
+
+       props = packprops[TYPE_NO_ENDIANNESS(datumtype)];
+       if (props) {
+           /* props nonzero means we can process this letter. */
+            const long size = props & PACK_SIZE_MASK;
+            const long howmany = (strend - s) / size;
+           if (len > howmany)
+               len = howmany;
+
+           if (!checksum || (props & PACK_SIZE_CANNOT_CSUM)) {
+               if (len && unpack_only_one) len = 1;
+               EXTEND(SP, len);
+               EXTEND_MORTAL(len);
            }
        }
            }
        }
+
        switch(TYPE_NO_ENDIANNESS(datumtype)) {
        default:
            Perl_croak(aTHX_ "Invalid type '%c' in unpack", (int)TYPE_NO_MODIFIERS(datumtype) );
        switch(TYPE_NO_ENDIANNESS(datumtype)) {
        default:
            Perl_croak(aTHX_ "Invalid type '%c' in unpack", (int)TYPE_NO_MODIFIERS(datumtype) );
@@ -1215,11 +1297,13 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
        case '(':
        {
             tempsym_t savsym = *symptr;
        case '(':
        {
             tempsym_t savsym = *symptr;
-           U32 group_modifiers = TYPE_MODIFIERS(datumtype & ~symptr->flags);
+            const U32 group_modifiers = TYPE_MODIFIERS(datumtype & ~symptr->flags);
            symptr->flags |= group_modifiers;
             symptr->patend = savsym.grpend;
            symptr->flags |= group_modifiers;
             symptr->patend = savsym.grpend;
+           symptr->previous = &savsym;
             symptr->level++;
            PUTBACK;
             symptr->level++;
            PUTBACK;
+           if (len && unpack_only_one) len = 1;
            while (len--) {
                symptr->patptr = savsym.grpbeg;
                if (utf8) symptr->flags |=  FLAG_PARSE_UTF8;
            while (len--) {
                symptr->patptr = savsym.grpbeg;
                if (utf8) symptr->flags |=  FLAG_PARSE_UTF8;
@@ -1229,14 +1313,46 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
                    break; /* No way to continue */
            }
            SPAGAIN;
                    break; /* No way to continue */
            }
            SPAGAIN;
-           symptr->flags &= ~group_modifiers;
-            savsym.flags = symptr->flags;
+            savsym.flags = symptr->flags & ~group_modifiers;
             *symptr = savsym;
            break;
        }
             *symptr = savsym;
            break;
        }
+#ifdef PERL_PACK_CAN_SHRIEKSIGN
+       case '.' | TYPE_IS_SHRIEKING:
+#endif
+       case '.': {
+           const char *from;
+           SV *sv;
+#ifdef PERL_PACK_CAN_SHRIEKSIGN
+           const bool u8 = utf8 && !(datumtype & TYPE_IS_SHRIEKING);
+#else /* PERL_PACK_CAN_SHRIEKSIGN */
+           const bool u8 = utf8;
+#endif
+           if (howlen == e_star) from = strbeg;
+           else if (len <= 0) from = s;
+           else {
+               tempsym_t *group = symptr;
+
+               while (--len && group) group = group->previous;
+               from = group ? strbeg + group->strbeg : strbeg;
+           }
+           sv = from <= s ?
+               newSVuv(  u8 ? (UV) utf8_length((const U8*)from, (const U8*)s) : (UV) (s-from)) :
+               newSViv(-(u8 ? (IV) utf8_length((const U8*)s, (const U8*)from) : (IV) (from-s)));
+           mXPUSHs(sv);
+           break;
+       }
+#ifdef PERL_PACK_CAN_SHRIEKSIGN
+       case '@' | TYPE_IS_SHRIEKING:
+#endif
        case '@':
        case '@':
-           if (utf8) {
-               s = strrelbeg;
+           s = strbeg + symptr->strbeg;
+#ifdef PERL_PACK_CAN_SHRIEKSIGN
+           if (utf8  && !(datumtype & TYPE_IS_SHRIEKING))
+#else /* PERL_PACK_CAN_SHRIEKSIGN */
+           if (utf8)
+#endif
+           {
                while (len > 0) {
                    if (s >= strend)
                        Perl_croak(aTHX_ "'@' outside of string in unpack");
                while (len > 0) {
                    if (s >= strend)
                        Perl_croak(aTHX_ "'@' outside of string in unpack");
@@ -1246,16 +1362,16 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
                if (s > strend)
                    Perl_croak(aTHX_ "'@' outside of string with malformed UTF-8 in unpack");
            } else {
                if (s > strend)
                    Perl_croak(aTHX_ "'@' outside of string with malformed UTF-8 in unpack");
            } else {
-           if (len > strend - strrelbeg)
-               Perl_croak(aTHX_ "'@' outside of string in unpack");
-           s = strrelbeg + len;
+               if (strend-s < len)
+                   Perl_croak(aTHX_ "'@' outside of string in unpack");
+               s += len;
            }
            break;
        case 'X' | TYPE_IS_SHRIEKING:
            if (!len)                   /* Avoid division by 0 */
                len = 1;
            if (utf8) {
            }
            break;
        case 'X' | TYPE_IS_SHRIEKING:
            if (!len)                   /* Avoid division by 0 */
                len = 1;
            if (utf8) {
-               char *hop, *last;
+               const char *hop, *last;
                I32 l = len;
                hop = last = strbeg;
                while (hop < s) {
                I32 l = len;
                hop = last = strbeg;
                while (hop < s) {
@@ -1264,7 +1380,7 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
                        last = hop;
                        l = len;
                    }
                        last = hop;
                        l = len;
                    }
-                   }
+               }
                if (last > s)
                    Perl_croak(aTHX_ "Malformed UTF-8 string in unpack");
                s = last;
                if (last > s)
                    Perl_croak(aTHX_ "Malformed UTF-8 string in unpack");
                s = last;
@@ -1284,18 +1400,20 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
                    len--;
                }
            } else {
                    len--;
                }
            } else {
-           if (len > s - strbeg)
-               Perl_croak(aTHX_ "'X' outside of string in unpack" );
-           s -= len;
+               if (len > s - strbeg)
+                   Perl_croak(aTHX_ "'X' outside of string in unpack" );
+               s -= len;
            }
            break;
            }
            break;
-       case 'x' | TYPE_IS_SHRIEKING:
+       case 'x' | TYPE_IS_SHRIEKING: {
+            I32 ai32;
            if (!len)                   /* Avoid division by 0 */
                len = 1;
            if (!len)                   /* Avoid division by 0 */
                len = 1;
-           if (utf8) ai32 = utf8_length(strbeg, s) % len;
-           else      ai32 = (s - strbeg)           % len;
+           if (utf8) ai32 = utf8_length((U8 *) strbeg, (U8 *) s) % len;
+           else      ai32 = (s - strbeg)                         % len;
            if (ai32 == 0) break;
            len -= ai32;
            if (ai32 == 0) break;
            len -= ai32;
+            }
            /* FALL THROUGH */
        case 'x':
            if (utf8) {
            /* FALL THROUGH */
        case 'x':
            if (utf8) {
@@ -1306,9 +1424,9 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
                    len--;
                }
            } else {
                    len--;
                }
            } else {
-           if (len > strend - s)
-               Perl_croak(aTHX_ "'x' outside of string in unpack");
-           s += len;
+               if (len > strend - s)
+                   Perl_croak(aTHX_ "'x' outside of string in unpack");
+               s += len;
            }
            break;
        case '/':
            }
            break;
        case '/':
@@ -1324,13 +1442,13 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
            }
            if (utf8) {
                I32 l;
            }
            if (utf8) {
                I32 l;
-               char *hop;
+               const char *hop;
                for (l=len, hop=s; l>0; l--, hop += UTF8SKIP(hop)) {
                    if (hop >= strend) {
                        if (hop > strend)
                            Perl_croak(aTHX_ "Malformed UTF-8 string in unpack");
                        break;
                for (l=len, hop=s; l>0; l--, hop += UTF8SKIP(hop)) {
                    if (hop >= strend) {
                        if (hop > strend)
                            Perl_croak(aTHX_ "Malformed UTF-8 string in unpack");
                        break;
-               }
+                   }
                }
                if (hop > strend)
                    Perl_croak(aTHX_ "Malformed UTF-8 string in unpack");
                }
                if (hop > strend)
                    Perl_croak(aTHX_ "Malformed UTF-8 string in unpack");
@@ -1340,7 +1458,7 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
 
            if (datumtype == 'Z') {
                /* 'Z' strips stuff after first null */
 
            if (datumtype == 'Z') {
                /* 'Z' strips stuff after first null */
-               char *ptr, *end;
+               const char *ptr, *end;
                end = s + len;
                for (ptr = s; ptr < end; ptr++) if (*ptr == 0) break;
                sv = newSVpvn(s, ptr-s);
                end = s + len;
                for (ptr = s; ptr < end; ptr++) if (*ptr == 0) break;
                sv = newSVpvn(s, ptr-s);
@@ -1348,10 +1466,20 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
                    len = ptr-s + (ptr != strend ? 1 : 0);
            } else if (datumtype == 'A') {
                /* 'A' strips both nulls and spaces */
                    len = ptr-s + (ptr != strend ? 1 : 0);
            } else if (datumtype == 'A') {
                /* 'A' strips both nulls and spaces */
-               char *ptr;
-               for (ptr = s+len-1; ptr >= s; ptr--)
-                   if (*ptr != 0 && !isSPACE(*ptr)) break;
-               ptr++;
+               const char *ptr;
+               if (utf8 && (symptr->flags & FLAG_WAS_UTF8)) {
+                   for (ptr = s+len-1; ptr >= s; ptr--)
+                       if (*ptr != 0 && !UTF8_IS_CONTINUATION(*ptr) &&
+                           !is_utf8_space((U8 *) ptr)) break;
+                   if (ptr >= s) ptr += UTF8SKIP(ptr);
+                   else ptr++;
+                   if (ptr > s+len)
+                       Perl_croak(aTHX_ "Malformed UTF-8 string in unpack");
+               } else {
+                   for (ptr = s+len-1; ptr >= s; ptr--)
+                       if (*ptr != 0 && !isSPACE(*ptr)) break;
+                   ptr++;
+               }
                sv = newSVpvn(s, ptr-s);
            } else sv = newSVpvn(s, len);
 
                sv = newSVpvn(s, ptr-s);
            } else sv = newSVpvn(s, len);
 
@@ -1361,7 +1489,7 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
                if (!(symptr->flags & FLAG_WAS_UTF8))
                    sv_utf8_downgrade(sv, 0);
            }
                if (!(symptr->flags & FLAG_WAS_UTF8))
                    sv_utf8_downgrade(sv, 0);
            }
-           XPUSHs(sv_2mortal(sv));
+           mXPUSHs(sv);
            s += len;
            break;
        case 'B':
            s += len;
            break;
        case 'B':
@@ -1370,30 +1498,16 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
            if (howlen == e_star || len > (strend - s) * 8)
                len = (strend - s) * 8;
            if (checksum) {
            if (howlen == e_star || len > (strend - s) * 8)
                len = (strend - s) * 8;
            if (checksum) {
-               if (!PL_bitcount) {
-                   int bits;
-                   Newz(601, PL_bitcount, 256, char);
-                   for (bits = 1; bits < 256; bits++) {
-                       if (bits & 1)   PL_bitcount[bits]++;
-                       if (bits & 2)   PL_bitcount[bits]++;
-                       if (bits & 4)   PL_bitcount[bits]++;
-                       if (bits & 8)   PL_bitcount[bits]++;
-                       if (bits & 16)  PL_bitcount[bits]++;
-                       if (bits & 32)  PL_bitcount[bits]++;
-                       if (bits & 64)  PL_bitcount[bits]++;
-                       if (bits & 128) PL_bitcount[bits]++;
-                   }
-               }
                if (utf8)
                    while (len >= 8 && s < strend) {
                        cuv += PL_bitcount[uni_to_byte(aTHX_ &s, strend, datumtype)];
                        len -= 8;
                    }
                else
                if (utf8)
                    while (len >= 8 && s < strend) {
                        cuv += PL_bitcount[uni_to_byte(aTHX_ &s, strend, datumtype)];
                        len -= 8;
                    }
                else
-               while (len >= 8) {
+                   while (len >= 8) {
                        cuv += PL_bitcount[*(U8 *)s++];
                        cuv += PL_bitcount[*(U8 *)s++];
-                   len -= 8;
-               }
+                       len -= 8;
+                   }
                if (len && s < strend) {
                    U8 bits;
                    bits = SHIFT_BYTE(utf8, s, strend, datumtype);
                if (len && s < strend) {
                    U8 bits;
                    bits = SHIFT_BYTE(utf8, s, strend, datumtype);
@@ -1407,16 +1521,16 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
                            if (bits & 0x80) cuv++;
                            bits <<= 1;
                        }
                            if (bits & 0x80) cuv++;
                            bits <<= 1;
                        }
-                   }
+               }
                break;
            }
 
                break;
            }
 
-           sv = sv_2mortal(NEWSV(35, len ? len : 1));
+           sv = sv_2mortal(newSV(len ? len : 1));
            SvPOK_on(sv);
            str = SvPVX(sv);
            if (datumtype == 'b') {
                U8 bits = 0;
            SvPOK_on(sv);
            str = SvPVX(sv);
            if (datumtype == 'b') {
                U8 bits = 0;
-               ai32 = len;
+               const I32 ai32 = len;
                for (len = 0; len < ai32; len++) {
                    if (len & 7) bits >>= 1;
                    else if (utf8) {
                for (len = 0; len < ai32; len++) {
                    if (len & 7) bits >>= 1;
                    else if (utf8) {
@@ -1427,7 +1541,7 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
                }
            } else {
                U8 bits = 0;
                }
            } else {
                U8 bits = 0;
-               ai32 = len;
+               const I32 ai32 = len;
                for (len = 0; len < ai32; len++) {
                    if (len & 7) bits <<= 1;
                    else if (utf8) {
                for (len = 0; len < ai32; len++) {
                    if (len & 7) bits <<= 1;
                    else if (utf8) {
@@ -1438,91 +1552,106 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
                }
            }
            *str = '\0';
                }
            }
            *str = '\0';
-           SvCUR_set(sv, str - SvPVX(sv));
+           SvCUR_set(sv, str - SvPVX_const(sv));
            XPUSHs(sv);
            break;
        }
        case 'H':
        case 'h': {
            XPUSHs(sv);
            break;
        }
        case 'H':
        case 'h': {
-           char *str;
-             /* Preliminary length estimate, acceptable for utf8 too */
+           char *str = NULL;
+           /* Preliminary length estimate, acceptable for utf8 too */
            if (howlen == e_star || len > (strend - s) * 2)
                len = (strend - s) * 2;
            if (howlen == e_star || len > (strend - s) * 2)
                len = (strend - s) * 2;
-             sv = sv_2mortal(NEWSV(35, len ? len : 1));
-           SvPOK_on(sv);
-           str = SvPVX(sv);
+           if (!checksum) {
+               sv = sv_2mortal(newSV(len ? len : 1));
+               SvPOK_on(sv);
+               str = SvPVX(sv);
+           }
            if (datumtype == 'h') {
                U8 bits = 0;
            if (datumtype == 'h') {
                U8 bits = 0;
-                 ai32 = len;
-                 for (len = 0; len < ai32; len++) {
-                     if (len & 1) bits >>= 4;
-                     else if (utf8) {
-                         if (s >= strend) break;
+               I32 ai32 = len;
+               for (len = 0; len < ai32; len++) {
+                   if (len & 1) bits >>= 4;
+                   else if (utf8) {
+                       if (s >= strend) break;
                        bits = uni_to_byte(aTHX_ &s, strend, datumtype);
                        bits = uni_to_byte(aTHX_ &s, strend, datumtype);
-                     } else bits = * (U8 *) s++;
-                   *str++ = PL_hexdigit[bits & 15];
+                   } else bits = * (U8 *) s++;
+                   if (!checksum)
+                       *str++ = PL_hexdigit[bits & 15];
                }
            } else {
                U8 bits = 0;
                }
            } else {
                U8 bits = 0;
-               ai32 = len;
+               const I32 ai32 = len;
                for (len = 0; len < ai32; len++) {
                    if (len & 1) bits <<= 4;
                    else if (utf8) {
                        if (s >= strend) break;
                        bits = uni_to_byte(aTHX_ &s, strend, datumtype);
                    } else bits = *(U8 *) s++;
                for (len = 0; len < ai32; len++) {
                    if (len & 1) bits <<= 4;
                    else if (utf8) {
                        if (s >= strend) break;
                        bits = uni_to_byte(aTHX_ &s, strend, datumtype);
                    } else bits = *(U8 *) s++;
-                   *str++ = PL_hexdigit[(bits >> 4) & 15];
+                   if (!checksum)
+                       *str++ = PL_hexdigit[(bits >> 4) & 15];
                }
            }
                }
            }
-           *str = '\0';
-           SvCUR_set(sv, str - SvPVX(sv));
-           XPUSHs(sv);
+           if (!checksum) {
+               *str = '\0';
+               SvCUR_set(sv, str - SvPVX_const(sv));
+               XPUSHs(sv);
+           }
            break;
        }
            break;
        }
+       case 'C':
+            if (len == 0) {
+                if (explicit_length)
+                   /* Switch to "character" mode */
+                   utf8 = (symptr->flags & FLAG_DO_UTF8) ? 1 : 0;
+               break;
+           }
+           /* FALL THROUGH */
        case 'c':
        case 'c':
-           while (len-- > 0) {
-               int aint = SHIFT_BYTE(utf8, s, strend, datumtype);
-               if (aint >= 128)        /* fake up signed chars */
+           while (len-- > 0 && s < strend) {
+               int aint;
+               if (utf8)
+                 {
+                   STRLEN retlen;
+                   aint = utf8n_to_uvchr((U8 *) s, strend-s, &retlen,
+                                ckWARN(WARN_UTF8) ? 0 : UTF8_ALLOW_ANY);
+                   if (retlen == (STRLEN) -1 || retlen == 0)
+                       Perl_croak(aTHX_ "Malformed UTF-8 string in unpack");
+                   s += retlen;
+                 }
+               else
+                 aint = *(U8 *)(s)++;
+               if (aint >= 128 && datumtype != 'C')    /* fake up signed chars */
                    aint -= 256;
                if (!checksum)
                    aint -= 256;
                if (!checksum)
-                   PUSHs(sv_2mortal(newSViv((IV)aint)));
+                   mPUSHi(aint);
                else if (checksum > bits_in_uv)
                    cdouble += (NV)aint;
                else
                    cuv += aint;
            }
            break;
                else if (checksum > bits_in_uv)
                    cdouble += (NV)aint;
                else
                    cuv += aint;
            }
            break;
-       case 'C':
        case 'W':
          W_checksum:
        case 'W':
          W_checksum:
-            if (len == 0) {
-                if (explicit_length && datumtype == 'C') 
-                   /* Switch to "character" mode */
-                   utf8 = (symptr->flags & FLAG_DO_UTF8) ? 1 : 0;
-               break;
-           }
-           if (datumtype == 'C' ? 
-                (symptr->flags & FLAG_DO_UTF8) &&
-               !(symptr->flags & FLAG_WAS_UTF8) : utf8) {
+           if (utf8) {
                while (len-- > 0 && s < strend) {
                while (len-- > 0 && s < strend) {
-                   UV val;
                    STRLEN retlen;
                    STRLEN retlen;
-                   val = utf8n_to_uvchr((U8 *) s, strend-s, &retlen,
+                   const UV val = utf8n_to_uvchr((U8 *) s, strend-s, &retlen,
                                         ckWARN(WARN_UTF8) ? 0 : UTF8_ALLOW_ANY);
                    if (retlen == (STRLEN) -1 || retlen == 0)
                        Perl_croak(aTHX_ "Malformed UTF-8 string in unpack");
                    s += retlen;
                    if (!checksum)
                                         ckWARN(WARN_UTF8) ? 0 : UTF8_ALLOW_ANY);
                    if (retlen == (STRLEN) -1 || retlen == 0)
                        Perl_croak(aTHX_ "Malformed UTF-8 string in unpack");
                    s += retlen;
                    if (!checksum)
-                       PUSHs(sv_2mortal(newSVuv((UV) val)));
+                       mPUSHu(val);
                    else if (checksum > bits_in_uv)
                        cdouble += (NV) val;
                    else
                        cuv += val;
                    else if (checksum > bits_in_uv)
                        cdouble += (NV) val;
                    else
                        cuv += val;
-           }
+               }
            } else if (!checksum)
                while (len-- > 0) {
            } else if (!checksum)
                while (len-- > 0) {
-                   U8 ch = *(U8 *) s++;
-                   PUSHs(sv_2mortal(newSVuv((UV) ch)));
+                   const U8 ch = *(U8 *) s++;
+                   mPUSHu(ch);
            }
            else if (checksum > bits_in_uv)
                while (len-- > 0) cdouble += (NV) *(U8 *) s++;
            }
            else if (checksum > bits_in_uv)
                while (len-- > 0) cdouble += (NV) *(U8 *) s++;
@@ -1531,7 +1660,7 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
            break;
        case 'U':
            if (len == 0) {
            break;
        case 'U':
            if (len == 0) {
-                if (explicit_length) {
+                if (explicit_length && howlen != e_star) {
                    /* Switch to "bytes in UTF-8" mode */
                    if (symptr->flags & FLAG_DO_UTF8) utf8 = 0;
                    else
                    /* Switch to "bytes in UTF-8" mode */
                    if (symptr->flags & FLAG_DO_UTF8) utf8 = 0;
                    else
@@ -1541,36 +1670,36 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
                break;
            }
            if (len > strend - s) len = strend - s;
                break;
            }
            if (len > strend - s) len = strend - s;
-               if (!checksum) {
+           if (!checksum) {
                if (len && unpack_only_one) len = 1;
                EXTEND(SP, len);
                EXTEND_MORTAL(len);
                if (len && unpack_only_one) len = 1;
                EXTEND(SP, len);
                EXTEND_MORTAL(len);
-               }
+           }
            while (len-- > 0 && s < strend) {
                STRLEN retlen;
                UV auv;
                if (utf8) {
                    U8 result[UTF8_MAXLEN];
            while (len-- > 0 && s < strend) {
                STRLEN retlen;
                UV auv;
                if (utf8) {
                    U8 result[UTF8_MAXLEN];
-                   char *ptr;
+                   const char *ptr = s;
                    STRLEN len;
                    STRLEN len;
-                   ptr = s;
                    /* Bug: warns about bad utf8 even if we are short on bytes
                       and will break out of the loop */
                    /* Bug: warns about bad utf8 even if we are short on bytes
                       and will break out of the loop */
-                   if (!uni_to_bytes(aTHX_ &ptr, strend, result, 1, 'U'))
+                   if (!uni_to_bytes(aTHX_ &ptr, strend, (char *) result, 1,
+                                     'U'))
                        break;
                    len = UTF8SKIP(result);
                        break;
                    len = UTF8SKIP(result);
-                   if (!uni_to_bytes(aTHX_ &ptr, strend, 
-                                     &result[1], len-1, 'U')) break;
-                   auv = utf8n_to_uvuni(result, len, &retlen, ckWARN(WARN_UTF8) ? 0 : UTF8_ALLOW_ANYUV);
+                   if (!uni_to_bytes(aTHX_ &ptr, strend,
+                                     (char *) &result[1], len-1, 'U')) break;
+                   auv = utf8n_to_uvuni(result, len, &retlen, UTF8_ALLOW_DEFAULT);
                    s = ptr;
                } else {
                    s = ptr;
                } else {
-                   auv = utf8n_to_uvuni((U8*)s, strend - s, &retlen, ckWARN(WARN_UTF8) ? 0 : UTF8_ALLOW_ANYUV);
+                   auv = utf8n_to_uvuni((U8*)s, strend - s, &retlen, UTF8_ALLOW_DEFAULT);
                    if (retlen == (STRLEN) -1 || retlen == 0)
                        Perl_croak(aTHX_ "Malformed UTF-8 string in unpack");
                    s += retlen;
                }
                if (!checksum)
                    if (retlen == (STRLEN) -1 || retlen == 0)
                        Perl_croak(aTHX_ "Malformed UTF-8 string in unpack");
                    s += retlen;
                }
                if (!checksum)
-                   PUSHs(sv_2mortal(newSVuv((UV) auv)));
+                   mPUSHu(auv);
                else if (checksum > bits_in_uv)
                    cdouble += (NV) auv;
                else
                else if (checksum > bits_in_uv)
                    cdouble += (NV) auv;
                else
@@ -1584,7 +1713,7 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
                SHIFT_VAR(utf8, s, strend, ashort, datumtype);
                DO_BO_UNPACK(ashort, s);
                if (!checksum)
                SHIFT_VAR(utf8, s, strend, ashort, datumtype);
                DO_BO_UNPACK(ashort, s);
                if (!checksum)
-                   PUSHs(sv_2mortal(newSViv((IV)ashort)));
+                   mPUSHi(ashort);
                else if (checksum > bits_in_uv)
                    cdouble += (NV)ashort;
                else
                else if (checksum > bits_in_uv)
                    cdouble += (NV)ashort;
                else
@@ -1608,7 +1737,7 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
                    ai16 -= 65536;
 #endif
                if (!checksum)
                    ai16 -= 65536;
 #endif
                if (!checksum)
-                   PUSHs(sv_2mortal(newSViv((IV)ai16)));
+                   mPUSHi(ai16);
                else if (checksum > bits_in_uv)
                    cdouble += (NV)ai16;
                else
                else if (checksum > bits_in_uv)
                    cdouble += (NV)ai16;
                else
@@ -1622,7 +1751,7 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
                SHIFT_VAR(utf8, s, strend, aushort, datumtype);
                DO_BO_UNPACK(aushort, s);
                if (!checksum)
                SHIFT_VAR(utf8, s, strend, aushort, datumtype);
                DO_BO_UNPACK(aushort, s);
                if (!checksum)
-                   PUSHs(sv_2mortal(newSVuv((UV) aushort)));
+                   mPUSHu(aushort);
                else if (checksum > bits_in_uv)
                    cdouble += (NV)aushort;
                else
                else if (checksum > bits_in_uv)
                    cdouble += (NV)aushort;
                else
@@ -1630,7 +1759,7 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
            }
            break;
 #else
            }
            break;
 #else
-            /* Fallhrough! */
+            /* Fallthrough! */
 #endif
        case 'v':
        case 'n':
 #endif
        case 'v':
        case 'n':
@@ -1651,7 +1780,7 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
                    au16 = vtohs(au16);
 #endif
                if (!checksum)
                    au16 = vtohs(au16);
 #endif
                if (!checksum)
-                   PUSHs(sv_2mortal(newSVuv((UV)au16)));
+                   mPUSHu(au16);
                else if (checksum > bits_in_uv)
                    cdouble += (NV) au16;
                else
                else if (checksum > bits_in_uv)
                    cdouble += (NV) au16;
                else
@@ -1676,7 +1805,7 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
                    ai16 = (I16) vtohs((U16) ai16);
 # endif /* HAS_VTOHS */
                if (!checksum)
                    ai16 = (I16) vtohs((U16) ai16);
 # endif /* HAS_VTOHS */
                if (!checksum)
-                   PUSHs(sv_2mortal(newSViv((IV)ai16)));
+                   mPUSHi(ai16);
                else if (checksum > bits_in_uv)
                    cdouble += (NV) ai16;
                else
                else if (checksum > bits_in_uv)
                    cdouble += (NV) ai16;
                else
@@ -1691,7 +1820,7 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
                SHIFT_VAR(utf8, s, strend, aint, datumtype);
                DO_BO_UNPACK(aint, i);
                if (!checksum)
                SHIFT_VAR(utf8, s, strend, aint, datumtype);
                DO_BO_UNPACK(aint, i);
                if (!checksum)
-                   PUSHs(sv_2mortal(newSViv((IV)aint)));
+                   mPUSHi(aint);
                else if (checksum > bits_in_uv)
                    cdouble += (NV)aint;
                else
                else if (checksum > bits_in_uv)
                    cdouble += (NV)aint;
                else
@@ -1705,7 +1834,7 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
                SHIFT_VAR(utf8, s, strend, auint, datumtype);
                DO_BO_UNPACK(auint, i);
                if (!checksum)
                SHIFT_VAR(utf8, s, strend, auint, datumtype);
                DO_BO_UNPACK(auint, i);
                if (!checksum)
-                   PUSHs(sv_2mortal(newSVuv((UV)auint)));
+                   mPUSHu(auint);
                else if (checksum > bits_in_uv)
                    cdouble += (NV)auint;
                else
                else if (checksum > bits_in_uv)
                    cdouble += (NV)auint;
                else
@@ -1726,7 +1855,7 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
                Perl_croak(aTHX_ "'j' not supported on this platform");
 #endif
                if (!checksum)
                Perl_croak(aTHX_ "'j' not supported on this platform");
 #endif
                if (!checksum)
-                   PUSHs(sv_2mortal(newSViv(aiv)));
+                   mPUSHi(aiv);
                else if (checksum > bits_in_uv)
                    cdouble += (NV)aiv;
                else
                else if (checksum > bits_in_uv)
                    cdouble += (NV)aiv;
                else
@@ -1747,7 +1876,7 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
                Perl_croak(aTHX_ "'J' not supported on this platform");
 #endif
                if (!checksum)
                Perl_croak(aTHX_ "'J' not supported on this platform");
 #endif
                if (!checksum)
-                   PUSHs(sv_2mortal(newSVuv(auv)));
+                   mPUSHu(auv);
                else if (checksum > bits_in_uv)
                    cdouble += (NV)auv;
                else
                else if (checksum > bits_in_uv)
                    cdouble += (NV)auv;
                else
@@ -1761,7 +1890,7 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
                SHIFT_VAR(utf8, s, strend, along, datumtype);
                DO_BO_UNPACK(along, l);
                if (!checksum)
                SHIFT_VAR(utf8, s, strend, along, datumtype);
                DO_BO_UNPACK(along, l);
                if (!checksum)
-                   PUSHs(sv_2mortal(newSViv((IV)along)));
+                   mPUSHi(along);
                else if (checksum > bits_in_uv)
                    cdouble += (NV)along;
                else
                else if (checksum > bits_in_uv)
                    cdouble += (NV)along;
                else
@@ -1783,7 +1912,7 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
                if (ai32 > 2147483647) ai32 -= 4294967296;
 #endif
                if (!checksum)
                if (ai32 > 2147483647) ai32 -= 4294967296;
 #endif
                if (!checksum)
-                   PUSHs(sv_2mortal(newSViv((IV)ai32)));
+                   mPUSHi(ai32);
                else if (checksum > bits_in_uv)
                    cdouble += (NV)ai32;
                else
                else if (checksum > bits_in_uv)
                    cdouble += (NV)ai32;
                else
@@ -1797,7 +1926,7 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
                SHIFT_VAR(utf8, s, strend, aulong, datumtype);
                DO_BO_UNPACK(aulong, l);
                if (!checksum)
                SHIFT_VAR(utf8, s, strend, aulong, datumtype);
                DO_BO_UNPACK(aulong, l);
                if (!checksum)
-                   PUSHs(sv_2mortal(newSVuv((UV)aulong)));
+                   mPUSHu(aulong);
                else if (checksum > bits_in_uv)
                    cdouble += (NV)aulong;
                else
                else if (checksum > bits_in_uv)
                    cdouble += (NV)aulong;
                else
@@ -1826,11 +1955,11 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
                    au32 = vtohl(au32);
 #endif
                if (!checksum)
                    au32 = vtohl(au32);
 #endif
                if (!checksum)
-                    PUSHs(sv_2mortal(newSVuv((UV)au32)));
-                else if (checksum > bits_in_uv)
-                    cdouble += (NV)au32;
-                else
-                    cuv += au32;
+                   mPUSHu(au32);
+               else if (checksum > bits_in_uv)
+                   cdouble += (NV)au32;
+               else
+                   cuv += au32;
            }
            break;
 #ifdef PERL_PACK_CAN_SHRIEKSIGN
            }
            break;
 #ifdef PERL_PACK_CAN_SHRIEKSIGN
@@ -1851,7 +1980,7 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
                    ai32 = (I32)vtohl((U32)ai32);
 # endif
                if (!checksum)
                    ai32 = (I32)vtohl((U32)ai32);
 # endif
                if (!checksum)
-                   PUSHs(sv_2mortal(newSViv((IV)ai32)));
+                   mPUSHi(ai32);
                else if (checksum > bits_in_uv)
                    cdouble += (NV)ai32;
                else
                else if (checksum > bits_in_uv)
                    cdouble += (NV)ai32;
                else
@@ -1861,18 +1990,18 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
 #endif /* PERL_PACK_CAN_SHRIEKSIGN */
        case 'p':
            while (len-- > 0) {
 #endif /* PERL_PACK_CAN_SHRIEKSIGN */
        case 'p':
            while (len-- > 0) {
-               char *aptr;
+               const char *aptr;
                SHIFT_VAR(utf8, s, strend, aptr, datumtype);
                SHIFT_VAR(utf8, s, strend, aptr, datumtype);
-               DO_BO_UNPACK_P(aptr);
+               DO_BO_UNPACK_PC(aptr);
                /* newSVpv generates undef if aptr is NULL */
                /* newSVpv generates undef if aptr is NULL */
-               PUSHs(sv_2mortal(newSVpv(aptr, 0)));
+               mPUSHs(newSVpv(aptr, 0));
            }
            break;
        case 'w':
            {
                UV auv = 0;
                U32 bytes = 0;
            }
            break;
        case 'w':
            {
                UV auv = 0;
                U32 bytes = 0;
-               
+
                while (len > 0 && s < strend) {
                    U8 ch;
                    ch = SHIFT_BYTE(utf8, s, strend, datumtype);
                while (len > 0 && s < strend) {
                    U8 ch;
                    ch = SHIFT_BYTE(utf8, s, strend, datumtype);
@@ -1880,16 +2009,15 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
                    /* UTF8_IS_XXXXX not right here - using constant 0x80 */
                    if (ch < 0x80) {
                        bytes = 0;
                    /* UTF8_IS_XXXXX not right here - using constant 0x80 */
                    if (ch < 0x80) {
                        bytes = 0;
-                       PUSHs(sv_2mortal(newSVuv(auv)));
+                       mPUSHu(auv);
                        len--;
                        auv = 0;
                        continue;
                    }
                    if (++bytes >= sizeof(UV)) {        /* promote to string */
                        len--;
                        auv = 0;
                        continue;
                    }
                    if (++bytes >= sizeof(UV)) {        /* promote to string */
-                       char *t;
-                       STRLEN n_a;
+                       const char *t;
 
 
-                       sv = Perl_newSVpvf(aTHX_ "%.*"UVf, (int)TYPE_DIGITS(UV), auv);
+                       sv = Perl_newSVpvf(aTHX_ "%.*"UVuf, (int)TYPE_DIGITS(UV), auv);
                        while (s < strend) {
                            ch = SHIFT_BYTE(utf8, s, strend, datumtype);
                            sv = mul128(sv, (U8)(ch & 0x7f));
                        while (s < strend) {
                            ch = SHIFT_BYTE(utf8, s, strend, datumtype);
                            sv = mul128(sv, (U8)(ch & 0x7f));
@@ -1898,11 +2026,11 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
                                break;
                            }
                        }
                                break;
                            }
                        }
-                       t = SvPV(sv, n_a);
+                       t = SvPV_nolen_const(sv);
                        while (*t == '0')
                            t++;
                        sv_chop(sv, t);
                        while (*t == '0')
                            t++;
                        sv_chop(sv, t);
-                       PUSHs(sv_2mortal(sv));
+                       mPUSHs(sv);
                        len--;
                        auv = 0;
                    }
                        len--;
                        auv = 0;
                    }
@@ -1915,12 +2043,12 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
            if (symptr->howlen == e_star)
                Perl_croak(aTHX_ "'P' must have an explicit size in unpack");
            EXTEND(SP, 1);
            if (symptr->howlen == e_star)
                Perl_croak(aTHX_ "'P' must have an explicit size in unpack");
            EXTEND(SP, 1);
-           if (sizeof(char*) <= strend - s) {
+           if (s + sizeof(char*) <= strend) {
                char *aptr;
                SHIFT_VAR(utf8, s, strend, aptr, datumtype);
                char *aptr;
                SHIFT_VAR(utf8, s, strend, aptr, datumtype);
-               DO_BO_UNPACK_P(aptr);
-           /* newSVpvn generates undef if aptr is NULL */
-           PUSHs(sv_2mortal(newSVpvn(aptr, len)));
+               DO_BO_UNPACK_PC(aptr);
+               /* newSVpvn generates undef if aptr is NULL */
+               PUSHs(newSVpvn_flags(aptr, len, SVs_TEMP));
            }
            break;
 #ifdef HAS_QUAD
            }
            break;
 #ifdef HAS_QUAD
@@ -1930,8 +2058,8 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
                SHIFT_VAR(utf8, s, strend, aquad, datumtype);
                DO_BO_UNPACK(aquad, 64);
                if (!checksum)
                SHIFT_VAR(utf8, s, strend, aquad, datumtype);
                DO_BO_UNPACK(aquad, 64);
                if (!checksum)
-                    PUSHs(sv_2mortal(aquad >= IV_MIN && aquad <= IV_MAX ?
-                                    newSViv((IV)aquad) : newSVnv((NV)aquad)));
+                    mPUSHs(aquad >= IV_MIN && aquad <= IV_MAX ?
+                          newSViv((IV)aquad) : newSVnv((NV)aquad));
                else if (checksum > bits_in_uv)
                    cdouble += (NV)aquad;
                else
                else if (checksum > bits_in_uv)
                    cdouble += (NV)aquad;
                else
@@ -1944,8 +2072,8 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
                SHIFT_VAR(utf8, s, strend, auquad, datumtype);
                DO_BO_UNPACK(auquad, 64);
                if (!checksum)
                SHIFT_VAR(utf8, s, strend, auquad, datumtype);
                DO_BO_UNPACK(auquad, 64);
                if (!checksum)
-                   PUSHs(sv_2mortal(auquad <= UV_MAX ?
-                                    newSVuv((UV)auquad):newSVnv((NV)auquad)));
+                   mPUSHs(auquad <= UV_MAX ?
+                          newSVuv((UV)auquad) : newSVnv((NV)auquad));
                else if (checksum > bits_in_uv)
                    cdouble += (NV)auquad;
                else
                else if (checksum > bits_in_uv)
                    cdouble += (NV)auquad;
                else
@@ -1960,10 +2088,10 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
                SHIFT_VAR(utf8, s, strend, afloat, datumtype);
                DO_BO_UNPACK_N(afloat, float);
                if (!checksum)
                SHIFT_VAR(utf8, s, strend, afloat, datumtype);
                DO_BO_UNPACK_N(afloat, float);
                if (!checksum)
-                   PUSHs(sv_2mortal(newSVnv((NV)afloat)));
+                   mPUSHn(afloat);
                else
                    cdouble += afloat;
                else
                    cdouble += afloat;
-               }
+           }
            break;
        case 'd':
            while (len-- > 0) {
            break;
        case 'd':
            while (len-- > 0) {
@@ -1971,63 +2099,46 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
                SHIFT_VAR(utf8, s, strend, adouble, datumtype);
                DO_BO_UNPACK_N(adouble, double);
                if (!checksum)
                SHIFT_VAR(utf8, s, strend, adouble, datumtype);
                DO_BO_UNPACK_N(adouble, double);
                if (!checksum)
-                   PUSHs(sv_2mortal(newSVnv((NV)adouble)));
+                   mPUSHn(adouble);
                else
                    cdouble += adouble;
                else
                    cdouble += adouble;
-               }
+           }
            break;
        case 'F':
            while (len-- > 0) {
            break;
        case 'F':
            while (len-- > 0) {
-               NV anv;
-               SHIFT_VAR(utf8, s, strend, anv, datumtype);
-               DO_BO_UNPACK_N(anv, NV);
+               NV_bytes anv;
+               SHIFT_BYTES(utf8, s, strend, anv.bytes, sizeof(anv.bytes), datumtype);
+               DO_BO_UNPACK_N(anv.nv, NV);
                if (!checksum)
                if (!checksum)
-                   PUSHs(sv_2mortal(newSVnv(anv)));
+                   mPUSHn(anv.nv);
                else
                else
-                   cdouble += anv;
-               }
+                   cdouble += anv.nv;
+           }
            break;
 #if defined(HAS_LONG_DOUBLE) && defined(USE_LONG_DOUBLE)
        case 'D':
            while (len-- > 0) {
            break;
 #if defined(HAS_LONG_DOUBLE) && defined(USE_LONG_DOUBLE)
        case 'D':
            while (len-- > 0) {
-               long double aldouble;
-               SHIFT_VAR(utf8, s, strend, aldouble, datumtype);
-               DO_BO_UNPACK_N(aldouble, long double);
+               ld_bytes aldouble;
+               SHIFT_BYTES(utf8, s, strend, aldouble.bytes, sizeof(aldouble.bytes), datumtype);
+               DO_BO_UNPACK_N(aldouble.ld, long double);
                if (!checksum)
                if (!checksum)
-                   PUSHs(sv_2mortal(newSVnv((NV)aldouble)));
+                   mPUSHn(aldouble.ld);
                else
                else
-                   cdouble += aldouble;
+                   cdouble += aldouble.ld;
            }
            break;
 #endif
        case 'u':
            }
            break;
 #endif
        case 'u':
-           /* MKS:
-            * Initialise the decode mapping.  By using a table driven
-             * algorithm, the code will be character-set independent
-             * (and just as fast as doing character arithmetic)
-             */
-            if (PL_uudmap['M'] == 0) {
-                int i;
-
-                for (i = 0; i < sizeof(PL_uuemap); i += 1)
-                    PL_uudmap[(U8)PL_uuemap[i]] = i;
-                /*
-                 * Because ' ' and '`' map to the same value,
-                 * we need to decode them both the same.
-                 */
-                PL_uudmap[' '] = 0;
-            }
-           {
-               STRLEN l = (STRLEN) (strend - s) * 3 / 4;
-               sv = sv_2mortal(NEWSV(42, l));
+           if (!checksum) {
+                const STRLEN l = (STRLEN) (strend - s) * 3 / 4;
+               sv = sv_2mortal(newSV(l));
                if (l) SvPOK_on(sv);
            }
            if (utf8) {
                while (next_uni_uu(aTHX_ &s, strend, &len)) {
                    I32 a, b, c, d;
                if (l) SvPOK_on(sv);
            }
            if (utf8) {
                while (next_uni_uu(aTHX_ &s, strend, &len)) {
                    I32 a, b, c, d;
-                   char hunk[4];
+                   char hunk[3];
 
 
-                   hunk[3] = '\0';
                    while (len > 0) {
                        next_uni_uu(aTHX_ &s, strend, &a);
                        next_uni_uu(aTHX_ &s, strend, &b);
                    while (len > 0) {
                        next_uni_uu(aTHX_ &s, strend, &a);
                        next_uni_uu(aTHX_ &s, strend, &b);
@@ -2036,56 +2147,61 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
                        hunk[0] = (char)((a << 2) | (b >> 4));
                        hunk[1] = (char)((b << 4) | (c >> 2));
                        hunk[2] = (char)((c << 6) | d);
                        hunk[0] = (char)((a << 2) | (b >> 4));
                        hunk[1] = (char)((b << 4) | (c >> 2));
                        hunk[2] = (char)((c << 6) | d);
-                       sv_catpvn(sv, hunk, (len > 3) ? 3 : len);
+                       if (!checksum)
+                           sv_catpvn(sv, hunk, (len > 3) ? 3 : len);
                        len -= 3;
                    }
                    if (s < strend) {
                        len -= 3;
                    }
                    if (s < strend) {
-                       if (*s == '\n') s++;
+                       if (*s == '\n') {
+                            s++;
+                        }
                        else {
                            /* possible checksum byte */
                        else {
                            /* possible checksum byte */
-                           char *skip = s+UTF8SKIP(s);
-                           if (skip < strend && *skip == '\n') s = skip+1;
+                           const char *skip = s+UTF8SKIP(s);
+                           if (skip < strend && *skip == '\n')
+                                s = skip+1;
                        }
                    }
                }
            } else {
                        }
                    }
                }
            } else {
-           while (s < strend && *s > ' ' && ISUUCHAR(*s)) {
-               I32 a, b, c, d;
-               char hunk[4];
+               while (s < strend && *s > ' ' && ISUUCHAR(*s)) {
+                   I32 a, b, c, d;
+                   char hunk[3];
 
 
-               hunk[3] = '\0';
-               len = PL_uudmap[*(U8*)s++] & 077;
-               while (len > 0) {
-                   if (s < strend && ISUUCHAR(*s))
-                       a = PL_uudmap[*(U8*)s++] & 077;
-                   else
-                       a = 0;
-                   if (s < strend && ISUUCHAR(*s))
-                       b = PL_uudmap[*(U8*)s++] & 077;
-                   else
-                       b = 0;
-                   if (s < strend && ISUUCHAR(*s))
-                       c = PL_uudmap[*(U8*)s++] & 077;
-                   else
-                       c = 0;
-                   if (s < strend && ISUUCHAR(*s))
-                       d = PL_uudmap[*(U8*)s++] & 077;
-                   else
-                       d = 0;
-                   hunk[0] = (char)((a << 2) | (b >> 4));
-                   hunk[1] = (char)((b << 4) | (c >> 2));
-                   hunk[2] = (char)((c << 6) | d);
-                   sv_catpvn(sv, hunk, (len > 3) ? 3 : len);
-                   len -= 3;
+                   len = PL_uudmap[*(U8*)s++] & 077;
+                   while (len > 0) {
+                       if (s < strend && ISUUCHAR(*s))
+                           a = PL_uudmap[*(U8*)s++] & 077;
+                       else
+                           a = 0;
+                       if (s < strend && ISUUCHAR(*s))
+                           b = PL_uudmap[*(U8*)s++] & 077;
+                       else
+                           b = 0;
+                       if (s < strend && ISUUCHAR(*s))
+                           c = PL_uudmap[*(U8*)s++] & 077;
+                       else
+                           c = 0;
+                       if (s < strend && ISUUCHAR(*s))
+                           d = PL_uudmap[*(U8*)s++] & 077;
+                       else
+                           d = 0;
+                       hunk[0] = (char)((a << 2) | (b >> 4));
+                       hunk[1] = (char)((b << 4) | (c >> 2));
+                       hunk[2] = (char)((c << 6) | d);
+                       if (!checksum)
+                           sv_catpvn(sv, hunk, (len > 3) ? 3 : len);
+                       len -= 3;
+                   }
+                   if (*s == '\n')
+                       s++;
+                   else        /* possible checksum byte */
+                       if (s + 1 < strend && s[1] == '\n')
+                           s += 2;
                }
                }
-               if (*s == '\n')
-                   s++;
-               else    /* possible checksum byte */
-                   if (s + 1 < strend && s[1] == '\n')
-                       s += 2;
            }
            }
-           }
-           XPUSHs(sv);
+           if (!checksum)
+               XPUSHs(sv);
            break;
        }
 
            break;
        }
 
@@ -2112,13 +2228,13 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
                }
                sv = newSVuv(cuv);
            }
                }
                sv = newSVuv(cuv);
            }
-           XPUSHs(sv_2mortal(sv));
+           mXPUSHs(sv);
            checksum = 0;
        }
            checksum = 0;
        }
-    
+
         if (symptr->flags & FLAG_SLASH){
             if (SP - PL_stack_base - start_sp_offset <= 0)
         if (symptr->flags & FLAG_SLASH){
             if (SP - PL_stack_base - start_sp_offset <= 0)
-                Perl_croak(aTHX_ "'/' must follow a numeric type in unpack");
+               break;
             if( next_symbol(symptr) ){
               if( symptr->howlen == e_number )
                Perl_croak(aTHX_ "Count after length/code in unpack" );
             if( next_symbol(symptr) ){
               if( symptr->howlen == e_number )
                Perl_croak(aTHX_ "Count after length/code in unpack" );
@@ -2148,15 +2264,16 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
 
 PP(pp_unpack)
 {
 
 PP(pp_unpack)
 {
+    dVAR;
     dSP;
     dPOPPOPssrl;
     I32 gimme = GIMME_V;
     STRLEN llen;
     STRLEN rlen;
     dSP;
     dPOPPOPssrl;
     I32 gimme = GIMME_V;
     STRLEN llen;
     STRLEN rlen;
-    char *pat = SvPV(left,  llen);
-    char *s   = SvPV(right, rlen);
-    char *strend = s + rlen;
-    char *patend = pat + llen;
+    const char *pat = SvPV_const(left,  llen);
+    const char *s   = SvPV_const(right, rlen);
+    const char *strend = s + rlen;
+    const char *patend = pat + llen;
     I32 cnt;
 
     PUTBACK;
     I32 cnt;
 
     PUTBACK;
@@ -2171,7 +2288,7 @@ PP(pp_unpack)
 }
 
 STATIC U8 *
 }
 
 STATIC U8 *
-doencodes(U8 *h, char *s, I32 len)
+doencodes(U8 *h, const char *s, I32 len)
 {
     *h++ = PL_uuemap[len];
     while (len > 2) {
 {
     *h++ = PL_uuemap[len];
     while (len > 2) {
@@ -2183,7 +2300,7 @@ doencodes(U8 *h, char *s, I32 len)
        len -= 3;
     }
     if (len > 0) {
        len -= 3;
     }
     if (len > 0) {
-       char r = (len > 1 ? s[1] : '\0');
+        const char r = (len > 1 ? s[1] : '\0');
        *h++ = PL_uuemap[(077 & (s[0] >> 2))];
        *h++ = PL_uuemap[(077 & (((s[0] << 4) & 060) | ((r >> 4) & 017)))];
        *h++ = PL_uuemap[(077 & ((r << 2) & 074))];
        *h++ = PL_uuemap[(077 & (s[0] >> 2))];
        *h++ = PL_uuemap[(077 & (((s[0] << 4) & 060) | ((r >> 4) & 017)))];
        *h++ = PL_uuemap[(077 & ((r << 2) & 074))];
@@ -2194,14 +2311,15 @@ doencodes(U8 *h, char *s, I32 len)
 }
 
 STATIC SV *
 }
 
 STATIC SV *
-S_is_an_int(pTHX_ char *s, STRLEN l)
+S_is_an_int(pTHX_ const char *s, STRLEN l)
 {
 {
-  STRLEN        n_a;
-  SV             *result = newSVpvn(s, l);
-  char           *result_c = SvPV(result, n_a);        /* convenience */
-  char           *out = result_c;
-  bool            skip = 1;
-  bool            ignore = 0;
+  SV *result = newSVpvn(s, l);
+  char *const result_c = SvPV_nolen(result);   /* convenience */
+  char *out = result_c;
+  bool skip = 1;
+  bool ignore = 0;
+
+  PERL_ARGS_ASSERT_IS_AN_INT;
 
   while (*s) {
     switch (*s) {
 
   while (*s) {
     switch (*s) {
@@ -2246,74 +2364,51 @@ S_is_an_int(pTHX_ char *s, STRLEN l)
 STATIC int
 S_div128(pTHX_ SV *pnum, bool *done)
 {
 STATIC int
 S_div128(pTHX_ SV *pnum, bool *done)
 {
-  STRLEN          len;
-  char           *s = SvPV(pnum, len);
-  int             m = 0;
-  int             r = 0;
-  char           *t = s;
-
-  *done = 1;
-  while (*t) {
-    int             i;
-
-    i = m * 10 + (*t - '0');
-    m = i & 0x7F;
-    r = (i >> 7);              /* r < 10 */
-    if (r) {
-      *done = 0;
+    STRLEN len;
+    char * const s = SvPV(pnum, len);
+    char *t = s;
+    int m = 0;
+
+    PERL_ARGS_ASSERT_DIV128;
+
+    *done = 1;
+    while (*t) {
+       const int i = m * 10 + (*t - '0');
+       const int r = (i >> 7); /* r < 10 */
+       m = i & 0x7F;
+       if (r) {
+           *done = 0;
+       }
+       *(t++) = '0' + r;
     }
     }
-    *(t++) = '0' + r;
-  }
-  *(t++) = '\0';
-  SvCUR_set(pnum, (STRLEN) (t - s));
-  return (m);
-}
-
-
-
-/*
-=for apidoc pack_cat
-
-The engine implementing pack() Perl function. Note: parameters next_in_list and
-flags are not used. This call should not be used; use packlist instead.
-
-=cut */
-
-
-void
-Perl_pack_cat(pTHX_ SV *cat, char *pat, register char *patend, register SV **beglist, SV **endlist, SV ***next_in_list, U32 flags)
-{
-    tempsym_t sym = { 0 };
-    sym.patptr = pat;
-    sym.patend = patend;
-    sym.flags  = FLAG_PACK;
-
-    (void)pack_rec( cat, &sym, beglist, endlist );
+    *(t++) = '\0';
+    SvCUR_set(pnum, (STRLEN) (t - s));
+    return (m);
 }
 
 }
 
-
 /*
 =for apidoc packlist
 
 The engine implementing pack() Perl function.
 
 /*
 =for apidoc packlist
 
 The engine implementing pack() Perl function.
 
-=cut */
-
+=cut
+*/
 
 void
 
 void
-Perl_packlist(pTHX_ SV *cat, char *pat, register char *patend, register SV **beglist, SV **endlist )
+Perl_packlist(pTHX_ SV *cat, const char *pat, const char *patend, register SV **beglist, SV **endlist )
 {
 {
-    STRLEN no_len;
-    tempsym_t sym = { 0 };
+    dVAR;
+    tempsym_t sym;
 
 
-    sym.patptr = pat;
-    sym.patend = patend;
-    sym.flags  = FLAG_PACK;
+    PERL_ARGS_ASSERT_PACKLIST;
+
+    TEMPSYM_INIT(&sym, pat, patend, FLAG_PACK);
 
     /* We're going to do changes through SvPVX(cat). Make sure it's valid.
        Also make sure any UTF8 flag is loaded */
 
     /* We're going to do changes through SvPVX(cat). Make sure it's valid.
        Also make sure any UTF8 flag is loaded */
-    SvPV_force(cat, no_len);
-    if (DO_UTF8(cat)) sym.flags |= FLAG_PARSE_UTF8 | FLAG_DO_UTF8;
+    SvPV_force_nolen(cat);
+    if (DO_UTF8(cat))
+       sym.flags |= FLAG_PARSE_UTF8 | FLAG_DO_UTF8;
 
     (void)pack_rec( cat, &sym, beglist, endlist );
 }
 
     (void)pack_rec( cat, &sym, beglist, endlist );
 }
@@ -2323,11 +2418,12 @@ STATIC void
 marked_upgrade(pTHX_ SV *sv, tempsym_t *sym_ptr) {
     STRLEN len;
     tempsym_t *group;
 marked_upgrade(pTHX_ SV *sv, tempsym_t *sym_ptr) {
     STRLEN len;
     tempsym_t *group;
-    char *from_ptr, *to_start, *to_ptr, **marks, **m, *from_start, *from_end;
+    const char *from_ptr, *from_start, *from_end, **marks, **m;
+    char *to_start, *to_ptr;
 
     if (SvUTF8(sv)) return;
 
 
     if (SvUTF8(sv)) return;
 
-    from_start = SvPVX(sv);
+    from_start = SvPVX_const(sv);
     from_end = from_start + SvCUR(sv);
     for (from_ptr = from_start; from_ptr < from_end; from_ptr++)
        if (!NATIVE_IS_INVARIANT(*from_ptr)) break;
     from_end = from_start + SvCUR(sv);
     for (from_ptr = from_start; from_ptr < from_end; from_ptr++)
        if (!NATIVE_IS_INVARIANT(*from_ptr)) break;
@@ -2338,11 +2434,11 @@ marked_upgrade(pTHX_ SV *sv, tempsym_t *sym_ptr) {
     }
 
     len = (from_end-from_ptr)*UTF8_EXPAND+(from_ptr-from_start)+1;
     }
 
     len = (from_end-from_ptr)*UTF8_EXPAND+(from_ptr-from_start)+1;
-    New('U', to_start, len, char);
+    Newx(to_start, len, char);
     Copy(from_start, to_start, from_ptr-from_start, char);
     to_ptr = to_start + (from_ptr-from_start);
 
     Copy(from_start, to_start, from_ptr-from_start, char);
     to_ptr = to_start + (from_ptr-from_start);
 
-    New('U', marks, sym_ptr->level+2, char *);
+    Newx(marks, sym_ptr->level+2, const char *);
     for (group=sym_ptr; group; group = group->previous)
        marks[group->level] = from_start + group->strbeg;
     marks[sym_ptr->level+1] = from_end+1;
     for (group=sym_ptr; group; group = group->previous)
        marks[group->level] = from_start + group->strbeg;
     marks[sym_ptr->level+1] = from_end+1;
@@ -2351,7 +2447,7 @@ marked_upgrade(pTHX_ SV *sv, tempsym_t *sym_ptr) {
 
     for (;from_ptr < from_end; from_ptr++) {
        while (*m == from_ptr) *m++ = to_ptr;
 
     for (;from_ptr < from_end; from_ptr++) {
        while (*m == from_ptr) *m++ = to_ptr;
-       to_ptr = uvchr_to_utf8(to_ptr, *(U8 *) from_ptr);
+       to_ptr = (char *) uvchr_to_utf8((U8 *) to_ptr, *(U8 *) from_ptr);
     }
     *to_ptr = 0;
 
     }
     *to_ptr = 0;
 
@@ -2359,7 +2455,8 @@ marked_upgrade(pTHX_ SV *sv, tempsym_t *sym_ptr) {
     if (m != marks + sym_ptr->level+1) {
        Safefree(marks);
        Safefree(to_start);
     if (m != marks + sym_ptr->level+1) {
        Safefree(marks);
        Safefree(to_start);
-       Perl_croak(aTHX_ "Assertion: marks beyond string end");
+       Perl_croak(aTHX_ "panic: marks beyond string end, m=%p, marks=%p, "
+                  "level=%d", m, marks, sym_ptr->level);
     }
     for (group=sym_ptr; group; group = group->previous)
        group->strbeg = marks[group->level] - to_start;
     }
     for (group=sym_ptr; group; group = group->previous)
        group->strbeg = marks[group->level] - to_start;
@@ -2367,7 +2464,7 @@ marked_upgrade(pTHX_ SV *sv, tempsym_t *sym_ptr) {
 
     if (SvOOK(sv)) {
        if (SvIVX(sv)) {
 
     if (SvOOK(sv)) {
        if (SvIVX(sv)) {
-           SvLEN(sv)  += SvIVX(sv);
+           SvLEN_set(sv, SvLEN(sv) + SvIVX(sv));
            from_start -= SvIVX(sv);
            SvIV_set(sv, 0);
        }
            from_start -= SvIVX(sv);
            SvIV_set(sv, 0);
        }
@@ -2375,9 +2472,9 @@ marked_upgrade(pTHX_ SV *sv, tempsym_t *sym_ptr) {
     }
     if (SvLEN(sv) != 0)
        Safefree(from_start);
     }
     if (SvLEN(sv) != 0)
        Safefree(from_start);
-    SvPVX(sv) = to_start;
-    SvCUR(sv) = to_ptr - to_start;
-    SvLEN(sv) = len;
+    SvPV_set(sv, to_start);
+    SvCUR_set(sv, to_ptr - to_start);
+    SvLEN_set(sv, len);
     SvUTF8_on(sv);
 }
 
     SvUTF8_on(sv);
 }
 
@@ -2386,10 +2483,13 @@ marked_upgrade(pTHX_ SV *sv, tempsym_t *sym_ptr) {
    Only grows the string if there is an actual lack of space
 */
 STATIC char *
    Only grows the string if there is an actual lack of space
 */
 STATIC char *
-sv_exp_grow(pTHX_ SV *sv, STRLEN needed) {
-    STRLEN cur = SvCUR(sv);
-    STRLEN len = SvLEN(sv);
+S_sv_exp_grow(pTHX_ SV *sv, STRLEN needed) {
+    const STRLEN cur = SvCUR(sv);
+    const STRLEN len = SvLEN(sv);
     STRLEN extend;
     STRLEN extend;
+
+    PERL_ARGS_ASSERT_SV_EXP_GROW;
+
     if (len - cur > needed) return SvPVX(sv);
     extend = needed > len ? needed : len;
     return SvGROW(sv, len+extend+1);
     if (len - cur > needed) return SvPVX(sv);
     extend = needed > len ? needed : len;
     return SvGROW(sv, len+extend+1);
@@ -2399,10 +2499,14 @@ STATIC
 SV **
 S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
 {
 SV **
 S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
 {
+    dVAR;
     tempsym_t lookahead;
     I32 items  = endlist - beglist;
     bool found = next_symbol(symptr);
     bool utf8 = (symptr->flags & FLAG_PARSE_UTF8) ? 1 : 0;
     tempsym_t lookahead;
     I32 items  = endlist - beglist;
     bool found = next_symbol(symptr);
     bool utf8 = (symptr->flags & FLAG_PARSE_UTF8) ? 1 : 0;
+    bool warn_utf8 = ckWARN(WARN_UTF8);
+
+    PERL_ARGS_ASSERT_PACK_REC;
 
     if (symptr->level == 0 && found && symptr->code == 'U') {
        marked_upgrade(aTHX_ cat, symptr);
 
     if (symptr->level == 0 && found && symptr->code == 'U') {
        marked_upgrade(aTHX_ cat, symptr);
@@ -2415,7 +2519,7 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
        SV *fromstr;
        STRLEN fromlen;
        I32 len;
        SV *fromstr;
        STRLEN fromlen;
        I32 len;
-       SV *lengthcode = Nullsv;
+       SV *lengthcode = NULL;
         I32 datumtype = symptr->code;
         howlen_t howlen = symptr->howlen;
        char *start = SvPVX(cat);
         I32 datumtype = symptr->code;
         howlen_t howlen = symptr->howlen;
        char *start = SvPVX(cat);
@@ -2424,7 +2528,7 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
 #define NEXTFROM (lengthcode ? lengthcode : items-- > 0 ? *beglist++ : &PL_sv_no)
 
         switch (howlen) {
 #define NEXTFROM (lengthcode ? lengthcode : items-- > 0 ? *beglist++ : &PL_sv_no)
 
         switch (howlen) {
-        case e_star:
+         case e_star:
            len = strchr("@Xxu", TYPE_NO_MODIFIERS(datumtype)) ?
                0 : items;
            break;
            len = strchr("@Xxu", TYPE_NO_MODIFIERS(datumtype)) ?
                0 : items;
            break;
@@ -2435,39 +2539,53 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
         }
 
        if (len) {
         }
 
        if (len) {
-           struct packsize_t *pack_props =
-               &packsize[(symptr->code & TYPE_IS_SHRIEKING) ?
-                         PACK_SIZE_SHRIEKING : PACK_SIZE_NORMAL];
-           const int rawtype = TYPE_NO_MODIFIERS(datumtype);
-           int offset = rawtype - pack_props->first;
-
-           if (offset >= 0 && offset < pack_props->size) {
-               /* Data about this template letter  */
-               unsigned char data = pack_props->array[offset];
-
-               if (data && !(data & PACK_SIZE_UNPREDICTABLE)) {
-                   /* We can process this letter.  */
-                   STRLEN size = data & PACK_SIZE_MASK;
-                   GROWING(utf8, cat, start, cur, (STRLEN) len * size);
-               }
-           }
+           packprops_t props = packprops[TYPE_NO_ENDIANNESS(datumtype)];
 
 
+           if (props && !(props & PACK_SIZE_UNPREDICTABLE)) {
+               /* We can process this letter. */
+               STRLEN size = props & PACK_SIZE_MASK;
+               GROWING(utf8, cat, start, cur, (STRLEN) len * size);
+           }
         }
 
         /* Look ahead for next symbol. Do we have code/code? */
         lookahead = *symptr;
         found = next_symbol(&lookahead);
         }
 
         /* Look ahead for next symbol. Do we have code/code? */
         lookahead = *symptr;
         found = next_symbol(&lookahead);
-       if ( symptr->flags & FLAG_SLASH ) {
+       if (symptr->flags & FLAG_SLASH) {
+           IV count;
            if (!found) Perl_croak(aTHX_ "Code missing after '/' in pack");
            if (!found) Perl_croak(aTHX_ "Code missing after '/' in pack");
-               if ( 0 == strchr( "aAZ", lookahead.code ) ||
-                     e_star != lookahead.howlen )
-                   Perl_croak(aTHX_ "'/' must be followed by 'a*', 'A*' or 'Z*' in pack");
-           lengthcode =
-               sv_2mortal(newSViv((items > 0 ? DO_UTF8(*beglist) ? sv_len_utf8(*beglist) : sv_len(*beglist) : 0) + (lookahead.code == 'Z' ? 1 : 0)));
+           if (strchr("aAZ", lookahead.code)) {
+               if (lookahead.howlen == e_number) count = lookahead.length;
+               else {
+                   if (items > 0) {
+                       if (SvGAMAGIC(*beglist)) {
+                           /* Avoid reading the active data more than once
+                              by copying it to a temporary.  */
+                           STRLEN len;
+                           const char *const pv = SvPV_const(*beglist, len);
+                           SV *const temp
+                               = newSVpvn_flags(pv, len,
+                                                SVs_TEMP | SvUTF8(*beglist));
+                           *beglist = temp;
+                       }
+                       count = DO_UTF8(*beglist) ?
+                           sv_len_utf8(*beglist) : sv_len(*beglist);
+                   }
+                   else count = 0;
+                   if (lookahead.code == 'Z') count++;
+               }
+           } else {
+               if (lookahead.howlen == e_number && lookahead.length < items)
+                   count = lookahead.length;
+               else count = items;
+           }
+           lookahead.howlen = e_number;
+           lookahead.length = count;
+           lengthcode = sv_2mortal(newSViv(count));
        }
 
        }
 
-       /* Code inside the switch must take care to properly update 
-          cat (CUR length and '\0' termination) if it updated *cur and 
+       /* Code inside the switch must take care to properly update
+          cat (CUR length and '\0' termination) if it updated *cur and
           doesn't simply leave using break */
        switch(TYPE_NO_ENDIANNESS(datumtype)) {
        default:
           doesn't simply leave using break */
        switch(TYPE_NO_ENDIANNESS(datumtype)) {
        default:
@@ -2475,31 +2593,66 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
                       (int) TYPE_NO_MODIFIERS(datumtype));
        case '%':
            Perl_croak(aTHX_ "'%%' may not be used in pack");
                       (int) TYPE_NO_MODIFIERS(datumtype));
        case '%':
            Perl_croak(aTHX_ "'%%' may not be used in pack");
+       {
+           char *from;
+#ifdef PERL_PACK_CAN_SHRIEKSIGN
+       case '.' | TYPE_IS_SHRIEKING:
+#endif
+       case '.':
+           if (howlen == e_star) from = start;
+           else if (len == 0) from = cur;
+           else {
+               tempsym_t *group = symptr;
+
+               while (--len && group) group = group->previous;
+               from = group ? start + group->strbeg : start;
+           }
+           fromstr = NEXTFROM;
+           len = SvIV(fromstr);
+           goto resize;
+#ifdef PERL_PACK_CAN_SHRIEKSIGN
+       case '@' | TYPE_IS_SHRIEKING:
+#endif
        case '@':
        case '@':
-           if (utf8) {
-               char *s = start + symptr->strbeg;
-               while (len > 0 && s < cur) {
-                   s += UTF8SKIP(s);
-                   len--;
+           from = start + symptr->strbeg;
+         resize:
+#ifdef PERL_PACK_CAN_SHRIEKSIGN
+           if (utf8  && !(datumtype & TYPE_IS_SHRIEKING))
+#else /* PERL_PACK_CAN_SHRIEKSIGN */
+           if (utf8)
+#endif
+               if (len >= 0) {
+                   while (len && from < cur) {
+                       from += UTF8SKIP(from);
+                       len--;
+                   }
+                   if (from > cur)
+                       Perl_croak(aTHX_ "Malformed UTF-8 string in pack");
+                   if (len) {
+                       /* Here we know from == cur */
+                     grow:
+                       GROWING(0, cat, start, cur, len);
+                       Zero(cur, len, char);
+                       cur += len;
+                   } else if (from < cur) {
+                       len = cur - from;
+                       goto shrink;
+                   } else goto no_change;
+               } else {
+                   cur = from;
+                   len = -len;
+                   goto utf8_shrink;
                }
                }
-               if (s > cur)
-                   Perl_croak(aTHX_ "Malformed UTF-8 string in pack");
-               if (len > 0) {
-                 grow:
-                   GROWING(0, cat, start, cur, len);
-                   Zero(cur, len, char);
-                   cur += len;
-               } else if (s < cur) cur = s;
-               else goto no_change;
-           } else {
-               len -= cur - (start+symptr->strbeg);
+           else {
+               len -= cur - from;
                if (len > 0) goto grow;
                if (len > 0) goto grow;
-           len = -len;
-               if (len > 0) goto shrink;
-               else goto no_change;
+               if (len == 0) goto no_change;
+               len = -len;
+               goto shrink;
            }
            break;
            }
            break;
-         case '(': {
+       }
+       case '(': {
             tempsym_t savsym = *symptr;
            U32 group_modifiers = TYPE_MODIFIERS(datumtype & ~symptr->flags);
            symptr->flags |= group_modifiers;
             tempsym_t savsym = *symptr;
            U32 group_modifiers = TYPE_MODIFIERS(datumtype & ~symptr->flags);
            symptr->flags |= group_modifiers;
@@ -2520,6 +2673,7 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
                if (savsym.howlen == e_star && beglist == endlist)
                    break;              /* No way to continue */
            }
                if (savsym.howlen == e_star && beglist == endlist)
                    break;              /* No way to continue */
            }
+           items = endlist - beglist;
            lookahead.flags  = symptr->flags & ~group_modifiers;
            goto no_change;
        }
            lookahead.flags  = symptr->flags & ~group_modifiers;
            goto no_change;
        }
@@ -2547,52 +2701,56 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
        case 'X':
            if (utf8) {
                if (len < 1) goto no_change;
        case 'X':
            if (utf8) {
                if (len < 1) goto no_change;
+             utf8_shrink:
                while (len > 0) {
                    if (cur <= start)
                while (len > 0) {
                    if (cur <= start)
-                       Perl_croak(aTHX_ "'X' outside of string in pack");
+                       Perl_croak(aTHX_ "'%c' outside of string in pack",
+                                  (int) TYPE_NO_MODIFIERS(datumtype));
                    while (--cur, UTF8_IS_CONTINUATION(*cur)) {
                        if (cur <= start)
                    while (--cur, UTF8_IS_CONTINUATION(*cur)) {
                        if (cur <= start)
-                           Perl_croak(aTHX_ "'X' outside of string in pack");
+                           Perl_croak(aTHX_ "'%c' outside of string in pack",
+                                      (int) TYPE_NO_MODIFIERS(datumtype));
                    }
                    len--;
                }
            } else {
                    }
                    len--;
                }
            } else {
-         shrink:
+             shrink:
                if (cur - start < len)
                if (cur - start < len)
-               Perl_croak(aTHX_ "'X' outside of string in pack");
+                   Perl_croak(aTHX_ "'%c' outside of string in pack",
+                              (int) TYPE_NO_MODIFIERS(datumtype));
                cur -= len;
            }
            if (cur < start+symptr->strbeg) {
                /* Make sure group starts don't point into the void */
                tempsym_t *group;
                cur -= len;
            }
            if (cur < start+symptr->strbeg) {
                /* Make sure group starts don't point into the void */
                tempsym_t *group;
-               STRLEN length = cur-start;
+               const STRLEN length = cur-start;
                for (group = symptr;
                     group && length < group->strbeg;
                     group = group->previous) group->strbeg = length;
                lookahead.strbeg = length;
            }
            break;
                for (group = symptr;
                     group && length < group->strbeg;
                     group = group->previous) group->strbeg = length;
                lookahead.strbeg = length;
            }
            break;
-         case 'x' | TYPE_IS_SHRIEKING: {
-             I32 ai32;
+       case 'x' | TYPE_IS_SHRIEKING: {
+           I32 ai32;
            if (!len)                   /* Avoid division by 0 */
                len = 1;
            if (!len)                   /* Avoid division by 0 */
                len = 1;
-             if (utf8) ai32 = utf8_length(start, cur) % len;
-             else      ai32 = (cur - start) % len;
-             if (ai32 == 0) goto no_change;
-             len -= ai32;
-         }
-           /* FALL THROUGH */
+           if (utf8) ai32 = utf8_length((U8 *) start, (U8 *) cur) % len;
+           else      ai32 = (cur - start) % len;
+           if (ai32 == 0) goto no_change;
+           len -= ai32;
+       }
+       /* FALL THROUGH */
        case 'x':
            goto grow;
        case 'A':
        case 'Z':
        case 'a': {
        case 'x':
            goto grow;
        case 'A':
        case 'Z':
        case 'a': {
-           char *aptr;
+           const char *aptr;
 
            fromstr = NEXTFROM;
 
            fromstr = NEXTFROM;
-           aptr = SvPV(fromstr, fromlen);
+           aptr = SvPV_const(fromstr, fromlen);
            if (DO_UTF8(fromstr)) {
            if (DO_UTF8(fromstr)) {
-               char *end, *s;
+                const char *end, *s;
 
                if (!utf8 && !SvUTF8(cat)) {
                    marked_upgrade(aTHX_ cat, symptr);
 
                if (!utf8 && !SvUTF8(cat)) {
                    marked_upgrade(aTHX_ cat, symptr);
@@ -2602,7 +2760,7 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
                    start = SvPVX(cat);
                    cur = start + SvCUR(cat);
                }
                    start = SvPVX(cat);
                    cur = start + SvCUR(cat);
                }
-           if (howlen == e_star) {   
+               if (howlen == e_star) {
                    if (utf8) goto string_copy;
                    len = fromlen+1;
                }
                    if (utf8) goto string_copy;
                    len = fromlen+1;
                }
@@ -2616,23 +2774,25 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
                if (s > end)
                    Perl_croak(aTHX_ "Malformed UTF-8 string in pack");
                if (utf8) {
                if (s > end)
                    Perl_croak(aTHX_ "Malformed UTF-8 string in pack");
                if (utf8) {
-               len = fromlen;
+                   len = fromlen;
                    if (datumtype == 'Z') len++;
                    fromlen = s-aptr;
                    len += fromlen;
                    if (datumtype == 'Z') len++;
                    fromlen = s-aptr;
                    len += fromlen;
-                     
+
                    goto string_copy;
                    goto string_copy;
-           }
+               }
                fromlen = len - fromlen;
                if (datumtype == 'Z') fromlen--;
                if (howlen == e_star) {
                    len = fromlen;
                    if (datumtype == 'Z') len++;
                fromlen = len - fromlen;
                if (datumtype == 'Z') fromlen--;
                if (howlen == e_star) {
                    len = fromlen;
                    if (datumtype == 'Z') len++;
-           }
+               }
                GROWING(0, cat, start, cur, len);
                GROWING(0, cat, start, cur, len);
-               if (!uni_to_bytes(aTHX_ &aptr, end, cur, fromlen, 
+               if (!uni_to_bytes(aTHX_ &aptr, end, cur, fromlen,
                                  datumtype | TYPE_IS_PACK))
                                  datumtype | TYPE_IS_PACK))
-                   Perl_croak(aTHX_ "Perl bug: predicted utf8 length not available");
+                   Perl_croak(aTHX_ "panic: predicted utf8 length not available, "
+                              "for '%c', aptr=%p end=%p cur=%p, fromlen=%"UVuf,
+                              (int)datumtype, aptr, end, cur, (UV)fromlen);
                cur += fromlen;
                len -= fromlen;
            } else if (utf8) {
                cur += fromlen;
                len -= fromlen;
            } else if (utf8) {
@@ -2644,16 +2804,16 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
                    fromlen = len;
                    if (datumtype == 'Z' && fromlen > 0) fromlen--;
                }
                    fromlen = len;
                    if (datumtype == 'Z' && fromlen > 0) fromlen--;
                }
-               /* assumes a byte expands to at most UTF8_EXPAND bytes on 
+               /* assumes a byte expands to at most UTF8_EXPAND bytes on
                   upgrade, so:
                   expected_length <= from_len*UTF8_EXPAND + (len-from_len) */
                GROWING(0, cat, start, cur, fromlen*(UTF8_EXPAND-1)+len);
                len -= fromlen;
                while (fromlen > 0) {
                   upgrade, so:
                   expected_length <= from_len*UTF8_EXPAND + (len-from_len) */
                GROWING(0, cat, start, cur, fromlen*(UTF8_EXPAND-1)+len);
                len -= fromlen;
                while (fromlen > 0) {
-                   cur = uvchr_to_utf8(cur, * (U8 *) aptr);
+                   cur = (char *) uvchr_to_utf8((U8 *) cur, * (U8 *) aptr);
                    aptr++;
                    fromlen--;
                    aptr++;
                    fromlen--;
-                   }
+               }
            } else {
              string_copy:
                if (howlen == e_star) {
            } else {
              string_copy:
                if (howlen == e_star) {
@@ -2671,22 +2831,23 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
            }
            memset(cur, datumtype == 'A' ? ' ' : '\0', len);
            cur += len;
            }
            memset(cur, datumtype == 'A' ? ' ' : '\0', len);
            cur += len;
+           SvTAINT(cat);
            break;
        }
        case 'B':
        case 'b': {
            break;
        }
        case 'B':
        case 'b': {
-           char *str, *end;
+           const char *str, *end;
            I32 l, field_len;
            U8 bits;
            bool utf8_source;
            U32 utf8_flags;
 
            I32 l, field_len;
            U8 bits;
            bool utf8_source;
            U32 utf8_flags;
 
-               fromstr = NEXTFROM;
-               str = SvPV(fromstr, fromlen);
+           fromstr = NEXTFROM;
+           str = SvPV_const(fromstr, fromlen);
            end = str + fromlen;
            if (DO_UTF8(fromstr)) {
                utf8_source = TRUE;
            end = str + fromlen;
            if (DO_UTF8(fromstr)) {
                utf8_source = TRUE;
-               utf8_flags  = ckWARN(WARN_UTF8) ? 0 : UTF8_ALLOW_ANY;
+               utf8_flags  = warn_utf8 ? 0 : UTF8_ALLOW_ANY;
            } else {
                utf8_source = FALSE;
                utf8_flags  = 0; /* Unused, but keep compilers happy */
            } else {
                utf8_source = FALSE;
                utf8_flags  = 0; /* Unused, but keep compilers happy */
@@ -2700,12 +2861,12 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
            if (datumtype == 'B')
                while (l++ < len) {
                    if (utf8_source) {
            if (datumtype == 'B')
                while (l++ < len) {
                    if (utf8_source) {
-                       UV val;
+                       UV val = 0;
                        NEXT_UNI_VAL(val, cur, str, end, utf8_flags);
                        bits |= val & 1;
                    } else bits |= *str++ & 1;
                    if (l & 7) bits <<= 1;
                        NEXT_UNI_VAL(val, cur, str, end, utf8_flags);
                        bits |= val & 1;
                    } else bits |= *str++ & 1;
                    if (l & 7) bits <<= 1;
-                       else {
+                   else {
                        PUSH_BYTE(utf8, cur, bits);
                        bits = 0;
                    }
                        PUSH_BYTE(utf8, cur, bits);
                        bits = 0;
                    }
@@ -2714,22 +2875,22 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
                /* datumtype == 'b' */
                while (l++ < len) {
                    if (utf8_source) {
                /* datumtype == 'b' */
                while (l++ < len) {
                    if (utf8_source) {
-                       UV val;
+                       UV val = 0;
                        NEXT_UNI_VAL(val, cur, str, end, utf8_flags);
                        if (val & 1) bits |= 0x80;
                    } else if (*str++ & 1)
                        bits |= 0x80;
                    if (l & 7) bits >>= 1;
                        NEXT_UNI_VAL(val, cur, str, end, utf8_flags);
                        if (val & 1) bits |= 0x80;
                    } else if (*str++ & 1)
                        bits |= 0x80;
                    if (l & 7) bits >>= 1;
-               else {
+                   else {
                        PUSH_BYTE(utf8, cur, bits);
                        bits = 0;
                    }
                }
            l--;
            if (l & 7) {
                        PUSH_BYTE(utf8, cur, bits);
                        bits = 0;
                    }
                }
            l--;
            if (l & 7) {
-                   if (datumtype == 'B')
+               if (datumtype == 'B')
                    bits <<= 7 - (l & 7);
                    bits <<= 7 - (l & 7);
-                   else
+               else
                    bits >>= 7 - (l & 7);
                PUSH_BYTE(utf8, cur, bits);
                l += 7;
                    bits >>= 7 - (l & 7);
                PUSH_BYTE(utf8, cur, bits);
                l += 7;
@@ -2744,18 +2905,18 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
        }
        case 'H':
        case 'h': {
        }
        case 'H':
        case 'h': {
-           char *str, *end;
+           const char *str, *end;
            I32 l, field_len;
            U8 bits;
            bool utf8_source;
            U32 utf8_flags;
 
            I32 l, field_len;
            U8 bits;
            bool utf8_source;
            U32 utf8_flags;
 
-               fromstr = NEXTFROM;
-               str = SvPV(fromstr, fromlen);
+           fromstr = NEXTFROM;
+           str = SvPV_const(fromstr, fromlen);
            end = str + fromlen;
            if (DO_UTF8(fromstr)) {
                utf8_source = TRUE;
            end = str + fromlen;
            if (DO_UTF8(fromstr)) {
                utf8_source = TRUE;
-               utf8_flags  = ckWARN(WARN_UTF8) ? 0 : UTF8_ALLOW_ANY;
+               utf8_flags  = warn_utf8 ? 0 : UTF8_ALLOW_ANY;
            } else {
                utf8_source = FALSE;
                utf8_flags  = 0; /* Unused, but keep compilers happy */
            } else {
                utf8_source = FALSE;
                utf8_flags  = 0; /* Unused, but keep compilers happy */
@@ -2769,7 +2930,7 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
            if (datumtype == 'H')
                while (l++ < len) {
                    if (utf8_source) {
            if (datumtype == 'H')
                while (l++ < len) {
                    if (utf8_source) {
-                       UV val;
+                       UV val = 0;
                        NEXT_UNI_VAL(val, cur, str, end, utf8_flags);
                        if (val < 256 && isALPHA(val))
                            bits |= (val + 9) & 0xf;
                        NEXT_UNI_VAL(val, cur, str, end, utf8_flags);
                        if (val < 256 && isALPHA(val))
                            bits |= (val + 9) & 0xf;
@@ -2780,7 +2941,7 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
                    else
                        bits |= *str++ & 0xf;
                    if (l & 1) bits <<= 4;
                    else
                        bits |= *str++ & 0xf;
                    if (l & 1) bits <<= 4;
-                       else {
+                   else {
                        PUSH_BYTE(utf8, cur, bits);
                        bits = 0;
                    }
                        PUSH_BYTE(utf8, cur, bits);
                        bits = 0;
                    }
@@ -2788,7 +2949,7 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
            else
                while (l++ < len) {
                    if (utf8_source) {
            else
                while (l++ < len) {
                    if (utf8_source) {
-                       UV val;
+                       UV val = 0;
                        NEXT_UNI_VAL(val, cur, str, end, utf8_flags);
                        if (val < 256 && isALPHA(val))
                            bits |= ((val + 9) & 0xf) << 4;
                        NEXT_UNI_VAL(val, cur, str, end, utf8_flags);
                        if (val < 256 && isALPHA(val))
                            bits |= ((val + 9) & 0xf) << 4;
@@ -2799,11 +2960,11 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
                    else
                        bits |= (*str++ & 0xf) << 4;
                    if (l & 1) bits >>= 4;
                    else
                        bits |= (*str++ & 0xf) << 4;
                    if (l & 1) bits >>= 4;
-               else {
+                   else {
                        PUSH_BYTE(utf8, cur, bits);
                        bits = 0;
                        PUSH_BYTE(utf8, cur, bits);
                        bits = 0;
-                       }
                    }
                    }
+               }
            l--;
            if (l & 1) {
                PUSH_BYTE(utf8, cur, bits);
            l--;
            if (l & 1) {
                PUSH_BYTE(utf8, cur, bits);
@@ -2816,17 +2977,16 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
            Zero(cur, field_len, char);
            cur += field_len;
            break;
            Zero(cur, field_len, char);
            cur += field_len;
            break;
-               }
-         case 'c':
+       }
+       case 'c':
            while (len-- > 0) {
                IV aiv;
                fromstr = NEXTFROM;
                aiv = SvIV(fromstr);
            while (len-- > 0) {
                IV aiv;
                fromstr = NEXTFROM;
                aiv = SvIV(fromstr);
-               if ((-128 > aiv || aiv > 127) &&
-                   ckWARN(WARN_PACK))
-                   Perl_warner(aTHX_ packWARN(WARN_PACK),
-                               "Character in 'c' format wrapped in pack");
-               PUSH_BYTE(utf8, cur, aiv & 0xff);
+               if ((-128 > aiv || aiv > 127))
+                   Perl_ck_warner(aTHX_ packWARN(WARN_PACK),
+                                  "Character in 'c' format wrapped in pack");
+               PUSH_BYTE(utf8, cur, (U8)(aiv & 0xff));
            }
            break;
        case 'C':
            }
            break;
        case 'C':
@@ -2834,117 +2994,115 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
                utf8 = (symptr->flags & FLAG_DO_UTF8) ? 1 : 0;
                break;
            }
                utf8 = (symptr->flags & FLAG_DO_UTF8) ? 1 : 0;
                break;
            }
-           GROWING(0, cat, start, cur, len);
            while (len-- > 0) {
                IV aiv;
                fromstr = NEXTFROM;
                aiv = SvIV(fromstr);
            while (len-- > 0) {
                IV aiv;
                fromstr = NEXTFROM;
                aiv = SvIV(fromstr);
-               if ((0 > aiv || aiv > 0xff) &&
-                       ckWARN(WARN_PACK))
-                       Perl_warner(aTHX_ packWARN(WARN_PACK),
-                                   "Character in 'C' format wrapped in pack");
-               *cur++ = aiv & 0xff;
-           }
-                   break;
-         case 'W': {
-             char *end;
-             U8 in_bytes = IN_BYTES;
-
-             end = start+SvLEN(cat)-1;
-             if (utf8) end -= UTF8_MAXLEN-1;
-             while (len-- > 0) {
-                 UV auv;
-                 fromstr = NEXTFROM;
-                 auv = SvUV(fromstr);
-                 if (in_bytes) auv = auv % 0x100;
-                 if (utf8) {
-                   W_utf8:
-                     if (cur > end) {
-                         *cur = '\0';
-                         SvCUR(cat) = cur - start;
-                         
-                         GROWING(0, cat, start, cur, len+UTF8_MAXLEN);
-                         end = start+SvLEN(cat)-UTF8_MAXLEN;
-                     }
-                     cur = uvuni_to_utf8_flags(cur, NATIVE_TO_UNI(auv),
-                                               ckWARN(WARN_UTF8) ?
-                                               0 : UNICODE_ALLOW_ANY);
-                 } else {
-                     if (auv >= 0x100) {
-                         if (!SvUTF8(cat)) {
-                             *cur = '\0';
-                             SvCUR(cat) = cur - start;
-                             marked_upgrade(aTHX_ cat, symptr);
-                             lookahead.flags |= FLAG_DO_UTF8;
-                             lookahead.strbeg = symptr->strbeg;
-                             utf8 = 1;
-                             start = SvPVX(cat);
-                             cur = start + SvCUR(cat);
-                             end = start+SvLEN(cat)-UTF8_MAXLEN;
-                             goto W_utf8;
-                         }
-                         if (ckWARN(WARN_PACK))
-                       Perl_warner(aTHX_ packWARN(WARN_PACK),
-                                         "Character in 'W' format wrapped in pack");
-                         auv &= 0xff;
-                     }
-                     if (cur >= end) {
-                         *cur = '\0';
-                         SvCUR(cat) = cur - start;
-                         GROWING(0, cat, start, cur, len+1);
-                         end = start+SvLEN(cat)-1;
-                     }
-                     *(U8 *) cur++ = auv;
+               if ((0 > aiv || aiv > 0xff))
+                   Perl_ck_warner(aTHX_ packWARN(WARN_PACK),
+                                  "Character in 'C' format wrapped in pack");
+               PUSH_BYTE(utf8, cur, (U8)(aiv & 0xff));
+           }
+           break;
+       case 'W': {
+           char *end;
+           U8 in_bytes = (U8)IN_BYTES;
+
+           end = start+SvLEN(cat)-1;
+           if (utf8) end -= UTF8_MAXLEN-1;
+           while (len-- > 0) {
+               UV auv;
+               fromstr = NEXTFROM;
+               auv = SvUV(fromstr);
+               if (in_bytes) auv = auv % 0x100;
+               if (utf8) {
+                 W_utf8:
+                   if (cur > end) {
+                       *cur = '\0';
+                       SvCUR_set(cat, cur - start);
+
+                       GROWING(0, cat, start, cur, len+UTF8_MAXLEN);
+                       end = start+SvLEN(cat)-UTF8_MAXLEN;
+                   }
+                   cur = (char *) uvuni_to_utf8_flags((U8 *) cur,
+                                                      NATIVE_TO_UNI(auv),
+                                                      warn_utf8 ?
+                                                      0 : UNICODE_ALLOW_ANY);
+               } else {
+                   if (auv >= 0x100) {
+                       if (!SvUTF8(cat)) {
+                           *cur = '\0';
+                           SvCUR_set(cat, cur - start);
+                           marked_upgrade(aTHX_ cat, symptr);
+                           lookahead.flags |= FLAG_DO_UTF8;
+                           lookahead.strbeg = symptr->strbeg;
+                           utf8 = 1;
+                           start = SvPVX(cat);
+                           cur = start + SvCUR(cat);
+                           end = start+SvLEN(cat)-UTF8_MAXLEN;
+                           goto W_utf8;
+                       }
+                       Perl_ck_warner(aTHX_ packWARN(WARN_PACK),
+                                      "Character in 'W' format wrapped in pack");
+                       auv &= 0xff;
+                   }
+                   if (cur >= end) {
+                       *cur = '\0';
+                       SvCUR_set(cat, cur - start);
+                       GROWING(0, cat, start, cur, len+1);
+                       end = start+SvLEN(cat)-1;
+                   }
+                   *(U8 *) cur++ = (U8)auv;
                }
            }
            break;
                }
            }
            break;
-         }
-         case 'U': {
-             char *end;
-
-             if (len == 0) {
-                 if (!(symptr->flags & FLAG_DO_UTF8)) {
-                     marked_upgrade(aTHX_ cat, symptr);
-                     lookahead.flags |= FLAG_DO_UTF8;
-                     lookahead.strbeg = symptr->strbeg;
-                 }
-                 utf8 = 0;
-                 goto no_change;
-             }
+       }
+       case 'U': {
+           char *end;
+
+           if (len == 0) {
+               if (!(symptr->flags & FLAG_DO_UTF8)) {
+                   marked_upgrade(aTHX_ cat, symptr);
+                   lookahead.flags |= FLAG_DO_UTF8;
+                   lookahead.strbeg = symptr->strbeg;
+               }
+               utf8 = 0;
+               goto no_change;
+           }
 
 
-             end = start+SvLEN(cat);
-             if (!utf8) end -= UTF8_MAXLEN;
+           end = start+SvLEN(cat);
+           if (!utf8) end -= UTF8_MAXLEN;
            while (len-- > 0) {
            while (len-- > 0) {
-                 UV auv;
+               UV auv;
                fromstr = NEXTFROM;
                fromstr = NEXTFROM;
-                 auv = SvUV(fromstr);
-                 if (utf8) {
-                     char buffer[UTF8_MAXLEN], *endb;
-                     endb = uvuni_to_utf8_flags(buffer, auv,
-                                                    ckWARN(WARN_UTF8) ?
-                                               0 : UNICODE_ALLOW_ANY);
-                     if (cur+(endb-buffer)*UTF8_EXPAND >= end) {
-                         *cur = '\0';
-                         SvCUR(cat) = cur - start;
-                         GROWING(0, cat, start, cur, 
-                                 len+(endb-buffer)*UTF8_EXPAND);
-                         end = start+SvLEN(cat);
-                     }
-                     bytes_to_uni(aTHX_ buffer, endb-buffer, &cur);
-                 } else {
-                     if (cur >= end) {
-                         *cur = '\0';
-                         SvCUR(cat) = cur - start;
-                         GROWING(0, cat, start, cur, len+UTF8_MAXLEN);
-                         end = start+SvLEN(cat)-UTF8_MAXLEN;
-                     }
-                     cur = uvuni_to_utf8_flags(cur, auv,
-                                                    ckWARN(WARN_UTF8) ?
-                                                0 : UNICODE_ALLOW_ANY);
-                 }
+               auv = SvUV(fromstr);
+               if (utf8) {
+                   U8 buffer[UTF8_MAXLEN], *endb;
+                   endb = uvuni_to_utf8_flags(buffer, auv,
+                                              warn_utf8 ?
+                                              0 : UNICODE_ALLOW_ANY);
+                   if (cur+(endb-buffer)*UTF8_EXPAND >= end) {
+                       *cur = '\0';
+                       SvCUR_set(cat, cur - start);
+                       GROWING(0, cat, start, cur,
+                               len+(endb-buffer)*UTF8_EXPAND);
+                       end = start+SvLEN(cat);
+                   }
+                   cur = bytes_to_uni(buffer, endb-buffer, cur);
+               } else {
+                   if (cur >= end) {
+                       *cur = '\0';
+                       SvCUR_set(cat, cur - start);
+                       GROWING(0, cat, start, cur, len+UTF8_MAXLEN);
+                       end = start+SvLEN(cat)-UTF8_MAXLEN;
+                   }
+                   cur = (char *) uvuni_to_utf8_flags((U8 *) cur, auv,
+                                                      warn_utf8 ?
+                                                      0 : UNICODE_ALLOW_ANY);
+               }
            }
            break;
            }
            break;
-         }
+       }
        /* Float and double added by gnb@melba.bby.oz.au  22/11/89 */
        case 'f':
            while (len-- > 0) {
        /* Float and double added by gnb@melba.bby.oz.au  22/11/89 */
        case 'f':
            while (len-- > 0) {
@@ -2954,25 +3112,28 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
                anv = SvNV(fromstr);
 #ifdef __VOS__
                /* VOS does not automatically map a floating-point overflow
                anv = SvNV(fromstr);
 #ifdef __VOS__
                /* VOS does not automatically map a floating-point overflow
-   during conversion from double to float into infinity, so we
-   do it by hand.  This code should either be generalized for
-   any OS that needs it, or removed if and when VOS implements
-   posix-976 (suggestion to support mapping to infinity).
-   Paul.Green@stratus.com 02-04-02.  */
+                  during conversion from double to float into infinity, so we
+                  do it by hand.  This code should either be generalized for
+                  any OS that needs it, or removed if and when VOS implements
+                  posix-976 (suggestion to support mapping to infinity).
+                  Paul.Green@stratus.com 02-04-02.  */
+{
+extern const float _float_constants[];
                if (anv > FLT_MAX)
                if (anv > FLT_MAX)
-                    afloat = _float_constants[0];   /* single prec. inf. */
+                   afloat = _float_constants[0];   /* single prec. inf. */
                else if (anv < -FLT_MAX)
                else if (anv < -FLT_MAX)
-                    afloat = _float_constants[0];   /* single prec. inf. */
+                   afloat = _float_constants[0];   /* single prec. inf. */
                else afloat = (float) anv;
                else afloat = (float) anv;
+}
 #else /* __VOS__ */
 # if defined(VMS) && !defined(__IEEE_FP)
                /* IEEE fp overflow shenanigans are unavailable on VAX and optional
 #else /* __VOS__ */
 # if defined(VMS) && !defined(__IEEE_FP)
                /* IEEE fp overflow shenanigans are unavailable on VAX and optional
- * on Alpha; fake it if we don't have them.
- */
               * on Alpha; fake it if we don't have them.
               */
                if (anv > FLT_MAX)
                if (anv > FLT_MAX)
-                    afloat = FLT_MAX;
+                   afloat = FLT_MAX;
                else if (anv < -FLT_MAX)
                else if (anv < -FLT_MAX)
-                    afloat = -FLT_MAX;
+                   afloat = -FLT_MAX;
                else afloat = (float)anv;
 # else
                afloat = (float)anv;
                else afloat = (float)anv;
 # else
                afloat = (float)anv;
@@ -2990,25 +3151,28 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
                anv = SvNV(fromstr);
 #ifdef __VOS__
                /* VOS does not automatically map a floating-point overflow
                anv = SvNV(fromstr);
 #ifdef __VOS__
                /* VOS does not automatically map a floating-point overflow
-   during conversion from long double to double into infinity,
-   so we do it by hand.  This code should either be generalized
-   for any OS that needs it, or removed if and when VOS
-   implements posix-976 (suggestion to support mapping to
-   infinity).  Paul.Green@stratus.com 02-04-02.  */
+                  during conversion from long double to double into infinity,
+                  so we do it by hand.  This code should either be generalized
+                  for any OS that needs it, or removed if and when VOS
+                  implements posix-976 (suggestion to support mapping to
+                  infinity).  Paul.Green@stratus.com 02-04-02.  */
+{
+extern const double _double_constants[];
                if (anv > DBL_MAX)
                if (anv > DBL_MAX)
-                    adouble = _double_constants[0];   /* double prec. inf. */
+                   adouble = _double_constants[0];   /* double prec. inf. */
                else if (anv < -DBL_MAX)
                else if (anv < -DBL_MAX)
-                    adouble = _double_constants[0];   /* double prec. inf. */
+                   adouble = _double_constants[0];   /* double prec. inf. */
                else adouble = (double) anv;
                else adouble = (double) anv;
+}
 #else /* __VOS__ */
 # if defined(VMS) && !defined(__IEEE_FP)
                /* IEEE fp overflow shenanigans are unavailable on VAX and optional
 #else /* __VOS__ */
 # if defined(VMS) && !defined(__IEEE_FP)
                /* IEEE fp overflow shenanigans are unavailable on VAX and optional
- * on Alpha; fake it if we don't have them.
- */
               * on Alpha; fake it if we don't have them.
               */
                if (anv > DBL_MAX)
                if (anv > DBL_MAX)
-                    adouble = DBL_MAX;
+                   adouble = DBL_MAX;
                else if (anv < -DBL_MAX)
                else if (anv < -DBL_MAX)
-                    adouble = -DBL_MAX;
+                   adouble = -DBL_MAX;
                else adouble = (double)anv;
 # else
                adouble = (double)anv;
                else adouble = (double)anv;
 # else
                adouble = (double)anv;
@@ -3018,30 +3182,40 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
                PUSH_VAR(utf8, cur, adouble);
            }
            break;
                PUSH_VAR(utf8, cur, adouble);
            }
            break;
-         case 'F': {
-             NV anv;
+       case 'F': {
+           NV_bytes anv;
            Zero(&anv, 1, NV); /* can be long double with unused bits */
            while (len-- > 0) {
                fromstr = NEXTFROM;
            Zero(&anv, 1, NV); /* can be long double with unused bits */
            while (len-- > 0) {
                fromstr = NEXTFROM;
-               anv = SvNV(fromstr);
+#ifdef __GNUC__
+               /* to work round a gcc/x86 bug; don't use SvNV */
+               anv.nv = sv_2nv(fromstr);
+#else
+               anv.nv = SvNV(fromstr);
+#endif
                DO_BO_PACK_N(anv, NV);
                DO_BO_PACK_N(anv, NV);
-                 PUSH_VAR(utf8, cur, anv);
+               PUSH_BYTES(utf8, cur, anv.bytes, sizeof(anv.bytes));
            }
            break;
            }
            break;
-         }
+       }
 #if defined(HAS_LONG_DOUBLE) && defined(USE_LONG_DOUBLE)
 #if defined(HAS_LONG_DOUBLE) && defined(USE_LONG_DOUBLE)
-         case 'D': {
-             long double aldouble;
+       case 'D': {
+           ld_bytes aldouble;
            /* long doubles can have unused bits, which may be nonzero */
            Zero(&aldouble, 1, long double);
            while (len-- > 0) {
                fromstr = NEXTFROM;
            /* long doubles can have unused bits, which may be nonzero */
            Zero(&aldouble, 1, long double);
            while (len-- > 0) {
                fromstr = NEXTFROM;
-               aldouble = (long double)SvNV(fromstr);
+#  ifdef __GNUC__
+               /* to work round a gcc/x86 bug; don't use SvNV */
+               aldouble.ld = (long double)sv_2nv(fromstr);
+#  else
+               aldouble.ld = (long double)SvNV(fromstr);
+#  endif
                DO_BO_PACK_N(aldouble, long double);
                DO_BO_PACK_N(aldouble, long double);
-                 PUSH_VAR(utf8, cur, aldouble);
+               PUSH_BYTES(utf8, cur, aldouble.bytes, sizeof(aldouble.bytes));
            }
            break;
            }
            break;
-         }
+       }
 #endif
 #ifdef PERL_PACK_CAN_SHRIEKSIGN
        case 'n' | TYPE_IS_SHRIEKING:
 #endif
 #ifdef PERL_PACK_CAN_SHRIEKSIGN
        case 'n' | TYPE_IS_SHRIEKING:
@@ -3073,33 +3247,33 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
            break;
         case 'S' | TYPE_IS_SHRIEKING:
 #if SHORTSIZE != SIZE16
            break;
         case 'S' | TYPE_IS_SHRIEKING:
 #if SHORTSIZE != SIZE16
-               while (len-- > 0) {
+           while (len-- > 0) {
                unsigned short aushort;
                unsigned short aushort;
-                   fromstr = NEXTFROM;
-                   aushort = SvUV(fromstr);
-                   DO_BO_PACK(aushort, s);
+               fromstr = NEXTFROM;
+               aushort = SvUV(fromstr);
+               DO_BO_PACK(aushort, s);
                PUSH_VAR(utf8, cur, aushort);
                PUSH_VAR(utf8, cur, aushort);
-            }
+           }
             break;
 #else
             /* Fall through! */
 #endif
        case 'S':
             break;
 #else
             /* Fall through! */
 #endif
        case 'S':
-               while (len-- > 0) {
+           while (len-- > 0) {
                U16 au16;
                U16 au16;
-                   fromstr = NEXTFROM;
-                   au16 = (U16)SvUV(fromstr);
-                   DO_BO_PACK(au16, 16);
+               fromstr = NEXTFROM;
+               au16 = (U16)SvUV(fromstr);
+               DO_BO_PACK(au16, 16);
                PUSH16(utf8, cur, &au16);
            }
            break;
        case 's' | TYPE_IS_SHRIEKING:
 #if SHORTSIZE != SIZE16
                PUSH16(utf8, cur, &au16);
            }
            break;
        case 's' | TYPE_IS_SHRIEKING:
 #if SHORTSIZE != SIZE16
-               while (len-- > 0) {
+           while (len-- > 0) {
                short ashort;
                short ashort;
-                   fromstr = NEXTFROM;
-                   ashort = SvIV(fromstr);
-                   DO_BO_PACK(ashort, s);
+               fromstr = NEXTFROM;
+               ashort = SvIV(fromstr);
+               DO_BO_PACK(ashort, s);
                PUSH_VAR(utf8, cur, ashort);
            }
             break;
                PUSH_VAR(utf8, cur, ashort);
            }
             break;
@@ -3167,7 +3341,7 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
 
                if (anv < 0) {
                    *cur = '\0';
 
                if (anv < 0) {
                    *cur = '\0';
-                   SvCUR(cat) = cur - start;
+                   SvCUR_set(cat, cur - start);
                    Perl_croak(aTHX_ "Cannot compress negative numbers in pack");
                }
 
                    Perl_croak(aTHX_ "Cannot compress negative numbers in pack");
                }
 
@@ -3192,7 +3366,7 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
                    goto w_string;
                else if (SvNOKp(fromstr)) {
                    /* 10**NV_MAX_10_EXP is the largest power of 10
                    goto w_string;
                else if (SvNOKp(fromstr)) {
                    /* 10**NV_MAX_10_EXP is the largest power of 10
-                      so 10**(NV_MAX_10_EXP+1) is definately unrepresentable
+                      so 10**(NV_MAX_10_EXP+1) is definitely unrepresentable
                       given 10**(NV_MAX_10_EXP+1) == 128 ** x solve for x:
                       x = (NV_MAX_10_EXP+1) * log (10) / log (128)
                       And with that many bytes only Inf can overflow.
                       given 10**(NV_MAX_10_EXP+1) == 128 ** x solve for x:
                       x = (NV_MAX_10_EXP+1) * log (10) / log (128)
                       And with that many bytes only Inf can overflow.
@@ -3210,9 +3384,9 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
 #endif
                    char  *in = buf + sizeof(buf);
 
 #endif
                    char  *in = buf + sizeof(buf);
 
-                    anv = Perl_floor(anv);
+                   anv = Perl_floor(anv);
                    do {
                    do {
-                       NV next = Perl_floor(anv / 128);
+                       const NV next = Perl_floor(anv / 128);
                        if (in <= buf)  /* this cannot happen ;-) */
                            Perl_croak(aTHX_ "Cannot compress integer in pack");
                        *--in = (unsigned char)(anv - (next * 128)) | 0x80;
                        if (in <= buf)  /* this cannot happen ;-) */
                            Perl_croak(aTHX_ "Cannot compress integer in pack");
                        *--in = (unsigned char)(anv - (next * 128)) | 0x80;
@@ -3222,18 +3396,19 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
                    PUSH_GROWING_BYTES(utf8, cat, start, cur,
                                       in, (buf + sizeof(buf)) - in);
                } else {
                    PUSH_GROWING_BYTES(utf8, cat, start, cur,
                                       in, (buf + sizeof(buf)) - in);
                } else {
-                   char           *from, *result, *in;
+                   const char     *from;
+                   char           *result, *in;
                    SV             *norm;
                    STRLEN          len;
                    bool            done;
 
                  w_string:
                    /* Copy string and check for compliance */
                    SV             *norm;
                    STRLEN          len;
                    bool            done;
 
                  w_string:
                    /* Copy string and check for compliance */
-                   from = SvPV(fromstr, len);
+                   from = SvPV_const(fromstr, len);
                    if ((norm = is_an_int(from, len)) == NULL)
                        Perl_croak(aTHX_ "Can only compress unsigned integers in pack");
 
                    if ((norm = is_an_int(from, len)) == NULL)
                        Perl_croak(aTHX_ "Can only compress unsigned integers in pack");
 
-                   New('w', result, len, char);
+                   Newx(result, len, char);
                    in = result + len;
                    done = FALSE;
                    while (!done) *--in = div128(norm, &done) | 0x80;
                    in = result + len;
                    done = FALSE;
                    while (!done) *--in = div128(norm, &done) | 0x80;
@@ -3242,7 +3417,7 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
                                       in, (result + len) - in);
                    Safefree(result);
                    SvREFCNT_dec(norm); /* free norm */
                                       in, (result + len) - in);
                    Safefree(result);
                    SvREFCNT_dec(norm); /* free norm */
-               }
+               }
            }
             break;
        case 'i':
            }
             break;
        case 'i':
@@ -3285,11 +3460,11 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
            break;
        case 'L' | TYPE_IS_SHRIEKING:
 #if LONGSIZE != SIZE32
            break;
        case 'L' | TYPE_IS_SHRIEKING:
 #if LONGSIZE != SIZE32
-               while (len-- > 0) {
+           while (len-- > 0) {
                unsigned long aulong;
                unsigned long aulong;
-                   fromstr = NEXTFROM;
-                   aulong = SvUV(fromstr);
-                   DO_BO_PACK(aulong, l);
+               fromstr = NEXTFROM;
+               aulong = SvUV(fromstr);
+               DO_BO_PACK(aulong, l);
                PUSH_VAR(utf8, cur, aulong);
            }
            break;
                PUSH_VAR(utf8, cur, aulong);
            }
            break;
@@ -3297,21 +3472,21 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
             /* Fall though! */
 #endif
        case 'L':
             /* Fall though! */
 #endif
        case 'L':
-               while (len-- > 0) {
+           while (len-- > 0) {
                U32 au32;
                U32 au32;
-                   fromstr = NEXTFROM;
-                   au32 = SvUV(fromstr);
-                   DO_BO_PACK(au32, 32);
+               fromstr = NEXTFROM;
+               au32 = SvUV(fromstr);
+               DO_BO_PACK(au32, 32);
                PUSH32(utf8, cur, &au32);
            }
            break;
        case 'l' | TYPE_IS_SHRIEKING:
 #if LONGSIZE != SIZE32
                PUSH32(utf8, cur, &au32);
            }
            break;
        case 'l' | TYPE_IS_SHRIEKING:
 #if LONGSIZE != SIZE32
-               while (len-- > 0) {
+           while (len-- > 0) {
                long along;
                long along;
-                   fromstr = NEXTFROM;
-                   along = SvIV(fromstr);
-                   DO_BO_PACK(along, l);
+               fromstr = NEXTFROM;
+               along = SvIV(fromstr);
+               DO_BO_PACK(along, l);
                PUSH_VAR(utf8, cur, along);
            }
            break;
                PUSH_VAR(utf8, cur, along);
            }
            break;
@@ -3353,82 +3528,82 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
            /* Fall through! */
        case 'p':
            while (len-- > 0) {
            /* Fall through! */
        case 'p':
            while (len-- > 0) {
-               char *aptr;
+               const char *aptr;
 
                fromstr = NEXTFROM;
                SvGETMAGIC(fromstr);
                if (!SvOK(fromstr)) aptr = NULL;
                else {
 
                fromstr = NEXTFROM;
                SvGETMAGIC(fromstr);
                if (!SvOK(fromstr)) aptr = NULL;
                else {
-                   STRLEN n_a;
                    /* XXX better yet, could spirit away the string to
                     * a safe spot and hang on to it until the result
                     * of pack() (and all copies of the result) are
                     * gone.
                     */
                    /* XXX better yet, could spirit away the string to
                     * a safe spot and hang on to it until the result
                     * of pack() (and all copies of the result) are
                     * gone.
                     */
-                   if (ckWARN(WARN_PACK) &&
-                       (SvTEMP(fromstr) || (SvPADTMP(fromstr) &&
-                                            !SvREADONLY(fromstr)))) {
-                       Perl_warner(aTHX_ packWARN(WARN_PACK),
-                               "Attempt to pack pointer to temporary value");
+                   if ((SvTEMP(fromstr) || (SvPADTMP(fromstr) &&
+                            !SvREADONLY(fromstr)))) {
+                       Perl_ck_warner(aTHX_ packWARN(WARN_PACK),
+                                      "Attempt to pack pointer to temporary value");
                    }
                    if (SvPOK(fromstr) || SvNIOK(fromstr))
                    }
                    if (SvPOK(fromstr) || SvNIOK(fromstr))
-                       aptr = SvPV_flags(fromstr, n_a, 0);
+                       aptr = SvPV_nomg_const_nolen(fromstr);
                    else
                    else
-                       aptr = SvPV_force_flags(fromstr, n_a, 0);
+                       aptr = SvPV_force_flags_nolen(fromstr, 0);
                }
                }
-               DO_BO_PACK_P(aptr);
+               DO_BO_PACK_PC(aptr);
                PUSH_VAR(utf8, cur, aptr);
            }
            break;
                PUSH_VAR(utf8, cur, aptr);
            }
            break;
-         case 'u': {
-             char *aptr, *aend;
-             bool from_utf8;
+       case 'u': {
+           const char *aptr, *aend;
+           bool from_utf8;
 
            fromstr = NEXTFROM;
 
            fromstr = NEXTFROM;
-             if (len <= 2) len = 45;
-             else len = len / 3 * 3;
-             if (len >= 64) {
-                 Perl_warner(aTHX_ packWARN(WARN_PACK),
-                             "Field too wide in 'u' format in pack");
-                 len = 63;
-             }
-           aptr = SvPV(fromstr, fromlen);
-             from_utf8 = DO_UTF8(fromstr);
-             if (from_utf8) {
-                 aend = aptr + fromlen;
-                 fromlen = sv_len_utf8(fromstr);
-             } else aend = NULL; /* Unused, but keep compilers happy */
-             GROWING(utf8, cat, start, cur, (fromlen+2) / 3 * 4 + (fromlen+len-1)/len * 2);
+           if (len <= 2) len = 45;
+           else len = len / 3 * 3;
+           if (len >= 64) {
+               Perl_ck_warner(aTHX_ packWARN(WARN_PACK),
+                              "Field too wide in 'u' format in pack");
+               len = 63;
+           }
+           aptr = SvPV_const(fromstr, fromlen);
+           from_utf8 = DO_UTF8(fromstr);
+           if (from_utf8) {
+               aend = aptr + fromlen;
+               fromlen = sv_len_utf8(fromstr);
+           } else aend = NULL; /* Unused, but keep compilers happy */
+           GROWING(utf8, cat, start, cur, (fromlen+2) / 3 * 4 + (fromlen+len-1)/len * 2);
            while (fromlen > 0) {
            while (fromlen > 0) {
-                 U8 *end;
+               U8 *end;
                I32 todo;
                I32 todo;
-                 U8 hunk[1+63/3*4+1];
+               U8 hunk[1+63/3*4+1];
 
                if ((I32)fromlen > len)
                    todo = len;
                else
                    todo = fromlen;
 
                if ((I32)fromlen > len)
                    todo = len;
                else
                    todo = fromlen;
-                 if (from_utf8) {
-                     char buffer[64];
-                     if (!uni_to_bytes(aTHX_ &aptr, aend, buffer, todo, 
-                                       'u' | TYPE_IS_PACK)) {
-                         *cur = '\0';
-                         SvCUR(cat) = cur - start;
-                         Perl_croak(aTHX_ "Assertion: string is shorter than advertised");
-                     }
-                     end = doencodes(hunk, buffer, todo);
-                 } else {
-                     end = doencodes(hunk, aptr, todo);
-               aptr += todo;
-           }
-                 PUSH_BYTES(utf8, cur, hunk, end-hunk);
-                 fromlen -= todo;
-             }
+               if (from_utf8) {
+                   char buffer[64];
+                   if (!uni_to_bytes(aTHX_ &aptr, aend, buffer, todo,
+                                     'u' | TYPE_IS_PACK)) {
+                       *cur = '\0';
+                       SvCUR_set(cat, cur - start);
+                       Perl_croak(aTHX_ "panic: string is shorter than advertised, "
+                                  "aptr=%p, aend=%p, buffer=%p, todo=%ld",
+                                  aptr, aend, buffer, (long) todo);
+                   }
+                   end = doencodes(hunk, buffer, todo);
+               } else {
+                   end = doencodes(hunk, aptr, todo);
+                   aptr += todo;
+               }
+               PUSH_BYTES(utf8, cur, hunk, end-hunk);
+               fromlen -= todo;
+           }
            break;
        }
        }
        *cur = '\0';
            break;
        }
        }
        *cur = '\0';
-       SvCUR(cat) = cur - start;
+       SvCUR_set(cat, cur - start);
       no_change:
        *symptr = lookahead;
     }
       no_change:
        *symptr = lookahead;
     }
@@ -3439,14 +3614,15 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
 
 PP(pp_pack)
 {
 
 PP(pp_pack)
 {
-    dSP; dMARK; dORIGMARK; dTARGET;
+    dVAR; dSP; dMARK; dORIGMARK; dTARGET;
     register SV *cat = TARG;
     STRLEN fromlen;
     register SV *cat = TARG;
     STRLEN fromlen;
-    register char *pat = SvPVx(*++MARK, fromlen);
-    register char *patend = pat + fromlen;
+    SV *pat_sv = *++MARK;
+    register const char *pat = SvPV_const(pat_sv, fromlen);
+    register const char *patend = pat + fromlen;
 
     MARK++;
 
     MARK++;
-    sv_setpvn(cat, "", 0);
+    sv_setpvs(cat, "");
     SvUTF8_off(cat);
 
     packlist(cat, pat, patend, MARK, SP + 1);
     SvUTF8_off(cat);
 
     packlist(cat, pat, patend, MARK, SP + 1);
@@ -3461,8 +3637,8 @@ PP(pp_pack)
  * Local variables:
  * c-indentation-style: bsd
  * c-basic-offset: 4
  * Local variables:
  * c-indentation-style: bsd
  * c-basic-offset: 4
- * indent-tabs-mode: t
+ * indent-tabs-mode: nil
  * End:
  *
  * End:
  *
- * vim: shiftwidth=4:
-*/
+ * ex: set ts=8 sts=4 sw=4 et:
+ */