зеркало из https://github.com/mozilla/pjs.git
583 строки
22 KiB
C++
583 строки
22 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
*
|
|
* 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.
|
|
*
|
|
* The Initial Developer of the Original Code is IBM Corporation.
|
|
* Portions created by IBM Corporation are Copyright (C) 2003
|
|
* IBM Corporation. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Scott Collins <scc@mozilla.org> (original author)
|
|
* Darin Fisher <darin@meer.net>
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this file under
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
#ifndef MOZILLA_INTERNAL_API
|
|
#error Cannot use internal string classes without MOZILLA_INTERNAL_API defined. Use the frozen header nsStringAPI.h instead.
|
|
#endif
|
|
|
|
/**
|
|
* The base for string comparators
|
|
*/
|
|
class NS_COM nsTStringComparator_CharT
|
|
{
|
|
public:
|
|
typedef CharT char_type;
|
|
|
|
nsTStringComparator_CharT() {}
|
|
|
|
virtual int operator()( const char_type*, const char_type*, PRUint32 length ) const = 0;
|
|
virtual int operator()( char_type, char_type ) const = 0;
|
|
};
|
|
|
|
|
|
/**
|
|
* The default string comparator (case-sensitive comparision)
|
|
*/
|
|
class NS_COM nsTDefaultStringComparator_CharT
|
|
: public nsTStringComparator_CharT
|
|
{
|
|
public:
|
|
typedef CharT char_type;
|
|
|
|
nsTDefaultStringComparator_CharT() {}
|
|
|
|
virtual int operator()( const char_type*, const char_type*, PRUint32 length ) const;
|
|
virtual int operator()( char_type, char_type ) const;
|
|
};
|
|
|
|
|
|
/**
|
|
* nsTAString is the most abstract class in the string hierarchy.
|
|
*
|
|
* In its original inception, nsTAString was designed to allow the data
|
|
* storage for a string to be separated into multiple fragments. This was
|
|
* intended to enable lazy string flattening or avoid string flattening
|
|
* altogether in some cases. This abstraction, however, meant that every
|
|
* single string operation (including simple operations such as IsEmpty() and
|
|
* BeginReading()) required virtual function calls. A virtual destructor was
|
|
* also required. This not only meant additional overhead for invoking
|
|
* string methods but also added to additional codesize at every callsite (to
|
|
* load the virtual function address).
|
|
*
|
|
* Today nsTAString exists mainly for backwards compatibility of the string
|
|
* API. It is restricted to representing a contiguous array of characters,
|
|
* where the character array is not necessarily null-terminated. Moreover,
|
|
* since nsTAString's virtual function table was frozen for Mozilla 1.0,
|
|
* nsTAString necessarily maintains ABI compatibility with older versions of
|
|
* Gecko. (nsTObsoleteAString provides that frozen ABI. See
|
|
* nsObsoleteAString.h for a description of how we solve the ABI
|
|
* compatibility requirement while eliminating virtual function calls on
|
|
* nsTAString.)
|
|
*
|
|
* XPIDL still generates C++ header files with references to nsTAStrings, so
|
|
* nsTAString will still be heavily used in real code.
|
|
*
|
|
* If the opportunity to break ABI compatibility with Mozilla 1.0 were to
|
|
* ever arise, our first move should be to make nsTAString equate to
|
|
* nsTSubstring. This may in fact be an option today for some Gecko-based
|
|
* products.
|
|
*/
|
|
class nsTAString_CharT
|
|
{
|
|
public:
|
|
|
|
typedef CharT char_type;
|
|
typedef nsCharTraits<char_type> char_traits;
|
|
|
|
typedef char_traits::incompatible_char_type incompatible_char_type;
|
|
|
|
typedef nsTAString_CharT self_type;
|
|
typedef nsTAString_CharT abstract_string_type;
|
|
typedef nsTObsoleteAString_CharT obsolete_string_type;
|
|
typedef nsTSubstring_CharT substring_type;
|
|
typedef nsTSubstringTuple_CharT substring_tuple_type;
|
|
|
|
typedef nsReadingIterator<char_type> const_iterator;
|
|
typedef nsWritingIterator<char_type> iterator;
|
|
|
|
typedef nsTStringComparator_CharT comparator_type;
|
|
|
|
typedef PRUint32 size_type;
|
|
typedef PRUint32 index_type;
|
|
|
|
#ifdef MOZ_V1_STRING_ABI
|
|
public:
|
|
|
|
// this acts like a virtual destructor
|
|
NS_COM NS_FASTCALL ~nsTAString_CharT();
|
|
|
|
|
|
/**
|
|
* BeginReading/EndReading can be used to get immutable access to the
|
|
* string's underlying buffer. EndReading returns a pointer to the
|
|
* end of the string's buffer. nsReadableUtils.h provides a collection
|
|
* of utility functions that work with these iterators.
|
|
*/
|
|
|
|
inline const_iterator& BeginReading( const_iterator& iter ) const
|
|
{
|
|
size_type len = GetReadableBuffer(&iter.mStart);
|
|
iter.mEnd = iter.mStart + len;
|
|
iter.mPosition = iter.mStart;
|
|
return iter;
|
|
}
|
|
|
|
inline const_iterator& EndReading( const_iterator& iter ) const
|
|
{
|
|
size_type len = GetReadableBuffer(&iter.mStart);
|
|
iter.mEnd = iter.mStart + len;
|
|
iter.mPosition = iter.mEnd;
|
|
return iter;
|
|
}
|
|
|
|
|
|
/**
|
|
* BeginWriting/EndWriting can be used to get mutable access to the
|
|
* string's underlying buffer. EndWriting returns a pointer to the
|
|
* end of the string's buffer. This iterator API cannot be used to
|
|
* grow a buffer. Use SetLength to resize the string's buffer.
|
|
*/
|
|
|
|
inline iterator& BeginWriting( iterator& iter )
|
|
{
|
|
size_type len = GetWritableBuffer(&iter.mStart);
|
|
iter.mEnd = iter.mStart + len;
|
|
iter.mPosition = iter.mStart;
|
|
return iter;
|
|
}
|
|
|
|
inline iterator& EndWriting( iterator& iter )
|
|
{
|
|
size_type len = GetWritableBuffer(&iter.mStart);
|
|
iter.mEnd = iter.mStart + len;
|
|
iter.mPosition = iter.mEnd;
|
|
return iter;
|
|
}
|
|
|
|
|
|
/**
|
|
* Length checking functions. IsEmpty is a helper function to avoid
|
|
* writing code like: |if (str.Length() == 0)|
|
|
*/
|
|
|
|
NS_COM size_type NS_FASTCALL Length() const;
|
|
PRBool IsEmpty() const { return Length() == 0; }
|
|
|
|
|
|
/**
|
|
* String equality tests. Pass a string comparator if you want to
|
|
* control how the strings are compared. By default, a binary
|
|
* "case-sensitive" comparision is performed.
|
|
*/
|
|
|
|
NS_COM PRBool NS_FASTCALL Equals( const self_type& ) const;
|
|
NS_COM PRBool NS_FASTCALL Equals( const self_type&, const comparator_type& ) const;
|
|
NS_COM PRBool NS_FASTCALL Equals( const char_type* ) const;
|
|
NS_COM PRBool NS_FASTCALL Equals( const char_type*, const comparator_type& ) const;
|
|
|
|
/**
|
|
* An efficient comparison with ASCII that can be used even
|
|
* for wide strings. Call this version when you know the
|
|
* length of 'data'.
|
|
*/
|
|
NS_COM PRBool NS_FASTCALL EqualsASCII( const char* data, size_type len ) const;
|
|
/**
|
|
* An efficient comparison with ASCII that can be used even
|
|
* for wide strings. Call this version when 'data' is
|
|
* null-terminated.
|
|
*/
|
|
NS_COM PRBool NS_FASTCALL EqualsASCII( const char* data ) const;
|
|
|
|
// EqualsLiteral must ONLY be applied to an actual literal string.
|
|
// Do not attempt to use it with a regular char* pointer, or with a char
|
|
// array variable.
|
|
// The template trick to acquire the array length at compile time without
|
|
// using a macro is due to Corey Kosak, with much thanks.
|
|
#ifdef NS_DISABLE_LITERAL_TEMPLATE
|
|
inline PRBool EqualsLiteral( const char* str ) const
|
|
{
|
|
return EqualsASCII(str);
|
|
}
|
|
#else
|
|
template<int N>
|
|
inline PRBool EqualsLiteral( const char (&str)[N] ) const
|
|
{
|
|
return EqualsASCII(str, N-1);
|
|
}
|
|
template<int N>
|
|
inline PRBool EqualsLiteral( char (&str)[N] ) const
|
|
{
|
|
const char* s = str;
|
|
return EqualsASCII(s, N-1);
|
|
}
|
|
#endif
|
|
|
|
// The LowerCaseEquals methods compare the lower case version of
|
|
// this string to some ASCII/Literal string. The ASCII string is
|
|
// *not* lowercased for you. If you compare to an ASCII or literal
|
|
// string that contains an uppercase character, it is guaranteed to
|
|
// return false. We will throw assertions too.
|
|
NS_COM PRBool NS_FASTCALL LowerCaseEqualsASCII( const char* data, size_type len ) const;
|
|
NS_COM PRBool NS_FASTCALL LowerCaseEqualsASCII( const char* data ) const;
|
|
|
|
// LowerCaseEqualsLiteral must ONLY be applied to an actual
|
|
// literal string. Do not attempt to use it with a regular char*
|
|
// pointer, or with a char array variable. Use
|
|
// LowerCaseEqualsASCII for them.
|
|
#ifdef NS_DISABLE_LITERAL_TEMPLATE
|
|
inline PRBool LowerCaseEqualsLiteral( const char* str ) const
|
|
{
|
|
return LowerCaseEqualsASCII(str);
|
|
}
|
|
#else
|
|
template<int N>
|
|
inline PRBool LowerCaseEqualsLiteral( const char (&str)[N] ) const
|
|
{
|
|
return LowerCaseEqualsASCII(str, N-1);
|
|
}
|
|
template<int N>
|
|
inline PRBool LowerCaseEqualsLiteral( char (&str)[N] ) const
|
|
{
|
|
const char* s = str;
|
|
return LowerCaseEqualsASCII(s, N-1);
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* A string always references a non-null data pointer. In some
|
|
* applications (e.g., the DOM) it is necessary for a string class
|
|
* to have some way to distinguish an empty string from a null (or
|
|
* void) string. These methods enable support for the concept of
|
|
* a void string.
|
|
*/
|
|
|
|
NS_COM PRBool NS_FASTCALL IsVoid() const;
|
|
NS_COM void NS_FASTCALL SetIsVoid( PRBool );
|
|
|
|
|
|
/**
|
|
* This method returns true if the string's underlying buffer is
|
|
* null-terminated. This should rarely be needed by applications.
|
|
* The PromiseFlatTString method should be used to ensure that a
|
|
* string's underlying buffer is null-terminated.
|
|
*/
|
|
|
|
NS_COM PRBool NS_FASTCALL IsTerminated() const;
|
|
|
|
|
|
/**
|
|
* These are contant time since nsTAString uses flat storage
|
|
*/
|
|
NS_COM char_type NS_FASTCALL First() const;
|
|
NS_COM char_type NS_FASTCALL Last() const;
|
|
|
|
|
|
/**
|
|
* Returns the number of occurances of the given character.
|
|
*/
|
|
NS_COM size_type NS_FASTCALL CountChar( char_type ) const;
|
|
|
|
|
|
/**
|
|
* Locates the offset of the first occurance of the character. Pass a
|
|
* non-zero offset to control where the search begins.
|
|
*/
|
|
|
|
NS_COM PRInt32 NS_FASTCALL FindChar( char_type, index_type offset = 0 ) const;
|
|
|
|
|
|
/**
|
|
* SetCapacity is not required to do anything; however, it can be used
|
|
* as a hint to the implementation to reduce allocations.
|
|
*
|
|
* SetCapacity(0) is a suggestion to discard all associated storage.
|
|
*/
|
|
NS_COM void NS_FASTCALL SetCapacity( size_type );
|
|
|
|
|
|
/**
|
|
* XXX talk to dbaron about this comment. we do need a method that
|
|
* XXX allows someone to resize a string's buffer so that it can be
|
|
* XXX populated using writing iterators. SetLength seems to be the
|
|
* XXX right method for the job, and we do use it in this capacity
|
|
* XXX in certain places.
|
|
*
|
|
* SetLength is used in two ways:
|
|
* 1) to |Cut| a suffix of the string;
|
|
* 2) to prepare to |Append| or move characters around.
|
|
*
|
|
* External callers are not allowed to use |SetLength| in this
|
|
* latter capacity, and should prefer |Truncate| for the former.
|
|
* In other words, |SetLength| is deprecated for all use outside
|
|
* of the string library and the internal use may at some point
|
|
* be replaced as well.
|
|
*
|
|
* This distinction makes me think the two different uses should
|
|
* be split into two distinct functions.
|
|
*/
|
|
NS_COM void NS_FASTCALL SetLength( size_type );
|
|
|
|
|
|
/**
|
|
* Can't use |Truncate| to make a string longer!
|
|
*/
|
|
void Truncate( size_type aNewLength=0 )
|
|
{
|
|
NS_ASSERTION(aNewLength <= Length(), "Truncate cannot make string longer");
|
|
SetLength(aNewLength);
|
|
}
|
|
|
|
|
|
/**
|
|
* |Assign| and |operator=| make |this| equivalent to the string or
|
|
* buffer given as an argument. If possible, they do this by sharing
|
|
* a reference counted buffer (see |nsTSubstring|). If not, they copy
|
|
* the buffer into their own buffer.
|
|
*/
|
|
|
|
NS_COM void NS_FASTCALL Assign( const self_type& readable );
|
|
NS_COM void NS_FASTCALL Assign( const substring_tuple_type& tuple );
|
|
NS_COM void NS_FASTCALL Assign( const char_type* data );
|
|
NS_COM void NS_FASTCALL Assign( const char_type* data, size_type length );
|
|
NS_COM void NS_FASTCALL Assign( char_type c );
|
|
|
|
NS_COM void NS_FASTCALL AssignASCII( const char* data, size_type length );
|
|
NS_COM void NS_FASTCALL AssignASCII( const char* data );
|
|
|
|
// AssignLiteral must ONLY be applied to an actual literal string.
|
|
// Do not attempt to use it with a regular char* pointer, or with a char
|
|
// array variable. Use AssignASCII for those.
|
|
#ifdef NS_DISABLE_LITERAL_TEMPLATE
|
|
void AssignLiteral( const char* str )
|
|
{ AssignASCII(str); }
|
|
#else
|
|
template<int N>
|
|
void AssignLiteral( const char (&str)[N] )
|
|
{ AssignASCII(str, N-1); }
|
|
template<int N>
|
|
void AssignLiteral( char (&str)[N] )
|
|
{ AssignASCII(str, N-1); }
|
|
#endif
|
|
|
|
// copy-assignment operator. I must define my own if I don't want the compiler to make me one
|
|
self_type& operator=( const self_type& readable ) { Assign(readable); return *this; }
|
|
self_type& operator=( const substring_tuple_type& tuple ) { Assign(tuple); return *this; }
|
|
self_type& operator=( const char_type* data ) { Assign(data); return *this; }
|
|
self_type& operator=( char_type c ) { Assign(c); return *this; }
|
|
|
|
|
|
|
|
/**
|
|
* |Append|, |operator+=| are used to add characters to the end of this string.
|
|
*/
|
|
|
|
NS_COM void NS_FASTCALL Append( const self_type& readable );
|
|
NS_COM void NS_FASTCALL Append( const substring_tuple_type& tuple );
|
|
NS_COM void NS_FASTCALL Append( const char_type* data );
|
|
NS_COM void NS_FASTCALL Append( const char_type* data, size_type length );
|
|
NS_COM void NS_FASTCALL Append( char_type c );
|
|
|
|
NS_COM void NS_FASTCALL AppendASCII( const char* data, size_type length );
|
|
NS_COM void NS_FASTCALL AppendASCII( const char* data );
|
|
|
|
// AppendLiteral must ONLY be applied to an actual literal string.
|
|
// Do not attempt to use it with a regular char* pointer, or with a char
|
|
// array variable. Use AppendASCII for those.
|
|
#ifdef NS_DISABLE_LITERAL_TEMPLATE
|
|
void AppendLiteral( const char* str )
|
|
{ AppendASCII(str); }
|
|
#else
|
|
template<int N>
|
|
void AppendLiteral( const char (&str)[N] )
|
|
{ AppendASCII(str, N-1); }
|
|
template<int N>
|
|
void AppendLiteral( char (&str)[N] )
|
|
{ AppendASCII(str, N-1); }
|
|
#endif
|
|
|
|
self_type& operator+=( const self_type& readable ) { Append(readable); return *this; }
|
|
self_type& operator+=( const substring_tuple_type& tuple ) { Append(tuple); return *this; }
|
|
self_type& operator+=( const char_type* data ) { Append(data); return *this; }
|
|
self_type& operator+=( char_type c ) { Append(c); return *this; }
|
|
|
|
|
|
/**
|
|
* |Insert| is used to add characters into this string at a given position.
|
|
* NOTE: It's a shame the |pos| parameter isn't at the front of the arg list.
|
|
*/
|
|
|
|
NS_COM void NS_FASTCALL Insert( const self_type& readable, index_type pos );
|
|
NS_COM void NS_FASTCALL Insert( const substring_tuple_type& tuple, index_type pos );
|
|
NS_COM void NS_FASTCALL Insert( const char_type* data, index_type pos );
|
|
NS_COM void NS_FASTCALL Insert( const char_type* data, index_type pos, size_type length );
|
|
NS_COM void NS_FASTCALL Insert( char_type c, index_type pos );
|
|
|
|
|
|
/**
|
|
* |Cut| is used to remove a range of characters from this string.
|
|
*/
|
|
|
|
NS_COM void NS_FASTCALL Cut( index_type cutStart, size_type cutLength );
|
|
|
|
|
|
/**
|
|
* |Replace| is used overwrite a range of characters from this string.
|
|
*/
|
|
|
|
NS_COM void NS_FASTCALL Replace( index_type cutStart, size_type cutLength, const self_type& readable );
|
|
NS_COM void NS_FASTCALL Replace( index_type cutStart, size_type cutLength, const substring_tuple_type& readable );
|
|
|
|
|
|
/**
|
|
* this is public to support automatic conversion of tuple to abstract
|
|
* string, which is necessary to support our API.
|
|
*/
|
|
nsTAString_CharT(const substring_tuple_type& tuple)
|
|
: mVTable(obsolete_string_type::sCanonicalVTable)
|
|
, mData(nsnull)
|
|
, mLength(0)
|
|
, mFlags(0)
|
|
{
|
|
Assign(tuple);
|
|
}
|
|
|
|
protected:
|
|
|
|
friend class nsTSubstringTuple_CharT;
|
|
|
|
// GCC 3.2 erroneously needs these (even though they are subclasses!)
|
|
friend class nsTSubstring_CharT;
|
|
friend class nsTDependentSubstring_CharT;
|
|
friend class nsTPromiseFlatString_CharT;
|
|
|
|
/**
|
|
* the address of our virtual function table. required for backwards
|
|
* compatibility with Mozilla 1.0 frozen nsAC?String interface.
|
|
*/
|
|
const void* mVTable;
|
|
|
|
/**
|
|
* these fields are "here" only when mVTable == sCanonicalVTable.
|
|
*
|
|
* they exist to support automatic construction of a nsTAString
|
|
* from a nsTSubstringTuple.
|
|
*/
|
|
char_type* mData;
|
|
size_type mLength;
|
|
PRUint32 mFlags;
|
|
|
|
/**
|
|
* nsTAString must be subclassed before it can be instantiated.
|
|
*/
|
|
nsTAString_CharT(char_type* data, size_type length, PRUint32 flags)
|
|
: mVTable(obsolete_string_type::sCanonicalVTable)
|
|
, mData(data)
|
|
, mLength(length)
|
|
, mFlags(flags)
|
|
{}
|
|
|
|
/**
|
|
* optional ctor for use by subclasses.
|
|
*
|
|
* NOTE: mData and mLength are intentionally left uninitialized.
|
|
*/
|
|
explicit
|
|
nsTAString_CharT(PRUint32 flags)
|
|
: mVTable(obsolete_string_type::sCanonicalVTable)
|
|
, mFlags(flags)
|
|
{}
|
|
|
|
/**
|
|
* get pointer to internal string buffer (may not be null terminated).
|
|
* return length of buffer.
|
|
*/
|
|
NS_COM size_type NS_FASTCALL GetReadableBuffer( const char_type **data ) const;
|
|
NS_COM size_type NS_FASTCALL GetWritableBuffer( char_type **data );
|
|
|
|
/**
|
|
* returns true if this tuple is dependent on (i.e., overlapping with)
|
|
* the given char sequence.
|
|
*/
|
|
PRBool NS_FASTCALL IsDependentOn(const char_type *start, const char_type *end) const;
|
|
|
|
/**
|
|
* we can be converted to a const nsTSubstring (dependent on this)
|
|
*/
|
|
const substring_type NS_FASTCALL ToSubstring() const;
|
|
|
|
/**
|
|
* type cast helpers
|
|
*/
|
|
|
|
const obsolete_string_type* AsObsoleteString() const
|
|
{
|
|
return NS_REINTERPRET_CAST(const obsolete_string_type*, this);
|
|
}
|
|
|
|
obsolete_string_type* AsObsoleteString()
|
|
{
|
|
return NS_REINTERPRET_CAST(obsolete_string_type*, this);
|
|
}
|
|
|
|
const substring_type* AsSubstring() const
|
|
{
|
|
return NS_REINTERPRET_CAST(const substring_type*, this);
|
|
}
|
|
|
|
substring_type* AsSubstring()
|
|
{
|
|
return NS_REINTERPRET_CAST(substring_type*, this);
|
|
}
|
|
|
|
private:
|
|
|
|
// GCC 2.95.3, EGCS-2.91.66, Sun Workshop/Forte, and IBM VisualAge C++
|
|
// require a public copy-constructor in order to support automatic
|
|
// construction of a nsTAString from a nsTSubstringTuple. I believe
|
|
// enabling the default copy-constructor is harmless, but I do not want
|
|
// it to be enabled by default because that might tempt people into
|
|
// using it (where it would be invalid).
|
|
#if !defined(__SUNPRO_CC) && \
|
|
!(defined(_AIX) && defined(__IBMCPP__)) && \
|
|
(!defined(__GNUC__) || __GNUC__ > 2 || __GNUC_MINOR__ > 95)
|
|
|
|
// NOT TO BE IMPLEMENTED
|
|
nsTAString_CharT( const self_type& );
|
|
|
|
#endif
|
|
|
|
// NOT TO BE IMPLEMENTED
|
|
void operator= ( incompatible_char_type );
|
|
void Assign ( incompatible_char_type );
|
|
void operator+= ( incompatible_char_type );
|
|
void Append ( incompatible_char_type );
|
|
void Insert ( incompatible_char_type, index_type );
|
|
#endif
|
|
};
|