RE: [PHP-DEV] A better strlcat()
| From: | Preston L. Bannister | Date: | Fri, 03 May 2002 00:58:08 +0000 |
| Subject: | RE: [PHP-DEV] A better strlcat() | ||
| References: | 1 | Groups: | php.dev |
| Request: | Send a blank email to php-dev+get-83630@lists.php.net to get a copy of this message | ||
Heh - there's a question :).
Looking at the two implementations, which do you think you better
understand (with complete certainty), and which would you be willing
to (say) bet your life on?
Personally I'm *almost* certain I know what the first does :).
I would not want to bet it is bug-free.
I'm sure the shorter implementation matches the *documented*
strlcat() function. The fact it runs faster is likely an artifact
of compiler optimizations specific to string functions.
I'll send this off to the OpenBSD folks just for grins.
From: Andi Gutmans [mailto:andi@zend.com]
> Are you sure it behaves *exactly* like the existing one?
> I stole the implementation from OpenBSD so it might be a good idea for you
> to send it to them.
> They are the best to shed light on this and see if it's exactly the same
> and faster.
>
> Andi
>
> At 09:00 02/05/2002 -0700, Preston L. Bannister wrote:
> >Was looking at the strlcat() implementation in the PHP source
> >and saw that it does not take advantage of history :).
> >
> >Look at xxx_strlcat() in the following example.
> >
> >You are almost always better off using the compiler's built-in
> >string functions, due to the optimizations built-in (many years ago)
> >to improve Dhrystone benchmark scores. The benchmark turned out to
> >be a bit bogus, but the improvements made to the string functions
> >are a benefit to us all :).
> >
> >This is more readable (and thus more likely correct), slightly faster
> >(about 4% in my testing), and in the debug build better checked.
> >
> >Full comparison benchmark program:
> >-----
> >#include <stdlib.h>
> >#include <stdio.h>
> >#include <string.h>
> >#include <time.h>
> >#include <assert.h>
> >
> >/*
> > * Appends src to string dst of size siz (unlike strncat, siz is the
> > * full size of dst, not space left). At most siz-1 characters
> > * will be copied. Always NUL terminates (unless siz == 0).
> > * Returns strlen(src); if retval >= siz, truncation occurred.
> > */
> >size_t php_strlcat(char* dst,const char* src,size_t siz)
> >{
> > register char *d = dst;
> > register const char *s = src;
> > register size_t n = siz;
> > size_t dlen;
> >
> > /* Find the end of dst and adjust bytes left but don't go past end */
> > while (*d != '\0' && n-- != 0)
> > d++;
> > dlen = d - dst;
> > n = siz - dlen;
> >
> > if (n == 0)
> > return(dlen + strlen(s));
> > while (*s != '\0') {
> > if (n != 1) {
> > *d++ = *s;
> > n--;
> > }
> > s++;
> > }
> > *d = '\0';
> >
> > return(dlen + (s - src)); /* count does not include NUL */
> >}
> >
> >/*
> > * Take full advantage of any compiler/library optimizations built in
> > * to the standard string routines. Look up the history of compiler
> > * comparisons with Dhrystone benchmark to understand why this wins.
> > * Pre-Internet history - check Google's USENET archive.
> > */
> >size_t xxx_strlcat(char* s1,const char* s2,size_t nMax)
> >{
> > size_t n1 = strlen(s1);
> > size_t n2 = strlen(s2);
> > size_t nCopy = (nMax <= (n1+n2)) ? (nMax - n1 - 1) : n2;
> >
> > /* Better to leave this out and require a valid string return */
> > if (!nMax) return 0;
> >
> > assert(n1 < nMax); /* Catch pre-existing oversize strings */
> > assert(0 < nMax); /* Insure return is always a valid string */
> >
> > memcpy(s1+n1,s2,nCopy);
> > s1[n1+nCopy] = 0;
> > return n1+n2; /* as documented for strlcat() */
> > /*return n1+nCopy; /* actual number of characters */
> >}
> >
> >/*
> > *
> > */
> >int main(int ac, char** av)
> >{
> > clock_t t1,dt;
> >#ifdef WIN32
> > __int64 nTotal;
> >#else
> > long long nTotal;
> >#endif
> > char s1[85];
> > static char s2[] = "1234567890";
> > int nLoops = 10;
> >
> > if (1 < ac) {
> > nLoops = atoi(av[1]);
> > }
> >
> > printf("Compare two strlcat() implementations for %d
> > iterations.\n",nLoops);
> >
> > t1 = clock();
> > nTotal = 0;
> > for (int i=0; i<nLoops; ++i) {
> > s1[0] = 0;
> > nTotal += php_strlcat(s1,s2,sizeof(s1));
> > nTotal += php_strlcat(s1,s2,sizeof(s1));
> > nTotal += php_strlcat(s1,s2,sizeof(s1));
> > nTotal += php_strlcat(s1,s2,sizeof(s1));
> > nTotal += php_strlcat(s1,s2,sizeof(s1));
> > nTotal += php_strlcat(s1,s2,sizeof(s1));
> > nTotal += php_strlcat(s1,s2,sizeof(s1));
> > nTotal += php_strlcat(s1,s2,sizeof(s1));
> > nTotal += php_strlcat(s1,s2,sizeof(s1));
> > nTotal += php_strlcat(s1,s2,sizeof(s1));
> > }
> > dt = clock() - t1;
> >
> > printf("php_strlcat() copied %1.0f characters in %ld
> > ticks\n",(double)nTotal,(long)dt);
> >
> > t1 = clock();
> > nTotal = 0;
> > for (i=0; i<nLoops; ++i) {
> > s1[0] = 0;
> > nTotal += xxx_strlcat(s1,s2,sizeof(s1));
> > nTotal += xxx_strlcat(s1,s2,sizeof(s1));
> > nTotal += xxx_strlcat(s1,s2,sizeof(s1));
> > nTotal += xxx_strlcat(s1,s2,sizeof(s1));
> > nTotal += xxx_strlcat(s1,s2,sizeof(s1));
> > nTotal += xxx_strlcat(s1,s2,sizeof(s1));
> > nTotal += xxx_strlcat(s1,s2,sizeof(s1));
> > nTotal += xxx_strlcat(s1,s2,sizeof(s1));
> > nTotal += xxx_strlcat(s1,s2,sizeof(s1));
> > nTotal += xxx_strlcat(s1,s2,sizeof(s1));
> > }
> > dt = clock() - t1;
> >
> > printf("xxx_strlcat() copied %1.0f characters in %ld
> > ticks\n",(double)nTotal,(long)dt);
> >
> > return 0;
> >}
> >-----