summaryrefslogtreecommitdiff
path: root/snprintfv/filament.c
diff options
context:
space:
mode:
Diffstat (limited to 'snprintfv/filament.c')
-rw-r--r--snprintfv/filament.c232
1 files changed, 232 insertions, 0 deletions
diff --git a/snprintfv/filament.c b/snprintfv/filament.c
new file mode 100644
index 0000000..a4b138f
--- /dev/null
+++ b/snprintfv/filament.c
@@ -0,0 +1,232 @@
+/* -*- Mode: C -*- */
+
+/* filament.c --- a bit like a string, but different =)O|
+ * Copyright (C) 1999 Gary V. Vaughan
+ * Originally by Gary V. Vaughan, 1999
+ * This file is part of Snprintfv
+ *
+ * Snprintfv is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * Snprintfv program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * As a special exception to the GNU General Public License, if you
+ * distribute this file as part of a program that also links with and
+ * uses the libopts library from AutoGen, you may include it under
+ * the same distribution terms used by the libopts library.
+ */
+
+/* Commentary:
+ *
+ * Try to exploit usage patterns to optimise string handling, and
+ * as a happy consequence handle NUL's embedded in strings properly.
+ *
+ * o Since finding the length of a (long) string is time consuming and
+ * requires careful coding to cache the result in local scope: We
+ * keep count of the length of a Filament all the time, so finding the
+ * length is O(1) at the expense of a little bookkeeping while
+ * manipulating the Filament contents.
+ *
+ * o Constantly resizing a block of memory to hold a string is memory
+ * efficient, but slow: Filaments are only ever expanded in size,
+ * doubling at each step to minimise the number of times the block
+ * needs to be reallocated and the contents copied (this problem is
+ * especially poignant for very large strings).
+ *
+ * o Most strings tend to be either relatively small and short-lived,
+ * or else long-lived but growing in asymptotically in size: To
+ * care for the former case, Filaments start off with a modest static
+ * buffer for the string contents to avoid any mallocations (except
+ * the initial one to get the structure!); the latter case is handled
+ * gracefully by the resizing algorithm in the previous point.
+ *
+ * o Extracting a C-style NUL terminated string from the Filament is
+ * an extremely common operation: We ensure there is always a
+ * terminating NUL character after the last character in the string
+ * so that the conversion can be performed quickly.
+ *
+ * In summary, Filaments are good where you need to do a lot of length
+ * calculations with your strings and/or gradually append more text
+ * onto existing strings. Filaments are also an easy way to get 8-bit
+ * clean strings is a more lightweight approach isn't required.
+ *
+ * They probably don't buy much if you need to do insertions and partial
+ * deletions, but optimising for that is a whole other problem!
+ */
+
+/* Code: */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef WITH_DMALLOC
+# include <dmalloc.h>
+#endif
+
+#include "mem.h"
+#include "filament.h"
+
+
+
+/**
+ * filnew: constructor
+ * @init: address of the first byte to copy into the new object.
+ * @len: the number of bytes to copy into the new object.
+ *
+ * Create a new Filament object, initialised to hold a copy of the
+ * first @len bytes starting at address @init. If @init is NULL, or
+ * @len is 0 (or less), then the initialised Filament will return the
+ * empty string, "", if its value is queried.
+ *
+ * Return value:
+ * A newly created Filament object is returned.
+ **/
+Filament *
+filnew (const char *const init, size_t len)
+{
+ Filament *new;
+
+ new = snv_new (Filament, 1);
+
+ new->value = new->buffer;
+ new->length = 0;
+ new->size = FILAMENT_BUFSIZ;
+
+ return (init || len) ? filinit (new, init, len) : new;
+}
+
+/**
+ * filinit:
+ * @fil: The Filament object to initialise.
+ * @init: address of the first byte to copy into the new object.
+ * @len: the number of bytes to copy into the new object.
+ *
+ * Initialise a Filament object to hold a copy of the first @len bytes
+ * starting at address @init. If @init is NULL, or @len is 0 (or less),
+ * then the Filament will be reset to hold the empty string, "".
+ *
+ * Return value:
+ * The initialised Filament object is returned.
+ **/
+Filament *
+filinit (Filament *fil, const char *const init, size_t len)
+{
+ if (init == NULL || len < 1)
+ {
+ /* Recycle any dynamic memory assigned to the previous
+ contents of @fil, and point back into the static buffer. */
+ if (fil->value != fil->buffer)
+ snv_delete (fil->value);
+
+ fil->value = fil->buffer;
+ fil->length = 0;
+ fil->size = FILAMENT_BUFSIZ;
+ }
+ else
+ {
+ if (len < FILAMENT_BUFSIZ)
+ {
+ /* We have initialisation data which will easily fit into
+ the static buffer: recycle any memory already assigned
+ and initialise in the static buffer. */
+ if (fil->value != fil->buffer)
+ {
+ snv_delete (fil->value);
+ fil->value = fil->buffer;
+ fil->size = FILAMENT_BUFSIZ;
+ }
+ }
+ else
+ {
+ /* If we get to here then we never try to shrink the already
+ allocated dynamic buffer (if any), we just leave it in
+ place all ready to expand into later... */
+ fil_maybe_extend (fil, len, false);
+ }
+
+ snv_assert (len < fil->size);
+
+ fil->length = len;
+ memcpy (fil->value, init, len);
+ }
+
+ return fil;
+}
+
+/**
+ * fildelete: destructor
+ * @fil: The Filament object for recycling.
+ *
+ * The memory being used by @fil is recycled.
+ *
+ * Return value:
+ * The original contents of @fil are converted to a null terminated
+ * string which is returned, either to be freed itself or else used
+ * as a normal C string. The entire Filament contents are copied into
+ * this string including any embedded nulls.
+ **/
+char *
+fildelete (Filament *fil)
+{
+ char *value;
+
+ if (fil->value == fil->buffer)
+ {
+ value = memcpy (snv_new (char, 1 + fil->length),
+ fil->buffer, 1 + fil->length);
+ value[fil->length] = '\0';
+ }
+ else
+ value = filval (fil);
+
+ snv_delete (fil);
+
+ return value;
+}
+
+/**
+ * _fil_extend:
+ * @fil: The Filament object which may need more string space.
+ * @len: The length of the data to be stored in @fil.
+ * @copy: whether to copy data from the static buffer on reallocation.
+ *
+ * This function will will assign a bigger block of memory to @fil
+ * considering the space left in @fil and @len, the length required
+ * for the prospective contents.
+ */
+void
+_fil_extend (Filament *fil, size_t len, bool copy)
+{
+ /* Usually we will simply double the amount of space previously
+ allocated, but if the extra data is larger than the current
+ size it *still* won't fit, so in that case we allocate enough
+ room plus some we leave the current free space to expand into. */
+ fil->size += MAX (len, fil->size);
+
+ if (fil->value == fil->buffer)
+ {
+ fil->value = snv_new (char, fil->size);
+ if (copy)
+ memcpy (fil->value, fil->buffer, fil->length);
+ }
+ else
+ fil->value = snv_renew (char, fil->value, fil->size);
+}
+
+/*
+ * Local Variables:
+ * mode: C
+ * c-file-style: "gnu"
+ * indent-tabs-mode: nil
+ * End:
+ * end of snprintfv/filament.c */