Add gwbuf_compare

With gwbuf_compare the content of two GWBUFs can be compared.
This commit is contained in:
Johan Wikman
2016-12-19 14:22:34 +02:00
parent 3a3632e75e
commit b62db91ab3
3 changed files with 209 additions and 0 deletions

View File

@ -211,6 +211,26 @@ extern void gwbuf_free(GWBUF *buf);
*/
extern GWBUF *gwbuf_clone(GWBUF *buf);
/**
* Compare two GWBUFs. Two GWBUFs are considered identical if their
* content is identical, irrespective of whether one is segmented and
* the other is not.
*
* @param lhs One GWBUF
* @param rhs Another GWBUF
*
* @return 0 if the content is identical,
* -1 if @c lhs is less than @c rhs, and
* 1 if @c lhs is more than @c rhs.
*
* @attention A NULL @c GWBUF is considered to be less than a non-NULL one,
* and a shorter @c GWBUF less than a longer one. Otherwise the
* the sign of the return value is determined by the sign of the
* difference between the first pair of bytes (interpreted as
* unsigned char) that differ in lhs and rhs.
*/
extern int gwbuf_compare(const GWBUF* lhs, const GWBUF* rhs);
/**
* Append a buffer onto a linked list of buffer structures.
*

View File

@ -439,6 +439,122 @@ GWBUF* gwbuf_split(GWBUF **buf, size_t length)
return head;
}
/**
* Get a byte from a GWBUF at a particular offset. Intended to be use like:
*
* GWBUF *buf = ...;
* size_t offset = 0;
* uint8_t c;
*
* while (gwbuf_get_byte(&buf, &offset, &c))
* {
* printf("%c", c);
* }
*
* @param buf Pointer to pointer to GWBUF. The GWBUF pointed to may be adjusted
* as a result of the call.
* @param offset Pointer to variable containing the offset. Value of variable will
* incremented as a result of the call.
* @param b Pointer to variable that upon successful return will contain the
* next byte.
*
* @return True, if offset refers to a byte in the GWBUF.
*/
static inline bool gwbuf_get_byte(const GWBUF** buf, size_t* offset, uint8_t* b)
{
bool rv = false;
// Ignore NULL buffer and walk past empty or too short buffers.
while (*buf && (GWBUF_LENGTH(*buf) <= *offset))
{
*offset -= GWBUF_LENGTH(*buf);
*buf = (*buf)->next;
}
ss_dassert(!*buf || (GWBUF_LENGTH(*buf) > *offset));
if (*buf)
{
*b = *(GWBUF_DATA(*buf) + *offset);
*offset += 1;
rv = true;
}
return rv;
}
int gwbuf_compare(const GWBUF* lhs, const GWBUF* rhs)
{
int rv;
if ((lhs == NULL) && (rhs == NULL))
{
rv = 0;
}
else if (lhs == NULL)
{
ss_dassert(rhs);
rv = -1;
}
else if (rhs == NULL)
{
ss_dassert(lhs);
rv = 1;
}
else
{
ss_dassert(lhs && rhs);
size_t llen = gwbuf_length(lhs);
size_t rlen = gwbuf_length(rhs);
if (llen < rlen)
{
rv = -1;
}
else if (rlen < llen)
{
rv = 1;
}
else
{
ss_dassert(llen == rlen);
rv = 0;
size_t i = 0;
size_t loffset = 0;
size_t roffset = 0;
while ((rv == 0) && (i < llen))
{
uint8_t lc;
uint8_t rc;
ss_debug(bool rv1 =) gwbuf_get_byte(&lhs, &loffset, &lc);
ss_debug(bool rv2 =) gwbuf_get_byte(&rhs, &roffset, &rc);
ss_dassert(rv1 && rv2);
rv = (int)lc - (int)rc;
++i;
}
if (rv < 0)
{
rv = -1;
}
else if (rv > 0)
{
rv = 1;
}
}
}
return rv;
}
GWBUF *
gwbuf_append(GWBUF *head, GWBUF *tail)
{

View File

@ -316,6 +316,78 @@ void test_consume()
consume_buffer(n_buffers - 1, -1);
}
void test_compare()
{
static const uint8_t data[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
ss_dfprintf(stderr, "testbuffer : testing GWBUF comparisons\n");
GWBUF* lhs = NULL;
GWBUF* rhs = NULL;
// Both NULL
ss_dassert(gwbuf_compare(lhs, rhs) == 0);
// Either (but not both) NULL
lhs = gwbuf_alloc_and_load(10, data);
ss_dassert(gwbuf_compare(lhs, rhs) > 0);
ss_dassert(gwbuf_compare(rhs, lhs) < 0);
// The same array
ss_dassert(gwbuf_compare(lhs, lhs) == 0);
// Identical array
gwbuf_free(rhs);
rhs = gwbuf_alloc_and_load(10, data);
ss_dassert(gwbuf_compare(lhs, rhs) == 0);
// One shorter
gwbuf_free(rhs);
rhs = gwbuf_alloc_and_load(9, data + 1);
ss_dassert(gwbuf_compare(lhs, rhs) > 0);
ss_dassert(gwbuf_compare(rhs, lhs) < 0);
// One segmented, but otherwise identical.
gwbuf_free(rhs);
rhs = NULL;
rhs = gwbuf_append(rhs, gwbuf_alloc_and_load(3, data));
rhs = gwbuf_append(rhs, gwbuf_alloc_and_load(3, data + 3));
rhs = gwbuf_append(rhs, gwbuf_alloc_and_load(4, data + 3 + 3));
ss_dassert(gwbuf_compare(lhs, rhs) == 0);
ss_dassert(gwbuf_compare(rhs, rhs) == 0);
// Both segmented, but otherwise identical.
gwbuf_free(lhs);
lhs = NULL;
lhs = gwbuf_append(lhs, gwbuf_alloc_and_load(5, data));
lhs = gwbuf_append(lhs, gwbuf_alloc_and_load(5, data + 5));
ss_dassert(gwbuf_compare(lhs, rhs) == 0);
ss_dassert(gwbuf_compare(rhs, lhs) == 0);
// Both segmented and of same length, but different.
gwbuf_free(lhs);
lhs = NULL;
lhs = gwbuf_append(lhs, gwbuf_alloc_and_load(5, data + 5)); // Values in different order
lhs = gwbuf_append(lhs, gwbuf_alloc_and_load(5, data));
ss_dassert(gwbuf_compare(lhs, rhs) > 0); // 5 > 1
ss_dassert(gwbuf_compare(rhs, lhs) < 0); // 5 > 1
// Identical, but one containing empty segments.
gwbuf_free(rhs);
rhs = NULL;
rhs = gwbuf_append(rhs, gwbuf_alloc_and_load(0, data));
rhs = gwbuf_append(rhs, gwbuf_alloc_and_load(5, data + 5));
rhs = gwbuf_append(rhs, gwbuf_alloc_and_load(0, data));
rhs = gwbuf_append(rhs, gwbuf_alloc_and_load(5, data));
rhs = gwbuf_append(rhs, gwbuf_alloc_and_load(0, data));
ss_dassert(gwbuf_compare(lhs, rhs) == 0);
ss_dassert(gwbuf_compare(rhs, lhs) == 0);
}
/**
* test1 Allocate a buffer and do lots of things
*
@ -433,6 +505,7 @@ test1()
test_split();
test_load_and_copy();
test_consume();
test_compare();
return 0;
}