]> git.seodisparate.com - UDPConnection/commitdiff
Impl "unsafe" versions of UDPC_atostr(...)
authorStephen Seo <seo.disparate@gmail.com>
Thu, 22 Jun 2023 04:25:26 +0000 (13:25 +0900)
committerStephen Seo <seo.disparate@gmail.com>
Thu, 22 Jun 2023 04:25:26 +0000 (13:25 +0900)
These "unsafe" versions are guaranteed to not have the returned address
strings be overwritten by UDPC, but they must be manually free'd later
(as mentioned in the documentation).

src/UDPC.h
src/UDPConnection.cpp
src/test/TestUDPC.cpp

index b6dbee2d5376207a56ac39cdfd96432882ffcdc0..b54b6be6bbac44a0e93c2853f13e740aca74a62d 100644 (file)
@@ -846,9 +846,71 @@ UDPC_EXPORT const char *UDPC_atostr_cid(UDPC_HContext ctx, UDPC_ConnectionId con
  * be considered undefined behavior.
  *
  * It may be easier to use UDPC_atostr_cid().
+ *
+ * UDPC internally uses UDPC_atostr() for logging. This means that while UDPC
+ * is running, a string created with UDPC_atostr() may be overwritten
+ * eventually by UDPC. To avoid this situation, UDPC_atostr_unsafe() or
+ * UDPC_atostr_unsafe_cid() may be used as the ptr returned by it will not be
+ * overwritten by UDPC as time passes.
  */
 UDPC_EXPORT const char *UDPC_atostr(UDPC_HContext ctx, UDPC_IPV6_ADDR_TYPE addr);
 
+/*!
+ * \brief Similar to UPDC_atostr(), but the returned ptr must be free'd.
+ *
+ * \warning The returned pointer must be freed with free(), or
+ * UDPC_atostr_unsafe_free(), or UDPC_atostr_unsafe_free_ptr().
+ *
+ * This function is thread-safe.
+ *
+ * UDPC internally uses UDPC_atostr() for logging. This means that while UDPC
+ * is running, a string created with UDPC_atostr() may be overwritten
+ * eventually by UDPC. To avoid this situation, UDPC_atostr_unsafe() or
+ * UDPC_atostr_unsafe_cid() may be used as the ptr returned by it will not be
+ * overwritten by UDPC as time passes.
+ *
+ * It may be easier to use UDPC_atostr_unsafe_cid().
+ */
+UDPC_EXPORT const char *UDPC_atostr_unsafe(UDPC_IPV6_ADDR_TYPE addr);
+
+/*!
+ * \brief Similar to UPDC_atostr(), but the returned ptr must be free'd.
+ *
+ * \warning The returned pointer must be freed with free(), or
+ * UDPC_atostr_unsafe_free(), or UDPC_atostr_unsafe_free_ptr().
+ *
+ * This function is thread-safe.
+ *
+ * UDPC internally uses UDPC_atostr() for logging. This means that while UDPC
+ * is running, a string created with UDPC_atostr() may be overwritten
+ * eventually by UDPC. To avoid this situation, UDPC_atostr_unsafe() or
+ * UDPC_atostr_unsafe_cid() may be used as the ptr returned by it will not be
+ * overwritten by UDPC as time passes.
+ */
+UDPC_EXPORT const char *UDPC_atostr_unsafe_cid(UDPC_ConnectionId cid);
+
+/*!
+ * \brief Free an addr string created with UDPC_atostr_unsafe().
+ *
+ * This function is thread-safe.
+ */
+UDPC_EXPORT void UDPC_atostr_unsafe_free(const char *addrBuf);
+
+/*!
+ * \brief Free an addr string created with UDPC_atostr_unsafe() and zeroes the
+ * pointer.
+ *
+ * This function is thread-safe.
+ *
+ * \code{.c}
+ * UDPC_ConnectionId aConnectionId = ...;
+ * const char *addrString = UDPC_atostr_unsafe_cid(aConnectionId);
+ * // Use addrString somewhere...
+ * UDPC_atostr_unsafe_free_ptr(&addrString);
+ * \endcode
+ */
+UDPC_EXPORT void UDPC_atostr_unsafe_free_ptr(const char **addrBuf);
+
 // =============================================================================
 // Helpers
 
index a1272bd71de3d0f8a635d50de49c7b3d09153dad..e7d1036ff5703602ae827bc678092dad38402b96 100644 (file)
@@ -2666,42 +2666,33 @@ const char *UDPC_atostr_cid(UDPC_HContext ctx, UDPC_ConnectionId connectionId) {
     return UDPC_atostr(ctx, connectionId.addr);
 }
 
-const char *UDPC_atostr(UDPC_HContext ctx, UDPC_IPV6_ADDR_TYPE addr) {
-    UDPC::Context *c = UDPC::verifyContext(ctx);
-    if(!c) {
-        return nullptr;
-    }
-    uint32_t headIndex;
-    {
-        // Use a lock to ensure that "atostrBufIndex" is always a valid value.
-        std::lock_guard<std::mutex> indexLock(c->atostrBufIndexMutex);
-        c->atostrBufIndex = (c->atostrBufIndex + UDPC_ATOSTR_BUFSIZE) % UDPC_ATOSTR_SIZE;
-        headIndex = c->atostrBufIndex;
-    }
+void UDPC_Helper_atostr(char *buffer,
+                        UDPC_IPV6_ADDR_TYPE addr,
+                        const uint32_t headIndex) {
     uint32_t index = headIndex;
     bool usedDouble = false;
 
     for(unsigned int i = 0; i < 16; ++i) {
         if(i != 0 && i % 2 == 0) {
-            if(headIndex - index > 1 && c->atostrBuf[index - 1] == ':') {
+            if(headIndex - index > 1 && buffer[index - 1] == ':') {
                 if(usedDouble) {
-                    if(c->atostrBuf[index - 2] != ':') {
-                        c->atostrBuf[index++] = '0';
-                        c->atostrBuf[index++] = ':';
+                    if(buffer[index - 2] != ':') {
+                        buffer[index++] = '0';
+                        buffer[index++] = ':';
                     } else {
                         // continue use of double :, do nothing here
                     }
                 } else {
                     usedDouble = true;
-                    c->atostrBuf[index++] = ':';
+                    buffer[index++] = ':';
                 }
             } else {
-                c->atostrBuf[index++] = ':';
+                buffer[index++] = ':';
             }
         }
 
         if(UDPC_IPV6_ADDR_SUB(addr)[i] == 0
-                && (headIndex - index <= 1 || c->atostrBuf[index] == ':')) {
+                && (headIndex - index <= 1 || buffer[index] == ':')) {
             continue;
         } else {
             std::stringstream sstream;
@@ -2709,7 +2700,7 @@ const char *UDPC_atostr(UDPC_HContext ctx, UDPC_IPV6_ADDR_TYPE addr) {
                 << std::hex << (unsigned int) UDPC_IPV6_ADDR_SUB(addr)[i];
             std::string out(sstream.str());
             unsigned int outOffset = 0;
-            if(headIndex - index <= 1 || c->atostrBuf[index - 1] == ':') {
+            if(headIndex - index <= 1 || buffer[index - 1] == ':') {
                 if(out[0] == '0') {
                     if(out[1] == '0') {
                         outOffset = 2;
@@ -2722,25 +2713,67 @@ const char *UDPC_atostr(UDPC_HContext ctx, UDPC_IPV6_ADDR_TYPE addr) {
                 continue;
             } else if(outOffset == 1) {
                 if(out[outOffset] != '0') {
-                    std::memcpy(c->atostrBuf + index, out.c_str() + outOffset, 1);
+                    std::memcpy(buffer + index, out.c_str() + outOffset, 1);
                     ++index;
                 }
             } else {
-                std::memcpy(c->atostrBuf + index, out.c_str() + outOffset, 2);
+                std::memcpy(buffer + index, out.c_str() + outOffset, 2);
                 index += 2;
             }
         }
     }
-    if(c->atostrBuf[index - 1] == ':'
-            && (headIndex - index <= 2 || c->atostrBuf[index - 2] != ':')) {
-        c->atostrBuf[index++] = '0';
+    if(buffer[index - 1] == ':'
+            && (headIndex - index <= 2 || buffer[index - 2] != ':')) {
+        buffer[index++] = '0';
     }
 
-    c->atostrBuf[index] = 0;
+    buffer[index] = 0;
+}
+
+const char *UDPC_atostr(UDPC_HContext ctx, UDPC_IPV6_ADDR_TYPE addr) {
+    UDPC::Context *c = UDPC::verifyContext(ctx);
+    if(!c) {
+        return nullptr;
+    }
+    uint32_t headIndex;
+    {
+        // Use a lock to ensure that "atostrBufIndex" is always a valid value.
+        std::lock_guard<std::mutex> indexLock(c->atostrBufIndexMutex);
+        c->atostrBufIndex = (c->atostrBufIndex + UDPC_ATOSTR_BUFSIZE) % UDPC_ATOSTR_SIZE;
+        headIndex = c->atostrBufIndex;
+    }
+
+    UDPC_Helper_atostr(c->atostrBuf, addr, headIndex);
 
     return c->atostrBuf + headIndex;
 }
 
+const char *UDPC_atostr_unsafe(UDPC_IPV6_ADDR_TYPE addr) {
+    char *buf = (char*)std::malloc(40);
+    std::memset(buf, 0, 40);
+
+    UDPC_Helper_atostr(buf, addr, 0);
+
+    return buf;
+}
+
+const char *UDPC_atostr_unsafe_cid(UDPC_ConnectionId cid) {
+    return UDPC_atostr_unsafe(cid.addr);
+}
+
+void UDPC_atostr_unsafe_free(const char *addrBuf) {
+    if (addrBuf) {
+        std::free((void*)addrBuf);
+    }
+}
+
+void UDPC_atostr_unsafe_free_ptr(const char **addrBuf) {
+    if (addrBuf && *addrBuf) {
+        std::free((void*)*addrBuf);
+        *addrBuf = nullptr;
+    }
+}
+
 UDPC_IPV6_ADDR_TYPE UDPC_strtoa(const char *addrStr) {
     UDPC_IPV6_ADDR_TYPE result = in6addr_loopback;
     std::cmatch matchResults;
index 37bd7198cfa28ae9fbdb56348823c5c71a668b76..1cdd83ea6e22fe4aa7954981a230834680af3e60 100644 (file)
@@ -155,6 +155,101 @@ TEST(UDPC, atostr_concurrent) {
     }
 }
 
+TEST(UDPC, atostr_unsafe) {
+    const char* results[64] = {
+        "::1111:1",
+        "::1111:2",
+        "::1111:3",
+        "::1111:4",
+        "::1111:5",
+        "::1111:6",
+        "::1111:7",
+        "::1111:8",
+        "::1111:9",
+        "::1111:a",
+        "::1111:b",
+        "::1111:c",
+        "::1111:d",
+        "::1111:e",
+        "::1111:f",
+        "::1111:10",
+        "::1111:11",
+        "::1111:12",
+        "::1111:13",
+        "::1111:14",
+        "::1111:15",
+        "::1111:16",
+        "::1111:17",
+        "::1111:18",
+        "::1111:19",
+        "::1111:1a",
+        "::1111:1b",
+        "::1111:1c",
+        "::1111:1d",
+        "::1111:1e",
+        "::1111:1f",
+        "::1111:20",
+        "::1111:21",
+        "::1111:22",
+        "::1111:23",
+        "::1111:24",
+        "::1111:25",
+        "::1111:26",
+        "::1111:27",
+        "::1111:28",
+        "::1111:29",
+        "::1111:2a",
+        "::1111:2b",
+        "::1111:2c",
+        "::1111:2d",
+        "::1111:2e",
+        "::1111:2f",
+        "::1111:30",
+        "::1111:31",
+        "::1111:32",
+        "::1111:33",
+        "::1111:34",
+        "::1111:35",
+        "::1111:36",
+        "::1111:37",
+        "::1111:38",
+        "::1111:39",
+        "::1111:3a",
+        "::1111:3b",
+        "::1111:3c",
+        "::1111:3d",
+        "::1111:3e",
+        "::1111:3f",
+        "::1111:40"
+    };
+    std::future<void> futures[32];
+    const char* ptrs[32];
+    for(unsigned int i = 0; i < 2; ++i) {
+        for(unsigned int j = 0; j < 32; ++j) {
+            futures[j] = std::async(std::launch::async, [] (unsigned int id, const char** ptr) {
+                UDPC_ConnectionId conId = {
+                    {0, 0, 0, 0,
+                    0, 0, 0, 0,
+                    0, 0, 0, 0,
+                    0x11, 0x11, 0x0, (unsigned char)(id + 1)},
+                    0,
+                    0
+                };
+                ptr[id] = UDPC_atostr_unsafe(conId.addr);
+            }, j, ptrs);
+        }
+        for(unsigned int j = 0; j < 32; ++j) {
+            ASSERT_TRUE(futures[j].valid());
+            futures[j].wait();
+        }
+        for(unsigned int j = 0; j < 32; ++j) {
+            EXPECT_STREQ(ptrs[j], results[j]);
+            UDPC_atostr_unsafe_free_ptr(ptrs + j);
+            UDPC_atostr_unsafe_free_ptr(ptrs + j);
+        }
+    }
+}
+
 TEST(UDPC, strtoa) {
     struct in6_addr addr;