Closed Bug 104663 Opened 23 years ago Closed 23 years ago

nsSharableString needs SetLength and SetCapacity

Categories

(Core :: XPCOM, defect, P2)

defect

Tracking

()

RESOLVED FIXED
mozilla0.9.7

People

(Reporter: dbaron, Assigned: dbaron)

References

Details

Attachments

(2 files, 3 obsolete files)

First, the reason nsXPIDLString::Assign doesn't work is that its SetLength and SetCapacity are the empty implementations on nsAString. However, before we rush ahead to implement them, I think we should fix up that part of the string API. I propose that we replace the following methods: nsAString::SetLength nsAString::SetCapacity nsAString::Truncate with the following nsAString::Truncate(PRUint32 aLength = 0) nsAString::EnsureCapacity(PRUint32 aLength) nsString::ReleaseStorage() Right now, SetLength does 2 things: * if the length is shorter than the current length, it truncates the string (the same as truncate) * if the length is longer than the current length, I *think* it expands the capacity if necessary, and then extends the length so that some characters in the string are garbage I think the latter of these two should not exist, and the former should become (and already is) |Truncate|. Right now, SetCapacity does two things: * Expands the allocated capacity of the string if the given length is below the current capacity. * |SetCapacity(0)| frees storage associated with the string. This is only used in two places (CNavDTD.cpp and nsGtkIMEHelper.cpp), and I propose we put ReleaseStorage on nsString until we decide what to do with these two callers. If we remove the second meaning of SetCapacity I think it's appropriate to rename it to |EnsureCapacity|. Finally, I think the sizes given to all these functions should *never* include a terminating null -- they should be equivalent to a length -- i.e., SetCapacity(32) on a flat string type would require allocating |33 * sizeof(char_type)| bytes. I'm not completely sure now.
s/I'm not completely sure now./I'm not completely sure whether they do now./
I've realized we do need SetLength for now, since that's the way everything writes into the string. It's ugly, but that's the way things currently work. So I think the best short-term solution is just to implement these methods on nsSharableString.
Status: NEW → ASSIGNED
Priority: -- → P2
Summary: SetLength and SetCapacity → nsSharableString needs SetLength and SetCapacity
Target Milestone: --- → mozilla0.9.6
The above patch works. Mozilla runs. I ran some summary leak and bloat stats -- this patch doesn't leak, but it increases total allocation a good bit, mainly since I didn't convert the XUL attributes to use nsSharableString, which would be rather difficult. (The real solution here may be nsStackString, our replacement for nsAutoString that works with sharing, which we don't have yet. We could probably also make atoms participate in the string hierarchy.) I think we need to move away from allowing NULL-ness, not towards it. Right now sharable strings' |get()| can return null. We need the shared-empty-buffer hack, I think, but attacking nsXPIDLString is a good bit of work. The main thing I did here that might be controversial is removing nsFlexBufferHandle and adding a word to nsSharedBufferHandle. The additional word on nsSharedBufferHandle was needed to make SetCapacity at all useful, which I think it probably needs to be (although once we convert nsString to a typedef to nsSharableString we could do performance testing to see if this is really true). I don't see how nsFlexBufferHandle is useful -- it wouldn't be all that useful for a Cut since the cut would still require a copy if the buffer was shared, and I'd think a better solution for that problem would be a sharing substring class that owns references to the buffer (or buffers) that it is a substring of. Was there some other need for having StorageEnd != DataEnd in the nsFlexBufferHandle?
(FWIW, I'm only assuming that it's the XUL attributes issue that's causing total allocations to increase. Also, I still need to verify in a debugger that sharing is actually happening...)
Oops, I forgot to actually enforce the: + // Since sharing should be transparent to the caller, we should + // therefore *unconditionally* truncate the current length of + // the string to the requested capacity.
Also we need a double-on-fault like nsString.
do_AssignFromReadable could fail if |aReadable| is dependent (e.g., a dependent concatentation) on |this|.
NULL-ness problems include things like: #1 0x402071c8 in nsDebug::Assertion(char const*, char const*, char const*, int) (aStr=0x402825e5 "trouble: no buffer!", aExpr=0x402825de "buffer", aFile=0x402825a0 "/builds/string/mozilla/string/src/nsASingleFragmentString.cpp", aLine=36) at /builds/string/mozilla/xpcom/base/nsDebug.cpp:290 #2 0x4022acb2 in nsASingleFragmentString::GetReadableFragment(nsReadableFragment<unsigned short>&, nsFragmentRequest, unsigned) const (this=0xbfffc490, aFragment=@0xbfffc240, aRequest=kFirstFragment, aOffset=0) at /builds/string/mozilla/string/src/nsASingleFragmentString.cpp:36 #3 0x40248a38 in nsAString::BeginReading(nsReadingIterator<unsigned short>&) const (this=0xbfffc490, aResult=@0xbfffc240) at ../../dist/include/string/nsAString.h:619 #4 0x4022b13a in Compare(nsAString const&, nsAString const&, nsStringComparator const&) (lhs=@0xbfffc490, rhs=@0x8254fec, aComparator=@0xbfffc470) at /builds/string/mozilla/string/src/nsAString.cpp:56 #5 0x41a0e05b in SelectorMatches(SelectorMatchesData&, nsCSSSelector*, int, signed char) (data=@0xbfffc5a0, aSelector=0x8254fa4, aTestState=1, aNegationIndex=0 '\000') at /builds/string/mozilla/content/html/style/src/nsCSSStyleSheet.cpp:3684 I think I'm going to try to change nsSharableString and nsXPIDLString to always have a real buffer handle, but make nsXPIDLString do some hacky things to return NULL so the API is the same, for now (but also implement IsVoid / SetIsVoid).
Target Milestone: mozilla0.9.6 → mozilla0.9.7
Comment on attachment 56970 [details] [diff] [review] additional patch for testing of real patch, not for checkin You might want to take a look at this code and see if it still works with your change: http://lxr.mozilla.org/seamonkey/source/content/base/src/nsScriptLoader.cpp#699 >Index: mozilla/js/src/xpconnect/src/xpcprivate.h >=================================================================== >RCS file: /cvsroot/mozilla/js/src/xpconnect/src/xpcprivate.h,v >retrieving revision 1.109 >diff -u -d -r1.109 xpcprivate.h >--- xpcprivate.h 2001/11/07 04:12:06 1.109 >+++ xpcprivate.h 2001/11/07 23:15:58 >@@ -2301,6 +2301,7 @@ > NS_REINTERPRET_CAST(PRUnichar *, > JS_GetStringChars(str) + > JS_GetStringLength(str)), >+ JS_GetStringLength(str) + 1, I think we should keep Capacity (StorageLength) to be the number of characters we want to store for the string, not counting the zero terminator. >Index: mozilla/string/public/nsXPIDLString.h >=================================================================== >RCS file: /cvsroot/mozilla/string/public/nsXPIDLString.h,v >retrieving revision 1.5 >diff -u -d -r1.5 nsXPIDLString.h >--- nsXPIDLString.h 2001/10/28 08:24:54 1.5 >+++ nsXPIDLString.h 2001/11/07 23:16:20 >@@ -32,9 +32,66 @@ ... >+ * Like nsCOMPtr, nsXPIDLString uses some syntactic sugar to make it >+ * painfully clear exactly what the code expects. You need to wrap an >+ * nsXPIDLString object with `getter_Copies()' >+ * before passing it to a getter: these tell the >+ * nsXPIDLString how ownership is being handled. Wrapping... >+ * >+ * In the case of `getter_Copies()', the callee is allocating a copy >+ * (which is usually the case). In the case where the >+ * callee is returning a const reference to `the real deal' (this can >+ * be done using the [shared] attribute in XPIDL) you can just use >+ * a |const char*|. const PRUnichar* / const char* >Index: mozilla/string/src/nsSharableString.cpp >=================================================================== >RCS file: /cvsroot/mozilla/string/src/nsSharableString.cpp,v >retrieving revision 1.5 >diff -u -d -r1.5 nsSharableString.cpp >--- nsSharableString.cpp 2001/10/13 15:01:21 1.5 >+++ nsSharableString.cpp 2001/11/07 23:16:20 >@@ -25,13 +25,111 @@ >+void >+nsSharableString::SetLength( size_type aNewLength ) > { >+ if ( aNewLength > mBuffer->DataLength() ) >+ { >+ SetCapacity(aNewLength); >+ mBuffer->DataEnd( mBuffer->DataStart() + aNewLength ); >+ } >+ else >+ { >+ mBuffer->DataEnd( mBuffer->DataStart() + aNewLength ); >+ *mBuffer->DataEnd() = char_type(0); // is this needed? I think so. Remind me to look at this bit below again: >Index: mozilla/string/src/nsXPIDLString.cpp >=================================================================== >RCS file: /cvsroot/mozilla/string/src/nsXPIDLString.cpp,v >retrieving revision 1.4 >diff -u -d -r1.4 nsXPIDLString.cpp >--- nsXPIDLString.cpp 2001/10/13 15:01:21 1.4 >+++ nsXPIDLString.cpp 2001/11/07 23:16:20 >@@ -100,15 +108,21 @@ > const nsXPIDLString::shared_buffer_handle_type* > nsXPIDLString::GetSharedBufferHandle() const > { >- const nsImportedStringHandle<char_type>* answer = NS_STATIC_CAST(const nsImportedStringHandle<char_type>*, mBuffer.get()); >- >- if ( answer && !answer->DataEnd() && answer->StorageStart() ) >- answer->RecalculateBoundaries(); >+ self_type* mutable_this = NS_CONST_CAST(self_type*, this); >+ if ( !mBuffer->DataStart() ) >+ // XXXldb This isn't any good. What if we just called >+ // PrepareForUseAsOutParam and it hasn't been filled in yet? >+ mutable_this->mBuffer = GetSharedEmptyBufferHandle(); >+ else if ( !mBuffer->DataEnd() ) >+ // Our handle may not be an nsImportedStringHandle. However, if it >+ // is not, this cast will still be safe since no other handle will >+ // be in this state. >+ NS_STATIC_CAST(const nsImportedStringHandle<char_type>*, mBuffer.get())->RecalculateBoundaries(); > > #if DEBUG_STRING_STATS > ++sShareCount; > #endif >- return answer; >+ return mBuffer.get(); > }
*** Bug 109972 has been marked as a duplicate of this bug. ***
> I think we should keep Capacity (StorageLength) to be the number of > characters we want to store for the string, not counting the zero > terminator. My inclination is to disagree. I think the storage length should always include room for the terminating null -- it's a description of the actual storage that has been allocated. On the other hand, for a string class that requires null-termination, SetCapacity should ensure that there is room for the terminating null. Not all implementations of nsAString need require null termination.
I'm reviewing the patch now ... but it's a big patch and deserves careful attention, so this will take me most of the day, I think. Sorry for the delay in getting to this.
I've been over this patch (still looking, but I've got a pretty good grasp, I think). Some things surprised me at first, but then I remembered that we had talked about them. Nothing bad has jumped out yet. More anon.
sr=scc
Comment on attachment 58266 [details] [diff] [review] revised patch (a few off-by-one errors fixed, comments fixed) r=jag
Attachment #58266 - Flags: superreview+
Attachment #58266 - Flags: review+
I just noticed there were some changes in nsAString.h that I didn't put in both classes (mostly comments, but also removal of the length check in Truncate()). I'll attach an additional patch later...
Fix checked in 2001-11-27 21:22 PDT.
Status: ASSIGNED → RESOLVED
Closed: 23 years ago
Resolution: --- → FIXED
Filed some of the issues mentioned in the first few comments as bug 114140.
Component: String → XPCOM
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: