Newsgroup: comp.lang.c


Date: Wed, 26 Apr 2006 09:36:32 +0300
From: Diomidis Spinellis <dds@aueb.gr>
Organization: Athens University of Economics and Business
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.2) Gecko/20060404 SeaMonkey/1.0.1
MIME-Version: 1.0
Newsgroups: comp.lang.c
Subject: Re: writing alibrary funtion ssprintf()
References: <1146028371.868585.203760@j33g2000cwa.googlegroups.com>
In-Reply-To: <1146028371.868585.203760@j33g2000cwa.googlegroups.com>
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 7bit
jitu.csewizard@gmail.com wrote:
> question:
> function ssprintf(<buffername>,<format string>, .....)
> i need to write a funtion in time and space optimized way so that i
> should allocate buffername after getting the string.(ignore the time of
> mallocing the memory...there is a different strategy for mallocing...it
> won't take much time)
> i have thought of a strategy:
> 1. writing the format string to /dev/null or to memory and get the
> length of the string and then again malloc(not exactly malloc ....there
> is a different optimized strategy) the required string.
> problem: a call to printf takes time if the string is long
> note:calling string functions like strlen also takes time.
> can you suggest me a better optimized strategy.

A portable approach would involve allocating a buffer with a default 
large size, and calling snprintf and (your) realloc on it.  Snprintf 
will return the size of the buffer required for printing the whole 
string, so you can always adjust the default size upwards to match that 
number, and keep it that way in subsequent calls.  For the first few 
calls your implementation will call snprintf multiple times to establish 
the size needed, but this overhead will be amortized over time.

One other more cumbersome approach is to take an open-source sprintf 
implementation and modify it to make it work in the way you want.  You 
will find that most printf implementations keep track of the size of the 
resulting string, in order to implement functions like snprintf.  For 
example, the FreeBSD/NetBSD implementation keeps the distinct elements 
to be printed in an iov vector of output objects, and tallies their 
length.  This is done by a call to a PRINT macro for each distinct element.

#define PRINT(ptr, len) { \
         iovp->iov_base = (ptr); \
         iovp->iov_len = (len); \
         uio.uio_resid += (len); \
         iovp++; \
         if (++uio.uio_iovcnt >= NIOV) { \
                 if (__sprint(fp, &uio)) \
                         goto error; \
                 iovp = iov; \
         } \
}

You can thus take uio.uio_resid and use it to allocate the string's 
buffer, before flushing the buffer to it.  For an example on how this is 
done, look at the FreeBSD asprintf implementation, which allocates the 
buffer memory with malloc.

A different (non-portable) approach is to create an stdio object that 
will write to a dynamically growing buffer.  Nowadays many stdio 
implementations are object-oriented, and provide you a (non-portable and 
sometimes undocumented) way to supply your own underlying I/O functions. 
  For example, the FreeBSD implementation's fwopen function takes as an 
argument a pointer to a write function.

FILE *fwopen(void *cookie, int (*writefn)(void *, const char *, int));

I assume that your optimized malloc implementation can grow objects 
dynamically.  If not, look at the GNU Obstack facility 
<http://gcc.gnu.org/onlinedocs/libiberty/Growing-Objects.html#Growing-Objects>.

Also, keep in mind that I/O processing is seldom a program's bottleneck: 
the underlying I/O operations (for example disk writes) are orders of 
magnitude more expensive.  Therefore be sure to profile your program to 
establish the need for implementing the more exotic solutions I described.

-- 
Diomidis Spinellis
Code Quality: The Open Source Perspective (Addison-Wesley 2006)
http://www.spinellis.gr/codequality?clc



Newsgroup comp.lang.c contents
Newsgroup list
Diomidis Spinellis home page

Creative Commons License Unless otherwise expressly stated, all original material on this page created by Diomidis Spinellis is licensed under a Creative Commons Attribution-Share Alike 3.0 Greece License.