Index: /trunk/include/iprt/mangling.h
===================================================================
--- /trunk/include/iprt/mangling.h	(revision 79561)
+++ /trunk/include/iprt/mangling.h	(revision 79562)
@@ -2064,4 +2064,5 @@
 # define RTStrCmp                                       RT_MANGLER(RTStrCmp)
 # define RTStrConvertHexBytes                           RT_MANGLER(RTStrConvertHexBytes)
+# define RTStrConvertHexBytesEx                         RT_MANGLER(RTStrConvertHexBytesEx)
 # define RTStrCopy                                      RT_MANGLER(RTStrCopy)
 # define RTStrCopyEx                                    RT_MANGLER(RTStrCopyEx)
Index: /trunk/include/iprt/string.h
===================================================================
--- /trunk/include/iprt/string.h	(revision 79561)
+++ /trunk/include/iprt/string.h	(revision 79562)
@@ -3143,7 +3143,40 @@
  * @param   pv          Output buffer.
  * @param   cb          The size of the output buffer.
- * @param   fFlags      Must be zero, reserved for future use.
+ * @param   fFlags      RTSTRCONVERTHEXBYTES_F_XXX.
  */
 RTDECL(int) RTStrConvertHexBytes(char const *pszHex, void *pv, size_t cb, uint32_t fFlags);
+
+/** @name RTSTRCONVERTHEXBYTES_F_XXX - Flags for RTStrConvertHexBytes() and RTStrConvertHexBytesEx().
+ * @{  */
+/** Accept colon as a byte separator. */
+#define RTSTRCONVERTHEXBYTES_F_SEP_COLON  RT_BIT(0)
+/** @} */
+
+/**
+ * Converts a string of hex bytes back into binary data, extended version.
+ *
+ * @returns IPRT status code.
+ * @retval  VERR_INVALID_POINTER if any of the pointers are wrong.
+ * @retval  VERR_BUFFER_OVERFLOW if the string contains too many hex bytes.
+ * @retval  VERR_BUFFER_UNDERFLOW if there aren't enough hex bytes to fill up
+ *          the output buffer and *pcbReturned is NULL.
+ * @retval  VINF_BUFFER_UNDERFLOW if there aren't enough hex bytes to fill up
+ *          the output buffer and *pcbReturned is not NULL, *pcbReturned holds
+ *          the actual number of bytes.
+ * @retval  VERR_UNEVEN_INPUT if the input contains a half byte.
+ * @retval  VERR_NO_DIGITS
+ * @retval  VWRN_TRAILING_CHARS
+ * @retval  VWRN_TRAILING_SPACES
+ *
+ * @param   pszHex      The string containing the hex bytes.
+ * @param   pv          Output buffer.
+ * @param   cb          The size of the output buffer.
+ * @param   fFlags      RTSTRCONVERTHEXBYTES_F_XXX.
+ * @param   ppszNext    Set to point at where we stopped decoding hex bytes.
+ *                      Optional.
+ * @param   pcbReturned Where to return the number of bytes found.  Optional.
+ */
+RTDECL(int) RTStrConvertHexBytesEx(char const *pszHex, void *pv, size_t cb, uint32_t fFlags,
+                                   const char **ppszNext, size_t *pcbReturned);
 
 /** @} */
Index: /trunk/src/VBox/Runtime/common/string/strtonum.cpp
===================================================================
--- /trunk/src/VBox/Runtime/common/string/strtonum.cpp	(revision 79561)
+++ /trunk/src/VBox/Runtime/common/string/strtonum.cpp	(revision 79562)
@@ -40,9 +40,11 @@
 *   Global Variables                                                                                                             *
 *********************************************************************************************************************************/
-/** 8-bit char -> digit. */
+/** 8-bit char -> digit.
+ * Non-digits have values 255 (most), 254 (zero), 253 (colon) and 252 (space).
+ */
 static const unsigned char g_auchDigits[256] =
 {
-    255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
-    255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9,255,255,255,255,255,255,
+    254,255,255,255,255,255,255,255,255,252,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+    252,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9,253,255,255,255,255,255,
     255, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,255,255,255,255,255,
     255, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,255,255,255,255,255,
@@ -52,4 +54,5 @@
     255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255
 };
+
 /** Approximated overflow shift checks. */
 static const char g_auchShift[36] =
@@ -75,4 +78,10 @@
         else if (i >= 'A' && i <= 'Z')
             ch = i - 'A' + 10;
+        else if (i == 0)
+            ch = 254;
+        else if (i == ':')
+            ch = 253;
+        else if (i == ' ' || i == '\t')
+            ch = 252;
         if (i == 0)
             printf("\n    %3d", ch);
@@ -973,43 +982,217 @@
 
 
+RTDECL(int) RTStrConvertHexBytesEx(char const *pszHex, void *pv, size_t cb, uint32_t fFlags,
+                                   const char **ppszNext, size_t *pcbReturned)
+{
+    size_t               cbDst  = cb;
+    uint8_t             *pbDst  = (uint8_t *)pv;
+    const unsigned char *pszSrc = (const unsigned char *)pszHex;
+    unsigned char        uchDigit;
+
+    if (pcbReturned)
+        *pcbReturned = 0;
+    if (ppszNext)
+        *ppszNext = NULL;
+    AssertPtrReturn(pszHex, VERR_INVALID_POINTER);
+    AssertReturn(!(fFlags & ~RTSTRCONVERTHEXBYTES_F_SEP_COLON), VERR_INVALID_FLAGS);
+
+    if (fFlags & RTSTRCONVERTHEXBYTES_F_SEP_COLON)
+    {
+        /*
+         * Optional colon separators.
+         */
+        bool fPrevColon = true; /* leading colon is taken to mean leading zero byte */
+        for (;;)
+        {
+            /* Pick the next two digit from the string. */
+            uchDigit = g_auchDigits[*pszSrc++];
+            if (uchDigit >= 16)
+            {
+                if (uchDigit == 253 /* colon */)
+                {
+                    Assert(pszSrc[-1] == ':');
+                    if (!fPrevColon)
+                        fPrevColon = true;
+                    /* Add zero byte if there is room. */
+                    else if (cbDst > 0)
+                    {
+                        cbDst--;
+                        *pbDst++ = 0;
+                    }
+                    else
+                    {
+                        if (pcbReturned)
+                            *pcbReturned = pbDst - (uint8_t *)pv;
+                        if (ppszNext)
+                            *ppszNext = (const char *)pszSrc - 1;
+                        return VERR_BUFFER_OVERFLOW;
+                    }
+                    continue;
+                }
+                else
+                    break;
+            }
+            else
+            {
+                /* Got one digit, check what comes next: */
+                unsigned char const uchDigit2 = g_auchDigits[*pszSrc++];
+                if (uchDigit2 < 16)
+                {
+                    if (cbDst > 0)
+                    {
+                        *pbDst++ = (uchDigit << 4) | uchDigit2;
+                        cbDst--;
+                        fPrevColon = false;
+                    }
+                    else
+                    {
+                        if (pcbReturned)
+                            *pcbReturned = pbDst - (uint8_t *)pv;
+                        if (ppszNext)
+                            *ppszNext = (const char *)pszSrc - 1;
+                        return VERR_BUFFER_OVERFLOW;
+                    }
+                }
+                /* Lone digits are only allowed if following a colon or at the very start, because
+                   if there is more than one byte it ambigious whether it is the lead or tail byte
+                   that only has one digit in it.
+                   Note! This also ensures better compatibility with the no-separator variant
+                         (except for single digit strings, which are accepted here but not below). */
+                else if (fPrevColon)
+                {
+                    if (cbDst > 0)
+                    {
+                        *pbDst++ = uchDigit;
+                        cbDst--;
+                    }
+                    else
+                    {
+                        if (pcbReturned)
+                            *pcbReturned = pbDst - (uint8_t *)pv;
+                        if (ppszNext)
+                            *ppszNext = (const char *)pszSrc - 1;
+                        return VERR_BUFFER_OVERFLOW;
+                    }
+                    if (uchDigit2 == 253 /* colon */)
+                    {
+                        Assert(pszSrc[-1] == ':');
+                        fPrevColon = true;
+                    }
+                    else
+                    {
+                        fPrevColon = false;
+                        uchDigit = uchDigit2;
+                        break;
+                    }
+                }
+                else
+                {
+                    if (pcbReturned)
+                        *pcbReturned = pbDst - (uint8_t *)pv;
+                    if (ppszNext)
+                        *ppszNext = (const char *)pszSrc - 2;
+                    return VERR_UNEVEN_INPUT;
+                }
+            }
+        }
+
+        /* Trailing colon means trailing zero byte: */
+        if (fPrevColon)
+        {
+            if (cbDst > 0)
+            {
+                *pbDst++ = 0;
+                cbDst--;
+            }
+            else
+            {
+                if (pcbReturned)
+                    *pcbReturned = pbDst - (uint8_t *)pv;
+                if (ppszNext)
+                    *ppszNext = (const char *)pszSrc - 1;
+                return VERR_BUFFER_OVERFLOW;
+            }
+        }
+    }
+    else
+    {
+        /*
+         * No separators.
+         */
+        for (;;)
+        {
+            /* Pick the next two digit from the string. */
+            uchDigit = g_auchDigits[*pszSrc++];
+            if (uchDigit < 16)
+            {
+                unsigned char const uchDigit2 = g_auchDigits[*pszSrc++];
+                if (uchDigit2 < 16)
+                {
+                    /* Add the byte to the output buffer. */
+                    if (cbDst)
+                    {
+                        cbDst--;
+                        *pbDst++ = (uchDigit << 4) | uchDigit2;
+                    }
+                    else
+                    {
+                        if (pcbReturned)
+                            *pcbReturned = pbDst - (uint8_t *)pv;
+                        if (ppszNext)
+                            *ppszNext = (const char *)pszSrc - 2;
+                        return VERR_BUFFER_OVERFLOW;
+                    }
+                }
+                else
+                {
+                    if (pcbReturned)
+                        *pcbReturned = pbDst - (uint8_t *)pv;
+                    if (ppszNext)
+                        *ppszNext = (const char *)pszSrc - 2;
+                    return VERR_UNEVEN_INPUT;
+                }
+            }
+            else
+                break;
+        }
+    }
+
+    /*
+     * End of hex bytes, look what comes next and figure out what to return.
+     */
+    if (pcbReturned)
+        *pcbReturned = pbDst - (uint8_t *)pv;
+    if (ppszNext)
+        *ppszNext = (const char *)pszSrc - 1;
+
+    if (uchDigit == 254)
+    {
+        Assert(pszSrc[-1] == '\0');
+        if (cbDst == 0)
+            return VINF_SUCCESS;
+        return pcbReturned ? VINF_BUFFER_UNDERFLOW : VERR_BUFFER_UNDERFLOW;
+    }
+    Assert(pszSrc[-1] != '\0');
+
+    if (cbDst != 0 && !pcbReturned)
+        return VERR_BUFFER_UNDERFLOW;
+
+    while (uchDigit == 252)
+    {
+        Assert(pszSrc[-1] == ' ' || pszSrc[-1] == '\t');
+        uchDigit = g_auchDigits[*pszSrc++];
+    }
+
+    Assert(pszSrc[-1] == '\0' ? uchDigit == 254 : uchDigit != 254);
+    return uchDigit == 254 ? VWRN_TRAILING_CHARS : VWRN_TRAILING_SPACES;
+
+}
+RT_EXPORT_SYMBOL(RTStrConvertHexBytesEx);
+
+
 RTDECL(int) RTStrConvertHexBytes(char const *pszHex, void *pv, size_t cb, uint32_t fFlags)
 {
-    size_t      cbDst;
-    uint8_t    *pbDst;
-    const char *pszSrc;
-
-    AssertPtrReturn(pszHex, VERR_INVALID_POINTER);
-    AssertReturn(!fFlags, VERR_INVALID_PARAMETER);
-
-    cbDst  = cb;
-    pbDst  = (uint8_t *)pv;
-    pszSrc = pszHex;
-    for (;;)
-    {
-        /* Pick the next two digit from the string. */
-        char ch = *pszSrc++;
-        unsigned char uchDigit1 = g_auchDigits[(unsigned char)ch];
-        unsigned char uchDigit2;
-        if (uchDigit1 >= 16)
-        {
-            if (!ch)
-                return cbDst == 0 ? VINF_SUCCESS : VERR_BUFFER_UNDERFLOW;
-
-            while (ch == ' ' || ch == '\t')
-                ch = *pszSrc++;
-            return ch ? VWRN_TRAILING_CHARS : VWRN_TRAILING_SPACES;
-        }
-
-        ch = *pszSrc++;
-        uchDigit2 = g_auchDigits[(unsigned char)ch];
-        if (uchDigit2 >= 16)
-            return VERR_UNEVEN_INPUT;
-
-        /* Add the byte to the output buffer. */
-        if (!cbDst)
-            return VERR_BUFFER_OVERFLOW;
-        cbDst--;
-        *pbDst++ = (uchDigit1 << 4) | uchDigit2;
-    }
+    return RTStrConvertHexBytesEx(pszHex, pv, cb, fFlags, NULL /*ppszNext*/, NULL /*pcbReturned*/);
+
 }
 RT_EXPORT_SYMBOL(RTStrConvertHexBytes);
