vtable_hide
git-svn-id: svn://10.0.0.236/trunk@149976 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
parent
8cf0dd6dee
commit
ed54e4945c
8
vtable_hide/string/Makefile
Normal file
8
vtable_hide/string/Makefile
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
CXXFLAGS = -g -Os -Wall -fno-exceptions
|
||||||
|
#CXXFLAGS = -g -Wall -fno-exceptions
|
||||||
|
test: test.o nsStrRef.o nsCharTraits.o
|
||||||
|
nsStrRef.o: nsStrRef.cpp nsStrRef.h nsCharTraits.h types.h Makefile
|
||||||
|
nsCharTraits: nsCharTraits.cpp nsCharTraits.h types.h Makefile
|
||||||
|
test.o: test.cpp nsStrRef.h nsCharTraits.h types.h Makefile
|
||||||
|
clean:
|
||||||
|
rm -f *.o test
|
||||||
3
vtable_hide/string/nsCharTraits.cpp
Normal file
3
vtable_hide/string/nsCharTraits.cpp
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
#include "nsCharTraits.h"
|
||||||
|
|
||||||
|
const char *nsCharTraits<char>::empty_string = "";
|
||||||
40
vtable_hide/string/nsCharTraits.h
Normal file
40
vtable_hide/string/nsCharTraits.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/* vim:set ts=2 sw=2 et cindent: */
|
||||||
|
#ifndef nsCharTraits_h__
|
||||||
|
#define nsCharTraits_h__
|
||||||
|
|
||||||
|
#include "types.h" // XXX temporary
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
template <class T> struct nsCharTraits {};
|
||||||
|
|
||||||
|
NS_SPECIALIZE_TEMPLATE
|
||||||
|
struct nsCharTraits<char>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef char char_type;
|
||||||
|
|
||||||
|
typedef PRInt32 (* comparator_func)
|
||||||
|
(const char_type *a, const char_type *b, int n);
|
||||||
|
|
||||||
|
static const char_type *empty_string;
|
||||||
|
|
||||||
|
static PRInt32 case_sensitive_comparator(const char_type *a,
|
||||||
|
const char_type *b, int n)
|
||||||
|
{ return (PRInt32) memcmp(a, b, n); }
|
||||||
|
|
||||||
|
static PRUint32 length_of(const char_type *data)
|
||||||
|
{ return (PRUint32) ::strlen(data); }
|
||||||
|
|
||||||
|
static void copy(char_type *dest, const char_type *src, PRUint32 len)
|
||||||
|
{ ::memcpy(dest, src, len); }
|
||||||
|
|
||||||
|
static void move(char_type *dest, const char_type *src, PRUint32 len)
|
||||||
|
{ ::memmove(dest, src, len); }
|
||||||
|
|
||||||
|
static const char_type *find_char(const char_type *start,
|
||||||
|
const char_type *end,
|
||||||
|
char_type c)
|
||||||
|
{ return (const char_type *) ::memchr(start, c, end - start); }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // nsCharTraits_h__
|
||||||
543
vtable_hide/string/nsStrRef.cpp
Normal file
543
vtable_hide/string/nsStrRef.cpp
Normal file
@ -0,0 +1,543 @@
|
|||||||
|
// vim:set ts=2 sw=2 et cindent:
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "nsStrRef.h"
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// nsStrFragment methods:
|
||||||
|
|
||||||
|
nsStrFragment::char_type
|
||||||
|
nsStrFragment::Last() const
|
||||||
|
{
|
||||||
|
char_type last;
|
||||||
|
|
||||||
|
if (mLength > 0)
|
||||||
|
{
|
||||||
|
last = mData[mLength - 1];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
NS_ERROR("|Last()| on an empty string");
|
||||||
|
last = char_type(0); // BOGUS
|
||||||
|
}
|
||||||
|
|
||||||
|
return last;
|
||||||
|
}
|
||||||
|
|
||||||
|
PRUint32
|
||||||
|
nsStrFragment::CountChar(char_type c) const
|
||||||
|
{
|
||||||
|
PRUint32 result = 0;
|
||||||
|
|
||||||
|
const char_type *start = mData;
|
||||||
|
const char_type *end = mData + mLength;
|
||||||
|
|
||||||
|
while (start && start != end)
|
||||||
|
{
|
||||||
|
start = char_traits::find_char(start, end, c);
|
||||||
|
if (start)
|
||||||
|
{
|
||||||
|
++result;
|
||||||
|
++start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
PRInt32
|
||||||
|
nsStrFragment::FindChar(char_type c, PRUint32 offset) const
|
||||||
|
{
|
||||||
|
const char_type *p = char_traits::find_char(mData + offset,
|
||||||
|
mData + mLength, c);
|
||||||
|
if (p)
|
||||||
|
return p - mData;
|
||||||
|
|
||||||
|
return -1; // XXX kNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
PRBool
|
||||||
|
nsStrFragment::Equals(const self_type &ref) const
|
||||||
|
{
|
||||||
|
return mLength == ref.mLength &&
|
||||||
|
char_traits::case_sensitive_comparator(mData, ref.mData, mLength) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
PRBool
|
||||||
|
nsStrFragment::Equals(const self_type &ref, comparator_func comp) const
|
||||||
|
{
|
||||||
|
return mLength == ref.mLength &&
|
||||||
|
comp(mData, ref.mData, mLength) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
PRInt32
|
||||||
|
nsStrFragment::CompareTo(const self_type &ref) const
|
||||||
|
{
|
||||||
|
if (mLength > ref.mLength)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (mLength < ref.mLength)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return char_traits::case_sensitive_comparator(mData, ref.mData, mLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
PRInt32
|
||||||
|
nsStrFragment::CompareTo(const self_type &ref, comparator_func comp) const
|
||||||
|
{
|
||||||
|
if (mLength > ref.mLength)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (mLength < ref.mLength)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return comp(mData, ref.mData, mLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// reference counted buffer methods:
|
||||||
|
|
||||||
|
inline void StrIncrementRef(void *data)
|
||||||
|
{
|
||||||
|
PRInt32 *buf = ((PRInt32 *) data) - 1;
|
||||||
|
PR_AtomicIncrement(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void StrDecrementRef(void *data)
|
||||||
|
{
|
||||||
|
PRInt32 *buf = ((PRInt32 *) data) - 1;
|
||||||
|
if (PR_AtomicDecrement(buf) == 0)
|
||||||
|
{
|
||||||
|
printf(">>> calling free on %p [%s]\n", data, (const char *) data);
|
||||||
|
free(buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void *StrAllocateSharedBuf(size_t n)
|
||||||
|
{
|
||||||
|
PRInt32 *buf = (PRInt32 *) malloc(n + sizeof(PRUint32));
|
||||||
|
if (buf)
|
||||||
|
{
|
||||||
|
buf[0] = 1;
|
||||||
|
buf++;
|
||||||
|
}
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// nsStrRef methods:
|
||||||
|
|
||||||
|
nsStrRef::~nsStrRef()
|
||||||
|
{
|
||||||
|
if (mData)
|
||||||
|
ReleaseData();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nsStrRef::ReleaseData()
|
||||||
|
{
|
||||||
|
NS_ASSERTION(mData, "should not call ReleaseData with null mData");
|
||||||
|
|
||||||
|
if (!(mFlags & F_DEPEND))
|
||||||
|
{
|
||||||
|
if (mFlags & F_SHARED)
|
||||||
|
{
|
||||||
|
StrDecrementRef(mData);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf(">>> calling free on %p [%s]\n", mData, mData);
|
||||||
|
free(mData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nsStrRef::Adopt(char_type *data, PRUint32 dataLen)
|
||||||
|
{
|
||||||
|
if (mData)
|
||||||
|
ReleaseData();
|
||||||
|
|
||||||
|
if (dataLen == PR_UINT32_MAX)
|
||||||
|
dataLen = char_traits::length_of(data);
|
||||||
|
|
||||||
|
mData = data;
|
||||||
|
mLength = dataLen;
|
||||||
|
mFlags = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nsStrRef::Assign(const char_type *data, PRUint32 dataLen)
|
||||||
|
{
|
||||||
|
if (dataLen == PR_UINT32_MAX)
|
||||||
|
dataLen = char_traits::length_of(data);
|
||||||
|
|
||||||
|
// do not release mData until we've created the new buffer. this allows for
|
||||||
|
// the case where data might reference part of mData.
|
||||||
|
|
||||||
|
char_type *buf =
|
||||||
|
(char_type *) StrAllocateSharedBuf((dataLen + 1) * sizeof(char_type));
|
||||||
|
if (buf)
|
||||||
|
{
|
||||||
|
char_traits::copy(buf, data, dataLen);
|
||||||
|
buf[dataLen] = 0;
|
||||||
|
|
||||||
|
// okay, now we can safely release mData
|
||||||
|
if (mData)
|
||||||
|
ReleaseData();
|
||||||
|
|
||||||
|
mData = buf;
|
||||||
|
mLength = dataLen;
|
||||||
|
mFlags = F_SHARED;
|
||||||
|
|
||||||
|
printf(">>> new buffer at %p [%s]\n", mData, mData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nsStrRef::Assign(const self_type &ref)
|
||||||
|
{
|
||||||
|
// self-assign is a no-op
|
||||||
|
if (this == &ref)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (ref.mFlags & F_SHARED)
|
||||||
|
{
|
||||||
|
// since ref.mData is reference counted, we can safely release our mData.
|
||||||
|
ReleaseData();
|
||||||
|
|
||||||
|
mData = ref.mData;
|
||||||
|
mLength = ref.mLength;
|
||||||
|
mFlags = ref.mFlags;
|
||||||
|
|
||||||
|
StrIncrementRef(mData);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Assign(ref.mData, ref.mLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nsStrRef::Assign(const fragment_tuple_type &tuple)
|
||||||
|
{
|
||||||
|
PRUint32 len = tuple.Length();
|
||||||
|
|
||||||
|
char_type *buf =
|
||||||
|
(char_type *) StrAllocateSharedBuf((len + 1) * sizeof(char_type));
|
||||||
|
if (buf)
|
||||||
|
{
|
||||||
|
tuple.WriteTo(buf, len);
|
||||||
|
buf[len] = char_type(0);
|
||||||
|
|
||||||
|
ReleaseData();
|
||||||
|
|
||||||
|
mData = buf;
|
||||||
|
mLength = len;
|
||||||
|
mFlags = nsStrRef::F_SHARED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nsStrRef::SetIsVoid(PRBool val)
|
||||||
|
{
|
||||||
|
if (val)
|
||||||
|
{
|
||||||
|
ReleaseData();
|
||||||
|
|
||||||
|
mData = NS_CONST_CAST(char_type *, char_traits::empty_string);
|
||||||
|
mLength = 0;
|
||||||
|
mFlags = F_DEPEND | F_ISVOID;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mFlags &= ~F_ISVOID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// nsStrFragmentTuple methods:
|
||||||
|
|
||||||
|
PRUint32
|
||||||
|
nsStrFragmentTuple::Length() const
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// fragments are enumerated right to left
|
||||||
|
//
|
||||||
|
PRUint32 len = mFragB.Length();
|
||||||
|
if (mHead)
|
||||||
|
len += mHead->Length();
|
||||||
|
else
|
||||||
|
len += mFragA.Length();
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nsStrFragmentTuple::WriteTo(char_type *buf, PRUint32 end) const
|
||||||
|
{
|
||||||
|
// we need to write out data into buf, ending at end. so our data
|
||||||
|
// needs to preceed |end| exactly. we trust that the buffer was
|
||||||
|
// properly sized!
|
||||||
|
|
||||||
|
char_traits::copy(buf + end - mFragB.Length(), mFragB.get(), mFragB.Length());
|
||||||
|
|
||||||
|
end -= mFragB.Length();
|
||||||
|
|
||||||
|
if (mHead)
|
||||||
|
mHead->WriteTo(buf, end);
|
||||||
|
else
|
||||||
|
char_traits::copy(buf + end - mFragA.Length(), mFragA.get(), mFragA.Length());
|
||||||
|
}
|
||||||
|
|
||||||
|
PRBool
|
||||||
|
nsStrFragmentTuple::IsDependentOn(const char_type *start,
|
||||||
|
const char_type *end) const
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// fragments are enumerated right to left
|
||||||
|
//
|
||||||
|
PRBool dependent = mFragB.IsDependentOn(start, end);
|
||||||
|
if (!dependent)
|
||||||
|
{
|
||||||
|
if (mHead)
|
||||||
|
dependent = mHead->IsDependentOn(start, end);
|
||||||
|
else
|
||||||
|
dependent = mFragA.IsDependentOn(start ,end);
|
||||||
|
}
|
||||||
|
return dependent;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// nsStrBuf methods:
|
||||||
|
|
||||||
|
void
|
||||||
|
nsStrBuf::Assign(const char_type *data, PRUint32 dataLen)
|
||||||
|
{
|
||||||
|
if (dataLen == PR_UINT32_MAX)
|
||||||
|
dataLen = char_traits::length_of(data);
|
||||||
|
|
||||||
|
Assign(nsStrFragment(data, dataLen));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nsStrBuf::Assign(const ref_type &ref)
|
||||||
|
{
|
||||||
|
// self-assign is a no-op
|
||||||
|
if (this == &ref)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// avoid copying if possible...
|
||||||
|
|
||||||
|
// XXX for some strange reason GCC 3.2.2 requires this cast
|
||||||
|
if (NS_STATIC_CAST(const self_type &, ref).mFlags & F_SHARED)
|
||||||
|
nsStrRef::Assign(ref);
|
||||||
|
else
|
||||||
|
Assign(NS_STATIC_CAST(const fragment_type &, ref));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nsStrBuf::Assign(const fragment_type &frag)
|
||||||
|
{
|
||||||
|
// self-assign is a no-op
|
||||||
|
if (this == &frag)
|
||||||
|
return;
|
||||||
|
|
||||||
|
PRUint32 len = frag.Length();
|
||||||
|
|
||||||
|
// check if the fragment depends on mData
|
||||||
|
if (frag.IsDependentOn(mData, mData + mLength))
|
||||||
|
{
|
||||||
|
nsStrAutoBuf temp(frag);
|
||||||
|
Assign(temp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EnsureCapacity(len + 1, PR_FALSE))
|
||||||
|
{
|
||||||
|
char_traits::copy(mData, frag.get(), len);
|
||||||
|
mData[len] = char_type(0);
|
||||||
|
mLength = len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nsStrBuf::Assign(const fragment_tuple_type &tuple)
|
||||||
|
{
|
||||||
|
PRUint32 len = tuple.Length();
|
||||||
|
|
||||||
|
printf("+++ %s [f=0x%x c=%u len=%u]\n", __PRETTY_FUNCTION__, mFlags, mCapacity, len);
|
||||||
|
|
||||||
|
// check if the fragment tuple depends on mData
|
||||||
|
if (tuple.IsDependentOn(mData, mData + mLength))
|
||||||
|
{
|
||||||
|
nsStrAutoBuf temp(tuple);
|
||||||
|
Assign(temp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EnsureCapacity(len + 1, PR_FALSE))
|
||||||
|
{
|
||||||
|
tuple.WriteTo(mData, len);
|
||||||
|
mData[len] = char_type(0);
|
||||||
|
mLength = len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nsStrBuf::Replace(PRUint32 cutOffset, PRUint32 cutLength, const char_type *data, PRUint32 dataLen)
|
||||||
|
{
|
||||||
|
if (dataLen == PR_UINT32_MAX)
|
||||||
|
dataLen = char_traits::length_of(data);
|
||||||
|
|
||||||
|
Replace(cutOffset, cutLength, nsStrFragment(data, dataLen));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nsStrBuf::Replace(PRUint32 cutOffset, PRUint32 cutLength,
|
||||||
|
const fragment_type &frag)
|
||||||
|
{
|
||||||
|
// check if the fragment depends on mData
|
||||||
|
if (frag.IsDependentOn(mData, mData + mLength))
|
||||||
|
{
|
||||||
|
nsStrAutoBuf temp(frag);
|
||||||
|
Replace(cutOffset, cutLength, temp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PRUint32 fragLen = frag.Length();
|
||||||
|
|
||||||
|
PRUint32 newLen = mLength - cutLength + fragLen;
|
||||||
|
if (EnsureCapacity(newLen + 1, PR_TRUE))
|
||||||
|
{
|
||||||
|
// make space for new substring. may require that we move part
|
||||||
|
// of the existing string.
|
||||||
|
if (fragLen != cutLength && cutOffset + cutLength < mLength)
|
||||||
|
{
|
||||||
|
PRUint32 from = cutOffset + cutLength;
|
||||||
|
PRUint32 fromLen = mLength - from;
|
||||||
|
PRUint32 to = cutOffset + fragLen;
|
||||||
|
char_traits::move(mData + to, mData + from, fromLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fragLen)
|
||||||
|
char_traits::copy(mData + cutOffset, frag.get(), fragLen);
|
||||||
|
|
||||||
|
mData[newLen] = char_type(0);
|
||||||
|
mLength = newLen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nsStrBuf::Replace(PRUint32 cutOffset, PRUint32 cutLength,
|
||||||
|
const fragment_tuple_type &tuple)
|
||||||
|
{
|
||||||
|
// check if the fragment depends on mData
|
||||||
|
if (tuple.IsDependentOn(mData, mData + mLength))
|
||||||
|
{
|
||||||
|
nsStrAutoBuf temp(tuple);
|
||||||
|
Replace(cutOffset, cutLength, temp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PRUint32 tupleLen = tuple.Length();
|
||||||
|
|
||||||
|
PRUint32 newLen = mLength - cutLength + tupleLen;
|
||||||
|
if (EnsureCapacity(newLen + 1, PR_TRUE))
|
||||||
|
{
|
||||||
|
// make space for new substring. may require that we move part
|
||||||
|
// of the existing string.
|
||||||
|
if (tupleLen != cutLength && cutOffset + cutLength < mLength)
|
||||||
|
{
|
||||||
|
PRUint32 from = cutOffset + cutLength;
|
||||||
|
PRUint32 fromLen = mLength - from;
|
||||||
|
PRUint32 to = cutOffset + tupleLen;
|
||||||
|
char_traits::move(mData + to, mData + from, fromLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tupleLen)
|
||||||
|
tuple.WriteTo(mData + cutOffset, tupleLen);
|
||||||
|
|
||||||
|
mData[newLen] = char_type(0);
|
||||||
|
mLength = newLen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nsStrBuf::SetLength(PRUint32 len)
|
||||||
|
{
|
||||||
|
EnsureCapacity(len + 1, PR_TRUE);
|
||||||
|
mData[len] = 0;
|
||||||
|
mLength = len;
|
||||||
|
}
|
||||||
|
|
||||||
|
PRBool
|
||||||
|
nsStrBuf::EnsureCapacity(PRUint32 capacity, PRBool preserveData)
|
||||||
|
{
|
||||||
|
// EnsureCapacity is called in preparation for writing. So, if we have a
|
||||||
|
// reference to a shared buffer, then we need to go ahead and drop that
|
||||||
|
// shared reference.
|
||||||
|
|
||||||
|
if (mFlags & F_SHARED)
|
||||||
|
{
|
||||||
|
char *buf = (char *) malloc(capacity * sizeof(char_type));
|
||||||
|
if (!buf)
|
||||||
|
return PR_FALSE;
|
||||||
|
|
||||||
|
if (preserveData)
|
||||||
|
{
|
||||||
|
PRUint32 maxLength = capacity - 1;
|
||||||
|
if (mLength > maxLength)
|
||||||
|
mLength = maxLength;
|
||||||
|
char_traits::copy(buf, mData, mLength);
|
||||||
|
buf[mLength] = char_type(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
ReleaseData();
|
||||||
|
|
||||||
|
mData = buf;
|
||||||
|
mCapacity = capacity;
|
||||||
|
mFlags = F_CAPACITYSET;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!(mFlags & F_CAPACITYSET))
|
||||||
|
{
|
||||||
|
// maybe someone called nsStrRef::Adopt
|
||||||
|
mCapacity = mLength + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (capacity > mCapacity)
|
||||||
|
{
|
||||||
|
printf(">>> increasing capacity\n");
|
||||||
|
|
||||||
|
// use doubling algorithm when forced to increase available capacity
|
||||||
|
PRUint32 temp = mCapacity;
|
||||||
|
while (temp < capacity)
|
||||||
|
temp <<= 1;
|
||||||
|
capacity = temp;
|
||||||
|
|
||||||
|
char *buf;
|
||||||
|
if (mFlags & F_DEPEND)
|
||||||
|
{
|
||||||
|
// we cannot realloc the existing buffer because we do not own it.
|
||||||
|
buf = (char *) malloc(capacity * sizeof(char_type));
|
||||||
|
if (!buf)
|
||||||
|
return PR_FALSE;
|
||||||
|
|
||||||
|
if (preserveData)
|
||||||
|
char_traits::copy(buf, mData, mLength + 1);
|
||||||
|
|
||||||
|
mFlags &= ~F_DEPEND;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
buf = (char *) realloc(mData, capacity * sizeof(char_type));
|
||||||
|
if (!buf)
|
||||||
|
return PR_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
mData = buf;
|
||||||
|
mCapacity = capacity;
|
||||||
|
|
||||||
|
printf(" new capacity = %u\n", mCapacity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return PR_TRUE;
|
||||||
|
}
|
||||||
514
vtable_hide/string/nsStrRef.h
Normal file
514
vtable_hide/string/nsStrRef.h
Normal file
@ -0,0 +1,514 @@
|
|||||||
|
// vim:set ts=2 sw=2 et cindent:
|
||||||
|
#ifndef nsStrRef_h__
|
||||||
|
#define nsStrRef_h__
|
||||||
|
|
||||||
|
#include "types.h" // XXX temporary
|
||||||
|
#include "nsCharTraits.h"
|
||||||
|
|
||||||
|
class nsStrRef;
|
||||||
|
class nsStrDependentRef;
|
||||||
|
class nsStrAutoRef;
|
||||||
|
class nsStrFragment;
|
||||||
|
class nsStrFragmentTuple;
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// container for an immutable string fragment, which is not necessarily
|
||||||
|
// null-terminated.
|
||||||
|
|
||||||
|
class nsStrFragment
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef NS_CHAR char_type;
|
||||||
|
typedef nsStrFragment self_type;
|
||||||
|
typedef nsCharTraits<char_type> char_traits;
|
||||||
|
typedef char_traits::comparator_func comparator_func;
|
||||||
|
|
||||||
|
nsStrFragment()
|
||||||
|
: mData(NS_CONST_CAST(char_type *, char_traits::empty_string))
|
||||||
|
, mLength(0) {}
|
||||||
|
|
||||||
|
nsStrFragment(const char_type *data, PRUint32 length)
|
||||||
|
: mData(NS_CONST_CAST(char_type *, data)), mLength(length) {}
|
||||||
|
|
||||||
|
// default copy constructor is appropriate
|
||||||
|
|
||||||
|
const char_type *get() const { return mData; }
|
||||||
|
|
||||||
|
PRUint32 Length() const { return mLength; }
|
||||||
|
|
||||||
|
PRBool IsEmpty() const { return mLength == 0; }
|
||||||
|
|
||||||
|
// character access
|
||||||
|
|
||||||
|
char_type First() const { return mData[0]; }
|
||||||
|
char_type Last() const;
|
||||||
|
|
||||||
|
PRUint32 CountChar(char_type c) const;
|
||||||
|
|
||||||
|
PRInt32 FindChar(char_type c, PRUint32 offset = 0) const;
|
||||||
|
|
||||||
|
// comparison
|
||||||
|
|
||||||
|
PRBool Equals(const self_type &ref) const;
|
||||||
|
PRBool Equals(const self_type &ref, comparator_func c) const;
|
||||||
|
|
||||||
|
PRInt32 CompareTo(const self_type &ref) const;
|
||||||
|
PRInt32 CompareTo(const self_type &ref, comparator_func c) const;
|
||||||
|
|
||||||
|
// returns true if this fragment is dependent on (i.e., overlapping with)
|
||||||
|
// the given char sequence.
|
||||||
|
PRBool IsDependentOn(const char_type *start, const char_type *end) const
|
||||||
|
{ return (mData >= start) && (mData < end); }
|
||||||
|
|
||||||
|
// XXX more functions...
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
// helper constructor for subclasses
|
||||||
|
nsStrFragment(char_type *data)
|
||||||
|
: mData(data) {}
|
||||||
|
nsStrFragment(char_type *data, PRUint32 length)
|
||||||
|
: mData(data), mLength(length) {}
|
||||||
|
|
||||||
|
// mData is non-const in this class since subclasses may manipulate it.
|
||||||
|
char_type *mData;
|
||||||
|
PRUint32 mLength;
|
||||||
|
};
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// container for a null-terminated, immutable string of characters. since the
|
||||||
|
// string data is immutable, this class permits sharing of the underyling
|
||||||
|
// string data between threads. assignment between nsStrRef instances is
|
||||||
|
// optimized to avoid copying.
|
||||||
|
|
||||||
|
class nsStrRef : public nsStrFragment
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef nsStrRef self_type;
|
||||||
|
typedef nsStrFragment fragment_type;
|
||||||
|
typedef nsStrFragmentTuple fragment_tuple_type;
|
||||||
|
|
||||||
|
~nsStrRef();
|
||||||
|
|
||||||
|
// constructors
|
||||||
|
|
||||||
|
nsStrRef()
|
||||||
|
: nsStrFragment(), mFlags(F_DEPEND) {}
|
||||||
|
|
||||||
|
nsStrRef(const char_type *data, PRUint32 dataLen = PR_UINT32_MAX)
|
||||||
|
: nsStrFragment(nsnull) { Assign(data, dataLen); }
|
||||||
|
|
||||||
|
explicit
|
||||||
|
nsStrRef(const self_type &ref)
|
||||||
|
: nsStrFragment(nsnull) { Assign(ref); }
|
||||||
|
|
||||||
|
explicit
|
||||||
|
nsStrRef(const fragment_type &frag)
|
||||||
|
: nsStrFragment(nsnull) { Assign(frag); }
|
||||||
|
|
||||||
|
explicit
|
||||||
|
nsStrRef(const fragment_tuple_type &tuple)
|
||||||
|
: nsStrFragment(nsnull) { Assign(tuple); }
|
||||||
|
|
||||||
|
// adopt given null-terminated raw string
|
||||||
|
|
||||||
|
void Adopt(char_type *data, PRUint32 dataLen = PR_UINT32_MAX);
|
||||||
|
|
||||||
|
// assign into string reference
|
||||||
|
|
||||||
|
void Assign(const char_type c)
|
||||||
|
{ Assign(&c, 1); }
|
||||||
|
|
||||||
|
void Assign(const char_type *data, PRUint32 dataLen = PR_UINT32_MAX);
|
||||||
|
|
||||||
|
void Assign(const self_type &ref);
|
||||||
|
|
||||||
|
void Assign(const fragment_type &frag)
|
||||||
|
{ Assign(frag.get(), frag.Length()); }
|
||||||
|
|
||||||
|
void Assign(const fragment_tuple_type &tuple);
|
||||||
|
|
||||||
|
// assignment operators
|
||||||
|
|
||||||
|
self_type &operator=(const self_type &ref)
|
||||||
|
{ Assign(ref); return *this; }
|
||||||
|
|
||||||
|
self_type &operator=(const fragment_type &frag)
|
||||||
|
{ Assign(frag); return *this; }
|
||||||
|
|
||||||
|
self_type &operator=(const fragment_tuple_type &tuple)
|
||||||
|
{ Assign(tuple); return *this; }
|
||||||
|
|
||||||
|
// support for voiding a string
|
||||||
|
|
||||||
|
PRBool IsVoid() const { return mFlags & F_ISVOID; }
|
||||||
|
void SetIsVoid(PRBool);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
// helper constructor for subclasses
|
||||||
|
nsStrRef(char_type *data, PRUint32 length, PRUint32 flags)
|
||||||
|
: nsStrFragment(data, length), mFlags(flags) {}
|
||||||
|
|
||||||
|
void ReleaseData();
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
F_DEPEND = 0x1, // if set, then we do not own mData
|
||||||
|
F_SHARED = 0x2, // if set, then mData can be shared
|
||||||
|
F_ISVOID = 0x4 // if set, then the string is void
|
||||||
|
};
|
||||||
|
PRUint32 mFlags;
|
||||||
|
PRUint32 mCapacity;
|
||||||
|
};
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// container for a null-terminated, immutable string of characters that is
|
||||||
|
// not owned by the container. this class is designed to be used as a wrapper
|
||||||
|
// around raw string literals so as to avoid copying the string literal.
|
||||||
|
|
||||||
|
class nsStrDependentRef : public nsStrRef
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef nsStrDependentRef self_type;
|
||||||
|
|
||||||
|
// constructors
|
||||||
|
|
||||||
|
explicit
|
||||||
|
nsStrDependentRef(const char_type *data)
|
||||||
|
{ mFlags = F_DEPEND; Rebind(data); }
|
||||||
|
|
||||||
|
nsStrDependentRef(const char_type *data, PRUint32 length)
|
||||||
|
{ mFlags = F_DEPEND; Rebind(data, length); }
|
||||||
|
|
||||||
|
nsStrDependentRef(const char_type *start, const char_type *end)
|
||||||
|
{ mFlags = F_DEPEND; Rebind(start, end); }
|
||||||
|
|
||||||
|
void Rebind(const char_type *data)
|
||||||
|
{ Rebind(data, char_traits::length_of(data)); }
|
||||||
|
|
||||||
|
void Rebind(const char_type *data, PRUint32 length)
|
||||||
|
{
|
||||||
|
NS_ASSERTION(data,
|
||||||
|
"nsStrDependentRef must wrap a non-NULL buffer");
|
||||||
|
NS_ASSERTION(!data[length],
|
||||||
|
"nsStrDependentRef must wrap only null-terminated buffer");
|
||||||
|
mData = NS_CONST_CAST(char_type *, data);
|
||||||
|
mLength = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Rebind(const char_type *start, const char_type *end)
|
||||||
|
{ Rebind(start, end - start); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
// NOT TO BE IMPLEMENTED
|
||||||
|
void operator=(const self_type &);
|
||||||
|
};
|
||||||
|
|
||||||
|
#define NS_STR_NAMED_LITERAL(name, val) nsStrDependentRef name(val, sizeof(val)-1)
|
||||||
|
#define NS_STR_LITERAL(val) nsStrDependentRef(val, sizeof(val)-1)
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// substring helpers
|
||||||
|
|
||||||
|
inline const nsStrFragment
|
||||||
|
Substring(const nsStrFragment &frag, PRUint32 offset, PRUint32 length)
|
||||||
|
{
|
||||||
|
return nsStrFragment(frag.get() + offset, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const nsStrFragment
|
||||||
|
Substring(const NS_CHAR *data, PRUint32 length)
|
||||||
|
{
|
||||||
|
return nsStrFragment(data, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const nsStrFragment
|
||||||
|
Substring(const NS_CHAR *start, const NS_CHAR *end)
|
||||||
|
{
|
||||||
|
return nsStrFragment(start, end - start);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const nsStrFragment
|
||||||
|
StringHead(const nsStrFragment &frag, PRUint32 length)
|
||||||
|
{
|
||||||
|
return nsStrFragment(frag.get(), length);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const nsStrFragment
|
||||||
|
StringTail(const nsStrFragment &frag, PRUint32 length)
|
||||||
|
{
|
||||||
|
return nsStrFragment(frag.get() + frag.Length() - length, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// the fragment tuple represents a concatenation of string fragments.
|
||||||
|
class nsStrFragmentTuple
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef NS_CHAR char_type;
|
||||||
|
typedef nsCharTraits<char_type> char_traits;
|
||||||
|
|
||||||
|
nsStrFragmentTuple(const nsStrFragment &a, const nsStrFragment &b)
|
||||||
|
: mHead(nsnull)
|
||||||
|
, mFragA(a)
|
||||||
|
, mFragB(b) {}
|
||||||
|
|
||||||
|
nsStrFragmentTuple(const nsStrFragmentTuple &head, const nsStrFragment &frag)
|
||||||
|
: mHead(&head)
|
||||||
|
, mFragA(frag) // this fragment is ignored when head != null
|
||||||
|
, mFragB(frag)
|
||||||
|
{}
|
||||||
|
|
||||||
|
// computes the aggregate string length
|
||||||
|
PRUint32 Length() const;
|
||||||
|
|
||||||
|
// writes the aggregate string to the given buffer. bufLen is assumed to
|
||||||
|
// be equal to or greater than the value returned by the Length() method.
|
||||||
|
// the string written to |buf| is not null-terminated.
|
||||||
|
void WriteTo(char_type *buf, PRUint32 bufLen) const;
|
||||||
|
|
||||||
|
// returns true if this tuple is dependent on (i.e., overlapping with)
|
||||||
|
// the given char sequence.
|
||||||
|
PRBool IsDependentOn(const char_type *start, const char_type *end) const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
const nsStrFragmentTuple *mHead;
|
||||||
|
const nsStrFragment &mFragA;
|
||||||
|
const nsStrFragment &mFragB;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline const nsStrFragmentTuple
|
||||||
|
operator+(const nsStrFragment &a, const nsStrFragment &b)
|
||||||
|
{
|
||||||
|
return nsStrFragmentTuple(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const nsStrFragmentTuple
|
||||||
|
operator+(const nsStrFragmentTuple &a, const nsStrFragment &b)
|
||||||
|
{
|
||||||
|
return nsStrFragmentTuple(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
|
||||||
|
// a container for a null-terminated, mutable string buffer. this class
|
||||||
|
// provides high-level string editing functions.
|
||||||
|
|
||||||
|
class nsStrBuf : public nsStrRef
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef nsStrRef ref_type;
|
||||||
|
typedef nsStrBuf self_type;
|
||||||
|
|
||||||
|
// constructors
|
||||||
|
|
||||||
|
nsStrBuf()
|
||||||
|
: nsStrRef(), mCapacity(0) {}
|
||||||
|
|
||||||
|
explicit
|
||||||
|
nsStrBuf(const char_type *data)
|
||||||
|
: nsStrRef(), mCapacity(0) { Assign(data); }
|
||||||
|
|
||||||
|
nsStrBuf(const char_type *data, PRUint32 dataLen)
|
||||||
|
: nsStrRef(), mCapacity(0) { Assign(data, dataLen); }
|
||||||
|
|
||||||
|
explicit
|
||||||
|
nsStrBuf(const ref_type &ref)
|
||||||
|
: nsStrRef(), mCapacity(0) { Assign(ref); }
|
||||||
|
|
||||||
|
explicit
|
||||||
|
nsStrBuf(const fragment_type &frag)
|
||||||
|
: nsStrRef(), mCapacity(0) { Assign(frag); }
|
||||||
|
|
||||||
|
explicit
|
||||||
|
nsStrBuf(const fragment_tuple_type &tuple)
|
||||||
|
: nsStrRef(), mCapacity(0) { Assign(tuple); }
|
||||||
|
|
||||||
|
// assign into string buffer
|
||||||
|
|
||||||
|
void Assign(const char_type c)
|
||||||
|
{ Assign(&c, 1); }
|
||||||
|
|
||||||
|
void Assign(const char_type *data, PRUint32 dataLen = PR_UINT32_MAX);
|
||||||
|
|
||||||
|
void Assign(const ref_type &ref);
|
||||||
|
|
||||||
|
void Assign(const fragment_type &frag);
|
||||||
|
|
||||||
|
void Assign(const fragment_tuple_type &tuple);
|
||||||
|
|
||||||
|
// assignment operators
|
||||||
|
|
||||||
|
self_type &operator=(const self_type &buf)
|
||||||
|
{ Assign(buf); return *this; }
|
||||||
|
|
||||||
|
self_type &operator=(const ref_type &ref)
|
||||||
|
{ Assign(ref); return *this; }
|
||||||
|
|
||||||
|
self_type &operator=(const fragment_type &frag)
|
||||||
|
{ Assign(frag); return *this; }
|
||||||
|
|
||||||
|
self_type &operator=(const fragment_tuple_type &tuple)
|
||||||
|
{ Assign(tuple); return *this; }
|
||||||
|
|
||||||
|
// replace part of string buffer
|
||||||
|
|
||||||
|
void Replace(PRUint32 cutOffset, PRUint32 cutLength, char_type c)
|
||||||
|
{ Replace(cutOffset, cutLength, &c, 1); }
|
||||||
|
|
||||||
|
void Replace(PRUint32 cutOffset, PRUint32 cutLength,
|
||||||
|
const char_type *data, PRUint32 dataLen = PR_UINT32_MAX);
|
||||||
|
|
||||||
|
void Replace(PRUint32 cutOffset, PRUint32 cutLength,
|
||||||
|
const fragment_type &frag);
|
||||||
|
|
||||||
|
void Replace(PRUint32 cutOffset, PRUint32 cutLength,
|
||||||
|
const fragment_tuple_type &tuple);
|
||||||
|
|
||||||
|
// append to string buffer
|
||||||
|
|
||||||
|
void Append(char_type c)
|
||||||
|
{ Replace(mLength, 0, &c, 1); }
|
||||||
|
|
||||||
|
void Append(const char_type *data, PRUint32 dataLen = PR_UINT32_MAX)
|
||||||
|
{ Replace(mLength, 0, data, dataLen); }
|
||||||
|
|
||||||
|
void Append(const fragment_type &frag)
|
||||||
|
{ Replace(mLength, 0, frag); }
|
||||||
|
|
||||||
|
void Append(const fragment_tuple_type &tuple)
|
||||||
|
{ Replace(mLength, 0, tuple); }
|
||||||
|
|
||||||
|
self_type &operator+=(char_type c)
|
||||||
|
{ Append(c); return *this; }
|
||||||
|
|
||||||
|
self_type &operator+=(const char_type *data)
|
||||||
|
{ Append(data); return *this; }
|
||||||
|
|
||||||
|
self_type &operator+=(const fragment_type &frag)
|
||||||
|
{ Append(frag); return *this; }
|
||||||
|
|
||||||
|
self_type &operator+=(const fragment_tuple_type &tuple)
|
||||||
|
{ Append(tuple); return *this; }
|
||||||
|
|
||||||
|
// insert into string buffer
|
||||||
|
|
||||||
|
void Insert(PRUint32 offset, char_type c)
|
||||||
|
{ Replace(offset, 0, &c, 1); }
|
||||||
|
|
||||||
|
void Insert(PRUint32 offset, const char_type *data,
|
||||||
|
PRUint32 dataLen = PR_UINT32_MAX)
|
||||||
|
{ Replace(offset, 0, data, dataLen); }
|
||||||
|
|
||||||
|
void Insert(PRUint32 offset, const fragment_type &frag)
|
||||||
|
{ Replace(offset, 0, frag); }
|
||||||
|
|
||||||
|
void Insert(PRUint32 offset, const fragment_tuple_type &tuple)
|
||||||
|
{ Replace(offset, 0, tuple); }
|
||||||
|
|
||||||
|
// cut out section of string buffer
|
||||||
|
|
||||||
|
void Cut(PRUint32 cutOffset, PRUint32 cutLength)
|
||||||
|
{ Replace(cutOffset, cutLength,
|
||||||
|
nsStrFragment(char_traits::empty_string, PRUint32(0))); }
|
||||||
|
|
||||||
|
void SetCapacity(PRUint32 capacity)
|
||||||
|
{ EnsureCapacity(capacity, PR_TRUE); }
|
||||||
|
|
||||||
|
void SetLength(PRUint32 length);
|
||||||
|
|
||||||
|
void Truncate(PRUint32 length)
|
||||||
|
{
|
||||||
|
NS_ASSERTION(length <= mLength, "|Trunate()| cannot make a string longer");
|
||||||
|
SetLength(length);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
// helper constructor for subclasses
|
||||||
|
nsStrBuf(char_type *data, PRUint32 length, PRUint32 flags, PRUint32 capacity)
|
||||||
|
: nsStrRef(data, length, flags), mCapacity(capacity) {}
|
||||||
|
|
||||||
|
PRBool EnsureCapacity(PRUint32 capacity, PRBool preserveData);
|
||||||
|
|
||||||
|
// additional values for mFlags
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
F_CAPACITYSET = 0x10 // if set, then mCapacity applies to mData
|
||||||
|
};
|
||||||
|
|
||||||
|
// holds the current size of the buffer at mData
|
||||||
|
PRUint32 mCapacity;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// subclass of nsStrBuf that makes use of fixed storage for small strings.
|
||||||
|
// this class is designed to be allocated on the stack.
|
||||||
|
|
||||||
|
class nsStrAutoBuf : public nsStrBuf
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef nsStrRef ref_type;
|
||||||
|
typedef nsStrBuf self_type;
|
||||||
|
|
||||||
|
// constructors
|
||||||
|
|
||||||
|
nsStrAutoBuf()
|
||||||
|
: nsStrBuf(mFixed, 0, F_DEPEND | F_CAPACITYSET, sizeof(mFixed))
|
||||||
|
{ mFixed[0] = char_type(0); }
|
||||||
|
|
||||||
|
explicit
|
||||||
|
nsStrAutoBuf(const char_type *data)
|
||||||
|
: nsStrBuf(mFixed, 0, F_DEPEND | F_CAPACITYSET, sizeof(mFixed))
|
||||||
|
{ Assign(data); }
|
||||||
|
|
||||||
|
nsStrAutoBuf(const char_type *data, PRUint32 dataLen)
|
||||||
|
: nsStrBuf(mFixed, 0, F_DEPEND | F_CAPACITYSET, sizeof(mFixed))
|
||||||
|
{ Assign(data, dataLen); }
|
||||||
|
|
||||||
|
explicit
|
||||||
|
nsStrAutoBuf(const ref_type &ref)
|
||||||
|
: nsStrBuf(mFixed, 0, F_DEPEND | F_CAPACITYSET, sizeof(mFixed))
|
||||||
|
{ Assign(ref); }
|
||||||
|
|
||||||
|
explicit
|
||||||
|
nsStrAutoBuf(const fragment_type &frag)
|
||||||
|
: nsStrBuf(mFixed, 0, F_DEPEND | F_CAPACITYSET, sizeof(mFixed))
|
||||||
|
{ Assign(frag); }
|
||||||
|
|
||||||
|
explicit
|
||||||
|
nsStrAutoBuf(const fragment_tuple_type &tuple)
|
||||||
|
: nsStrBuf(mFixed, 0, F_DEPEND | F_CAPACITYSET, sizeof(mFixed))
|
||||||
|
{ Assign(tuple); }
|
||||||
|
|
||||||
|
// assign
|
||||||
|
|
||||||
|
self_type &operator=(const self_type &buf)
|
||||||
|
{ Assign(buf); return *this; }
|
||||||
|
|
||||||
|
self_type &operator=(const ref_type &ref)
|
||||||
|
{ Assign(ref); return *this; }
|
||||||
|
|
||||||
|
self_type &operator=(const fragment_type &frag)
|
||||||
|
{ Assign(frag); return *this; }
|
||||||
|
|
||||||
|
self_type &operator=(const fragment_tuple_type &tuple)
|
||||||
|
{ Assign(tuple); return *this; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
char_type mFixed[64]; // this is in use if F_DEPEND is set.
|
||||||
|
};
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#endif // nsStrRef_h__
|
||||||
62
vtable_hide/string/test.cpp
Normal file
62
vtable_hide/string/test.cpp
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include "nsStrRef.h"
|
||||||
|
|
||||||
|
void func(const char *p)
|
||||||
|
{
|
||||||
|
printf("--%s--\n", p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void concat_and_print(const nsStrRef &a, const nsStrRef &b)
|
||||||
|
{
|
||||||
|
nsStrRef r(a + b);
|
||||||
|
printf("%s\n", r.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
nsStrRef a("howdy");
|
||||||
|
NS_STR_NAMED_LITERAL(b, "howdy");
|
||||||
|
printf("%s %s %d\n", a.get(), b.get(), a.Equals(b));
|
||||||
|
|
||||||
|
nsStrRef d;
|
||||||
|
d.Assign('x');
|
||||||
|
printf("%s\n", d.get());
|
||||||
|
|
||||||
|
nsStrRef r(Substring(a, 1, 3));
|
||||||
|
printf("%s\n", r.get());
|
||||||
|
|
||||||
|
nsStrDependentRef c("foopity");
|
||||||
|
r = a + nsStrRef("/balance/") + b + c + nsStrDependentRef("bob");
|
||||||
|
printf("%s\n", r.get());
|
||||||
|
|
||||||
|
func(nsStrRef("xyz").get());
|
||||||
|
|
||||||
|
nsStrDependentRef aa("foo"), bb("bar");
|
||||||
|
concat_and_print(aa, bb);
|
||||||
|
|
||||||
|
nsStrAutoBuf a_buf;
|
||||||
|
|
||||||
|
a_buf = Substring(aa, 1, 2) + r;
|
||||||
|
printf("1:%s\n", a_buf.get());
|
||||||
|
|
||||||
|
a_buf = NS_STR_LITERAL("$w$") + a_buf + NS_STR_LITERAL("$you$");
|
||||||
|
printf("2:%s\n", a_buf.get());
|
||||||
|
|
||||||
|
a_buf.Append(NS_STR_LITERAL("--") + r + NS_STR_LITERAL("--") + r);
|
||||||
|
printf("3:%s\n", a_buf.get());
|
||||||
|
|
||||||
|
a_buf.Insert(10, "((xyz))");
|
||||||
|
printf("4:%s\n", a_buf.get());
|
||||||
|
|
||||||
|
/*
|
||||||
|
nsStrRef &a_ref = a_buf;
|
||||||
|
a_ref.Assign(NS_STR_LITERAL("one day on the road"));
|
||||||
|
*/
|
||||||
|
|
||||||
|
a_buf += aa + NS_STR_LITERAL("[[ ") + a_buf + NS_STR_LITERAL(" ]]") + c;
|
||||||
|
printf("5:%s\n", a_buf.get());
|
||||||
|
|
||||||
|
a_buf.Replace(0, 5, a);
|
||||||
|
printf("6:%s\n", a_buf.get());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
28
vtable_hide/string/tt.cc
Normal file
28
vtable_hide/string/tt.cc
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
|
||||||
|
class A
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
A(int x, int y, int z)
|
||||||
|
: _x(x), _y(y), _z(z) {}
|
||||||
|
private:
|
||||||
|
int _x, _y, _z;
|
||||||
|
};
|
||||||
|
|
||||||
|
class B
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
B(int x, int y, int z)
|
||||||
|
{ _x = x;
|
||||||
|
_z = z;
|
||||||
|
_y = y;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
int _x, _y, _z;
|
||||||
|
};
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
A a(1, 2, 3);
|
||||||
|
B b(1, 2, 3);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
28
vtable_hide/string/types.h
Normal file
28
vtable_hide/string/types.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
/* vim:set ts=2 sw=2 et cindent: */
|
||||||
|
|
||||||
|
#ifndef str_types_h__
|
||||||
|
#define str_types_h__
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
#include <stdio.h>
|
||||||
|
typedef int PRInt32;
|
||||||
|
typedef unsigned int PRUint32;
|
||||||
|
typedef unsigned int PRBool;
|
||||||
|
#define PR_TRUE 1
|
||||||
|
#define PR_FALSE 0
|
||||||
|
typedef char NS_CHAR;
|
||||||
|
#define NS_CHAR_SIZE sizeof(NS_CHAR)
|
||||||
|
#define NS_COM
|
||||||
|
#define NS_ASSERTION(x, s) if (!(x)) printf("### ASSERTION [%s]: %s\n", # x, s)
|
||||||
|
#define NS_ERROR(s) printf("### ERROR: %s\n", s)
|
||||||
|
#define PR_MIN(x,y) ((x) < (y) ? (x) : (y))
|
||||||
|
#define NS_CONST_CAST(x,y) const_cast<x>(y)
|
||||||
|
#define NS_STATIC_CAST(x,y) static_cast<x>(y)
|
||||||
|
#define nsnull 0
|
||||||
|
#define NS_SPECIALIZE_TEMPLATE template <>
|
||||||
|
#define PR_AtomicIncrement(x) (++(*(x)))
|
||||||
|
#define PR_AtomicDecrement(x) (--(*(x)))
|
||||||
|
#define PR_UINT32_MAX PRUint32(-1)
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
#endif
|
||||||
13
vtable_hide/vtable_foo/Makefile
Normal file
13
vtable_hide/vtable_foo/Makefile
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# vim:set ts=8 sw=8 noet:
|
||||||
|
|
||||||
|
all: libfoo.so libfoo++.so test test++
|
||||||
|
libfoo.so: libfoo.c Makefile
|
||||||
|
gcc -g -fPIC -shared $< -o $@
|
||||||
|
libfoo++.so: libfoo++.cc Makefile
|
||||||
|
gcc -g -fPIC -fno-exceptions -shared $< -o $@ -lsupc++
|
||||||
|
test: test.cc libfoo.h Makefile
|
||||||
|
gcc -g -fno-exceptions $< -o $@ -L. -lfoo -lsupc++
|
||||||
|
test++: test.cc libfoo.h Makefile
|
||||||
|
gcc -g -fno-exceptions $< -o $@ -L. -lfoo++ -lsupc++
|
||||||
|
clean:
|
||||||
|
rm -f test libfoo.so
|
||||||
41
vtable_hide/vtable_foo/libfoo++.cc
Normal file
41
vtable_hide/vtable_foo/libfoo++.cc
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
class Foo
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static Foo sFoo;
|
||||||
|
|
||||||
|
Foo() {}
|
||||||
|
virtual ~Foo()
|
||||||
|
{
|
||||||
|
printf("~Foo [this=%p]\n", this);
|
||||||
|
}
|
||||||
|
virtual int meth1()
|
||||||
|
{
|
||||||
|
if ( *((int *) this) == *((int *) &sFoo) )
|
||||||
|
printf("vptrs match\n");
|
||||||
|
printf("meth1 [this=%p]\n", this);
|
||||||
|
}
|
||||||
|
virtual int meth2(const char *s)
|
||||||
|
{ printf("meth2 [this=%p s=\"%s\"]\n", this, s); }
|
||||||
|
virtual int meth3(int x, const char *s)
|
||||||
|
{ printf("meth3 [this=%p x=%d s=\"%s\"]\n", this, x, s); }
|
||||||
|
virtual int meth4(int x, const char *s, char c)
|
||||||
|
{ printf("meth4 [this=%p x=%d s=\"%s\" c=%c]\n", this, x, s, c); }
|
||||||
|
};
|
||||||
|
|
||||||
|
Foo Foo::sFoo;
|
||||||
|
|
||||||
|
extern "C" Foo *
|
||||||
|
CreateFoo()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
Foo *f = (Foo *) malloc(sizeof(Foo));
|
||||||
|
|
||||||
|
new (f) Foo();
|
||||||
|
|
||||||
|
return f;
|
||||||
|
*/
|
||||||
|
return new Foo();
|
||||||
|
}
|
||||||
65
vtable_hide/vtable_foo/libfoo.c
Normal file
65
vtable_hide/vtable_foo/libfoo.c
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
struct Foo;
|
||||||
|
struct FooMethods;
|
||||||
|
|
||||||
|
struct FooMethods
|
||||||
|
{
|
||||||
|
void (* dtor)(struct Foo * const);
|
||||||
|
int (* meth1)(struct Foo * const);
|
||||||
|
int (* meth2)(struct Foo *, const char *);
|
||||||
|
int (* meth3)(struct Foo *, int, const char *);
|
||||||
|
int (* meth4)(struct Foo *, int, const char *, char);
|
||||||
|
void (* dummy1)(struct Foo *);
|
||||||
|
void (* dummy2)(struct Foo *);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Foo
|
||||||
|
{
|
||||||
|
struct FooMethods *vptr;
|
||||||
|
int x;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void foo_dtor(struct Foo * const self)
|
||||||
|
{
|
||||||
|
printf("foo_dtor [self=%p]\n", self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int foo_meth1(struct Foo *self)
|
||||||
|
{
|
||||||
|
printf("foo_meth1 [self=%p]\n", self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int foo_meth2(struct Foo *self, const char *s)
|
||||||
|
{
|
||||||
|
printf("foo_meth2 [self=%p s=\"%s\"]\n", self, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int foo_meth3(struct Foo *self, int x, const char *s)
|
||||||
|
{
|
||||||
|
printf("foo_meth3 [self=%p x=%d s=\"%s\"]\n", self, x, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int foo_meth4(struct Foo *self, int x, const char *s, char c)
|
||||||
|
{
|
||||||
|
printf("foo_meth4 [self=%p x=%d s=\"%s\" c=%c]\n", self, x, s, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void foo_dummy(struct Foo *self)
|
||||||
|
{
|
||||||
|
printf("foo_dummy [self=%p]\n", self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct FooMethods foo_methods = {
|
||||||
|
&foo_dtor,
|
||||||
|
&foo_dummy,
|
||||||
|
&foo_meth1,
|
||||||
|
&foo_meth2,
|
||||||
|
&foo_meth3,
|
||||||
|
&foo_meth4,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Foo *CreateFoo()
|
||||||
|
{
|
||||||
|
struct Foo *f = (struct Foo *) malloc(sizeof(struct Foo));
|
||||||
|
f->vptr = &foo_methods;
|
||||||
|
return f;
|
||||||
|
}
|
||||||
18
vtable_hide/vtable_foo/libfoo.h
Normal file
18
vtable_hide/vtable_foo/libfoo.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/* vim:set ts=2 sw=2 et cindent: */
|
||||||
|
|
||||||
|
#ifndef LIBFOO_H__
|
||||||
|
#define LIBFOO_H__
|
||||||
|
|
||||||
|
class Foo
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~Foo() { }
|
||||||
|
virtual int meth1() = 0;
|
||||||
|
virtual int meth2(const char *) = 0;
|
||||||
|
virtual int meth3(int, const char *) = 0;
|
||||||
|
virtual int meth4(int, const char *, char) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern "C" Foo * CreateFoo();
|
||||||
|
|
||||||
|
#endif
|
||||||
BIN
vtable_hide/vtable_foo/test
Executable file
BIN
vtable_hide/vtable_foo/test
Executable file
Binary file not shown.
BIN
vtable_hide/vtable_foo/test++
Executable file
BIN
vtable_hide/vtable_foo/test++
Executable file
Binary file not shown.
17
vtable_hide/vtable_foo/test.cc
Normal file
17
vtable_hide/vtable_foo/test.cc
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
#include "libfoo.h"
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
Foo *f = CreateFoo();
|
||||||
|
if (f)
|
||||||
|
{
|
||||||
|
f->meth1();
|
||||||
|
f->meth2("hello");
|
||||||
|
f->meth3(10, "hello");
|
||||||
|
f->meth4(10, "hello", 'x');
|
||||||
|
f->~Foo();
|
||||||
|
// free(f);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
BIN
vtable_hide/vtable_hide.tar.gz
Normal file
BIN
vtable_hide/vtable_hide.tar.gz
Normal file
Binary file not shown.
11
vtable_hide/vtable_hide/Makefile
Normal file
11
vtable_hide/vtable_hide/Makefile
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# vim:set noet ts=4 sw=4:
|
||||||
|
|
||||||
|
all: test
|
||||||
|
shim.o: shim.cc shim.h inner.h Makefile
|
||||||
|
gcc -c -g -fno-exceptions $< -o $@
|
||||||
|
test.o: test.cc shim.h inner.h Makefile
|
||||||
|
gcc -c -g -fno-exceptions $< -o $@
|
||||||
|
test: test.o shim.o
|
||||||
|
gcc $^ -o $@ -lsupc++
|
||||||
|
clean:
|
||||||
|
rm -f *.o test
|
||||||
12
vtable_hide/vtable_hide/inner.h
Normal file
12
vtable_hide/vtable_hide/inner.h
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#ifndef inner_h__
|
||||||
|
#define inner_h__
|
||||||
|
|
||||||
|
class Inner
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~Inner() {}
|
||||||
|
virtual void foo(int x) = 0;
|
||||||
|
virtual void bar(int x, const char *s) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
98
vtable_hide/vtable_hide/shim.cc
Normal file
98
vtable_hide/vtable_hide/shim.cc
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
// vim:set cindent:
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "shim.h"
|
||||||
|
#include "inner.h"
|
||||||
|
|
||||||
|
class InnerImpl : public Inner
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
InnerImpl() {}
|
||||||
|
|
||||||
|
virtual ~InnerImpl()
|
||||||
|
{
|
||||||
|
printf("InnerImpl::~InnerImpl [this=%p]\n", this);
|
||||||
|
ShimDtor((Shim *) this);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void foo(int x)
|
||||||
|
{
|
||||||
|
printf("InnerImpl::foo [this=%p x=%d]\n", this, x);
|
||||||
|
ShimFoo((Shim *) this, x);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void bar(int x, const char *s)
|
||||||
|
{
|
||||||
|
printf("InnerImpl::bar [this=%p x=%d s=\"%s\"]\n", this, x, s);
|
||||||
|
ShimBar((Shim *) this, x, s);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// placement new
|
||||||
|
inline void *operator new(size_t size, Shim *s) { return s; }
|
||||||
|
|
||||||
|
static Shim
|
||||||
|
GetOurImpl()
|
||||||
|
{
|
||||||
|
Shim ourImpl;
|
||||||
|
new (&ourImpl) InnerImpl();
|
||||||
|
return ourImpl;
|
||||||
|
}
|
||||||
|
static Shim gOurImpl = GetOurImpl();
|
||||||
|
#define IS_EXTERNAL_IMPL(self) (self->vptr != gOurImpl.vptr)
|
||||||
|
|
||||||
|
void ShimCtor(Shim *self)
|
||||||
|
{
|
||||||
|
printf("ShimCtor [self=%p]\n", self);
|
||||||
|
|
||||||
|
new (self) InnerImpl();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* initialize our members after placement new since sizeof InnerImpl
|
||||||
|
* may be bigger than sizeof(void *)
|
||||||
|
*/
|
||||||
|
|
||||||
|
self->data = 0;
|
||||||
|
self->length = 1;
|
||||||
|
self->flags = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShimDtor(Shim *self)
|
||||||
|
{
|
||||||
|
printf("ShimDtor [self=%p]\n", self);
|
||||||
|
|
||||||
|
if (IS_EXTERNAL_IMPL(self))
|
||||||
|
{
|
||||||
|
((Inner *) self)->~Inner();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf(" ++ running our dtor!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShimFoo(Shim *self, int x)
|
||||||
|
{
|
||||||
|
printf("ShimFoo [self=%p x=%d]\n", self, x);
|
||||||
|
|
||||||
|
if (IS_EXTERNAL_IMPL(self))
|
||||||
|
{
|
||||||
|
((Inner *) self)->foo(x);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf(" ++ running our foo!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShimBar(Shim *self, int x, const char *s)
|
||||||
|
{
|
||||||
|
printf("ShimBar [self=%p x=%d s=\"%s\"]\n", self, x, s);
|
||||||
|
|
||||||
|
if (IS_EXTERNAL_IMPL(self))
|
||||||
|
{
|
||||||
|
((Inner *) self)->bar(x, s);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf(" ++ running our bar!\n");
|
||||||
|
}
|
||||||
17
vtable_hide/vtable_hide/shim.h
Normal file
17
vtable_hide/vtable_hide/shim.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#ifndef shim_h__
|
||||||
|
#define shim_h__
|
||||||
|
|
||||||
|
struct Shim
|
||||||
|
{
|
||||||
|
void *vptr;
|
||||||
|
char *data;
|
||||||
|
int length;
|
||||||
|
int flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
void ShimCtor(Shim *);
|
||||||
|
void ShimDtor(Shim *);
|
||||||
|
void ShimFoo(Shim *, int x);
|
||||||
|
void ShimBar(Shim *, int x, const char *s);
|
||||||
|
|
||||||
|
#endif
|
||||||
70
vtable_hide/vtable_hide/test.cc
Normal file
70
vtable_hide/vtable_hide/test.cc
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
// vim:set cindent:
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "shim.h"
|
||||||
|
#include "inner.h"
|
||||||
|
|
||||||
|
class Outer
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
Shim shim;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Outer() { ShimCtor(&shim); }
|
||||||
|
~Outer() { ShimDtor(&shim); }
|
||||||
|
|
||||||
|
void foo(int x) { ShimFoo(&shim, x); }
|
||||||
|
void bar(int x, const char *s) { ShimBar(&shim, x,s); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class ExtImpl : public Inner
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ExtImpl() {}
|
||||||
|
|
||||||
|
virtual ~ExtImpl()
|
||||||
|
{
|
||||||
|
printf(" -- ExtImpl::~ExtImpl [this=%p]\n", this);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void foo(int x)
|
||||||
|
{
|
||||||
|
printf(" -- ExtImpl::foo [this=%p x=%d]\n", this, x);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void bar(int x, const char *s)
|
||||||
|
{
|
||||||
|
printf(" -- ExtImpl::bar [this=%p x=%d s=\"%s\"]\n", this, x, s);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
printf("\ntest use of our class directly:\n\n");
|
||||||
|
{
|
||||||
|
Outer outer;
|
||||||
|
outer.foo(10);
|
||||||
|
outer.bar(10, "hello");
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\n\ntest use of embedders class, interpreted as if it were our class:\n\n");
|
||||||
|
{
|
||||||
|
ExtImpl *ext = new ExtImpl();
|
||||||
|
Outer *outer = reinterpret_cast<Outer *>(ext);
|
||||||
|
outer->foo(5);
|
||||||
|
outer->bar(5, "world");
|
||||||
|
delete outer;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\n\ntest use of our class, interpreted as if it were abstract class:\n\n");
|
||||||
|
{
|
||||||
|
Outer *outer = new Outer();
|
||||||
|
Inner *inner = reinterpret_cast<Inner *>(outer);
|
||||||
|
inner->foo(1);
|
||||||
|
inner->bar(1, "crazy");
|
||||||
|
delete inner;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user