/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* * The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is Mozilla strings. * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 2000 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): * Scott Collins (original author) * */ /* nsBufferHandle.h --- the collection of classes that describe the atomic hunks of strings */ #ifndef nsBufferHandle_h___ #define nsBufferHandle_h___ #ifndef nsStringDefines_h___ #include "nsStringDefines.h" #endif #include "prtypes.h" // for |PRBool| #include "nsDebug.h" // for |NS_ASSERTION| #include "nscore.h" // for |PRUnichar|, |NS_REINTERPRET_CAST| #ifdef XP_WIN // VC++ erroneously warns about incompatible linkage in these templates // It's a lie, and it's bothersome. This |#pragma| quiets the warning. #pragma warning( disable: 4251 ) #endif /* * The classes in this file are collectively called `buffer handles'. * All buffer handles begin with a pointer-tuple that delimits the * useful content of a hunk of string. A buffer handle that points to * a sharable hunk of string data additionally has a field which * multiplexes some flags and a reference count. * * * ns[Const]BufferHandle nsSharedBufferHandle * +-----+-----+-----+-----+ +-----+-----+-----+-----+ * | mDataStart | | mDataStart | * +-----+-----+-----+-----+ +-----+-----+-----+-----+ * | mDataEnd | | mDataEnd | * +-----+-----+-----+-----+ +-----+-----+-----+-----+ * | mFlags | * +-----+-----+-----+-----+ * | mStorageLength | * +-----+-----+-----+-----+ * . mAllocator . * ......................... * * Given only a |ns[Const]BufferHandle|, there is no legal way to tell * if it is sharable. In all cases, the data might immediately follow * the handle in the same allocated block. From the |mFlags| field, * you can tell exactly what configuration of a handle you actually * have. * * An |nsSharedBufferHandle| has the limitation that its |mDataStart| * must also be the beginning of the allocated storage. However, * allowing otherwise would introduce significant additional * complexity for a feature that would better be handled by allowing * an owninng substring string class that owned a reference to the * buffer handle. */ /** * * @status FROZEN */ template class nsBufferHandle { public: typedef PRUint32 size_type; nsBufferHandle() { } nsBufferHandle( CharT* aDataStart, CharT* aDataEnd ) : mDataStart(aDataStart), mDataEnd(aDataEnd) { } void DataStart( CharT* aNewDataStart ) { mDataStart = aNewDataStart; } CharT* DataStart() { return mDataStart; } const CharT* DataStart() const { return mDataStart; } void DataEnd( CharT* aNewDataEnd ) { mDataEnd = aNewDataEnd; } CharT* DataEnd() { return mDataEnd; } const CharT* DataEnd() const { return mDataEnd; } size_type DataLength() const { return mDataEnd - mDataStart; } protected: CharT* mDataStart; CharT* mDataEnd; }; /** * * @status FROZEN */ template class nsConstBufferHandle { public: typedef PRUint32 size_type; nsConstBufferHandle() { } nsConstBufferHandle( const CharT* aDataStart, const CharT* aDataEnd ) : mDataStart(aDataStart), mDataEnd(aDataEnd) { } void DataStart( const CharT* aNewDataStart ) { mDataStart = aNewDataStart; } const CharT* DataStart() const { return mDataStart; } void DataEnd( const CharT* aNewDataEnd ) { mDataEnd = aNewDataEnd; } const CharT* DataEnd() const { return mDataEnd; } size_type DataLength() const { return mDataEnd - mDataStart; } protected: const CharT* mDataStart; const CharT* mDataEnd; }; /** * string allocator stuff needs to move to its own file * also see http://bugzilla.mozilla.org/show_bug.cgi?id=70087 */ template class nsStringAllocator { public: // more later virtual void Deallocate( CharT* ) const = 0; }; /** * the following two routines must be provided by the client embedding strings */ NS_COM nsStringAllocator& StringAllocator_char(); NS_COM nsStringAllocator& StringAllocator_wchar_t(); /** * this traits class lets templated clients pick the appropriate non-template global allocator */ template struct nsStringAllocatorTraits { }; NS_SPECIALIZE_TEMPLATE struct nsStringAllocatorTraits { static nsStringAllocator& global_string_allocator() { return StringAllocator_char(); } }; NS_SPECIALIZE_TEMPLATE struct nsStringAllocatorTraits { static nsStringAllocator& global_string_allocator() { return StringAllocator_wchar_t(); } }; // end of string allocator stuff that needs to move /** * * @status FROZEN */ template class nsSharedBufferHandle : public nsBufferHandle { public: typedef PRUint32 size_type; protected: enum { kIsImmutable = 0x01000000, // if this is set, the buffer cannot be modified even if its refcount is 1 kIsSingleAllocationWithBuffer = 0x02000000, // handle and buffer are one piece, no separate deallocation is possible for the buffer kIsUserAllocator = 0x04000000, // can't |delete|, call a hook instead // the following flags are opaque to the string library itself kIsNULL = 0x80000000, // the most common request of external clients is a scheme by which they can express `NULL'-ness kImplementationFlagsMask = 0xF0000000, // 4 bits for use by site-implementations, e.g., for `NULL'-ness kFlagsMask = 0xFF000000, kRefCountMask = 0x00FFFFFF }; public: nsSharedBufferHandle( CharT* aDataStart, CharT* aDataEnd, size_type aStorageLength, PRBool isSingleAllocation ) : nsBufferHandle(aDataStart, aDataEnd), mFlags(0), mStorageLength(aStorageLength) { if ( isSingleAllocation ) mFlags |= kIsSingleAllocationWithBuffer; } ~nsSharedBufferHandle(); void AcquireReference() const { nsSharedBufferHandle* mutable_this = NS_CONST_CAST(nsSharedBufferHandle*, this); mutable_this->set_refcount( get_refcount()+1 ); } void ReleaseReference() const; PRBool IsReferenced() const { return get_refcount() != 0; } PRBool IsMutable() const { // (get_refcount() == 1) && !(GetImplementationFlags() & kIsImmutable) return (mFlags & (kRefCountMask | kIsImmutable) == 1); } void StorageLength( size_type aNewStorageLength ) { mStorageLength = aNewStorageLength; } size_type StorageLength() const { return mStorageLength; } PRUint32 GetImplementationFlags() const { return mFlags & kImplementationFlagsMask; } void SetImplementationFlags( PRUint32 aNewFlags ) { mFlags = (mFlags & ~kImplementationFlagsMask) | (aNewFlags & kImplementationFlagsMask); } protected: PRUint32 mFlags; size_type mStorageLength; PRUint32 get_refcount() const { return mFlags & kRefCountMask; } PRUint32 set_refcount( PRUint32 aNewRefCount ) { NS_ASSERTION(aNewRefCount <= kRefCountMask, "aNewRefCount <= kRefCountMask"); mFlags = (mFlags & kFlagsMask) | aNewRefCount; return aNewRefCount; } nsStringAllocator& get_allocator() const; }; template class nsSharedBufferHandleWithAllocator : public nsSharedBufferHandle { public: // why is this needed again? typedef PRUint32 size_type; nsSharedBufferHandleWithAllocator( CharT* aDataStart, CharT* aDataEnd, size_type aStorageLength, nsStringAllocator& aAllocator ) : nsSharedBufferHandle(aDataStart, aDataEnd, aStorageLength, PR_FALSE), mAllocator(aAllocator) { this->mFlags |= this->kIsUserAllocator; } nsStringAllocator& get_allocator() const { return mAllocator; } protected: nsStringAllocator& mAllocator; }; // Derive from this class to implement a |Destroy| method. template class nsSharedBufferHandleWithDestroy : public nsSharedBufferHandle { public: // why is this needed again? typedef PRUint32 size_type; nsSharedBufferHandleWithDestroy( CharT* aDataStart, CharT* aDataEnd, size_type aStorageLength) : nsSharedBufferHandle(aDataStart, aDataEnd, aStorageLength, PR_FALSE) { this->mFlags |= this->kIsUserAllocator | this->kIsSingleAllocationWithBuffer; } virtual void Destroy() = 0; // This doesn't really need to be |virtual|, but it saves us from // having to turn off gcc warnings that might be useful to // someone. virtual ~nsSharedBufferHandleWithDestroy() { } }; template class nsNonDestructingSharedBufferHandle : public nsSharedBufferHandleWithDestroy { public: // why is this needed again? typedef PRUint32 size_type; nsNonDestructingSharedBufferHandle( CharT* aDataStart, CharT* aDataEnd, size_type aStorageLength) : nsSharedBufferHandleWithDestroy(aDataStart, aDataEnd, aStorageLength) { } virtual void Destroy() { // Oops, threads raced to set the refcount. Set the refcount // back to 1. this->set_refcount(1); } }; template nsStringAllocator& nsSharedBufferHandle::get_allocator() const // really don't want this to be |inline| { if ( mFlags & kIsUserAllocator ) { return NS_REINTERPRET_CAST(const nsSharedBufferHandleWithAllocator*, this)->get_allocator(); } return nsStringAllocatorTraits::global_string_allocator(); } template nsSharedBufferHandle::~nsSharedBufferHandle() // really don't want this to be |inline| { NS_ASSERTION(!IsReferenced(), "!IsReferenced()"); if ( !(mFlags & kIsSingleAllocationWithBuffer) ) { CharT* string_storage = this->mDataStart; get_allocator().Deallocate(string_storage); } } template void nsSharedBufferHandle::ReleaseReference() const { nsSharedBufferHandle* mutable_this = NS_CONST_CAST(nsSharedBufferHandle*, this); if ( !mutable_this->set_refcount( get_refcount()-1 ) ) { if ( ~mFlags & (kIsUserAllocator|kIsSingleAllocationWithBuffer) ) delete mutable_this; else // If |kIsUserAllocator| and |kIsSingleAllocationWithBuffer|, // the handle is an |nsSharedBufferHandleWithDestroy|, // so call its |Destroy| method. NS_STATIC_CAST(nsSharedBufferHandleWithDestroy*, mutable_this)->Destroy(); } } #endif // !defined(nsBufferHandle_h___)