summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Monnerat <patrick@monnerat.net>2021-03-23 02:02:18 +0100
committerDaniel Stenberg <daniel@haxx.se>2021-04-22 09:05:53 +0200
commit34cf40321c3cfa5160e34a87a816b7c6030c0296 (patch)
tree5b9e2844d85a620027c2aa18f87d446b00875090
parenta2a8f9d7300f3a3773cb046005e0ecbc9a7080db (diff)
downloadcurl-34cf40321c3cfa5160e34a87a816b7c6030c0296.tar.gz
bufref: buffer reference support
A struct bufref holds a buffer pointer, a data size and a destructor. When freed or its contents are changed, the previous buffer is implicitly released by the associated destructor. The data size, although not used internally, allows binary data support. A unit test checks its handling methods: test 1661 Closes #6654
-rw-r--r--docs/BUFREF.md81
-rw-r--r--docs/Makefile.am1
-rw-r--r--lib/Makefile.inc2
-rw-r--r--lib/bufref.c127
-rw-r--r--lib/bufref.h46
-rw-r--r--tests/data/Makefile.inc2
-rw-r--r--tests/data/test166122
-rw-r--r--tests/unit/Makefile.inc7
-rw-r--r--tests/unit/unit1661.c113
9 files changed, 398 insertions, 3 deletions
diff --git a/docs/BUFREF.md b/docs/BUFREF.md
new file mode 100644
index 000000000..e8ef63080
--- /dev/null
+++ b/docs/BUFREF.md
@@ -0,0 +1,81 @@
+# bufref
+
+This is an internal module for handling buffer references. A referenced
+buffer is associated with its destructor function that is implicitly called
+when the reference is invalidated. Once referenced, a buffer cannot be
+reallocated.
+
+A data length is stored within the reference for binary data handling
+purpose; it is not used by the bufref API.
+
+The `struct bufref` is used to hold data referencing a buffer. The members of
+that structure **MUST NOT** be accessed or modified without using the dedicated
+bufref API.
+
+## init
+
+```c
+void Curl_bufref_init(struct bufref *br);
+```
+
+Initialises a `bufref` structure. This function **MUST** be called before any
+other operation is performed on the structure.
+
+Upon completion, the referenced buffer is `NULL` and length is zero.
+
+This function may also be called to bypass referenced buffer destruction while
+invalidating the current reference.
+
+## free
+
+```c
+void Curl_bufref_free(struct bufref *br);
+```
+
+Destroys the previously referenced buffer using its destructor and
+reinitialises the structure for a possible subsequent reuse.
+
+## set
+
+```c
+void Curl_bufref_set(struct bufref *br, const void *buffer, size_t length,
+ void (*destructor)(void *));
+```
+
+Releases the previouly referenced buffer, then assigns the new `buffer` to
+the structure, associated with its `destructor` function. The later can be
+specified as `NULL`: this will be the case when the referenced buffer is
+static.
+
+if `buffer` is NULL, `length`must be zero.
+
+## memdup
+
+```c
+CURLcode Curl_bufref_memdup(struct bufref *br, const void *data, size_t length);
+```
+
+Releases the previouly referenced buffer, then duplicates the `length`-byte
+`data` into a buffer allocated via `malloc()` and references the later
+associated with destructor `curl_free()`.
+
+An additional trailing byte is allocated and set to zero as a possible
+string zero-terminator; it is not counted in the stored length.
+
+Returns `CURLE_OK` if successful, else `CURLE_OUT_OF_MEMORY`.
+
+## ptr
+
+```c
+const unsigned char *Curl_bufref_ptr(const struct bufref *br);
+```
+
+Returns a `const unsigned char *` to the referenced bufffer.
+
+## len
+
+```c
+size_t Curl_bufref_len(const struct bufref *br);
+```
+
+Returns the stored length of the referenced buffer.
diff --git a/docs/Makefile.am b/docs/Makefile.am
index f73671b09..656d1ace0 100644
--- a/docs/Makefile.am
+++ b/docs/Makefile.am
@@ -44,6 +44,7 @@ EXTRA_DIST = \
$(noinst_man_MANS) \
ALTSVC.md \
BINDINGS.md \
+ BUFREF.md \
BUG-BOUNTY.md \
BUGS.md \
CHECKSRC.md \
diff --git a/lib/Makefile.inc b/lib/Makefile.inc
index 90ec6aa05..3e9ddec12 100644
--- a/lib/Makefile.inc
+++ b/lib/Makefile.inc
@@ -97,6 +97,7 @@ LIB_CFILES = \
asyn-ares.c \
asyn-thread.c \
base64.c \
+ bufref.c \
c-hyper.c \
conncache.c \
connect.c \
@@ -217,6 +218,7 @@ LIB_HFILES = \
amigaos.h \
arpa_telnet.h \
asyn.h \
+ bufref.h \
c-hyper.h \
conncache.h \
connect.h \
diff --git a/lib/bufref.c b/lib/bufref.c
new file mode 100644
index 000000000..b84511e18
--- /dev/null
+++ b/lib/bufref.c
@@ -0,0 +1,127 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+#include "urldata.h"
+#include "bufref.h"
+
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#define SIGNATURE 0x5c48e9b2 /* Random pattern. */
+
+/*
+ * Init a bufref struct.
+ */
+void Curl_bufref_init(struct bufref *br)
+{
+ DEBUGASSERT(br);
+ br->dtor = NULL;
+ br->ptr = NULL;
+ br->len = 0;
+
+#ifdef DEBUGBUILD
+ br->signature = SIGNATURE;
+#endif
+}
+
+/*
+ * Free the buffer and re-init the necessary fields. It doesn't touch the
+ * 'signature' field and thus this buffer reference can be reused.
+ */
+
+void Curl_bufref_free(struct bufref *br)
+{
+ DEBUGASSERT(br);
+ DEBUGASSERT(br->signature == SIGNATURE);
+ DEBUGASSERT(br->ptr || !br->len);
+
+ if(br->ptr && br->dtor)
+ br->dtor((void *) br->ptr);
+
+ br->dtor = NULL;
+ br->ptr = NULL;
+ br->len = 0;
+}
+
+/*
+ * Set the buffer reference to new values. The previously referenced buffer
+ * is released before assignment.
+ */
+void Curl_bufref_set(struct bufref *br, const void *ptr, size_t len,
+ void (*dtor)(void *))
+{
+ DEBUGASSERT(ptr || !len);
+ DEBUGASSERT(len <= CURL_MAX_INPUT_LENGTH);
+
+ Curl_bufref_free(br);
+ br->ptr = (const unsigned char *) ptr;
+ br->len = len;
+ br->dtor = dtor;
+}
+
+/*
+ * Get a pointer to the referenced buffer.
+ */
+const unsigned char *Curl_bufref_ptr(const struct bufref *br)
+{
+ DEBUGASSERT(br);
+ DEBUGASSERT(br->signature == SIGNATURE);
+ DEBUGASSERT(br->ptr || !br->len);
+
+ return br->ptr;
+}
+
+/*
+ * Get the length of the referenced buffer data.
+ */
+size_t Curl_bufref_len(const struct bufref *br)
+{
+ DEBUGASSERT(br);
+ DEBUGASSERT(br->signature == SIGNATURE);
+ DEBUGASSERT(br->ptr || !br->len);
+
+ return br->len;
+}
+
+CURLcode Curl_bufref_memdup(struct bufref *br, const void *ptr, size_t len)
+{
+ unsigned char *cpy = NULL;
+
+ DEBUGASSERT(br);
+ DEBUGASSERT(br->signature == SIGNATURE);
+ DEBUGASSERT(br->ptr || !br->len);
+ DEBUGASSERT(ptr || !len);
+ DEBUGASSERT(len <= CURL_MAX_INPUT_LENGTH);
+
+ if(ptr) {
+ cpy = malloc(len + 1);
+ if(!cpy)
+ return CURLE_OUT_OF_MEMORY;
+ if(len)
+ memcpy(cpy, ptr, len);
+ cpy[len] = '\0';
+ }
+
+ Curl_bufref_set(br, cpy, len, curl_free);
+ return CURLE_OK;
+}
diff --git a/lib/bufref.h b/lib/bufref.h
new file mode 100644
index 000000000..25f65d894
--- /dev/null
+++ b/lib/bufref.h
@@ -0,0 +1,46 @@
+#ifndef HEADER_CURL_BUFREF_H
+#define HEADER_CURL_BUFREF_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+/*
+ * Generic buffer reference.
+ */
+struct bufref {
+ void (*dtor)(void *); /* Associated destructor. */
+ const unsigned char *ptr; /* Referenced data buffer. */
+ size_t len; /* The data size in bytes. */
+#ifdef DEBUGBUILD
+ int signature; /* Detect API use mistakes. */
+#endif
+};
+
+
+void Curl_bufref_init(struct bufref *br);
+void Curl_bufref_set(struct bufref *br, const void *ptr, size_t len,
+ void (*dtor)(void *));
+const unsigned char *Curl_bufref_ptr(const struct bufref *br);
+size_t Curl_bufref_len(const struct bufref *br);
+CURLcode Curl_bufref_memdup(struct bufref *br, const void *ptr, size_t len);
+void Curl_bufref_free(struct bufref *br);
+
+#endif
diff --git a/tests/data/Makefile.inc b/tests/data/Makefile.inc
index c4a8e25bc..d21c9fa15 100644
--- a/tests/data/Makefile.inc
+++ b/tests/data/Makefile.inc
@@ -203,7 +203,7 @@ test1620 test1621 \
test1630 test1631 test1632 test1633 test1634 \
\
test1650 test1651 test1652 test1653 test1654 test1655 \
-test1660 \
+test1660 test1661 \
\
test1700 test1701 test1702 test1703 \
\
diff --git a/tests/data/test1661 b/tests/data/test1661
new file mode 100644
index 000000000..0e02d78d0
--- /dev/null
+++ b/tests/data/test1661
@@ -0,0 +1,22 @@
+<testcase>
+<info>
+<keywords>
+unittest
+bufref
+</keywords>
+</info>
+
+#
+# Client-side
+<client>
+<server>
+none
+</server>
+<features>
+unittest
+</features>
+ <name>
+bufref unit tests
+ </name>
+</client>
+</testcase>
diff --git a/tests/unit/Makefile.inc b/tests/unit/Makefile.inc
index 0b03b4e69..854e3af38 100644
--- a/tests/unit/Makefile.inc
+++ b/tests/unit/Makefile.inc
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
@@ -35,7 +35,7 @@ UNITPROGS = unit1300 unit1301 unit1302 unit1303 unit1304 unit1305 unit1307 \
unit1608 unit1609 unit1610 unit1611 unit1612 \
unit1620 unit1621 \
unit1650 unit1651 unit1652 unit1653 unit1654 unit1655 \
- unit1660
+ unit1660 unit1661
unit1300_SOURCES = unit1300.c $(UNITFILES)
unit1300_CPPFLAGS = $(AM_CPPFLAGS)
@@ -157,3 +157,6 @@ unit1655_CPPFLAGS = $(AM_CPPFLAGS)
unit1660_SOURCES = unit1660.c $(UNITFILES)
unit1660_CPPFLAGS = $(AM_CPPFLAGS)
+
+unit1661_SOURCES = unit1661.c $(UNITFILES)
+unit1661_CPPFLAGS = $(AM_CPPFLAGS)
diff --git a/tests/unit/unit1661.c b/tests/unit/unit1661.c
new file mode 100644
index 000000000..c32d6f164
--- /dev/null
+++ b/tests/unit/unit1661.c
@@ -0,0 +1,113 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+#include "curlcheck.h"
+
+#include "bufref.h"
+
+static struct bufref bufref;
+
+static int freecount = 0;
+
+static void test_free(void *p)
+{
+ fail_unless(p, "pointer to free may not be NULL");
+ freecount++;
+ free(p);
+}
+
+static CURLcode unit_setup(void)
+{
+ Curl_bufref_init(&bufref);
+ return CURLE_OK;
+}
+
+static void unit_stop(void)
+{
+}
+
+UNITTEST_START
+{
+ char *buffer = NULL;
+ CURLcode result = CURLE_OK;
+
+ /**
+ * testing Curl_bufref_init.
+ * @assumptions:
+ * 1: data size will be 0
+ * 2: reference will be NULL
+ * 3: destructor will be NULL
+ */
+
+ fail_unless(!bufref.ptr, "Initial reference must be NULL");
+ fail_unless(!bufref.len, "Initial length must be NULL");
+ fail_unless(!bufref.dtor, "Destructor must be NULL");
+
+ /**
+ * testing Curl_bufref_set
+ */
+
+ buffer = malloc(13);
+ abort_unless(buffer, "Out of memory");
+ Curl_bufref_set(&bufref, buffer, 13, test_free);
+
+ fail_unless((char *) bufref.ptr == buffer, "Referenced data badly set");
+ fail_unless(bufref.len == 13, "Data size badly set");
+ fail_unless(bufref.dtor == test_free, "Destructor badly set");
+
+ /**
+ * testing Curl_bufref_ptr
+ */
+
+ fail_unless((char *) Curl_bufref_ptr(&bufref) == buffer,
+ "Wrong pointer value returned");
+
+ /**
+ * testing Curl_bufref_len
+ */
+
+ fail_unless(Curl_bufref_len(&bufref) == 13, "Wrong data size returned");
+
+ /**
+ * testing Curl_bufref_memdup
+ */
+
+ result = Curl_bufref_memdup(&bufref, "1661", 3);
+ abort_unless(result == CURLE_OK, curl_easy_strerror(result));
+ fail_unless(freecount == 1, "Destructor not called");
+ fail_unless((char *) bufref.ptr != buffer, "Returned pointer not set");
+ buffer = (char *) Curl_bufref_ptr(&bufref);
+ fail_unless(buffer, "Allocated pointer is NULL");
+ fail_unless(bufref.len == 3, "Wrong data size stored");
+ fail_unless(!buffer[3], "Duplicated data should have been truncated");
+ fail_unless(!strcmp(buffer, "166"), "Bad duplicated data");
+
+ /**
+ * testing Curl_bufref_free
+ */
+
+ Curl_bufref_free(&bufref);
+ fail_unless(freecount == 1, "Wrong destructor called");
+ fail_unless(!bufref.ptr, "Initial reference must be NULL");
+ fail_unless(!bufref.len, "Initial length must be NULL");
+ fail_unless(!bufref.dtor, "Destructor must be NULL");
+}
+UNITTEST_STOP