VirtualBox

Changeset 25005 in vbox for trunk/src/VBox/Runtime


Ignore:
Timestamp:
Nov 26, 2009 2:42:59 PM (15 years ago)
Author:
vboxsync
Message:

IPRT: Optimized version of RTStrVersionCompare (thanks to Knut!), made it behave like strcmp().

Location:
trunk/src/VBox/Runtime
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Runtime/common/string/strversion.cpp

    r24894 r25005  
    3737
    3838#include <iprt/assert.h>
     39#include <iprt/ctype.h>
    3940#include <iprt/err.h>
    40 #include <iprt/mem.h>
    4141
    4242
    4343/*******************************************************************************
    44 *   Defined Constants                                                          *
     44*   Defined Constants And Macros                                               *
    4545*******************************************************************************/
    46 #define ISDIGIT(c) ((c) >= '0' && (c) <= '9')
     46#define RTSTRVER_IS_PUNCTUACTION(ch)    \
     47    ( (ch) == '_' || (ch) == '-' || (ch) == '+' || RT_C_IS_PUNCT(ch) )
    4748
    4849
    49 /*******************************************************************************
    50 *   Internal Functions                                                         *
    51 *******************************************************************************/
    52 static uint16_t RTStrVersionGetBlockCount(const char *pszVer)
     50/**
     51 * Parses a out the next block from a version string.
     52 *
     53 * @returns true if numeric, false if not.
     54 * @param   ppszVer             The string cursor, IN/OUT.
     55 * @param   pu32Value           Where to return the value if numeric.
     56 * @param   pcchBlock           Where to return the block length.
     57 */
     58static bool rtStrVersionParseBlock(const char **ppszVer, uint32_t *pu32Value, size_t *pcchBlock)
    5359{
    54     uint16_t l = 0;
    55     const char *pszCur = pszVer;
    56     while (pszCur = RTStrStr(pszCur, "."))
     60    const char *psz = *ppszVer;
     61
     62    /* Check for end-of-string. */
     63    if (!*psz)
    5764    {
    58         if (pszCur == NULL)
    59             break;
    60         l++;
    61         pszCur++;
     65        *pu32Value = 0;
     66        *pcchBlock = 0;
     67        return false;
    6268    }
    63     /* Adjust block count to also count in the very first block */
    64     if (*pszVer != '\0')
    65         l++;
    66     return l;
     69
     70    bool fNumeric = RT_C_IS_DIGIT(*psz);
     71    if (fNumeric)
     72    {
     73        do
     74            psz++;
     75        while (*psz && RT_C_IS_DIGIT(*psz));
     76
     77        char *pszNext;
     78        int rc = RTStrToUInt32Ex(*ppszVer, &pszNext, 10, pu32Value);
     79        AssertRC(rc);
     80        Assert(pszNext == psz);
     81        if (RT_FAILURE(rc) || rc == VWRN_NUMBER_TOO_BIG)
     82        {
     83            fNumeric = false;
     84            *pu32Value = 0;
     85        }
     86    }
     87    else
     88    {
     89        do
     90            psz++;
     91        while (*psz && !RT_C_IS_DIGIT(*psz) && !RTSTRVER_IS_PUNCTUACTION(*psz));
     92        *pu32Value = 0;
     93    }
     94    *pcchBlock = psz - *ppszVer;
     95
     96    /* skip punctuation */
     97    if (RTSTRVER_IS_PUNCTUACTION(*psz))
     98        psz++;
     99    *ppszVer = psz;
     100
     101    return fNumeric;
    67102}
    68103
    69104
    70 static int RTStrVersionGetUInt32(const char *pszVer, uint16_t u16Block, uint32_t *pu32)
    71 {
    72     /* First make a copy of the version string so that we can modify it */
    73     char *pszString;
    74     int rc = RTStrDupEx(&pszString, pszVer);
    75     if (RT_FAILURE(rc))
    76         return rc;
    77 
    78     /* Go to the beginning of the block we want to parse */
    79     char *pszCur = pszString;
    80     for (uint16_t i = 0; i < u16Block; i++)
    81     {
    82         pszCur = RTStrStr(pszCur, ".");
    83         if (pszCur == NULL)
    84             break;
    85         if (*pszCur != '\0')
    86             pszCur++;
    87     }
    88 
    89     if (pszCur != NULL && *pszCur != '\0')
    90     {
    91         /* Skip trailing non-digits at the start of the block */
    92         while (pszCur && *pszCur != '\0')
    93         {
    94             if (ISDIGIT(*pszCur))
    95                 break;
    96             pszCur++;
    97         }
    98 
    99         /* Mark ending of the block */
    100         char *pszEnd = RTStrStr(pszCur, ".");
    101         if (NULL != pszEnd)
    102             *pszEnd = '\0';
    103 
    104         /* Convert to number */
    105         rc = RTStrToUInt32Ex(pszCur, NULL /* ppszNext */, 10 /* Base */, pu32);
    106         /* Skip trailing warnings */
    107         if (   rc == VWRN_TRAILING_CHARS
    108             || rc == VWRN_TRAILING_SPACES)
    109             rc = VINF_SUCCESS;
    110     }
    111     else
    112         rc = VERR_NOT_FOUND;
    113 
    114     RTStrFree(pszString);
    115     return rc;
    116 }
    117 
    118 
    119 /**
    120  * Compares two version strings and returns the result. The version string has
    121  * to be made of at least one number section, each section delimited by a ".",
    122  * e.g. "123.45.67". Trailing zeros at the beginning and non-digits in a section
    123  * will be skipped, so "12.foo006" becomes "12.6".
    124  *
    125  * @returns iprt status code.
    126  *          Warnings are used to indicate convertion problems.
    127  * @retval  VWRN_NUMBER_TOO_BIG
    128  * @retval  VWRN_TRAILING_CHARS
    129  * @retval  VWRN_TRAILING_SPACES
    130  * @retval  VINF_SUCCESS
    131  * @retval  VERR_NO_MEMORY
    132  * @retval  VERR_NO_DIGITS
    133  *
    134  * @param   pszVer1     First version string to compare.
    135  * @param   pszVer2     First version string to compare.
    136  * @param   pui8Res     Pointer uint8_t value where to store the comparison result:
    137  *                      0 if equal, 1 if pszVer1 is greater, 2 if pszVer2 is greater.
    138  */
    139 int RTStrVersionCompare(const char *pszVer1, const char *pszVer2, uint8_t *pui8Res)
     105RTDECL(int) RTStrVersionCompare(const char *pszVer1, const char *pszVer2)
    140106{
    141107    AssertPtr(pszVer1);
    142108    AssertPtr(pszVer2);
    143109
    144     uint16_t len1 = RTStrVersionGetBlockCount(pszVer1);
    145     uint16_t len2 = RTStrVersionGetBlockCount(pszVer2);
     110    /*
     111     * Do a parallel parse of the strings.
     112     */
     113    int iRes = 0;
     114    while (*pszVer1 || *pszVer2)
     115    {
     116        const char *pszBlock1 = pszVer1;
     117        size_t      cchBlock1;
     118        uint32_t    uVal1;
     119        bool        fNumeric1 = rtStrVersionParseBlock(&pszVer1, &uVal1, &cchBlock1);
    146120
    147     int rc = 0;
    148     if (len1 > 0 && len2 > 0)
    149     {
    150         /* Figure out which version string is longer and set the corresponding
    151          * pointers */
    152         uint16_t range;
    153         uint16_t padding;
    154         const char *pszShorter, *pszLonger;
    155         if (len1 >= len2)
     121        const char *pszBlock2 = pszVer2;
     122        size_t      cchBlock2;
     123        uint32_t    uVal2;
     124        bool        fNumeric2 = rtStrVersionParseBlock(&pszVer2, &uVal2, &cchBlock2);
     125
     126        if (fNumeric1 && fNumeric2)
    156127        {
    157             range = len1;
    158             padding = len1 - len2;
    159             pszLonger = pszVer1;
    160             pszShorter = pszVer2;
     128            if (uVal1 != uVal2)
     129            {
     130                iRes = uVal1 > uVal2 ? 1 : 2;
     131                break;
     132            }
    161133        }
    162         else if (len2 > len1)
     134        else if (   !fNumeric1 && fNumeric2 && uVal2 == 0 && cchBlock1 == 0
     135                 || !fNumeric2 && fNumeric1 && uVal1 == 0 && cchBlock2 == 0
     136                )
    163137        {
    164             range = len2;
    165             padding = len2 - len1;
    166             pszLonger = pszVer2;
    167             pszShorter = pszVer1;
     138            /* 1.0 == 1.0.0.0.0. */;
    168139        }
    169 
    170         /* Now process each section (delimited by a ".") */
    171         AssertPtr(pszShorter);
    172         AssertPtr(pszLonger);
    173         AssertPtr(pui8Res);
    174         *pui8Res = 0;
    175         uint32_t val1, val2;
    176         for (uint16_t i = 0;    i < range
    177                              && *pui8Res == 0
    178                              && RT_SUCCESS(rc)
    179                            ; i++)
     140        else
    180141        {
    181             rc = RTStrVersionGetUInt32(pszLonger, i, &val1);
    182             if (RT_SUCCESS(rc))
     142            int iDiff = RTStrNICmp(pszBlock1, pszBlock2, RT_MIN(cchBlock1, cchBlock2));
     143            if (!iDiff && cchBlock1 != cchBlock2)
     144                iDiff = cchBlock1 < cchBlock2 ? -1 : 1;
     145            if (iDiff)
    183146            {
    184                 if (i >= range - padding)
    185                 {
    186                     /* If we're in the padding range, there are no numbers left
    187                      * to compare with anymore, so just assume "0" then */
    188                     val2 = 0;
    189                 }
    190                 else
    191                 {
    192                     rc = RTStrVersionGetUInt32(pszShorter, i, &val2);
    193                 }
    194             }
    195 
    196             if (RT_SUCCESS(rc))
    197             {
    198                 if (val1 > val2)
    199                 {
    200                     *pui8Res = (pszLonger == pszVer1) ? 1 : 2;
    201                     break;
    202                 }
    203                 else if (val2 > val1)
    204                 {
    205                     *pui8Res = (pszShorter == pszVer1) ? 1 : 2;
    206                     break;
    207                 }
     147                iRes = iDiff > 0 ? 1 : 2;
     148                break;
    208149            }
    209150        }
    210151    }
    211     else
    212     {
    213         rc = VERR_NO_DIGITS;
    214     }
    215 
    216     if (RT_FAILURE(rc))
    217         *pui8Res = 0; /* Zero out value */
    218     return rc;
     152    return iRes;
    219153}
    220154RT_EXPORT_SYMBOL(RTStrVersionCompare);
  • trunk/src/VBox/Runtime/testcase/tstStrToVer.cpp

    r24893 r25005  
    3939    const char *pszVer1;
    4040    const char *pszVer2;
    41     int         rc;
    4241    uint8_t     Result;
    4342};
     
    4746    do \
    4847    { \
    49         Type Result; \
    50         int rc = Fun(Test.pszVer1, Test.pszVer2, &Result); \
     48        Type Result = Fun(Test.pszVer1, Test.pszVer2); \
    5149        if (Result != Test.Result) \
    5250        { \
    5351            RTPrintf("failure: '%s' <-> '%s' -> " Fmt ", expected " Fmt ". (%s/%u)\n", Test.pszVer1, Test.pszVer2, Result, Test.Result, #Fun, iTest); \
    54             cErrors++; \
    55         } \
    56         else if (rc != Test.rc) \
    57         { \
    58             RTPrintf("failure: '%s' <-> '%s' -> rc=%Rrc, expected %Rrc. (%s/%u)\n", Test.pszVer1, Test.pszVer2, rc, Test.rc, #Fun, iTest); \
    5952            cErrors++; \
    6053        } \
     
    7770    static const struct TstU8 aTstU8[] =
    7871    {
    79         { "", "",                         VERR_NO_DIGITS, 0 },
    80         { "asdf", "",                     VERR_NO_DIGITS, 0 },
    81         { "asdf234", "1.4.5",             VINF_SUCCESS, 1 },
    82         { "12.foo006", "12.6",            VINF_SUCCESS, 0 },
    83         { "1", "1",                       VINF_SUCCESS, 0 },
    84         { "1", "100",                     VINF_SUCCESS, 2 },
    85         { "100", "1",                     VINF_SUCCESS, 1 },
    86         { "3", "4",                       VINF_SUCCESS, 2 },
    87         { "1", "0.1",                     VINF_SUCCESS, 1 },
    88         { "1", "0.0.0.0.10000",           VINF_SUCCESS, 1 },
    89         { "0100", "100",                  VINF_SUCCESS, 0 },
    90         { "1.0.0", "1",                   VINF_SUCCESS, 0 },
    91         { "1.0.0", "100.0.0",             VINF_SUCCESS, 2 },
    92         { "1", "1.0.3.0",                 VINF_SUCCESS, 2 },
    93         { "1.4.5", "1.2.3",               VINF_SUCCESS, 1 },
    94         { "1.2.3", "1.4.5",               VINF_SUCCESS, 2 },
    95         { "1.2.3", "4.5.6",               VINF_SUCCESS, 2 },
    96         { "1.0.4", "1.0.3",               VINF_SUCCESS, 1 },
    97         { "0.1", "0.0.1",                 VINF_SUCCESS, 1 },
    98         { "0.0.1", "0.1.1",               VINF_SUCCESS, 2 },
    99         { "3.1.0", "3.0.14",              VINF_SUCCESS, 1 },
    100         { "2.0.12", "3.0.14",             VINF_SUCCESS, 2 },
    101         { "3.1", "3.0.22",                VINF_SUCCESS, 1 },
    102         { "3.0.14", "3.1.0",              VINF_SUCCESS, 2 },
    103         { "45.63", "04.560.30",           VINF_SUCCESS, 1 },
    104         { "45.006", "45.6",               VINF_SUCCESS, 0 },
    105         { "23.206", "23.06",              VINF_SUCCESS, 1 },
    106         { "23.2", "23.060",               VINF_SUCCESS, 2 },
     72        { "", "",                         0 },
     73        { "asdf", "",                     1 }, /* "asdf" is bigger than "" */
     74        { "asdf234", "1.4.5",             1 },
     75        { "12.foo006", "12.6",            1 }, /* "12.foo006" is bigger than "12.6" */
     76        { "1", "1",                       0 },
     77        { "1", "100",                     2 },
     78        { "100", "1",                     1 },
     79        { "3", "4",                       2 },
     80        { "1", "0.1",                     1 },
     81        { "1", "0.0.0.0.10000",           1 },
     82        { "0100", "100",                  0 },
     83        { "1.0.0", "1",                   0 },
     84        { "1.0.0", "100.0.0",             2 },
     85        { "1", "1.0.3.0",                 2 },
     86        { "1.4.5", "1.2.3",               1 },
     87        { "1.2.3", "1.4.5",               2 },
     88        { "1.2.3", "4.5.6",               2 },
     89        { "1.0.4", "1.0.3",               1 },
     90        { "0.1", "0.0.1",                 1 },
     91        { "0.0.1", "0.1.1",               2 },
     92        { "3.1.0", "3.0.14",              1 },
     93        { "2.0.12", "3.0.14",             2 },
     94        { "3.1", "3.0.22",                1 },
     95        { "3.0.14", "3.1.0",              2 },
     96        { "45.63", "04.560.30",           1 },
     97        { "45.006", "45.6",               0 },
     98        { "23.206", "23.06",              1 },
     99        { "23.2", "23.060",               2 },
    107100
    108         { "VirtualBox-2.0.8-Beta2", "VirtualBox-2.0.8_Beta3-r12345", VINF_SUCCESS, 0 },
    109         { "VirtualBox-2.2.4-Beta2", "VirtualBox-2.2.2", VINF_SUCCESS, 1 },
    110         { "VirtualBox-3.1.0", "VirtualBox-3.1.2_Beta1", VINF_SUCCESS, 2 },
    111         { "3.1.0_BETA-r12345", "3.1.2", VINF_SUCCESS, 2 },
     101        { "VirtualBox-2.0.8-Beta2", "VirtualBox-2.0.8_Beta3-r12345", 2 },
     102        { "VirtualBox-2.2.4-Beta2", "VirtualBox-2.2.2", 1 },
     103        { "VirtualBox-2.2.4-Beta3", "VirtualBox-2.2.2-Beta4", 1 },
     104        { "VirtualBox-3.1.8-Alpha1", "VirtualBox-3.1.8-Alpha1-r61454", 2 },
     105        { "VirtualBox-3.1.0", "VirtualBox-3.1.2_Beta1", 2 },
     106        { "3.1.0_BETA-r12345", "3.1.2", 2 },
    112107    };
    113     RUN_TESTS(aTstU8, uint8_t, "%#d", RTStrVersionCompare);
     108    RUN_TESTS(aTstU8, int, "%#d", RTStrVersionCompare);
    114109
    115110    /*
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette