Closed
Bug 209499
Opened 22 years ago
Closed 21 years ago
convert PL_str* impls to use ANSI C equivalents
Categories
(NSPR :: NSPR, defect)
NSPR
NSPR
Tracking
(Not tracked)
RESOLVED
FIXED
4.6
People
(Reporter: aaronl, Assigned: dwitte)
Details
(Keywords: perf)
Attachments
(3 files, 6 obsolete files)
7.16 KB,
patch
|
dwitte
:
review+
|
Details | Diff | Splinter Review |
5.10 KB,
patch
|
dwitte
:
review+
|
Details | Diff | Splinter Review |
6.28 KB,
patch
|
Details | Diff | Splinter Review |
NSPR includes many string functions that are redundant: PL_strlen, PL_strcat,
...{cpy,ncpy,ncat,strpbrk}, etc. are all implementations of functions provided
by the C standard. There are many advantages to using the standard functions.
Not only do C libraries often provide assembly-optimized versions, but some
compilers are able to inline optimised standard string functions when they
decide that it is appropriate.
I am replacing uses of the functions provided by the C standard with the
standard versions. If this is an unwanted change, please mark this bug INVALID.
Functions where NSPR is necessary for portability, such as the PL_strdup and
PL_str[n]case{cmp,str} family, are preserved. I am also changing calls to
PL_strfree() into normal free() calls, since that's all PL_strfree() does. This
is because I don't expect PL_str* code to change its memory allocation scheme,
seeing that it already seems somewhat deprecated. Anyway, it's easy for me to
undo the removals of PL_strfree calls if that's what the reviewer wants.
The nsCRT::str* functions that take const char* arguments are also a target, but
I haven't gotten to those yet.
Reporter | ||
Comment 1•22 years ago
|
||
This does the conversion in the following subdirs: netwerk layout caps content
directory dom editor ipc js xpinstall
More to come if people like this.
Comment 2•22 years ago
|
||
>I am also changing calls to
>PL_strfree() into normal free() calls, since that's all PL_strfree() does. This
>is because I don't expect PL_str* code to change its memory allocation scheme,
I don't think that you should make assumptions about NSPR's memory allocator.
Comment on attachment 125712 [details] [diff] [review]
First stab
indeed PL_strfree is *not* equivalent to free. it's explictly for PL_strdup, if
you're replacing the PL_strdup then you can (must) change the PL_strfree at the
same time. otherwise please leave it alone.
Attachment #125712 -
Flags: review-
Reporter | ||
Comment 4•22 years ago
|
||
Fair enough.
Reporter | ||
Updated•22 years ago
|
Attachment #125712 -
Attachment is obsolete: true
Assignee | ||
Comment 5•22 years ago
|
||
Index: netwerk/protocol/http/src/nsHttpTransaction.cpp
===================================================================
- char *p = PL_strchr(PL_strchr(buf.get(), ' ')+1, ' ');
+ char *p = strchr(PL_strchr(buf.get(), ' ')+1, ' ');
looks like you missed one there.
Assignee | ||
Comment 6•22 years ago
|
||
This won't work in all cases - you can't blindly replace PL_str* functions with
ANSI C equivalents, because the PL_str* family is nullsafe. ANSI C string
functions are not. We probably rely on this behavior in many cases.
The PL_str* implementations are definitely slower than the ANSI C ones, so it
would be nice to do something here; perhaps we could redefine the PL_str*
functions to be nullsafe wrappers around ANSI counterparts? Then, consumers that
don't need nullsafety could be converted later, but that's a laborious task
since each occurrence needs to be checked by hand.
Comment 7•22 years ago
|
||
what dwitte said. instead of trying to manually replace all PL_strlen with
strlen, let's try to change the implementation of PL_strlen instead ;)
i'm not sure that NSPR will want to change the definitions of these functions
since they are designed to work with really ancient operating systems that often
don't follow ANSI C in very subtle ways. cc'ing wtc for his input. it seems
like it should be safe to make PL_strlen a macro on platforms that have a good
strlen. e.g., strlen is a single CPU instruction on ix86.
same applies to other string routines (like PL_strchr in particular).
Comment 8•22 years ago
|
||
The PL_str functions must remain functions for binary
compatibility. We can change their implementations to
call the corresponding str functions. Then the only
optimization you lose is the inlining of the str functions.
Assignee | ||
Comment 9•22 years ago
|
||
Do we know of any platforms where the ANSI functions are broken or inconsistent?
Or should we just forge ahead and do this for all platforms?
Which functions must remain with their NSPR implementation (Aaron mentioned
PL_strdup and PL_str[n]case{cmp,str}, where did this list come from)?
Once these are answered, I guess we just need someone to do the typing. ;)
Comment 10•22 years ago
|
||
It is safe to do this for all platforms.
The functions that must remain with their NSPR implementation
are those that are not in the Standard C Library.
Comment 11•22 years ago
|
||
You probably confused strdup w/ strndup. strndup, strcasecmp, and strcasestr aren't part of the standard.
Reporter | ||
Comment 12•22 years ago
|
||
The manpage for strdup I have here says:
CONFORMING TO
SVID 3, BSD 4.3. strndup(), strdupa(), and strndupa() are GNU exten-
sions.
Notice no ISO conformance. But it's dated 1993...
Comment 13•22 years ago
|
||
We should assume that only the str* functions listed
in The C Programming Language, 2nd Ed., are
universally available at present. strdup is not one
of them.
Assignee | ||
Comment 14•21 years ago
|
||
according to an ANSI C reference i found, the following functions are provided:
strlen
strcpy
strncpy
strcat
strncat
strcmp
strncmp
strchr
strrchr
memcpy
memmove
memcmp
memchr
memset
if this list is incorrect for our purposes, please correct me. ;)
Assignee | ||
Comment 15•21 years ago
|
||
add the following to the above list:
strspn
strcspn
strpbrk
strstr
strtok
Comment 16•21 years ago
|
||
dwitte: avoid ANSI C strtok... nsCRT::strtok is better ;)
Assignee | ||
Comment 17•21 years ago
|
||
i definitely will be, since ANSI C strtok isn't threadsafe at all :)
so i've got a first-cut patch for nspr to convert pretty much everything i can
over to ANSI C... coming right up!
Assignee: aaronl → dwitte
Severity: trivial → normal
Component: Browser-General → NSPR
Product: Browser → NSPR
Summary: Remove uses of PL_str* functions that are provided by ANSI C → convert PL_str* impls to use ANSI C equivalents
Version: Trunk → other
Comment 18•21 years ago
|
||
aren't nspr PR_str* functions null-safe, contrary to ANSI C ones?
Assignee | ||
Comment 19•21 years ago
|
||
so this changes most of the nspr string functions to use libc... pretty much
first-cut at this point, to see what wtc and other folk think.
it keeps binary and API compatibility untouched. i've measured a 3.1%
improvement in Txul with this patch (linux gtk2, gcc 3.3.2). i'm unable to
measure Tp at this point.
Attachment #125777 -
Attachment is obsolete: true
Assignee | ||
Comment 20•21 years ago
|
||
Comment on attachment 135490 [details] [diff] [review]
first-cut nspr patch
wtc, what do you think? :)
Attachment #135490 -
Flags: review?(wchang0222)
Assignee | ||
Comment 21•21 years ago
|
||
oh, one note, about the comment
/* error checking in case we have a 64-bit platform -- make sure we dont
* have ultra long strings that overflow a int32
*/
in strlen.c. the reason i removed the assertion here is that it doesn't seem to
be implemented consistently in any of the other PL_str* functions that deal with
int32's in an unsafe fashion - so, i chose to be consistent and drop it
altogether, rather than making a mess of the string functions. comments on this
approach are welcome.
Assignee | ||
Updated•21 years ago
|
Attachment #135490 -
Flags: review?(wchang0222)
Assignee | ||
Comment 22•21 years ago
|
||
err, ignore that... nspr's handling of int32 overflows was perfectly correct.
this patch fixes up that part for 64-bit platforms, by using PRSize rather than
PRUint32 when dealing with ANSI C functions.
Attachment #135490 -
Attachment is obsolete: true
Assignee | ||
Updated•21 years ago
|
Attachment #135491 -
Flags: review?(wchang0222)
Comment 23•21 years ago
|
||
Nit: if you're going to keep nspr's wacky if ((char *)0 == tests, you should do
so for your ?: tests too. Also, some || return consistency would be nice.
Comment 24•21 years ago
|
||
Comment on attachment 135491 [details] [diff] [review]
second-cut nspr patch
some review comments (from me hoping to help this patch along)...
>Index: nsprpub/lib/libc/src/strcat.c
> PL_strncat(char *dest, const char *src, PRUint32 max)
...
>+ char *end = strchr(dest, '\0');
>+ (void)strncpy(end, src, (PRSize)max);
nit: s/PRSize/size_t/ ??
i know this shouldn't matter, but isn't size_t likewise part of ANSI-C?
this nit applies throughout the patch...
> PL_strcatn(char *dest, PRUint32 max, const char *src)
> {
>+ if( ((char *)0 == dest) || ((const char *)0 == src) )
>+ return dest;
>
>+ PRSize dl = strlen(dest);
>+ PRSize n = (PRSize)max - (dl + 1);
>+ if( n > 0 )
>+ {
>+ char *end = dest + dl;
>+ (void)strncat(end, src, n);
>+ }
this looks fishy to me. suppose:
dest -> "xyz"
max -> 5
src -> "abc"
the expected result is: "xyza" (null terminated)
with this code:
dl -> 3
n -> 5 - 3 - 1 -> 1
end -> dest + 3 -> "" (pointing at the null terminator)
strncat would basically copy one byte from src to end and that's it.
i believe the result would not be null terminated because strncat
does not guarantee null termination. in fact, it only null terminates
if the selected range of src data ends with a null terminator.
i recommend keeping the existing code that calls PL_strncpyz.
>Index: nsprpub/lib/libc/src/strchr.c
> PR_IMPLEMENT(char *)
> PL_strnchr(const char *s, char c, PRUint32 n)
> {
>+ if( ((const char *)0 == s) ) return (char *)0;
>
>+ const char *end = (const char *)memchr(s, '\0', (PRSize)n);
>
>+ return (char *)memchr(s, c, end ? end - s + 1 : (PRSize)n);
> }
this might be clearer:
/* do not search beyond a null terminator if one exists */
const char *end = (const char *) memchr(s, '\0', (size_t)n);
if (end)
n = end - s + 1;
return (char *)memchr(s, c, (size_t)n);
>Index: nsprpub/lib/libc/src/strcpy.c
> PL_strncpy(char *dest, const char *src, PRUint32 max)
...
>-#ifdef JLRU
>- while( --max )
>- *++dest = '\0';
>-#endif /* JLRU */
what's up with this ifdef? do we need to preserve it? wtc?
> PL_strncpyz(char *dest, const char *src, PRUint32 max)
...
>+ if( ((char *)0 == dest) || ((const char *)0 == src) ) return (char *)0;
nit: elsewhere you have put the return statement on a new line -- be consistent
;)
i think the return on the same line is the prevailing style fwiw.
>Index: nsprpub/lib/libc/src/strdup.c
> PL_strdup(const char *s)
> {
> char *rv;
>
> if( (const char *)0 == s )
>+ {
>+ rv = (char *)malloc(1);
>+
>+ if( (char *)0 != rv )
>+ *rv = '\0';
>+ }
> else
>+ {
>+ rv = (char *)malloc(strlen(s) + 1);
>+
>+ if( (char *)0 != rv )
>+ (void)strcpy(rv, s);
it would be more efficient to not compute strlen twice. like this:
size_t n = strlen(s) + 1;
rv = (char *)malloc(n);
if( (char *)0 != rv )
(void)memcpy(rv, s, n);
> PL_strndup(const char *s, PRUint32 max)
i think the old code for this function might have been better. it
is certainly more compact. i'm not sure what benefit is to be had
by reimplementing this function.
Assignee | ||
Comment 25•21 years ago
|
||
thanks for the comments darin! re the stylistic mess... yeah, i should tidy it
up a bit more. partly i couldn't be bothered until wtc says what style he'd
prefer (that goes for size_t as well) :)
>with this code:
>
> dl -> 3
> n -> 5 - 3 - 1 -> 1
> end -> dest + 3 -> "" (pointing at the null terminator)
>
>strncat would basically copy one byte from src to end and that's it.
>i believe the result would not be null terminated because strncat
>does not guarantee null termination. in fact, it only null terminates
>if the selected range of src data ends with a null terminator.
hmm, are you sure? my reference for strncat says that it copies up to n chars
(where n is the 3rd arg), and _then_ nullterminates. maybe you're confusing it
with strncpy, which behaves how you describe?
so it would copy one byte from src to end, and then nullterminate as expected.
http://www.mkssoftware.com/docs/man3/strncat.3.asp
>it would be more efficient to not compute strlen twice.
yup, just a late-night goof. Neil pointed that out and i've made the change
locally :)
>i think the old code for this function might have been better.
sure... i just felt like inlining as much as possible, since codesize isn't
important for these tiny functions. i can change it back.
>this might be clearer:
agreed :)
Comment 26•21 years ago
|
||
here's the MAN page entry:
The strcat() function appends the src string to the dest string over-
writing the ‘\0’ character at the end of dest, and then adds a termi-
nating ‘\0’ character. The strings may not overlap, and the dest
string must have enough space for the result.
The strncat() function is similar, except that only the first n charac-
ters of src are appended to dest.
so, yeah... hmm... it isn't exactly clear. does "similar" mean similar in that
it also null terminates? maybe there is a better reference for strncat somewhere?
Comment 27•21 years ago
|
||
Comment on attachment 135491 [details] [diff] [review]
second-cut nspr patch
I reviewed this patch last weekend. I didn't finish it.
This patch must be reviewed with extreme care. It is very
easy to introduce off-by-one errors when working on character
strings.
Some of the changes are obviously correct (for example, those
that handle null pointer arguments and then simply call the
libc equivalents) and can be checked in first.
Comment 28•21 years ago
|
||
Can we check in some of that?
Assignee | ||
Comment 29•21 years ago
|
||
wtc, any chance you can take a look at this patch sometime? (or if you'd rather
some of the changes wait for further review/testing, could you indicate what
you'd be happy with?)
thanks!
Comment 30•21 years ago
|
||
Comment on attachment 135491 [details] [diff] [review]
second-cut nspr patch
Dan,
Sorry about the delay. I reviewed this patch
before but forgot to write my comments here.
One reason for the delay is that some of the
changes in this patch have a high risk of
introducing off-by-one bugs. The strategy
I have in mind is to check in the other
low-risk changes first.
Comment 31•21 years ago
|
||
These are the safest changes in dwitte's "second-cut nspr patch".
I will check these in first.
I only made some minor changes:
1. I changed PRSize to size_t.
2. In PL_strstr, I restored the checks for empty strings because
if 'little' is an empty string, PL_strstr returns (char *)0
whereas strstr returns 'big'.
3. The change for PL_strlen can't declare 'l' in the middle of
the function (this is C code).
Updated•21 years ago
|
Attachment #146924 -
Flags: review?(dwitte)
Comment 32•21 years ago
|
||
Comment on attachment 146924 [details] [diff] [review]
safest changes (PL_strxxx checks for null pointers and then calls strxxx)
Dan, please at least review my change to your
patch for PL_strstr.
Updated•21 years ago
|
Attachment #146924 -
Flags: review?(dwitte)
Comment 33•21 years ago
|
||
I removed the patch for PL_strncpy from this set
because I noticed a subtle difference between
PL_strncpy and strncpy in the padding of null bytes
if the 'src' string has less than 'max' chars.
(See the code in PL_strncpy ifdef'd with JLRU.)
Attachment #146924 -
Attachment is obsolete: true
Updated•21 years ago
|
Attachment #146929 -
Flags: superreview?(darin)
Attachment #146929 -
Flags: review?(dwitte)
Comment 34•21 years ago
|
||
Attachment #135491 -
Attachment is obsolete: true
Updated•21 years ago
|
Attachment #135491 -
Flags: review?(wchang0222)
Comment 35•21 years ago
|
||
Comment on attachment 146930 [details] [diff] [review]
remaining changes
after bug 94580, while you're touching strdup.c, you could replace
+ {
+ rv = (char *)malloc(strlen(s) + 1);
+
+ if( (char *)0 != rv )
+ (void)strcpy(rv, s);
+ }
by
+ {
+ size_t l;
+
+ l = strlen(s) + 1;
+ rv = (char *)malloc(l);
+ if( (char *)0 != rv )
+ (void)memcpy(rv, s, l);
+ }
Assignee | ||
Comment 36•21 years ago
|
||
Comment on attachment 146929 [details] [diff] [review]
safest changes (PL_strxxx checks for null pointers and then calls strxxx)
looks like a good start, thanks wtc!
r=dwitte
Attachment #146929 -
Flags: review?(dwitte) → review+
Comment 37•21 years ago
|
||
Comment on attachment 146929 [details] [diff] [review]
safest changes (PL_strxxx checks for null pointers and then calls strxxx)
We should ensure that we are compiling with -O2 under GCC (in release builds)
to ensure that the inline assembly code for many of these functions is used.
NOTE: if compiled with -Os, then none of these will be expanded to inline
assembly. the result might not be so good due to the function call overhead
(extra jumps through PLT on ELF, for example).
Assignee | ||
Comment 38•21 years ago
|
||
back when i wrote this patch, i tested using #define __USE_STRING_INLINES... it
didn't seem to make any difference in perf (Txul), compared to non-inline (see
comment 19). (aside: it's also possible to achieve inlining with -Os, by
#undef'ing __OPTIMIZE_SIZE__, in the .c files of interest.)
the probable reason for this is that, for glibc on linux (my test platform), the
inline versions of the string functions (where they exist) are _not_ the same as
the non-inline versions that get used normally. they are more compact for
codesize reasons, which generally means they're slower, and they're only
provided for one arch iirc... so, the inline versions will always be written in
a compact i386 asm, whereas the noninline ones might be a faster i486/i686 asm.
unfortunate that glibc still worries about consumer codesize even when you
specify to use inlines... it'd be nice if it gave the consumer that choice. ;)
Comment 39•21 years ago
|
||
I've reviewed all the remaining changes. Here
are the changes that I think are okay. The only
substantial changes are in strdup.c (which I rewrote
to handle a null pointer as an empty string and to
use memcpy to copy strings) and strstr.c.
I will explain next why the other changes are not
accepted.
Updated•21 years ago
|
Attachment #147466 -
Flags: superreview?(darin)
Attachment #147466 -
Flags: review?(dwitte)
Comment 40•21 years ago
|
||
Comments on the remaining changes.
1. The patches for PL_strncat, PL_strncpy, and
PL_strncpyz are wrong.
PL_strncat, PL_strncpy: PL_strncpy and strncpy are
not interchangeable because strncpy always stores
'max' characters in the 'dest' buffer while
PL_strncpy terminates after copying the null
character.
PL_strncpyz: this statement is wrong:
dest[--max] = '\0';
because the 'dest' buffer is not guaranteed to
be 'max' character long.
2. The patch for PL_strcatn may be okay. Are
you sure the new code is faster than the old
code?
3. As for the other changes, it is not obvious
that the new code is faster than the old code.
I am not sure that
r = (const char *)memchr(s, '\0', (PRSize)max);
is faster than
for( r = s; max && *r; r++, max-- )
;
or
r = strchr(s, '\0');
is faster than
for( r = s; *r; r++ )
;
My gut feeling is that we won't get perceivable
application performance gain with these changes.
The effort to review these changes and to prove
that the new code is faster than the old code
may not be worthwhile.
Attachment #146930 -
Attachment is obsolete: true
Comment 41•21 years ago
|
||
Comment on attachment 147481 [details] [diff] [review]
remaining changes
I also want to comment on the patch for
PL_strnchr. I am not sure if the new code
(two memchr calls) is faster than the old
code (a single for loop).
Assignee | ||
Updated•21 years ago
|
Attachment #147466 -
Flags: review?(dwitte) → review+
Comment 42•21 years ago
|
||
I've checked in the patches "safest changes" and
"wtc's second patch" on the NSPR tip (NSPR 4.6)
and NSPRPUB_PRE_4_2_CLIENT_BRANCH (Mozilla 1.8
alpha).
I've obtained Dan Witte's agreement that we do
not check in the remaining changes.
I'd like to thank Dan for contributing a high-quality
patch. I am impressed that he followed the coding
style of the original author exactly.
Status: NEW → RESOLVED
Closed: 21 years ago
Resolution: --- → FIXED
Target Milestone: --- → 4.6
Updated•20 years ago
|
Attachment #146929 -
Flags: superreview?(darin)
Updated•20 years ago
|
Attachment #147466 -
Flags: superreview?(darin)
You need to log in
before you can comment on or make changes to this bug.
Description
•