mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-22 16:33:29 +00:00
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:
parent
543b3df634
commit
c6df2e3852
8 changed files with 677 additions and 761 deletions
16
ChangeLog
16
ChangeLog
|
@ -1,3 +1,19 @@
|
|||
2004-05-14 Richard Frith-Macdonald <rfm@gnu.org>
|
||||
|
||||
* Source/GSFormat.h:
|
||||
* Source/GSFormat.m:
|
||||
* Source/GSPrivate.h:
|
||||
* Source/GSString.m:
|
||||
* Source/GSeq.h:
|
||||
* Source/NSString.m:
|
||||
Updates to allow GSFormat to write directly into an existing string
|
||||
in either 16bit or 8bit format. Also tidyups and modifications to
|
||||
use temporary buffers on stack rather than malloc/free as long as
|
||||
the buffers are reasonably sized.
|
||||
* Source/benchmark.m: Trivial string format benchmarks ...
|
||||
Performance improvement of 10% for initWithFormat and 90% for
|
||||
appendFormat operations.
|
||||
|
||||
2004-05-13 Richard Frith-Macdonald <rfm@gnu.org>
|
||||
|
||||
* Source/NSCalendarDate.m: ([initWithString:calendarFormat:locale:])
|
||||
|
|
|
@ -26,18 +26,12 @@
|
|||
#define __GSFormat_H_
|
||||
|
||||
#include <Foundation/NSZone.h>
|
||||
#include "GSPrivate.h"
|
||||
|
||||
@class NSDictionary;
|
||||
|
||||
typedef struct {
|
||||
unichar *buf;
|
||||
size_t len;
|
||||
size_t size;
|
||||
NSZone *z;
|
||||
} FormatBuf_t;
|
||||
|
||||
void
|
||||
GSFormat(FormatBuf_t *fb, const unichar *fmt, va_list ap, NSDictionary *loc);
|
||||
GSFormat(GSStr fb, const unichar *fmt, va_list ap, NSDictionary *loc);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -127,7 +127,7 @@ struct printf_info
|
|||
};
|
||||
|
||||
/* Type of a printf specifier-handler function.
|
||||
STREAM is the FormatBuf_t on which to write output.
|
||||
STREAM is the GSStr on which to write output.
|
||||
INFO gives information about the format specification.
|
||||
ARGS is a vector of pointers to the argument data;
|
||||
the number of pointers will be the number returned
|
||||
|
@ -733,32 +733,8 @@ parse_one_spec (const unichar *format, size_t posn, struct printf_spec *spec,
|
|||
}
|
||||
|
||||
|
||||
#define outchar(Ch) \
|
||||
do \
|
||||
{ \
|
||||
register const wint_t outc = (Ch); \
|
||||
if (s->len+1 >= s->size) { \
|
||||
s->size += s->size / 2; \
|
||||
s->buf = NSZoneRealloc(s->z, s->buf, s->size*sizeof(s->buf[0])); \
|
||||
} \
|
||||
s->buf[s->len++] = outc; \
|
||||
++done; \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#define outstring(String, Len) \
|
||||
do \
|
||||
{ \
|
||||
unsigned i; \
|
||||
\
|
||||
if (s->len+((unsigned)(Len)) >= s->size) { \
|
||||
s->size += s->size/2 > ((unsigned)(Len))? s->size/2: (unsigned)(Len); \
|
||||
s->buf = NSZoneRealloc(s->z, s->buf, s->size*sizeof(s->buf[0])); \
|
||||
} \
|
||||
for (i=0; i < ((unsigned)(Len)); i++) s->buf[s->len++] = (String)[i]; \
|
||||
done += (unsigned)(Len); \
|
||||
} \
|
||||
while (0)
|
||||
#define outchar(Ch) GSStrAppendUnichar(s, Ch)
|
||||
#define outstring(String, Len) GSStrAppendUnichars(s, String, Len)
|
||||
|
||||
/* For handling long_double and longlong we use the same flag. If
|
||||
`long' and `long long' are effectively the same type define it to
|
||||
|
@ -787,7 +763,7 @@ static const unichar null[] = {'(','n','u','l','l',')','\0'};
|
|||
|
||||
|
||||
/* Handle unknown format specifier. */
|
||||
static int printf_unknown (FormatBuf_t *, const struct printf_info *,
|
||||
static int printf_unknown (GSStr, const struct printf_info *,
|
||||
const void *const *);
|
||||
|
||||
/* Group digits of number string. */
|
||||
|
@ -796,7 +772,7 @@ static unichar *group_number (unichar *, unichar *, const char *, NSString *);
|
|||
|
||||
/* The function itself. */
|
||||
void
|
||||
GSFormat (FormatBuf_t *s, const unichar *format, va_list ap,
|
||||
GSFormat (GSStr s, const unichar *format, va_list ap,
|
||||
NSDictionary *locale)
|
||||
{
|
||||
/* The character used as thousands separator. */
|
||||
|
@ -866,12 +842,6 @@ NSDictionary *locale)
|
|||
#define CHAR_CLASS(Ch) (jump_table[(wint_t) (Ch) - ' '])
|
||||
# define JUMP_TABLE_TYPE const void *const
|
||||
|
||||
if (s->size == 0)
|
||||
{
|
||||
s->buf = NSZoneMalloc(s->z, 100*sizeof(unichar));
|
||||
s->size = 100;
|
||||
}
|
||||
|
||||
/* Initialize local variables. */
|
||||
done = 0;
|
||||
grouping = (const char *) -1;
|
||||
|
@ -1768,7 +1738,7 @@ NSDictionary *locale)
|
|||
/* Allocate dynamically an array which definitely is long
|
||||
enough for the wide character version. */
|
||||
if (len < 8192
|
||||
|| ((string = (unichar *) NSZoneMalloc(s->z, len * sizeof (unichar)))
|
||||
|| ((string = (unichar *) NSZoneMalloc(s->_zone, len * sizeof (unichar)))
|
||||
== NULL))
|
||||
string = (unichar *) alloca (len * sizeof (unichar));
|
||||
else
|
||||
|
@ -1795,7 +1765,7 @@ NSDictionary *locale)
|
|||
/* Allocate dynamically an array which definitely is long
|
||||
enough for the wide character version. */
|
||||
if (len < 8192
|
||||
|| ((string = (unichar *) NSZoneMalloc(s->z, len * sizeof (unichar)))
|
||||
|| ((string = (unichar *) NSZoneMalloc(s->_zone, len * sizeof (unichar)))
|
||||
== NULL))
|
||||
string = (unichar *) alloca (len * sizeof (unichar));
|
||||
else
|
||||
|
@ -1819,7 +1789,7 @@ NSDictionary *locale)
|
|||
PAD (' ');
|
||||
}
|
||||
if (string_malloced)
|
||||
NSZoneFree(s->z, string);
|
||||
NSZoneFree(s->_zone, string);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1851,7 +1821,7 @@ NSDictionary *locale)
|
|||
/* Allocate dynamically an array which definitely is long
|
||||
enough for the wide character version. */
|
||||
if (len < 8192
|
||||
|| ((string = (unichar *) NSZoneMalloc(s->z, len * sizeof (unichar)))
|
||||
|| ((string = (unichar *) NSZoneMalloc(s->_zone, len * sizeof (unichar)))
|
||||
== NULL))
|
||||
string = (unichar *) alloca (len * sizeof (unichar));
|
||||
else
|
||||
|
@ -1875,7 +1845,7 @@ NSDictionary *locale)
|
|||
PAD (' ');
|
||||
}
|
||||
if (string_malloced)
|
||||
NSZoneFree(s->z, string);
|
||||
NSZoneFree(s->_zone, string);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1919,7 +1889,7 @@ all_done:
|
|||
/* Handle an unknown format specifier. This prints out a canonicalized
|
||||
representation of the format spec itself. */
|
||||
static int
|
||||
printf_unknown (FormatBuf_t *s, const struct printf_info *info,
|
||||
printf_unknown (GSStr s, const struct printf_info *info,
|
||||
const void *const *args)
|
||||
|
||||
{
|
||||
|
|
|
@ -106,6 +106,42 @@ GS_EXPORT BOOL GSIsByteEncoding(NSStringEncoding encoding);
|
|||
}
|
||||
@end
|
||||
|
||||
/*
|
||||
* GSMutableString - concrete mutable string, capable of changing its storage
|
||||
* from holding 8-bit to 16-bit character set.
|
||||
*/
|
||||
@interface GSMutableString : NSMutableString
|
||||
{
|
||||
union {
|
||||
unichar *u;
|
||||
unsigned char *c;
|
||||
} _contents;
|
||||
unsigned int _count;
|
||||
struct {
|
||||
unsigned int wide: 1;
|
||||
unsigned int free: 1;
|
||||
unsigned int unused: 2;
|
||||
unsigned int hash: 28;
|
||||
} _flags;
|
||||
NSZone *_zone;
|
||||
unsigned int _capacity;
|
||||
}
|
||||
@end
|
||||
|
||||
/*
|
||||
* Typedef for access to internals of concrete string objects.
|
||||
*/
|
||||
typedef struct {
|
||||
@defs(GSMutableString)
|
||||
} GSStr_t;
|
||||
typedef GSStr_t *GSStr;
|
||||
|
||||
/*
|
||||
* Functions to append to GSStr
|
||||
*/
|
||||
extern void GSStrAppendUnichar(GSStr s, unichar);
|
||||
extern void GSStrAppendUnichars(GSStr s, const unichar *u, unsigned l);
|
||||
|
||||
/*
|
||||
* Enumeration for MacOS-X compatibility user defaults settings.
|
||||
* For efficiency, we save defaults information which is used by the
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -290,14 +290,14 @@ static inline void GSeq_uppercase(GSeq seq)
|
|||
* Set up macros for dealing with 'self' on the basis of GSQ_S
|
||||
*/
|
||||
#if GSEQ_S == GSEQ_US
|
||||
#define GSEQ_ST ivars
|
||||
#define GSEQ_ST GSStr
|
||||
#define GSEQ_SLEN s->_count
|
||||
#define GSEQ_SGETC(I) s->_contents.u[I]
|
||||
#define GSEQ_SGETR(B,R) memcpy(B, &s->_contents.u[R.location], 2*(R).length)
|
||||
#define GSEQ_SRANGE(I) (*srImp)((id)s, ranSel, I)
|
||||
#else
|
||||
#if GSEQ_S == GSEQ_CS
|
||||
#define GSEQ_ST ivars
|
||||
#define GSEQ_ST GSStr
|
||||
#define GSEQ_SLEN s->_count
|
||||
#define GSEQ_SGETC(I) (unichar)s->_contents.c[I]
|
||||
#define GSEQ_SGETR(B,R) ( { \
|
||||
|
@ -322,14 +322,14 @@ static inline void GSeq_uppercase(GSeq seq)
|
|||
* Set up macros for dealing with 'other' string on the basis of GSQ_O
|
||||
*/
|
||||
#if GSEQ_O == GSEQ_US
|
||||
#define GSEQ_OT ivars
|
||||
#define GSEQ_OT GSStr
|
||||
#define GSEQ_OLEN o->_count
|
||||
#define GSEQ_OGETC(I) o->_contents.u[I]
|
||||
#define GSEQ_OGETR(B,R) memcpy(B, &o->_contents.u[R.location], 2*(R).length)
|
||||
#define GSEQ_ORANGE(I) (*orImp)((id)o, ranSel, I)
|
||||
#else
|
||||
#if GSEQ_O == GSEQ_CS
|
||||
#define GSEQ_OT ivars
|
||||
#define GSEQ_OT GSStr
|
||||
#define GSEQ_OLEN o->_count
|
||||
#define GSEQ_OGETC(I) (unichar)o->_contents.c[I]
|
||||
#define GSEQ_OGETR(B,R) ( { \
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -371,6 +371,7 @@ bench_str()
|
|||
{
|
||||
int i;
|
||||
NSString *str;
|
||||
NSMutableString *ms;
|
||||
id plist;
|
||||
NSString *plstr;
|
||||
Class arc = [NSArchiver class];
|
||||
|
@ -408,6 +409,25 @@ bench_str()
|
|||
plstr = [plist description];
|
||||
|
||||
printf("NSString\n");
|
||||
|
||||
START_TIMER;
|
||||
for (i = 0; i < MAX_COUNT; i++)
|
||||
{
|
||||
str = [[stringClass alloc] initWithFormat: @"Hello %d", i];
|
||||
RELEASE(str);
|
||||
}
|
||||
END_TIMER;
|
||||
PRINT_TIMER("NSString (1 initWithFormat:) \t");
|
||||
|
||||
ms = [NSMutableString stringWithCapacity: 0];
|
||||
START_TIMER;
|
||||
for (i = 0; i < MAX_COUNT; i++)
|
||||
{
|
||||
[ms appendFormat: @"%d", i];
|
||||
}
|
||||
END_TIMER;
|
||||
PRINT_TIMER("NSString (1 appendFormat:) \t\t");
|
||||
|
||||
START_TIMER;
|
||||
for (i = 0; i < MAX_COUNT; i++)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue