From 322b4739cb51aa45568d9479224f2b07ac82a35f Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Thu, 24 Jan 2013 08:56:46 +0100 Subject: Add generic buffer code Represents a block of memory that can be added to, parsed and so on --- common/Makefile.am | 1 + common/buffer.c | 180 ++++++++++++++++++++++++++++++++++++++ common/buffer.h | 82 +++++++++++++++++ common/tests/Makefile.am | 1 + common/tests/test-buffer.c | 214 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 478 insertions(+) create mode 100644 common/buffer.c create mode 100644 common/buffer.h create mode 100644 common/tests/test-buffer.c (limited to 'common') diff --git a/common/Makefile.am b/common/Makefile.am index 5d38c88..ce6dd2a 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -20,6 +20,7 @@ libp11_compat_la_SOURCES = \ libp11_library_la_SOURCES = \ attrs.c attrs.h \ array.c array.h \ + buffer.c buffer.h \ debug.c debug.h \ dict.c dict.h \ library.c library.h \ diff --git a/common/buffer.c b/common/buffer.c new file mode 100644 index 0000000..a91623e --- /dev/null +++ b/common/buffer.c @@ -0,0 +1,180 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* buffer.c - Generic data buffer, used by openssh, gnome-keyring + + Copyright (C) 2007, 2012 Stefan Walter + Copyright (C) 2013 Red Hat Inc. + + The Gnome Keyring Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The Gnome Keyring Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the Gnome Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Author: Stef Walter +*/ + +#include "config.h" + +#include "buffer.h" +#include "debug.h" + +#include +#include +#include +#include + +static bool +buffer_realloc (p11_buffer *buffer, + size_t size) +{ + void *data; + + /* Memory owned elsewhere can't be reallocated */ + return_val_if_fail (buffer->frealloc != NULL, false); + + /* Reallocate built in buffer using allocator */ + data = (buffer->frealloc) (buffer->data, size); + if (!data && size > 0) { + p11_buffer_fail (buffer); + return_val_if_reached (false); + } + + buffer->data = data; + buffer->size = size; + return true; +} + +bool +p11_buffer_init (p11_buffer *buffer, + size_t reserve) +{ + p11_buffer_init_full (buffer, NULL, 0, 0, realloc, free); + return buffer_realloc (buffer, reserve); +} + +bool +p11_buffer_init_null (p11_buffer *buffer, + size_t reserve) +{ + p11_buffer_init_full (buffer, NULL, 0, P11_BUFFER_NULL, realloc, free); + return buffer_realloc (buffer, reserve); +} + +void +p11_buffer_init_full (p11_buffer *buffer, + void *data, + size_t len, + int flags, + void * (* frealloc) (void *, size_t), + void (* ffree) (void *)) +{ + memset (buffer, 0, sizeof (*buffer)); + + buffer->data = data; + buffer->len = len; + buffer->size = len; + buffer->flags = flags; + buffer->frealloc = frealloc; + buffer->ffree = ffree; + + return_if_fail (!(flags & P11_BUFFER_FAILED)); +} + +void +p11_buffer_uninit (p11_buffer *buffer) +{ + return_if_fail (buffer != NULL); + + if (buffer->ffree && buffer->data) + (buffer->ffree) (buffer->data); + memset (buffer, 0, sizeof (*buffer)); +} + +void * +p11_buffer_steal (p11_buffer *buffer, + size_t *length) +{ + void *data; + + return_val_if_fail (p11_buffer_ok (buffer), NULL); + + if (length) + *length = buffer->len; + data = buffer->data; + + buffer->data = NULL; + buffer->size = 0; + buffer->len = 0; + return data; +} + +bool +p11_buffer_reset (p11_buffer *buffer, + size_t reserve) +{ + buffer->flags &= ~P11_BUFFER_FAILED; + buffer->len = 0; + + if (reserve < buffer->size) + return true; + return buffer_realloc (buffer, reserve); +} + +void * +p11_buffer_append (p11_buffer *buffer, + size_t length) +{ + unsigned char *data; + size_t terminator; + size_t newlen; + size_t reserve; + + return_val_if_fail (p11_buffer_ok (buffer), NULL); + + terminator = (buffer->flags & P11_BUFFER_NULL) ? 1 : 0; + reserve = terminator + length + buffer->len; + + if (reserve > buffer->size) { + + /* Calculate a new length, minimize number of buffer allocations */ + newlen = buffer->size * 2; + if (!newlen) + newlen = 16; + if (reserve > newlen) + newlen = reserve; + + if (!buffer_realloc (buffer, newlen)) + return_val_if_reached (NULL); + } + + data = buffer->data; + data += buffer->len; + buffer->len += length; + if (terminator) + data[length] = '\0'; + return data; +} + +void +p11_buffer_add (p11_buffer *buffer, + const void *data, + ssize_t length) +{ + void *at; + + if (length < 0) + length = strlen (data); + + at = p11_buffer_append (buffer, length); + return_if_fail (at != NULL); + memcpy (at, data, length); +} diff --git a/common/buffer.h b/common/buffer.h new file mode 100644 index 0000000..d5335f8 --- /dev/null +++ b/common/buffer.h @@ -0,0 +1,82 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* buffer.h - Generic data buffer, used by openssh, gnome-keyring + + Copyright (C) 2007, 2012 Stefan Walter + Copyright (C) 2012 Red Hat Inc. + + The Gnome Keyring Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The Gnome Keyring Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the Gnome Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Author: Stef Walter +*/ + +#ifndef P11_BUFFER_H_ +#define P11_BUFFER_H_ + +#include "compat.h" + +enum { + P11_BUFFER_FAILED = 1 << 0, + P11_BUFFER_NULL = 1 << 1, +}; + +typedef struct { + void *data; + size_t len; + + int flags; + size_t size; + void * (* frealloc) (void *, size_t); + void (* ffree) (void *); +} p11_buffer; + +bool p11_buffer_init (p11_buffer *buffer, + size_t size); + +bool p11_buffer_init_null (p11_buffer *buffer, + size_t size); + +void p11_buffer_init_full (p11_buffer *buffer, + void *data, + size_t len, + int flags, + void * (* frealloc) (void *, size_t), + void (* ffree) (void *)); + +void p11_buffer_uninit (p11_buffer *buffer); + +void * p11_buffer_steal (p11_buffer *buffer, + size_t *length); + +bool p11_buffer_reset (p11_buffer *buffer, + size_t size); + +void * p11_buffer_append (p11_buffer *buffer, + size_t length); + +void p11_buffer_add (p11_buffer *buffer, + const void *data, + ssize_t length); + +#define p11_buffer_fail(buf) \ + ((buf)->flags |= P11_BUFFER_FAILED) + +#define p11_buffer_ok(buf) \ + (((buf)->flags & P11_BUFFER_FAILED) ? false : true) + +#define p11_buffer_failed(buf) \ + (((buf)->flags & P11_BUFFER_FAILED) ? true : false) + +#endif /* BUFFER_H */ diff --git a/common/tests/Makefile.am b/common/tests/Makefile.am index 4badbb5..3b6b986 100644 --- a/common/tests/Makefile.am +++ b/common/tests/Makefile.am @@ -21,6 +21,7 @@ CHECK_PROGS = \ test-dict \ test-array \ test-attrs \ + test-buffer \ $(NULL) noinst_PROGRAMS = \ diff --git a/common/tests/test-buffer.c b/common/tests/test-buffer.c new file mode 100644 index 0000000..9db5f83 --- /dev/null +++ b/common/tests/test-buffer.c @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2012 Red Hat Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the + * above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * * The names of contributors to this software may not be + * used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * Author: Stef Walter + */ + +#include "config.h" +#include "CuTest.h" + +#include +#include +#include + +#include "debug.h" +#include "buffer.h" + +static void +test_init_uninit (CuTest *tc) +{ + p11_buffer buffer; + + p11_buffer_init (&buffer, 10); + CuAssertPtrNotNull (tc, buffer.data); + CuAssertIntEquals (tc, 0, buffer.len); + CuAssertIntEquals (tc, 0, buffer.flags); + CuAssertTrue (tc, buffer.size >= 10); + CuAssertPtrNotNull (tc, buffer.ffree); + CuAssertPtrNotNull (tc, buffer.frealloc); + + p11_buffer_uninit (&buffer); +} + +static void +test_append (CuTest *tc) +{ + p11_buffer buffer; + + p11_buffer_init (&buffer, 10); + buffer.len = 5; + p11_buffer_append (&buffer, 35); + CuAssertIntEquals (tc, 5 + 35, buffer.len); + CuAssertTrue (tc, buffer.size >= 35 + 5); + + p11_buffer_append (&buffer, 15); + CuAssertIntEquals (tc, 5 + 35 + 15, buffer.len); + CuAssertTrue (tc, buffer.size >= 5 + 35 + 15); + + p11_buffer_uninit (&buffer); +} + +static void +test_null (CuTest *tc) +{ + p11_buffer buffer; + + p11_buffer_init_null (&buffer, 10); + p11_buffer_add (&buffer, "Blah", -1); + p11_buffer_add (&buffer, " blah", -1); + + CuAssertStrEquals (tc, "Blah blah", buffer.data); + + p11_buffer_uninit (&buffer); +} + +static int mock_realloced = 0; +static int mock_freed = 0; + +static void * +mock_realloc (void *data, + size_t size) +{ + mock_realloced++; + return realloc (data, size); +} + +static void +mock_free (void *data) +{ + mock_freed++; + free (data); +} + +static void +test_init_for_data (CuTest *tc) +{ + p11_buffer buffer; + unsigned char *ret; + size_t len; + + mock_realloced = 0; + mock_freed = 0; + + p11_buffer_init_full (&buffer, (unsigned char *)strdup ("blah"), 4, 0, + mock_realloc, mock_free); + + CuAssertPtrNotNull (tc, buffer.data); + CuAssertStrEquals (tc, "blah", (char *)buffer.data); + CuAssertIntEquals (tc, 4, buffer.len); + CuAssertIntEquals (tc, 0, buffer.flags); + CuAssertIntEquals (tc, 4, buffer.size); + CuAssertPtrEquals (tc, mock_free, buffer.ffree); + CuAssertPtrEquals (tc, mock_realloc, buffer.frealloc); + + CuAssertIntEquals (tc, 0, mock_realloced); + CuAssertIntEquals (tc, 0, mock_freed); + + len = buffer.len; + ret = p11_buffer_append (&buffer, 1024); + CuAssertPtrEquals (tc, (char *)buffer.data + len, ret); + CuAssertIntEquals (tc, 1, mock_realloced); + + p11_buffer_uninit (&buffer); + CuAssertIntEquals (tc, 1, mock_realloced); + CuAssertIntEquals (tc, 1, mock_freed); +} + +static void +test_steal (CuTest *tc) +{ + p11_buffer buffer; + char *string; + size_t length; + + mock_freed = 0; + + p11_buffer_init_full (&buffer, (unsigned char *)strdup ("blah"), 4, + P11_BUFFER_NULL, mock_realloc, mock_free); + + CuAssertPtrNotNull (tc, buffer.data); + CuAssertStrEquals (tc, "blah", buffer.data); + + p11_buffer_add (&buffer, " yada", -1); + CuAssertStrEquals (tc, "blah yada", buffer.data); + + string = p11_buffer_steal (&buffer, &length); + p11_buffer_uninit (&buffer); + + CuAssertStrEquals (tc, "blah yada", string); + CuAssertIntEquals (tc, 9, length); + CuAssertIntEquals (tc, 0, mock_freed); + + free (string); +} + +static void +test_add (CuTest *tc) +{ + p11_buffer buffer; + + p11_buffer_init (&buffer, 10); + + p11_buffer_add (&buffer, (unsigned char *)"Planet Express", 15); + CuAssertIntEquals (tc, 15, buffer.len); + CuAssertStrEquals (tc, "Planet Express", (char *)buffer.data); + CuAssertTrue (tc, p11_buffer_ok (&buffer)); + + p11_buffer_uninit (&buffer); +} + +int +main (void) +{ + CuString *output = CuStringNew (); + CuSuite* suite = CuSuiteNew (); + int ret; + + setenv ("P11_KIT_STRICT", "1", 1); + p11_debug_init (); + + SUITE_ADD_TEST (suite, test_init_uninit); + SUITE_ADD_TEST (suite, test_init_for_data); + SUITE_ADD_TEST (suite, test_append); + SUITE_ADD_TEST (suite, test_null); + SUITE_ADD_TEST (suite, test_add); + SUITE_ADD_TEST (suite, test_steal); + + CuSuiteRun (suite); + CuSuiteSummary (suite, output); + CuSuiteDetails (suite, output); + printf ("%s\n", output->buffer); + ret = suite->failCount; + CuSuiteDelete (suite); + CuStringDelete (output); + + return ret; +} -- cgit v1.1