Index: /trunk/src/VBox/Runtime/testcase/tstStrToNum.cpp
===================================================================
--- /trunk/src/VBox/Runtime/testcase/tstStrToNum.cpp	(revision 79561)
+++ /trunk/src/VBox/Runtime/testcase/tstStrToNum.cpp	(revision 79562)
@@ -25,5 +25,9 @@
  */
 
-#include <iprt/initterm.h>
+
+/*********************************************************************************************************************************
+*   Header Files                                                                                                                 *
+*********************************************************************************************************************************/
+#include <iprt/test.h>
 #include <iprt/string.h>
 #include <iprt/stream.h>
@@ -69,13 +73,7 @@
         int rc = Fun(Test.psz, NULL, Test.uBase, &Result); \
         if (Result != Test.Result) \
-        { \
-            RTPrintf("failure: '%s' -> " Fmt " expected " Fmt ". (%s/%u)\n", Test.psz, Result, Test.Result, #Fun, iTest); \
-            cErrors++; \
-        } \
+            RTTestIFailed("'%s' -> " Fmt " expected " Fmt ". (%s/%u)\n", Test.psz, Result, Test.Result, #Fun, iTest); \
         else if (rc != Test.rc) \
-        { \
-            RTPrintf("failure: '%s' -> rc=%Rrc expected %Rrc. (%s/%u)\n", Test.psz, rc, Test.rc, #Fun, iTest); \
-            cErrors++; \
-        } \
+            RTTestIFailed("'%s' -> rc=%Rrc expected %Rrc. (%s/%u)\n", Test.psz, rc, Test.rc, #Fun, iTest); \
     } while (0)
 
@@ -92,7 +90,9 @@
 int main()
 {
-    RTR3InitExeNoArguments(0);
-
-    int cErrors = 0;
+    RTTEST     hTest;
+    RTEXITCODE rcExit = RTTestInitAndCreate("tstRTStrToNum", &hTest);
+    if (rcExit != RTEXITCODE_SUCCESS)
+        return rcExit;
+
     static const struct TstU64 aTstU64[] =
     {
@@ -159,5 +159,4 @@
 
 
-
     static const struct TstI32 aTstI32[] =
     {
@@ -223,11 +222,57 @@
     RUN_TESTS(aTstU32, uint32_t, "%#x", RTStrToUInt32Ex);
 
+
+    /*
+     * Test the some hex stuff too.
+     */
+    static const struct
+    {
+        const char *pszHex;
+        size_t      cbOut;
+        size_t      offNext;
+        uint8_t     bLast;
+        bool        fColon;
+        int         rc;
+    } s_aConvertHexTests[] =
+    {
+        { "00",          1,  2, 0x00,  true, VINF_SUCCESS },
+        { "00",          1,  2, 0x00, false, VINF_SUCCESS },
+        { "000102",      3,  6, 0x02,  true, VINF_SUCCESS },
+        { "00019",       2,  4, 0x01, false, VERR_UNEVEN_INPUT },
+        { "00019",       2,  4, 0x01,  true, VERR_UNEVEN_INPUT },
+        { "0001:9",      3,  6, 0x09,  true, VINF_SUCCESS},
+        { "000102",      3,  6, 0x02, false, VINF_SUCCESS },
+        { "0:1",         2,  3, 0x01,  true, VINF_SUCCESS },
+        { ":",           2,  1, 0x00,  true, VINF_SUCCESS },
+        { "0:01",        2,  4, 0x01,  true, VINF_SUCCESS },
+        { "00:01",       2,  5, 0x01,  true, VINF_SUCCESS },
+        { ":1:2:3:4:5",  6, 10, 0x05,  true, VINF_SUCCESS },
+        { ":1:2:3::5",   6,  9, 0x05,  true, VINF_SUCCESS },
+        { ":1:2:3:4:",   6,  9, 0x00,  true, VINF_SUCCESS },
+    };
+    for (unsigned i = 0; i < RT_ELEMENTS(s_aConvertHexTests); i++)
+    {
+        uint8_t abBuf[1024];
+        memset(abBuf, 0xf6, sizeof(abBuf));
+        const char *pszExpectNext = &s_aConvertHexTests[i].pszHex[s_aConvertHexTests[i].offNext];
+        const char *pszNext       = "";
+        size_t      cbReturned    = 77777;
+        int rc = RTStrConvertHexBytesEx(s_aConvertHexTests[i].pszHex, abBuf, s_aConvertHexTests[i].cbOut,
+                                        s_aConvertHexTests[i].fColon ? RTSTRCONVERTHEXBYTES_F_SEP_COLON : 0,
+                                        &pszNext, &cbReturned);
+        if (   rc      != s_aConvertHexTests[i].rc
+            || pszNext != pszExpectNext
+            || abBuf[s_aConvertHexTests[i].cbOut - 1] != s_aConvertHexTests[i].bLast
+            )
+            RTTestFailed(hTest, "RTStrConvertHexBytesEx/#%u %s -> %Rrc %p %#zx %#02x, expected %Rrc %p %#zx %#02x\n",
+                         i, s_aConvertHexTests[i].pszHex,
+                         rc, pszNext, cbReturned, abBuf[s_aConvertHexTests[i].cbOut - 1],
+                         s_aConvertHexTests[i].rc, pszExpectNext, s_aConvertHexTests[i].cbOut, s_aConvertHexTests[i].bLast);
+    }
+
+
     /*
      * Summary.
      */
-    if (!cErrors)
-        RTPrintf("tstStrToNum: SUCCESS\n");
-    else
-        RTPrintf("tstStrToNum: FAILURE - %d errors\n", cErrors);
-    return !!cErrors;
+    return RTTestSummaryAndDestroy(hTest);
 }
