зеркало из https://github.com/mozilla/gecko-dev.git
405 строки
13 KiB
C++
Executable File
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___)
|