pjs/string/public/nsBufferHandle.h

405 строки
13 KiB
C++
Executable File

/* -*- 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 <scc@mozilla.org> (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.
*/
/**
*
*/
template <class CharT>
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;
};
template <class CharT>
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 CharT>
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<char>& StringAllocator_char();
NS_COM nsStringAllocator<PRUnichar>& StringAllocator_wchar_t();
/**
* this traits class lets templated clients pick the appropriate non-template global allocator
*/
template <class T>
struct nsStringAllocatorTraits
{
};
NS_SPECIALIZE_TEMPLATE
struct nsStringAllocatorTraits<char>
{
static nsStringAllocator<char>& global_string_allocator() { return StringAllocator_char(); }
};
NS_SPECIALIZE_TEMPLATE
struct nsStringAllocatorTraits<PRUnichar>
{
static nsStringAllocator<PRUnichar>& global_string_allocator() { return StringAllocator_wchar_t(); }
};
// end of string allocator stuff that needs to move
/**
*
*/
template <class CharT>
class nsSharedBufferHandle
: public nsBufferHandle<CharT>
{
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<CharT>(aDataStart, aDataEnd),
mFlags(0),
mStorageLength(aStorageLength)
{
if ( isSingleAllocation )
mFlags |= kIsSingleAllocationWithBuffer;
}
~nsSharedBufferHandle();
void
AcquireReference() const
{
nsSharedBufferHandle<CharT>* mutable_this = NS_CONST_CAST(nsSharedBufferHandle<CharT>*, 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<CharT>& get_allocator() const;
};
template <class CharT>
class nsSharedBufferHandleWithAllocator
: public nsSharedBufferHandle<CharT>
{
public:
// why is this needed again?
typedef PRUint32 size_type;
nsSharedBufferHandleWithAllocator( CharT* aDataStart, CharT* aDataEnd, size_type aStorageLength, nsStringAllocator<CharT>& aAllocator )
: nsSharedBufferHandle<CharT>(aDataStart, aDataEnd,
aStorageLength, PR_FALSE),
mAllocator(aAllocator)
{
this->mFlags |= this->kIsUserAllocator;
}
nsStringAllocator<CharT>& get_allocator() const { return mAllocator; }
protected:
nsStringAllocator<CharT>& mAllocator;
};
// Derive from this class to implement a |Destroy| method.
template <class CharT>
class nsSharedBufferHandleWithDestroy
: public nsSharedBufferHandle<CharT>
{
public:
// why is this needed again?
typedef PRUint32 size_type;
nsSharedBufferHandleWithDestroy( CharT* aDataStart, CharT* aDataEnd, size_type aStorageLength)
: nsSharedBufferHandle<CharT>(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 CharT>
class nsNonDestructingSharedBufferHandle
: public nsSharedBufferHandleWithDestroy<CharT>
{
public:
// why is this needed again?
typedef PRUint32 size_type;
nsNonDestructingSharedBufferHandle( CharT* aDataStart, CharT* aDataEnd, size_type aStorageLength)
: nsSharedBufferHandleWithDestroy<CharT>(aDataStart, aDataEnd,
aStorageLength)
{
}
virtual void Destroy()
{
// Oops, threads raced to set the refcount. Set the refcount
// back to 1.
this->set_refcount(1);
}
};
template <class CharT>
nsStringAllocator<CharT>&
nsSharedBufferHandle<CharT>::get_allocator() const
// really don't want this to be |inline|
{
if ( mFlags & kIsUserAllocator )
{
return NS_REINTERPRET_CAST(const nsSharedBufferHandleWithAllocator<CharT>*, this)->get_allocator();
}
return nsStringAllocatorTraits<CharT>::global_string_allocator();
}
template <class CharT>
nsSharedBufferHandle<CharT>::~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 <class CharT>
void
nsSharedBufferHandle<CharT>::ReleaseReference() const
{
nsSharedBufferHandle<CharT>* mutable_this = NS_CONST_CAST(nsSharedBufferHandle<CharT>*, 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<CharT>|,
// so call its |Destroy| method.
NS_STATIC_CAST(nsSharedBufferHandleWithDestroy<CharT>*,
mutable_this)->Destroy();
}
}
#endif // !defined(nsBufferHandle_h___)