1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
|
/* -*- 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, see <http://www.gnu.org/licenses>.
*
* 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 */
|