]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
bufref: buffer reference support
authorPatrick Monnerat <patrick@monnerat.net>
Tue, 23 Mar 2021 01:02:18 +0000 (02:02 +0100)
committerDaniel Stenberg <daniel@haxx.se>
Thu, 22 Apr 2021 07:05:53 +0000 (09:05 +0200)
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

docs/BUFREF.md [new file with mode: 0644]
docs/Makefile.am
lib/Makefile.inc
lib/bufref.c [new file with mode: 0644]
lib/bufref.h [new file with mode: 0644]
tests/data/Makefile.inc
tests/data/test1661 [new file with mode: 0644]
tests/unit/Makefile.inc
tests/unit/unit1661.c [new file with mode: 0644]

diff --git a/docs/BUFREF.md b/docs/BUFREF.md
new file mode 100644 (file)
index 0000000..e8ef630
--- /dev/null
@@ -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.
index f73671b09d5829f457d7d4e1b5b403157edfb290..656d1ace0b7e3762dab5fa73b6bc520ffb887038 100644 (file)
@@ -44,6 +44,7 @@ EXTRA_DIST =                                    \
  $(noinst_man_MANS)                             \
  ALTSVC.md                                      \
  BINDINGS.md                                    \
+ BUFREF.md                                      \
  BUG-BOUNTY.md                                  \
  BUGS.md                                        \
  CHECKSRC.md                                    \
index 90ec6aa05c0f7d03b6d194a8fd7c5087c28b2c22..3e9ddec12c2a680cfd2b4c0d24be6a9f0ecd10d6 100644 (file)
@@ -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 (file)
index 0000000..b84511e
--- /dev/null
@@ -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 (file)
index 0000000..25f65d8
--- /dev/null
@@ -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
index c4a8e25bc0b4b60fa89bfe93ba33d9032bb69f5c..d21c9fa15c8b4f11ee9517d2582c1e4536f6130e 100644 (file)
@@ -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 (file)
index 0000000..0e02d78
--- /dev/null
@@ -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>
index 0b03b4e6946acd69c7c3aeadc50ad9f9e8bb0828..854e3af388b05eaaf7d29920ced0964ce31331e3 100644 (file)
@@ -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 (file)
index 0000000..c32d6f1
--- /dev/null
@@ -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