This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
sv_setuv_mg is so rarely called that the IV optimisation test is not
[perl5.git] / util.c
diff --git a/util.c b/util.c
index b562f2f..6ef7a01 100644 (file)
--- a/util.c
+++ b/util.c
@@ -152,7 +152,6 @@ Perl_safesysfree(Malloc_t where)
 #endif
     DEBUG_m( PerlIO_printf(Perl_debug_log, "0x%"UVxf": (%05ld) free\n",PTR2UV(where),(long)PL_an++));
     if (where) {
-       /*SUPPRESS 701*/
        PerlMem_free(where);
     }
 }
@@ -261,7 +260,6 @@ Perl_delimcpy(pTHX_ register char *to, register const char *toend, register cons
 char *
 Perl_instr(pTHX_ register const char *big, register const char *little)
 {
-    register const char *s, *x;
     register I32 first;
 
     if (!little)
@@ -270,6 +268,7 @@ Perl_instr(pTHX_ register const char *big, register const char *little)
     if (!first)
        return (char*)big;
     while (*big) {
+       register const char *s, *x;
        if (*big++ != first)
            continue;
        for (x=big,s=little; *s; /**/ ) {
@@ -291,7 +290,6 @@ Perl_instr(pTHX_ register const char *big, register const char *little)
 char *
 Perl_ninstr(pTHX_ register const char *big, register const char *bigend, const char *little, const char *lend)
 {
-    register const char *s, *x;
     register const I32 first = *little;
     register const char *littleend = lend;
 
@@ -301,6 +299,7 @@ Perl_ninstr(pTHX_ register const char *big, register const char *bigend, const c
        return Nullch;
     bigend -= littleend - little++;
     while (big <= bigend) {
+       register const char *s, *x;
        if (*big++ != first)
            continue;
        for (x=big,s=little; s < littleend; /**/ ) {
@@ -321,7 +320,6 @@ char *
 Perl_rninstr(pTHX_ register const char *big, const char *bigend, const char *little, const char *lend)
 {
     register const char *bigbeg;
-    register const char *s, *x;
     register const I32 first = *little;
     register const char *littleend = lend;
 
@@ -330,6 +328,7 @@ Perl_rninstr(pTHX_ register const char *big, const char *bigend, const char *lit
     bigbeg = big;
     big = bigend - (littleend - little++);
     while (big >= bigbeg) {
+       register const char *s, *x;
        if (*big-- != first)
            continue;
        for (x=big+2,s=little; s < littleend; /**/ ) {
@@ -384,13 +383,9 @@ Perl_fbm_compile(pTHX_ SV *sv, U32 flags)
     if (len == 0)              /* TAIL might be on a zero-length string. */
        return;
     if (len > 2) {
-       U8 mlen;
        const unsigned char *sb;
+       const U8 mlen = (len>255) ? 255 : (U8)len;
 
-       if (len > 255)
-           mlen = 255;
-       else
-           mlen = (U8)len;
        Sv_Grow(sv, len + 256 + FBM_TABLE_OFFSET);
        table = (unsigned char*)(SvPVX_mutable(sv) + len + FBM_TABLE_OFFSET);
        s = table - 1 - FBM_TABLE_OFFSET;       /* last char */
@@ -492,8 +487,8 @@ Perl_fbm_instr(pTHX_ unsigned char *big, register unsigned char *bigend, SV *lit
            /* This should be better than FBM if c1 == c2, and almost
               as good otherwise: maybe better since we do less indirection.
               And we save a lot of memory by caching no table. */
-           register unsigned char c1 = little[0];
-           register unsigned char c2 = little[1];
+           const unsigned char c1 = little[0];
+           const unsigned char c2 = little[1];
 
            s = big + 1;
            bigend--;
@@ -588,14 +583,13 @@ Perl_fbm_instr(pTHX_ unsigned char *big, register unsigned char *bigend, SV *lit
            register I32 tmp;
 
          top2:
-           /*SUPPRESS 560*/
            if ((tmp = table[*s])) {
                if ((s += tmp) < bigend)
                    goto top2;
                goto check_end;
            }
            else {              /* less expensive than calling strncmp() */
-               register unsigned char *olds = s;
+               register unsigned char * const olds = s;
 
                tmp = littlelen;
 
@@ -638,14 +632,13 @@ Perl_fbm_instr(pTHX_ unsigned char *big, register unsigned char *bigend, SV *lit
 char *
 Perl_screaminstr(pTHX_ SV *bigstr, SV *littlestr, I32 start_shift, I32 end_shift, I32 *old_posp, I32 last)
 {
-    register unsigned char *s, *x;
-    register unsigned char *big;
+    const register unsigned char *big;
     register I32 pos;
     register I32 previous;
     register I32 first;
-    register unsigned char *little;
+    const register unsigned char *little;
     register I32 stop_pos;
-    register unsigned char *littleend;
+    const register unsigned char *littleend;
     I32 found = 0;
 
     if (*old_posp == -1
@@ -654,7 +647,7 @@ Perl_screaminstr(pTHX_ SV *bigstr, SV *littlestr, I32 start_shift, I32 end_shift
       cant_find:
        if ( BmRARE(littlestr) == '\n'
             && BmPREVIOUS(littlestr) == SvCUR(littlestr) - 1) {
-           little = (unsigned char *)(SvPVX(littlestr));
+           little = (const unsigned char *)(SvPVX_const(littlestr));
            littleend = little + SvCUR(littlestr);
            first = *little++;
            goto check_tail;
@@ -662,12 +655,12 @@ Perl_screaminstr(pTHX_ SV *bigstr, SV *littlestr, I32 start_shift, I32 end_shift
        return Nullch;
     }
 
-    little = (unsigned char *)(SvPVX(littlestr));
+    little = (const unsigned char *)(SvPVX_const(littlestr));
     littleend = little + SvCUR(littlestr);
     first = *little++;
     /* The value of pos we can start at: */
     previous = BmPREVIOUS(littlestr);
-    big = (unsigned char *)(SvPVX(bigstr));
+    big = (const unsigned char *)(SvPVX_const(bigstr));
     /* The value of pos we can stop at: */
     stop_pos = SvCUR(bigstr) - end_shift - (SvCUR(littlestr) - 1 - previous);
     if (previous + start_shift > stop_pos) {
@@ -687,6 +680,7 @@ Perl_screaminstr(pTHX_ SV *bigstr, SV *littlestr, I32 start_shift, I32 end_shift
     }
     big -= previous;
     do {
+       const register unsigned char *s, *x;
        if (pos >= stop_pos) break;
        if (big[pos] != first)
            continue;
@@ -708,7 +702,7 @@ Perl_screaminstr(pTHX_ SV *bigstr, SV *littlestr, I32 start_shift, I32 end_shift
     if (!SvTAIL(littlestr) || (end_shift > 0))
        return Nullch;
     /* Ignore the trailing "\n".  This code is not microoptimized */
-    big = (unsigned char *)(SvPVX(bigstr) + SvCUR(bigstr));
+    big = (const unsigned char *)(SvPVX_const(bigstr) + SvCUR(bigstr));
     stop_pos = littleend - little;     /* Actual littlestr len */
     if (stop_pos == 0)
        return (char*)big;
@@ -765,20 +759,15 @@ be freed with the C<Safefree()> function.
 char *
 Perl_savepv(pTHX_ const char *pv)
 {
-    register char *newaddr;
-#ifdef PERL_MALLOC_WRAP
-    STRLEN pvlen;
-#endif
     if (!pv)
        return Nullch;
+    else {
+       char *newaddr;
+       const STRLEN pvlen = strlen(pv)+1;
+       New(902,newaddr,pvlen,char);
+       return memcpy(newaddr,pv,pvlen);
+    }
 
-#ifdef PERL_MALLOC_WRAP
-    pvlen = strlen(pv)+1;
-    New(902,newaddr,pvlen,char);
-#else
-    New(902,newaddr,strlen(pv)+1,char);
-#endif
-    return strcpy(newaddr,pv);
 }
 
 /* same thing but with a known length */
@@ -823,16 +812,18 @@ char *
 Perl_savesharedpv(pTHX_ const char *pv)
 {
     register char *newaddr;
+    STRLEN pvlen;
     if (!pv)
        return Nullch;
 
-    newaddr = (char*)PerlMemShared_malloc(strlen(pv)+1);
+    pvlen = strlen(pv)+1;
+    newaddr = (char*)PerlMemShared_malloc(pvlen);
     if (!newaddr) {
        PerlLIO_write(PerlIO_fileno(Perl_error_log),
                      PL_no_mem, strlen(PL_no_mem));
        my_exit(1);
     }
-    return strcpy(newaddr,pv);
+    return memcpy(newaddr,pv,pvlen);
 }
 
 /*
@@ -848,7 +839,7 @@ char *
 Perl_savesvpv(pTHX_ SV *sv)
 {
     STRLEN len;
-    const char *pv = SvPV(sv, len);
+    const char *pv = SvPV_const(sv, len);
     register char *newaddr;
 
     ++len;
@@ -961,7 +952,7 @@ Perl_mess(pTHX_ const char *pat, ...)
 }
 
 STATIC COP*
-S_closest_cop(pTHX_ COP *cop, OP *o)
+S_closest_cop(pTHX_ COP *cop, const OP *o)
 {
     /* Look for PL_op starting from o.  cop is the last COP we've seen. */
 
@@ -988,7 +979,7 @@ S_closest_cop(pTHX_ COP *cop, OP *o)
 
     /* Nothing found. */
 
-    return 0;
+    return Null(COP *);
 }
 
 SV *
@@ -1062,9 +1053,9 @@ Perl_write_to_stderr(pTHX_ const char* message, int msglen)
     else {
 #ifdef USE_SFIO
        /* SFIO can really mess with your errno */
-       int e = errno;
+       const int e = errno;
 #endif
-       PerlIO *serr = Perl_error_log;
+       PerlIO * const serr = Perl_error_log;
 
        PERL_WRITE_MSG_TO_CONSOLE(serr, message, msglen);
        (void)PerlIO_flush(serr);
@@ -1117,22 +1108,22 @@ S_vdie_common(pTHX_ const char *message, STRLEN msglen, I32 utf8)
     }
 }
 
-STATIC char *
+STATIC const char *
 S_vdie_croak_common(pTHX_ const char* pat, va_list* args, STRLEN* msglen,
                    I32* utf8)
 {
     dVAR;
-    char *message;
+    const char *message;
 
     if (pat) {
        SV *msv = vmess(pat, args);
        if (PL_errors && SvCUR(PL_errors)) {
            sv_catsv(PL_errors, msv);
-           message = SvPV(PL_errors, *msglen);
+           message = SvPV_const(PL_errors, *msglen);
            SvCUR_set(PL_errors, 0);
        }
        else
-           message = SvPV(msv,*msglen);
+           message = SvPV_const(msv,*msglen);
        *utf8 = SvUTF8(msv);
     }
     else {
@@ -1212,7 +1203,7 @@ Perl_vcroak(pTHX_ const char* pat, va_list *args)
        JMPENV_JUMP(3);
     }
     else if (!message)
-       message = SvPVx(ERRSV, msglen);
+       message = SvPVx_const(ERRSV, msglen);
 
     write_to_stderr(message, msglen);
     my_failure_exit();
@@ -1265,21 +1256,18 @@ void
 Perl_vwarn(pTHX_ const char* pat, va_list *args)
 {
     dVAR;
-    char *message;
-    HV *stash;
-    GV *gv;
-    CV *cv;
-    SV *msv;
     STRLEN msglen;
-    I32 utf8 = 0;
-
-    msv = vmess(pat, args);
-    utf8 = SvUTF8(msv);
-    message = SvPV(msv, msglen);
+    SV * const msv = vmess(pat, args);
+    const I32 utf8 = SvUTF8(msv);
+    const char * const message = SvPV_const(msv, msglen);
 
     if (PL_warnhook) {
        /* sv_2cv might call Perl_warn() */
-       SV *oldwarnhook = PL_warnhook;
+       SV * const oldwarnhook = PL_warnhook;
+       CV * cv;
+       HV * stash;
+       GV * gv;
+
        ENTER;
        SAVESPTR(PL_warnhook);
        PL_warnhook = Nullsv;
@@ -1368,7 +1356,7 @@ Perl_vwarner(pTHX_ U32  err, const char* pat, va_list* args)
     if (ckDEAD(err)) {
        SV * const msv = vmess(pat, args);
        STRLEN msglen;
-       const char *message = SvPV(msv, msglen);
+       const char *message = SvPV_const(msv, msglen);
        const I32 utf8 = SvUTF8(msv);
 
        if (PL_diehook) {
@@ -1388,6 +1376,58 @@ Perl_vwarner(pTHX_ U32  err, const char* pat, va_list* args)
     }
 }
 
+/* implements the ckWARN? macros */
+
+bool
+Perl_ckwarn(pTHX_ U32 w)
+{
+    return
+       (
+              isLEXWARN_on
+           && PL_curcop->cop_warnings != pWARN_NONE
+           && (
+                  PL_curcop->cop_warnings == pWARN_ALL
+               || isWARN_on(PL_curcop->cop_warnings, unpackWARN1(w))
+               || (unpackWARN2(w) &&
+                    isWARN_on(PL_curcop->cop_warnings, unpackWARN2(w)))
+               || (unpackWARN3(w) &&
+                    isWARN_on(PL_curcop->cop_warnings, unpackWARN3(w)))
+               || (unpackWARN4(w) &&
+                    isWARN_on(PL_curcop->cop_warnings, unpackWARN4(w)))
+               )
+       )
+       ||
+       (
+           isLEXWARN_off && PL_dowarn & G_WARN_ON
+       )
+       ;
+}
+
+/* implements the ckWARN?_d macro */
+
+bool
+Perl_ckwarn_d(pTHX_ U32 w)
+{
+    return
+          isLEXWARN_off
+       || PL_curcop->cop_warnings == pWARN_ALL
+       || (
+             PL_curcop->cop_warnings != pWARN_NONE 
+          && (
+                  isWARN_on(PL_curcop->cop_warnings, unpackWARN1(w))
+             || (unpackWARN2(w) &&
+                  isWARN_on(PL_curcop->cop_warnings, unpackWARN2(w)))
+             || (unpackWARN3(w) &&
+                  isWARN_on(PL_curcop->cop_warnings, unpackWARN3(w)))
+             || (unpackWARN4(w) &&
+                  isWARN_on(PL_curcop->cop_warnings, unpackWARN4(w)))
+             )
+          )
+       ;
+}
+
+
+
 /* since we've already done strlen() for both nam and val
  * we can use that info to make things faster than
  * sprintf(s, "%s=%s", nam, val)
@@ -1421,7 +1461,6 @@ Perl_my_setenv(pTHX_ const char *nam, const char *val)
        I32 max;
        char **tmpenv;
 
-       /*SUPPRESS 530*/
        for (max = i; environ[max]; max++) ;
        tmpenv = (char**)safesysmalloc((max+2) * sizeof(char*));
        for (j=0; j<max; j++) {         /* copy environment */
@@ -1458,7 +1497,8 @@ Perl_my_setenv(pTHX_ const char *nam, const char *val)
     setenv(nam, val, 1);
 #   else
     char *new_env;
-    int nlen = strlen(nam), vlen;
+    const int nlen = strlen(nam);
+    int vlen;
     if (!val) {
        val = "";
     }
@@ -1500,7 +1540,8 @@ Perl_my_setenv(pTHX_ const char *nam, const char *val)
 I32
 Perl_setenv_getix(pTHX_ const char *nam)
 {
-    register I32 i, len = strlen(nam);
+    register I32 i;
+    const register I32 len = strlen(nam);
 
     for (i = 0; environ[i]; i++) {
        if (
@@ -2160,7 +2201,6 @@ Perl_my_popen(pTHX_ char *cmd, char *mode)
            PerlProc__exit(1);
        }
 #endif /* defined OS2 */
-       /*SUPPRESS 560*/
        if ((tmpgv = gv_fetchpv("$",TRUE, SVt_PV))) {
            SvREADONLY_off(GvSV(tmpgv));
            sv_setiv(GvSV(tmpgv), PerlProc_getpid());
@@ -2641,7 +2681,6 @@ Perl_wait4pid(pTHX_ Pid_t pid, int *statusp, int flags)
 #endif /* !DOSISH || OS2 || WIN32 || NETWARE */
 
 void
-/*SUPPRESS 590*/
 Perl_pidgone(pTHX_ Pid_t pid, int status)
 {
     register SV *sv;
@@ -2772,7 +2811,7 @@ Perl_find_script(pTHX_ const char *scriptname, bool dosearch, const char **searc
     int extidx = 0, i = 0;
     const char *curext = Nullch;
 #else
-    (void)search_ext;
+    PERL_UNUSED_ARG(search_ext);
 #  define MAX_EXT_LEN 0
 #endif
 
@@ -2855,6 +2894,7 @@ Perl_find_script(pTHX_ const char *scriptname, bool dosearch, const char **searc
                len = strlen(scriptname);
                if (len+MAX_EXT_LEN+1 >= sizeof(tmpbuf))
                    break;
+               /* FIXME? Convert to memcpy  */
                cur = strcpy(tmpbuf, scriptname);
            }
        } while (extidx >= 0 && ext[extidx]     /* try an extension? */
@@ -2909,15 +2949,17 @@ Perl_find_script(pTHX_ const char *scriptname, bool dosearch, const char **searc
                tmpbuf[len++] = ':';
 #else
            if (len
-#if defined(atarist) || defined(__MINT__) || defined(DOSISH)
+#  if defined(atarist) || defined(__MINT__) || defined(DOSISH)
                && tmpbuf[len - 1] != '/'
                && tmpbuf[len - 1] != '\\'
-#endif
+#  endif
               )
                tmpbuf[len++] = '/';
            if (len == 2 && tmpbuf[0] == '.')
                seen_dot = 1;
 #endif
+           /* FIXME? Convert to memcpy by storing previous strlen(scriptname)
+            */
            (void)strcpy(tmpbuf + len, scriptname);
 #endif  /* !VMS */
 
@@ -2969,8 +3011,7 @@ Perl_find_script(pTHX_ const char *scriptname, bool dosearch, const char **searc
            }
            scriptname = Nullch;
        }
-       if (xfailed)
-           Safefree(xfailed);
+       Safefree(xfailed);
        scriptname = xfound;
     }
     return (scriptname ? savepv(scriptname) : Nullch);
@@ -3003,7 +3044,7 @@ Perl_get_context(void)
 void
 Perl_set_context(void *t)
 {
-   dVAR;
+    dVAR;
 #if defined(USE_ITHREADS)
 #  ifdef I_MACH_CTHREADS
     cthread_set_data(cthread_self(), t);
@@ -3012,7 +3053,7 @@ Perl_set_context(void *t)
        Perl_croak_nocontext("panic: pthread_setspecific");
 #  endif
 #else
-    (void)t;
+    PERL_UNUSED_ARG(t);
 #endif
 }
 
@@ -3061,7 +3102,7 @@ Perl_get_ppaddr(pTHX)
 char *
 Perl_getenv_len(pTHX_ const char *env_elem, unsigned long *len)
 {
-    char *env_trans = PerlEnv_getenv(env_elem);
+    char * const env_trans = PerlEnv_getenv(env_elem);
     if (env_trans)
        *len = strlen(env_trans);
     return env_trans;
@@ -3355,11 +3396,13 @@ Perl_init_tm(pTHX_ struct tm *ptm)      /* see mktime, strftime and asctime */
 {
 #ifdef HAS_TM_TM_ZONE
     Time_t now;
-    struct tm* my_tm;
+    const struct tm* my_tm;
     (void)time(&now);
     my_tm = localtime(&now);
     if (my_tm)
         Copy(my_tm, ptm, 1, struct tm);
+#else
+    PERL_UNUSED_ARG(ptm);
 #endif
 }
 
@@ -3834,18 +3877,30 @@ it doesn't.
 =cut
 */
 
-char *
+const char *
 Perl_scan_version(pTHX_ const char *s, SV *rv, bool qv)
 {
     const char *start = s;
-    const char *pos = s;
-    I32 saw_period = 0;
-    bool saw_under = 0;
-    SV* sv = newSVrv(rv, "version"); /* create an SV and upgrade the RV */
-    (void)sv_upgrade(sv, SVt_PVAV); /* needs to be an AV type */
-    AvREAL_on((AV*)sv);
-
-    /* pre-scan the imput string to check for decimals */
+    const char *pos;
+    const char *last;
+    int saw_period = 0;
+    int saw_under = 0;
+    int width = 3;
+    AV *av = newAV();
+    SV* hv = newSVrv(rv, "version"); /* create an SV and upgrade the RV */
+    (void)sv_upgrade(hv, SVt_PVHV); /* needs to be an HV type */
+#ifndef NODEFAULT_SHAREKEYS
+    HvSHAREKEYS_on(hv);         /* key-sharing on by default */
+#endif
+
+    if (*s == 'v') {
+       s++;  /* get past 'v' */
+       qv = 1; /* force quoted version processing */
+    }
+
+    last = pos = s;
+
+    /* pre-scan the input string to check for decimals/underbars */
     while ( *pos == '.' || *pos == '_' || isDIGIT(*pos) )
     {
        if ( *pos == '.' )
@@ -3853,38 +3908,45 @@ Perl_scan_version(pTHX_ const char *s, SV *rv, bool qv)
            if ( saw_under )
                Perl_croak(aTHX_ "Invalid version format (underscores before decimal)");
            saw_period++ ;
+           last = pos;
        }
        else if ( *pos == '_' )
        {
            if ( saw_under )
                Perl_croak(aTHX_ "Invalid version format (multiple underscores)");
            saw_under = 1;
+           width = pos - last - 1; /* natural width of sub-version */
        }
        pos++;
     }
-    pos = s;
 
-    if (*pos == 'v') {
-       pos++;  /* get past 'v' */
+    if ( saw_period > 1 ) {
        qv = 1; /* force quoted version processing */
     }
+
+    pos = s;
+
+    if ( qv )
+       hv_store((HV *)hv, "qv", 2, &PL_sv_yes, 0);
+    if ( saw_under ) {
+       hv_store((HV *)hv, "alpha", 5, &PL_sv_yes, 0);
+    }
+    if ( !qv && width < 3 )
+       hv_store((HV *)hv, "width", 5, newSViv(width), 0);
+    
     while (isDIGIT(*pos))
        pos++;
     if (!isALPHA(*pos)) {
        I32 rev;
 
-       if (*s == 'v') s++;  /* get past 'v' */
-
        for (;;) {
            rev = 0;
            {
                /* this is atoi() that delimits on underscores */
-               const char *end = pos;
+               const char *end = pos;
                I32 mult = 1;
                I32 orev;
-               if ( s < pos && s > start && *(s-1) == '_' ) {
-                       mult *= -1;     /* alpha version */
-               }
+
                /* the following if() will only be true after the decimal
                 * point of a version originally created with a bare
                 * floating point number, i.e. not quoted in any way
@@ -3898,6 +3960,8 @@ Perl_scan_version(pTHX_ const char *s, SV *rv, bool qv)
                        if ( PERL_ABS(orev) > PERL_ABS(rev) )
                            Perl_croak(aTHX_ "Integer overflow in version");
                        s++;
+                       if ( *s == '_' )
+                           s++;
                    }
                }
                else {
@@ -3910,10 +3974,12 @@ Perl_scan_version(pTHX_ const char *s, SV *rv, bool qv)
                    }
                } 
            }
-  
+
            /* Append revision */
-           av_push((AV *)sv, newSViv(rev));
-           if ( (*pos == '.' || *pos == '_') && isDIGIT(pos[1]))
+           av_push(av, newSViv(rev));
+           if ( *pos == '.' && isDIGIT(pos[1]) )
+               s = ++pos;
+           else if ( *pos == '_' && isDIGIT(pos[1]) )
                s = ++pos;
            else if ( isDIGIT(*pos) )
                s = pos;
@@ -3921,15 +3987,22 @@ Perl_scan_version(pTHX_ const char *s, SV *rv, bool qv)
                s = pos;
                break;
            }
-           while ( isDIGIT(*pos) ) {
-               if ( saw_period == 1 && pos-s == 3 )
-                   break;
-               pos++;
+           if ( qv ) {
+               while ( isDIGIT(*pos) )
+                   pos++;
+           }
+           else {
+               int digits = 0;
+               while ( ( isDIGIT(*pos) || *pos == '_' ) && digits < 3 ) {
+                   if ( *pos != '_' )
+                       digits++;
+                   pos++;
+               }
            }
        }
     }
-    if ( qv ) { /* quoted versions always become full version objects */
-       I32 len = av_len((AV *)sv);
+    if ( qv ) { /* quoted versions always get at least three terms*/
+       I32 len = av_len(av);
        /* This for loop appears to trigger a compiler bug on OS X, as it
           loops infinitely. Yes, len is negative. No, it makes no sense.
           Compiler in question is:
@@ -3939,9 +4012,15 @@ Perl_scan_version(pTHX_ const char *s, SV *rv, bool qv)
        */
        len = 2 - len;
        while (len-- > 0)
-           av_push((AV *)sv, newSViv(0));
+           av_push(av, newSViv(0));
     }
-    return (char *)s;
+
+    if ( av_len(av) == -1 ) /* oops, someone forgot to pass a value */
+       av_push(av, newSViv(0));
+
+    /* And finally, store the AV in the hash */
+    hv_store((HV *)hv, "version", 7, (SV *)av, 0);
+    return s;
 }
 
 /*
@@ -3964,23 +4043,49 @@ Perl_new_version(pTHX_ SV *ver)
     if ( sv_derived_from(ver,"version") ) /* can just copy directly */
     {
        I32 key;
-       AV *av = (AV *)SvRV(ver);
-       SV* sv = newSVrv(rv, "version"); /* create an SV and upgrade the RV */
-       (void)sv_upgrade(sv, SVt_PVAV); /* needs to be an AV type */
-       AvREAL_on((AV*)sv);
-       for ( key = 0; key <= av_len(av); key++ )
+       AV * const av = newAV();
+       AV *sav;
+       /* This will get reblessed later if a derived class*/
+       SV*  const hv = newSVrv(rv, "version"); 
+       (void)sv_upgrade(hv, SVt_PVHV); /* needs to be an HV type */
+#ifndef NODEFAULT_SHAREKEYS
+       HvSHAREKEYS_on(hv);         /* key-sharing on by default */
+#endif
+
+       if ( SvROK(ver) )
+           ver = SvRV(ver);
+
+       /* Begin copying all of the elements */
+       if ( hv_exists((HV *)ver, "qv", 2) )
+           hv_store((HV *)hv, "qv", 2, &PL_sv_yes, 0);
+
+       if ( hv_exists((HV *)ver, "alpha", 5) )
+           hv_store((HV *)hv, "alpha", 5, &PL_sv_yes, 0);
+       
+       if ( hv_exists((HV*)ver, "width", 5 ) )
        {
-           const I32 rev = SvIV(*av_fetch(av, key, FALSE));
-           av_push((AV *)sv, newSViv(rev));
+           const I32 width = SvIV(*hv_fetch((HV*)ver, "width", 5, FALSE));
+           hv_store((HV *)hv, "width", 5, newSViv(width), 0);
        }
+
+       sav = (AV *)*hv_fetch((HV*)ver, "version", 7, FALSE);
+       /* This will get reblessed later if a derived class*/
+       for ( key = 0; key <= av_len(sav); key++ )
+       {
+           const I32 rev = SvIV(*av_fetch(sav, key, FALSE));
+           av_push(av, newSViv(rev));
+       }
+
+       hv_store((HV *)hv, "version", 7, (SV *)av, 0);
        return rv;
     }
 #ifdef SvVOK
     if ( SvVOK(ver) ) { /* already a v-string */
        char *version;
        MAGIC* mg = mg_find(ver,PERL_MAGIC_vstring);
-       version = savepvn( (const char*)mg->mg_ptr,mg->mg_len );
-       sv_setpv(rv,version);
+       const STRLEN len = mg->mg_len;
+       version = savepvn( (const char*)mg->mg_ptr, len);
+       sv_setpvn(rv,version,len);
        Safefree(version);
     }
     else {
@@ -4026,7 +4131,7 @@ Perl_upg_version(pTHX_ SV *ver)
 #endif
     else /* must be a string or something like a string */
     {
-       version = savesvpv(ver);
+       version = savepv(SvPV_nolen(ver));
     }
     (void)scan_version(version, ver, qv);
     Safefree(version);
@@ -4052,37 +4157,62 @@ SV *
 Perl_vnumify(pTHX_ SV *vs)
 {
     I32 i, len, digit;
-    SV *sv = newSV(0);
+    int width;
+    bool alpha = FALSE;
+    SV * const sv = newSV(0);
+    AV *av;
     if ( SvROK(vs) )
        vs = SvRV(vs);
-    len = av_len((AV *)vs);
+
+    /* see if various flags exist */
+    if ( hv_exists((HV*)vs, "alpha", 5 ) )
+       alpha = TRUE;
+    if ( hv_exists((HV*)vs, "width", 5 ) )
+       width = SvIV(*hv_fetch((HV*)vs, "width", 5, FALSE));
+    else
+       width = 3;
+
+
+    /* attempt to retrieve the version array */
+    if ( !(av = (AV *)*hv_fetch((HV*)vs, "version", 7, FALSE) ) ) {
+       sv_catpvn(sv,"0",1);
+       return sv;
+    }
+
+    len = av_len(av);
     if ( len == -1 )
     {
-       Perl_sv_catpv(aTHX_ sv,"0");
+       sv_catpvn(sv,"0",1);
        return sv;
     }
-    digit = SvIVX(*av_fetch((AV *)vs, 0, 0));
+
+    digit = SvIV(*av_fetch(av, 0, 0));
     Perl_sv_setpvf(aTHX_ sv,"%d.", (int)PERL_ABS(digit));
     for ( i = 1 ; i < len ; i++ )
     {
-       digit = SvIVX(*av_fetch((AV *)vs, i, 0));
-       Perl_sv_catpvf(aTHX_ sv,"%03d", (int)PERL_ABS(digit));
+       digit = SvIV(*av_fetch(av, i, 0));
+       if ( width < 3 ) {
+           const int denom = (int)pow(10,(3-width));
+           const div_t term = div((int)PERL_ABS(digit),denom);
+           Perl_sv_catpvf(aTHX_ sv,"%0*d_%d", width, term.quot, term.rem);
+       }
+       else {
+           Perl_sv_catpvf(aTHX_ sv,"%0*d", width, (int)digit);
+       }
     }
 
     if ( len > 0 )
     {
-       digit = SvIVX(*av_fetch((AV *)vs, len, 0));
-       if ( (int)PERL_ABS(digit) != 0 || len == 1 )
-       {
-           if ( digit < 0 ) /* alpha version */
-               Perl_sv_catpv(aTHX_ sv,"_");
-           /* Don't display additional trailing zeros */
-           Perl_sv_catpvf(aTHX_ sv,"%03d", (int)PERL_ABS(digit));
-       }
+       digit = SvIV(*av_fetch(av, len, 0));
+       if ( alpha && width == 3 ) /* alpha version */
+           Perl_sv_catpv(aTHX_ sv,"_");
+       /* Don't display additional trailing zeros */
+       if ( digit > 0 )
+           Perl_sv_catpvf(aTHX_ sv,"%0*d", width, (int)digit);
     }
-    else /* len == 0 */
+    else /* len == 1 */
     {
-        Perl_sv_catpv(aTHX_ sv,"000");
+        sv_catpvn(sv,"000",3);
     }
     return sv;
 }
@@ -4105,33 +4235,44 @@ SV *
 Perl_vnormal(pTHX_ SV *vs)
 {
     I32 i, len, digit;
+    bool alpha = FALSE;
     SV *sv = newSV(0);
+    AV *av;
     if ( SvROK(vs) )
        vs = SvRV(vs);
-    len = av_len((AV *)vs);
-    if ( len == -1 )
-    {
-       Perl_sv_catpv(aTHX_ sv,"");
+
+    if ( hv_exists((HV*)vs, "alpha", 5 ) )
+       alpha = TRUE;
+    av = (AV *)*hv_fetch((HV*)vs, "version", 7, FALSE);
+
+    len = av_len(av);
+    if ( len == -1 ) {
+       sv_catpvn(sv,"",0);
        return sv;
     }
-    digit = SvIVX(*av_fetch((AV *)vs, 0, 0));
-    Perl_sv_setpvf(aTHX_ sv,"%"IVdf,(IV)digit);
-    for ( i = 1 ; i <= len ; i++ )
-    {
-       digit = SvIVX(*av_fetch((AV *)vs, i, 0));
-       if ( digit < 0 )
-           Perl_sv_catpvf(aTHX_ sv,"_%"IVdf,(IV)-digit);
+    digit = SvIV(*av_fetch(av, 0, 0));
+    Perl_sv_setpvf(aTHX_ sv,"v%"IVdf,(IV)digit);
+    for ( i = 1 ; i <= len-1 ; i++ ) {
+       digit = SvIV(*av_fetch(av, i, 0));
+       Perl_sv_catpvf(aTHX_ sv, ".%"IVdf, (IV)digit);
+    }
+
+    if ( len > 0 ) {
+       /* handle last digit specially */
+       digit = SvIV(*av_fetch(av, len, 0));
+       if ( alpha )
+           Perl_sv_catpvf(aTHX_ sv, "_%"IVdf, (IV)digit);
        else
-           Perl_sv_catpvf(aTHX_ sv,".%"IVdf,(IV)digit);
+           Perl_sv_catpvf(aTHX_ sv, ".%"IVdf, (IV)digit);
     }
-    
+
     if ( len <= 2 ) { /* short version, must be at least three */
        for ( len = 2 - len; len != 0; len-- )
-           Perl_sv_catpv(aTHX_ sv,".0");
+           sv_catpvn(sv,".0",2);
     }
 
     return sv;
-} 
+}
 
 /*
 =for apidoc vstringify
@@ -4147,16 +4288,17 @@ the original version contained 1 or more dots, respectively
 SV *
 Perl_vstringify(pTHX_ SV *vs)
 {
-    I32 len, digit;
+    I32 qv = 0;
     if ( SvROK(vs) )
        vs = SvRV(vs);
-    len = av_len((AV *)vs);
-    digit = SvIVX(*av_fetch((AV *)vs, len, 0));
     
-    if ( len < 2 || ( len == 2 && digit < 0 ) )
-       return vnumify(vs);
-    else
+    if ( hv_exists((HV *)vs, "qv", 2) )
+       qv = 1;
+    
+    if ( qv )
        return vnormal(vs);
+    else
+       return vnumify(vs);
 }
 
 /*
@@ -4169,40 +4311,65 @@ converted into version objects.
 */
 
 int
-Perl_vcmp(pTHX_ SV *lsv, SV *rsv)
+Perl_vcmp(pTHX_ SV *lhv, SV *rhv)
 {
     I32 i,l,m,r,retval;
-    if ( SvROK(lsv) )
-       lsv = SvRV(lsv);
-    if ( SvROK(rsv) )
-       rsv = SvRV(rsv);
-    l = av_len((AV *)lsv);
-    r = av_len((AV *)rsv);
+    bool lalpha = FALSE;
+    bool ralpha = FALSE;
+    I32 left = 0;
+    I32 right = 0;
+    AV *lav, *rav;
+    if ( SvROK(lhv) )
+       lhv = SvRV(lhv);
+    if ( SvROK(rhv) )
+       rhv = SvRV(rhv);
+
+    /* get the left hand term */
+    lav = (AV *)*hv_fetch((HV*)lhv, "version", 7, FALSE);
+    if ( hv_exists((HV*)lhv, "alpha", 5 ) )
+       lalpha = TRUE;
+
+    /* and the right hand term */
+    rav = (AV *)*hv_fetch((HV*)rhv, "version", 7, FALSE);
+    if ( hv_exists((HV*)rhv, "alpha", 5 ) )
+       ralpha = TRUE;
+
+    l = av_len(lav);
+    r = av_len(rav);
     m = l < r ? l : r;
     retval = 0;
     i = 0;
     while ( i <= m && retval == 0 )
     {
-       I32 left  = SvIV(*av_fetch((AV *)lsv,i,0));
-       I32 right = SvIV(*av_fetch((AV *)rsv,i,0));
-       bool lalpha = left  < 0 ? 1 : 0;
-       bool ralpha = right < 0 ? 1 : 0;
-       left  = abs(left);
-       right = abs(right);
-       if ( left < right || (left == right && lalpha && !ralpha) )
+       left  = SvIV(*av_fetch(lav,i,0));
+       right = SvIV(*av_fetch(rav,i,0));
+       if ( left < right  )
            retval = -1;
-       if ( left > right || (left == right && ralpha && !lalpha) )
+       if ( left > right )
            retval = +1;
        i++;
     }
 
+    /* tiebreaker for alpha with identical terms */
+    if ( retval == 0 && l == r && left == right && ( lalpha || ralpha ) )
+    {
+       if ( lalpha && !ralpha )
+       {
+           retval = -1;
+       }
+       else if ( ralpha && !lalpha)
+       {
+           retval = +1;
+       }
+    }
+
     if ( l != r && retval == 0 ) /* possible match except for trailing 0's */
     {
        if ( l < r )
        {
            while ( i <= r && retval == 0 )
            {
-               if ( SvIV(*av_fetch((AV *)rsv,i,0)) != 0 )
+               if ( SvIV(*av_fetch(rav,i,0)) != 0 )
                    retval = -1; /* not a match after all */
                i++;
            }
@@ -4211,7 +4378,7 @@ Perl_vcmp(pTHX_ SV *lsv, SV *rsv)
        {
            while ( i <= l && retval == 0 )
            {
-               if ( SvIV(*av_fetch((AV *)lsv,i,0)) != 0 )
+               if ( SvIV(*av_fetch(lav,i,0)) != 0 )
                    retval = +1; /* not a match after all */
                i++;
            }
@@ -4496,7 +4663,7 @@ some level of strict-ness.
 void
 Perl_sv_nosharing(pTHX_ SV *sv)
 {
-    (void)sv;
+    PERL_UNUSED_ARG(sv);
 }
 
 /*
@@ -4512,7 +4679,7 @@ some level of strict-ness.
 void
 Perl_sv_nolocking(pTHX_ SV *sv)
 {
-    (void)sv;
+    PERL_UNUSED_ARG(sv);
 }
 
 
@@ -4529,7 +4696,7 @@ some level of strict-ness.
 void
 Perl_sv_nounlocking(pTHX_ SV *sv)
 {
-    (void)sv;
+    PERL_UNUSED_ARG(sv);
 }
 
 U32
@@ -4704,6 +4871,23 @@ Perl_get_hash_seed(pTHX)
      return myseed;
 }
 
+#ifdef USE_ITHREADS
+bool
+Perl_stashpv_hvname_match(pTHX_ const COP *c, const HV *hv)
+{
+    const char * const stashpv = CopSTASHPV(c);
+    const char * const name = HvNAME_get(hv);
+
+    if (stashpv == name)
+       return TRUE;
+    if (stashpv && name)
+       if (strEQ(stashpv, name))
+           return TRUE;
+    return FALSE;
+}
+#endif
+
+
 #ifdef PERL_GLOBAL_STRUCT
 
 struct perl_vars *