Restructuring and various optimisations to drastically improve appendFormat:

performance.


git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@19307 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
CaS 2004-05-14 10:52:30 +00:00
parent 80741506f0
commit 35b7fbf96e
8 changed files with 677 additions and 761 deletions

View file

@ -69,6 +69,7 @@
// For private method _decodePropertyListForKey:
#include "Foundation/NSKeyedArchiver.h"
#include "GNUstepBase/GSMime.h"
#include "GSPrivate.h"
#include "GSFormat.h"
#include <limits.h>
#include <sys/stat.h>
@ -1153,321 +1154,70 @@ handle_printf_atsign (FILE *stream,
locale: (NSDictionary*)locale
arguments: (va_list)argList
{
FormatBuf_t f;
unichar *fmt;
unsigned char buf[2048];
GSStr_t f;
unichar fbuf[1024];
unichar *fmt = fbuf;
size_t len;
/*
* First we provide an array of unichar characters containing the
* format string. For performance reasons we try to use an on-stack
* buffer if the format string is small enough ... it almost always
* will be.
*/
len = [format length];
fmt = objc_malloc((len+1)*sizeof(unichar));
if (len >= 1024)
{
fmt = objc_malloc((len+1)*sizeof(unichar));
}
[format getCharacters: fmt];
fmt[len] = '\0';
f.z = NSDefaultMallocZone();
f.buf = NSZoneMalloc(f.z, 100*sizeof(unichar));
f.len = 0;
f.size = 100;
/*
* Now set up 'f' as a GSMutableString object whose initial buffer is
* allocated on the stack. The GSFormat function can write into it.
*/
f.isa = GSMutableStringClass;
f._zone = NSDefaultMallocZone();
f._contents.c = buf;
f._capacity = sizeof(buf);
f._count = 0;
f._flags.wide = 0;
f._flags.free = 0;
GSFormat(&f, fmt, argList, locale);
objc_free(fmt);
// don't use noCopy because f.size > f.len!
self = [self initWithCharacters: f.buf length: f.len];
NSZoneFree(f.z, f.buf);
return self;
}
#if 0
/* xxx Change this when we have non-CString classes */
- (id) initWithFormat: (NSString*)format
locale: (NSDictionary*)locale
arguments: (va_list)argList
{
#if defined(HAVE_VSPRINTF) || defined(HAVE_VASPRINTF)
const char *format_cp = [format lossyCString];
int format_len = strlen (format_cp);
#ifdef HAVE_VASPRINTF
char *buf;
int printed_len = 0;
NSString *ret;
#ifndef HAVE_REGISTER_PRINTF_FUNCTION
NSZone *z = GSObjCZone(self);
/* If the available libc doesn't have `register_printf_function()', then
the `%@' printf directive isn't available with printf() and friends.
Here we make a feable attempt to handle it. */
{
/* We need a local copy since we change it. (Changing and undoing
the change doesn't work because some format strings are constant
strings, placed in a non-writable section of the executable, and
writing to them will cause a segfault.) */
char format_cp_copy[format_len+1];
char *atsign_pos; /* points to a location inside format_cp_copy */
char *format_to_go = format_cp_copy;
char *buf_l;
#define _PRINTF_BUF_LEN 256
int printed_local_len, avail_len = _PRINTF_BUF_LEN;
int cstring_len;
buf = NSZoneMalloc(z, _PRINTF_BUF_LEN);
strcpy (format_cp_copy, format_cp);
/* Loop once for each `%@' in the format string. */
while ((atsign_pos = strstr (format_to_go, "%@")))
{
const char *cstring;
char *formatter_pos; // Position for formatter.
/* If there is a "%%@", then do the right thing: print it literally. */
if ((*(atsign_pos-1) == '%')
&& atsign_pos != format_cp_copy)
continue;
/* Temporarily terminate the string before the `%@'. */
*atsign_pos = '\0';
/* Print the part before the '%@' */
printed_local_len = VASPRINTF_LENGTH (vasprintf (&buf_l,
format_to_go, argList));
if(buf_l)
{
if(avail_len < printed_local_len+1)
{
NS_DURING
{
buf = NSZoneRealloc(z, buf,
printed_len+printed_local_len+_PRINTF_BUF_LEN);
avail_len += _PRINTF_BUF_LEN;
}
NS_HANDLER
{
free(buf_l);
[localException raise];
}
NS_ENDHANDLER
}
memcpy(&buf[printed_len], buf_l, printed_local_len+1);
avail_len -= printed_local_len;
printed_len += printed_local_len;
free(buf_l);
}
else
{
[NSException raise: NSMallocException
format: @"No available memory"];
}
/* Skip arguments used in last vsprintf(). */
while ((formatter_pos = strchr(format_to_go, '%')))
{
char *spec_pos; // Position of conversion specifier.
if (*(formatter_pos+1) == '%')
{
format_to_go = formatter_pos+2;
continue;
}
spec_pos = strpbrk(formatter_pos+1, "dioxXucsfeEgGpn\0");
switch (*spec_pos)
{
#ifndef powerpc
/* FIXME: vsprintf on powerpc apparently advances the arg list
so this doesn't need to be done. Make a more general check
for this */
case 'd': case 'i': case 'o':
case 'x': case 'X': case 'u': case 'c':
va_arg(argList, int);
break;
case 's':
if (*(spec_pos - 1) == '*')
va_arg(argList, int*);
va_arg(argList, char*);
break;
case 'f': case 'e': case 'E': case 'g': case 'G':
va_arg(argList, double);
break;
case 'p':
va_arg(argList, void*);
break;
case 'n':
va_arg(argList, int*);
break;
#endif /* NOT powerpc */
case '\0':
spec_pos--;
break;
}
format_to_go = spec_pos+1;
}
/* Get a C-string (char*) from the String object, and print it. */
cstring = [[(id) va_arg (argList, id) description] lossyCString];
if (!cstring)
cstring = "<null string>";
cstring_len = strlen(cstring);
if(cstring_len)
{
if(avail_len < cstring_len+1)
{
buf = NSZoneRealloc(z, buf,
printed_len+cstring_len+_PRINTF_BUF_LEN);
avail_len += _PRINTF_BUF_LEN;
}
memcpy(&buf[printed_len], cstring, cstring_len+1);
avail_len -= cstring_len;
printed_len += cstring_len;
}
/* Skip over this `%@', and look for another one. */
format_to_go = atsign_pos + 2;
}
/* Print the rest of the string after the last `%@'. */
printed_local_len = VASPRINTF_LENGTH (vasprintf (&buf_l,
format_to_go, argList));
if(buf_l)
{
if(avail_len < printed_local_len+1)
{
NS_DURING
{
buf = NSZoneRealloc(z, buf,
printed_len+printed_local_len+_PRINTF_BUF_LEN);
avail_len += _PRINTF_BUF_LEN;
}
NS_HANDLER
{
free(buf_l);
[localException raise];
}
NS_ENDHANDLER
}
memcpy(&buf[printed_len], buf_l, printed_local_len+1);
avail_len -= printed_local_len;
printed_len += printed_local_len;
free(buf_l);
}
else
{
[NSException raise: NSMallocException
format: @"No available memory"];
}
}
#else /* HAVE_VSPRINTF */
/* The available libc has `register_printf_function()', so the `%@'
printf directive is handled by printf and friends. */
printed_len = VASPRINTF_LENGTH (vasprintf (&buf, format_cp, argList));
if(!buf)
if (fmt != fbuf)
{
[NSException raise: NSMallocException
format: @"No available memory"];
objc_free(fmt);
}
#endif /* !HAVE_REGISTER_PRINTF_FUNCTION */
ret = [self initWithCString: buf];
#ifndef HAVE_REGISTER_PRINTF_FUNCTION
NSZoneFree(z, buf);
#else
free(buf);
#endif
return ret;
#else
/* xxx horrible disgusting BUFFER_EXTRA arbitrary limit; fix this! */
#define BUFFER_EXTRA 1024*500
char buf[format_len + BUFFER_EXTRA];
int printed_len = 0;
/*
* Don't use noCopy because f._contents.u may be memory on the stack,
* and even if it wasn't f._capacity may be greater than f._count so
* we could be wasting quite a bit of space. Better to accept a
* performance hit due to copying data (and allocating/deallocating
* the temporary buffer) for large strings. For most strings, the
* on-stack memory will have been used, so we will get better performance.
*/
if (f._flags.wide == 1)
{
self = [self initWithCharacters: f._contents.u length: f._count];
}
else
{
self = [self initWithCString: f._contents.c length: f._count];
}
#ifndef HAVE_REGISTER_PRINTF_FUNCTION
/* If the available libc doesn't have `register_printf_function()', then
the `%@' printf directive isn't available with printf() and friends.
Here we make a feable attempt to handle it. */
{
/* We need a local copy since we change it. (Changing and undoing
the change doesn't work because some format strings are constant
strings, placed in a non-writable section of the executable, and
writing to them will cause a segfault.) */
char format_cp_copy[format_len+1];
char *atsign_pos; /* points to a location inside format_cp_copy */
char *format_to_go = format_cp_copy;
strcpy (format_cp_copy, format_cp);
/* Loop once for each `%@' in the format string. */
while ((atsign_pos = strstr (format_to_go, "%@")))
{
const char *cstring;
char *formatter_pos; // Position for formatter.
/* If there is a "%%@", then do the right thing: print it literally. */
if ((*(atsign_pos-1) == '%')
&& atsign_pos != format_cp_copy)
continue;
/* Temporarily terminate the string before the `%@'. */
*atsign_pos = '\0';
/* Print the part before the '%@' */
printed_len += VSPRINTF_LENGTH (vsprintf (buf+printed_len,
format_to_go, argList));
/* Skip arguments used in last vsprintf(). */
while ((formatter_pos = strchr(format_to_go, '%')))
{
char *spec_pos; // Position of conversion specifier.
if (*(formatter_pos+1) == '%')
{
format_to_go = formatter_pos+2;
continue;
}
spec_pos = strpbrk(formatter_pos+1, "dioxXucsfeEgGpn\0");
switch (*spec_pos)
{
#ifndef powerpc
/* FIXME: vsprintf on powerpc apparently advances the arg list
so this doesn't need to be done. Make a more general check
for this */
case 'd': case 'i': case 'o':
case 'x': case 'X': case 'u': case 'c':
(void)va_arg(argList, int);
break;
case 's':
if (*(spec_pos - 1) == '*')
(void)va_arg(argList, int*);
(void)va_arg(argList, char*);
break;
case 'f': case 'e': case 'E': case 'g': case 'G':
(void)va_arg(argList, double);
break;
case 'p':
(void)va_arg(argList, void*);
break;
case 'n':
(void)va_arg(argList, int*);
break;
#endif /* NOT powerpc */
case '\0':
spec_pos--;
break;
}
format_to_go = spec_pos+1;
}
/* Get a C-string (char*) from the String object, and print it. */
cstring = [[(id) va_arg (argList, id) description] lossyCString];
if (!cstring)
cstring = "<null string>";
strcat (buf+printed_len, cstring);
printed_len += strlen (cstring);
/* Skip over this `%@', and look for another one. */
format_to_go = atsign_pos + 2;
}
/* Print the rest of the string after the last `%@'. */
printed_len += VSPRINTF_LENGTH (vsprintf (buf+printed_len,
format_to_go, argList));
}
#else
/* The available libc has `register_printf_function()', so the `%@'
printf directive is handled by printf and friends. */
printed_len = VSPRINTF_LENGTH (vsprintf (buf, format_cp, argList));
#endif /* !HAVE_REGISTER_PRINTF_FUNCTION */
/* Raise an exception if we overran our buffer. */
NSParameterAssert (printed_len < format_len + BUFFER_EXTRA - 1);
return [self initWithCString: buf];
#endif /* HAVE_VASPRINTF */
#else /* HAVE_VSPRINTF || HAVE_VASPRINTF */
[self notImplemented: _cmd];
/*
* If the string had to grow beyond the initial buffer size, we must
* release any allocated memory.
*/
if (f._flags.free == 1)
{
NSZoneFree(f._zone, f._contents.c);
}
return self;
#endif
}
#endif
/**
* Initialises the receiver with the supplied data, using the