VirtualBox

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


Ignore:
Timestamp:
Nov 26, 2009 2:42:59 PM (15 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
55314
Message:

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

File:
1 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);
Note: See TracChangeset for help on using the changeset viewer.

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