mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-02 10:00:54 +00:00
365 lines
12 KiB
C++
Executable File
365 lines
12 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___
|
|
|
|
#include <stddef.h>
|
|
// for |ptrdiff_t|
|
|
|
|
#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 mFlexBufferHandle
|
|
+-----+-----+-----+-----+ +-----+-----+-----+-----+ +-----+-----+-----+-----+
|
|
| mDataStart | | mDataStart | | mDataStart |
|
|
+-----+-----+-----+-----+ +-----+-----+-----+-----+ +-----+-----+-----+-----+
|
|
| mDataEnd | | mDataEnd | | mDataEnd |
|
|
+-----+-----+-----+-----+ +-----+-----+-----+-----+ +-----+-----+-----+-----+
|
|
| mFlags | | mFlags |
|
|
+-----+-----+-----+-----+ +-----+-----+-----+-----+
|
|
. mAllocator . | mStorageStart |
|
|
......................... +-----+-----+-----+-----+
|
|
| mStorageEnd |
|
|
+-----+-----+-----+-----+
|
|
. 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.
|
|
*/
|
|
|
|
|
|
/**
|
|
*
|
|
*/
|
|
template <class CharT>
|
|
class nsBufferHandle
|
|
{
|
|
public:
|
|
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; }
|
|
|
|
ptrdiff_t DataLength() const { return mDataEnd - mDataStart; }
|
|
|
|
protected:
|
|
CharT* mDataStart;
|
|
CharT* mDataEnd;
|
|
};
|
|
|
|
template <class CharT>
|
|
class nsConstBufferHandle
|
|
{
|
|
public:
|
|
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; }
|
|
|
|
ptrdiff_t 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>
|
|
{
|
|
protected:
|
|
enum
|
|
{
|
|
kIsShared = 0x08000000, // one reason _not_ to set this is for a stack based handle that wants to express `NULL'-ness et al
|
|
kIsSingleAllocationWithBuffer = 0x04000000, // handle and buffer are one piece, no separate deallocation is possible for the buffer
|
|
kIsStorageDefinedSeparately = 0x02000000, // i.e., we're using the ``flex'' structure defined below
|
|
kIsUserAllocator = 0x01000000, // 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 )
|
|
: nsBufferHandle<CharT>(aDataStart, aDataEnd)
|
|
{
|
|
mFlags = kIsShared;
|
|
}
|
|
|
|
nsSharedBufferHandle( CharT* aDataStart, CharT* aDataEnd, CharT*, CharT*, PRBool isSingleAllocation )
|
|
: nsBufferHandle<CharT>(aDataStart, aDataEnd)
|
|
{
|
|
mFlags = kIsShared;
|
|
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
|
|
{
|
|
nsSharedBufferHandle<CharT>* mutable_this = NS_CONST_CAST(nsSharedBufferHandle<CharT>*, this);
|
|
if ( !mutable_this->set_refcount( get_refcount()-1 ) )
|
|
delete mutable_this;
|
|
// hmm, what if |kIsUserAllocator| and |kIsSingleAllocationWithBuffer|?
|
|
}
|
|
|
|
PRBool
|
|
IsReferenced() const
|
|
{
|
|
return get_refcount() != 0;
|
|
}
|
|
|
|
PRUint32
|
|
GetImplementationFlags() const
|
|
{
|
|
return mFlags & kImplementationFlagsMask;
|
|
}
|
|
|
|
void
|
|
SetImplementationFlags( PRUint32 aNewFlags )
|
|
{
|
|
mFlags = (mFlags & ~kImplementationFlagsMask) | (aNewFlags & kImplementationFlagsMask);
|
|
}
|
|
|
|
protected:
|
|
PRUint32 mFlags;
|
|
|
|
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 nsFlexBufferHandle
|
|
: public nsSharedBufferHandle<CharT>
|
|
{
|
|
public:
|
|
nsFlexBufferHandle( CharT* aDataStart, CharT* aDataEnd, CharT* aStorageStart, CharT* aStorageEnd )
|
|
: nsSharedBufferHandle<CharT>(aDataStart, aDataEnd),
|
|
mStorageStart(aStorageStart),
|
|
mStorageEnd(aStorageEnd)
|
|
{
|
|
this->mFlags |= this->kIsStorageDefinedSeparately;
|
|
}
|
|
|
|
void StorageStart( CharT* aNewStorageStart ) { mStorageStart = aNewStorageStart; }
|
|
CharT* StorageStart() { return mStorageStart; }
|
|
const CharT* StorageStart() const { return mStorageStart; }
|
|
|
|
void StorageEnd( CharT* aNewStorageEnd ) { mStorageEnd = aNewStorageEnd; }
|
|
CharT* StorageEnd() { return mStorageEnd; }
|
|
const CharT* StorageEnd() const { return mStorageEnd; }
|
|
|
|
ptrdiff_t StorageLength() const { return mStorageEnd - mStorageStart; }
|
|
|
|
protected:
|
|
CharT* mStorageStart;
|
|
CharT* mStorageEnd;
|
|
};
|
|
|
|
template <class CharT>
|
|
class nsSharedBufferHandleWithAllocator
|
|
: public nsSharedBufferHandle<CharT>
|
|
{
|
|
public:
|
|
nsSharedBufferHandleWithAllocator( CharT* aDataStart, CharT* aDataEnd, nsStringAllocator<CharT>& aAllocator )
|
|
: nsSharedBufferHandle<CharT>(aDataStart, aDataEnd),
|
|
mAllocator(aAllocator)
|
|
{
|
|
this->mFlags |= this->kIsUserAllocator;
|
|
}
|
|
|
|
nsStringAllocator<CharT>& get_allocator() const { return mAllocator; }
|
|
|
|
protected:
|
|
nsStringAllocator<CharT>& mAllocator;
|
|
};
|
|
|
|
template <class CharT>
|
|
class nsFlexBufferHandleWithAllocator
|
|
: public nsFlexBufferHandle<CharT>
|
|
{
|
|
public:
|
|
nsFlexBufferHandleWithAllocator( CharT* aDataStart, CharT* aDataEnd,
|
|
CharT* aStorageStart, CharT* aStorageEnd,
|
|
nsStringAllocator<CharT>& aAllocator )
|
|
: nsFlexBufferHandle<CharT>(aDataStart, aDataEnd, aStorageStart, aStorageEnd),
|
|
mAllocator(aAllocator)
|
|
{
|
|
this->mFlags |= this->kIsUserAllocator;
|
|
}
|
|
|
|
nsStringAllocator<CharT>& get_allocator() const { return mAllocator; }
|
|
|
|
protected:
|
|
nsStringAllocator<CharT>& mAllocator;
|
|
};
|
|
|
|
|
|
template <class CharT>
|
|
nsStringAllocator<CharT>&
|
|
nsSharedBufferHandle<CharT>::get_allocator() const
|
|
// really don't want this to be |inline|
|
|
{
|
|
if ( mFlags & kIsUserAllocator )
|
|
{
|
|
if ( mFlags & kIsStorageDefinedSeparately )
|
|
return NS_REINTERPRET_CAST(const nsFlexBufferHandleWithAllocator<CharT>*, this)->get_allocator();
|
|
else
|
|
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;
|
|
if ( mFlags & kIsStorageDefinedSeparately )
|
|
string_storage = NS_REINTERPRET_CAST(nsFlexBufferHandle<CharT>*, this)->StorageStart();
|
|
|
|
get_allocator().Deallocate(string_storage);
|
|
}
|
|
}
|
|
|
|
|
|
#endif // !defined(nsBufferHandle_h___)
|