VirtualBox

Changeset 24893 in vbox for trunk/src


Ignore:
Timestamp:
Nov 24, 2009 12:09:03 PM (15 years ago)
Author:
vboxsync
Message:

IPRT: Improved RTStrVersionCompare() + testcase.

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

Legend:

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

    r24662 r24893  
    3737
    3838#include <iprt/assert.h>
    39 #include <iprt/ctype.h> /* needed for RT_C_IS_DIGIT */
    4039#include <iprt/err.h>
    4140#include <iprt/mem.h>
    4241
    4342
     43/*******************************************************************************
     44*   Defined Constants                                                          *
     45*******************************************************************************/
     46#define ISDIGIT(c) ((c) >= '0' && (c) <= '9')
     47
     48
     49/*******************************************************************************
     50*   Internal Functions                                                         *
     51*******************************************************************************/
     52static uint16_t RTStrVersionGetBlockCount(const char *pszVer)
     53{
     54    uint16_t l = 0;
     55    const char *pszCur = pszVer;
     56    while (pszCur = RTStrStr(pszCur, "."))
     57    {
     58        if (pszCur == NULL)
     59            break;
     60        l++;
     61        pszCur++;
     62    }
     63    /* Adjust block count to also count in the very first block */
     64    if (*pszVer != '\0')
     65        l++;
     66    return l;
     67}
     68
     69
     70static 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
    44118
    45119/**
    46  * Converts a string representation of a version number to an unsigned number.
     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".
    47124 *
    48125 * @returns iprt status code.
     
    55132 * @retval  VERR_NO_DIGITS
    56133 *
    57  * @param   pszValue    Pointer to the string value.
    58  * @param   pu32        Where to store the converted number.
    59  *
    60  * @todo    r=bird: The returned value isn't really suitable for comparing two
    61  *          version strings.  Try see which result you get when converting
    62  *          "3.0.14" and "3.1.0" and comparing the values.  The way to fix this
    63  *          deficiency would be to convert the individual parts and dividing the
    64  *          return value into sections: bits 31:24 FirstNumber; 23:16 Second;
    65  *          15:8 Third; 7:0 Forth.  It would probably be a good idea to use a
    66  *          64-bit return value instead of a 32-bit one, so there is room for
    67  *          revision number when found.
    68  *
    69  *          Actually, because of the above, the kind of API I had in mind was
    70  *          int RTStrVersionCompare(const char *pszVer1, const char *pszVer2).
    71  *          It wouldn't try convert input to numbers, just do a parallel parse.
    72  *          This would allow easy handling beta/alpha/++ indicators and any
    73  *          number of dots and dashes.
     134 * @todo    Deal with prefixes and suffixes!
     135 * @param   pszVer1     First version string to compare.
     136 * @param   pszVer2     First version string to compare.*
     137 * @param   pui8Res     Pointer uint8_t value where to store the comparison result:
     138 *                      0 if equal, 1 if pszVer1 is greater, 2 if pszVer2 is greater.
    74139 */
    75 RTDECL(int) RTStrVersionToUInt32(const char *pszVer, uint32_t *pu32)
     140int RTStrVersionCompare(const char *pszVer1, const char *pszVer2, uint8_t *pui8Res)
    76141{
    77     const char *psz = pszVer;
    78     AssertPtr(pu32);
    79     AssertPtr(psz);
    80 
    81     char *pszNew = (char*)RTMemAllocZ((strlen(pszVer) + 1) * sizeof(char));
    82     if (pszNew == NULL)
    83         return VERR_NO_MEMORY;
    84 
    85     unsigned    i            = 0;
    86     bool        fLastInvalid = false;
    87     while (    psz
    88            && *psz != '\0')
    89     {
    90         if (fLastInvalid)
    91         {
    92             if (   *psz == '-'
    93                 || *psz == '_')
    94                 fLastInvalid = false;
    95         }
    96         else
    97         {
    98             if (RT_C_IS_DIGIT(*psz))
    99                 pszNew[i++] = *psz;
    100             else if (   *psz != '.'
    101                      && i == 0)
    102                 fLastInvalid = true;
    103         }
    104         psz++;
    105     }
    106     pszNew[i] = '\0';
    107 
    108     /* Convert final number string to number */
    109     int rc;
    110     if (fLastInvalid)
    111     {
    112         *pu32 = 0;
     142    AssertPtr(pszVer1);
     143    AssertPtr(pszVer2);
     144
     145    uint16_t len1 = RTStrVersionGetBlockCount(pszVer1);
     146    uint16_t len2 = RTStrVersionGetBlockCount(pszVer2);
     147
     148    int rc = 0;
     149    if (len1 > 0 && len2 > 0)
     150    {
     151        /* Figure out which version string is longer and set the corresponding
     152         * pointers */
     153        uint16_t range;
     154        uint16_t padding;
     155        const char *pszShorter, *pszLonger;
     156        if (len1 >= len2)
     157        {
     158            range = len1;
     159            padding = len1 - len2;
     160            pszLonger = pszVer1;
     161            pszShorter = pszVer2;
     162        }
     163        else if (len2 > len1)
     164        {
     165            range = len2;
     166            padding = len2 - len1;
     167            pszLonger = pszVer2;
     168            pszShorter = pszVer1;
     169        }
     170
     171        /* Now process each section (delimited by a ".") */
     172        AssertPtr(pszShorter);
     173        AssertPtr(pszLonger);
     174        AssertPtr(pui8Res);
     175        *pui8Res = 0;
     176        uint32_t val1, val2;
     177        for (uint16_t i = 0;    i < range
     178                             && *pui8Res == 0
     179                             && RT_SUCCESS(rc)
     180                           ; i++)
     181        {
     182            rc = RTStrVersionGetUInt32(pszLonger, i, &val1);
     183            if (RT_SUCCESS(rc))
     184            {
     185                if (i >= range - padding)
     186                {
     187                    /* If we're in the padding range, there are no numbers left
     188                     * to compare with anymore, so just assume "0" then */
     189                    val2 = 0;
     190                }
     191                else
     192                {
     193                    rc = RTStrVersionGetUInt32(pszShorter, i, &val2);
     194                }
     195            }
     196
     197            if (RT_SUCCESS(rc))
     198            {
     199                if (val1 > val2)
     200                {
     201                    *pui8Res = (pszLonger == pszVer1) ? 1 : 2;
     202                    break;
     203                }
     204                else if (val2 > val1)
     205                {
     206                    *pui8Res = (pszShorter == pszVer1) ? 1 : 2;
     207                    break;
     208                }
     209            }
     210        }
     211    }
     212    else
     213    {
    113214        rc = VERR_NO_DIGITS;
    114215    }
    115     else
    116     {
    117         rc = RTStrToUInt32Ex(pszNew, NULL /*pszNext*/, 10 /*uBase*/, pu32);
    118         if (rc != VINF_SUCCESS)
    119             *pu32 = 0;
    120     }
    121     RTStrFree(pszNew);
     216
     217    if (RT_FAILURE(rc))
     218        *pui8Res = 0; /* Zero out value */
    122219    return rc;
    123220}
    124 
     221RT_EXPORT_SYMBOL(RTStrVersionCompare);
  • trunk/src/VBox/Runtime/testcase/tstStrToVer.cpp

    r24658 r24893  
    11/* $Id$ */
    22/** @file
    3  * IPRT Testcase - String To Version Number Conversion.
     3 * IPRT Testcase - Version String Comparison.
    44 */
    55
     
    3030
    3131
    32 /*******************************************************************************
    33 *   Header Files                                                               *
    34 *******************************************************************************/
     32#include <iprt/initterm.h>
    3533#include <iprt/string.h>
    3634#include <iprt/stream.h>
    37 #include <iprt/err.h>
    3835
    3936
    40 struct TstU32
     37struct TstU8
    4138{
    42     const char *psz;
     39    const char *pszVer1;
     40    const char *pszVer2;
    4341    int         rc;
    44     uint32_t    Result;
     42    uint8_t     Result;
    4543};
    4644
     
    5048    { \
    5149        Type Result; \
    52         int rc = Fun(Test.psz, &Result); \
     50        int rc = Fun(Test.pszVer1, Test.pszVer2, &Result); \
    5351        if (Result != Test.Result) \
    5452        { \
    55             RTPrintf("failure: '%s' -> " Fmt " expected " Fmt ". (%s/%u)\n", Test.psz, Result, Test.Result, #Fun, iTest); \
     53            RTPrintf("failure: '%s' <-> '%s' -> " Fmt ", expected " Fmt ". (%s/%u)\n", Test.pszVer1, Test.pszVer2, Result, Test.Result, #Fun, iTest); \
    5654            cErrors++; \
    5755        } \
    5856        else if (rc != Test.rc) \
    5957        { \
    60             RTPrintf("failure: '%s' -> rc=%Rrc expected %Rrc. (%s/%u)\n", Test.psz, rc, Test.rc, #Fun, iTest); \
     58            RTPrintf("failure: '%s' <-> '%s' -> rc=%Rrc, expected %Rrc. (%s/%u)\n", Test.pszVer1, Test.pszVer2, rc, Test.rc, #Fun, iTest); \
    6159            cErrors++; \
    6260        } \
    6361    } while (0)
    64 
    6562
    6663#define RUN_TESTS(aTests, Type, Fmt, Fun) \
     
    7370    } while (0)
    7471
    75 
    7672int main()
    7773{
    78     /** @todo r=bird: IPRT init is missing. Use the RTTest framework (see any
    79      *        tstRT*.cpp file for examples of this). */
     74    RTR3Init();
     75
    8076    int cErrors = 0;
     77    static const struct TstU8 aTstU8[] =
     78    {
     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 },
    81107
    82     static const struct TstU32 aTstU32[] =
    83     {
    84         { "asdf",                               VERR_NO_DIGITS, 0 },
    85         { "asdf234",                            VERR_NO_DIGITS, 0 },
    86         { "123",                                VINF_SUCCESS, 123 },
    87         { "45.63",                              VINF_SUCCESS, 4563 },
    88         { "68.54.123",                          VINF_SUCCESS, 6854123 },
    89         { "1.0.3-asdf",                         VINF_SUCCESS, 103 },
    90         { "aasdf-1",                            VINF_SUCCESS, 1 },
    91         { "qwer-123.34",                        VINF_SUCCESS, 12334 },
    92         { "aasdf45-r4545-5",                    VINF_SUCCESS, 5 },
    93         { "foo41-r2431-6.9.8",                  VINF_SUCCESS, 698 },
    94         { "bar43-r3517-7.1.2-beta",             VINF_SUCCESS, 712 },
    95         { "bar43-r3517-7.1.2.53412344556-beta", VWRN_NUMBER_TOO_BIG, 0 },
     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 },
    96112    };
    97     RUN_TESTS(aTstU32, uint32_t, "%#ld", RTStrVersionToUInt32);
     113    RUN_TESTS(aTstU8, uint8_t, "%#d", RTStrVersionCompare);
    98114
    99115    /*
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