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
This commit is contained in:
Patrick Monnerat 2021-03-23 02:02:18 +01:00 committed by Daniel Stenberg
parent a2a8f9d730
commit 34cf40321c
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
9 changed files with 398 additions and 3 deletions

81
docs/BUFREF.md Normal file
View file

@ -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.

View file

@ -44,6 +44,7 @@ EXTRA_DIST = \
$(noinst_man_MANS) \
ALTSVC.md \
BINDINGS.md \
BUFREF.md \
BUG-BOUNTY.md \
BUGS.md \
CHECKSRC.md \

View 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 \

127
lib/bufref.c Normal file
View file

@ -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;
}

46
lib/bufref.h Normal file
View file

@ -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

View 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 \
\

22
tests/data/test1661 Normal file
View file

@ -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>

View 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)

113
tests/unit/unit1661.c Normal file
View file

@ -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