diff --git a/vtable_hide/string/Makefile b/vtable_hide/string/Makefile new file mode 100644 index 00000000000..333f3e40737 --- /dev/null +++ b/vtable_hide/string/Makefile @@ -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 diff --git a/vtable_hide/string/nsCharTraits.cpp b/vtable_hide/string/nsCharTraits.cpp new file mode 100644 index 00000000000..b011c1fadf7 --- /dev/null +++ b/vtable_hide/string/nsCharTraits.cpp @@ -0,0 +1,3 @@ +#include "nsCharTraits.h" + +const char *nsCharTraits::empty_string = ""; diff --git a/vtable_hide/string/nsCharTraits.h b/vtable_hide/string/nsCharTraits.h new file mode 100644 index 00000000000..cdde39a54be --- /dev/null +++ b/vtable_hide/string/nsCharTraits.h @@ -0,0 +1,40 @@ +/* vim:set ts=2 sw=2 et cindent: */ +#ifndef nsCharTraits_h__ +#define nsCharTraits_h__ + +#include "types.h" // XXX temporary +#include + +template struct nsCharTraits {}; + +NS_SPECIALIZE_TEMPLATE +struct nsCharTraits +{ + 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__ diff --git a/vtable_hide/string/nsStrRef.cpp b/vtable_hide/string/nsStrRef.cpp new file mode 100644 index 00000000000..abae158ba92 --- /dev/null +++ b/vtable_hide/string/nsStrRef.cpp @@ -0,0 +1,543 @@ +// vim:set ts=2 sw=2 et cindent: + +#include +#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; +} diff --git a/vtable_hide/string/nsStrRef.h b/vtable_hide/string/nsStrRef.h new file mode 100644 index 00000000000..af1f956c8e5 --- /dev/null +++ b/vtable_hide/string/nsStrRef.h @@ -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_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_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__ diff --git a/vtable_hide/string/test.cpp b/vtable_hide/string/test.cpp new file mode 100644 index 00000000000..94c96637ed2 --- /dev/null +++ b/vtable_hide/string/test.cpp @@ -0,0 +1,62 @@ +#include +#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; +} diff --git a/vtable_hide/string/tt.cc b/vtable_hide/string/tt.cc new file mode 100644 index 00000000000..9718000e7fc --- /dev/null +++ b/vtable_hide/string/tt.cc @@ -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; +} diff --git a/vtable_hide/string/types.h b/vtable_hide/string/types.h new file mode 100644 index 00000000000..14d8f684c06 --- /dev/null +++ b/vtable_hide/string/types.h @@ -0,0 +1,28 @@ +/* vim:set ts=2 sw=2 et cindent: */ + +#ifndef str_types_h__ +#define str_types_h__ + +/* -------------------------------------------------------------------------- */ +#include +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(y) +#define NS_STATIC_CAST(x,y) static_cast(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 diff --git a/vtable_hide/vtable_foo/Makefile b/vtable_hide/vtable_foo/Makefile new file mode 100644 index 00000000000..d2c48c64992 --- /dev/null +++ b/vtable_hide/vtable_foo/Makefile @@ -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 diff --git a/vtable_hide/vtable_foo/libfoo++.cc b/vtable_hide/vtable_foo/libfoo++.cc new file mode 100644 index 00000000000..52ec986c2e8 --- /dev/null +++ b/vtable_hide/vtable_foo/libfoo++.cc @@ -0,0 +1,41 @@ +#include +#include + +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(); +} diff --git a/vtable_hide/vtable_foo/libfoo.c b/vtable_hide/vtable_foo/libfoo.c new file mode 100644 index 00000000000..802c45b87d5 --- /dev/null +++ b/vtable_hide/vtable_foo/libfoo.c @@ -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; +} diff --git a/vtable_hide/vtable_foo/libfoo.h b/vtable_hide/vtable_foo/libfoo.h new file mode 100644 index 00000000000..a01e05d584a --- /dev/null +++ b/vtable_hide/vtable_foo/libfoo.h @@ -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 diff --git a/vtable_hide/vtable_foo/test b/vtable_hide/vtable_foo/test new file mode 100755 index 00000000000..752a285ffff Binary files /dev/null and b/vtable_hide/vtable_foo/test differ diff --git a/vtable_hide/vtable_foo/test++ b/vtable_hide/vtable_foo/test++ new file mode 100755 index 00000000000..c9331a9dc2e Binary files /dev/null and b/vtable_hide/vtable_foo/test++ differ diff --git a/vtable_hide/vtable_foo/test.cc b/vtable_hide/vtable_foo/test.cc new file mode 100644 index 00000000000..1ddde9a19db --- /dev/null +++ b/vtable_hide/vtable_foo/test.cc @@ -0,0 +1,17 @@ +#include +#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; +} diff --git a/vtable_hide/vtable_hide.tar.gz b/vtable_hide/vtable_hide.tar.gz new file mode 100644 index 00000000000..99e5e34d201 Binary files /dev/null and b/vtable_hide/vtable_hide.tar.gz differ diff --git a/vtable_hide/vtable_hide/Makefile b/vtable_hide/vtable_hide/Makefile new file mode 100644 index 00000000000..d44e7a284d8 --- /dev/null +++ b/vtable_hide/vtable_hide/Makefile @@ -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 diff --git a/vtable_hide/vtable_hide/inner.h b/vtable_hide/vtable_hide/inner.h new file mode 100644 index 00000000000..a8b37a5cb04 --- /dev/null +++ b/vtable_hide/vtable_hide/inner.h @@ -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 diff --git a/vtable_hide/vtable_hide/shim.cc b/vtable_hide/vtable_hide/shim.cc new file mode 100644 index 00000000000..22f40fe239d --- /dev/null +++ b/vtable_hide/vtable_hide/shim.cc @@ -0,0 +1,98 @@ +// vim:set cindent: + +#include +#include +#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"); +} diff --git a/vtable_hide/vtable_hide/shim.h b/vtable_hide/vtable_hide/shim.h new file mode 100644 index 00000000000..3c641027bba --- /dev/null +++ b/vtable_hide/vtable_hide/shim.h @@ -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 diff --git a/vtable_hide/vtable_hide/test.cc b/vtable_hide/vtable_hide/test.cc new file mode 100644 index 00000000000..b2c8775a347 --- /dev/null +++ b/vtable_hide/vtable_hide/test.cc @@ -0,0 +1,70 @@ +// vim:set cindent: + +#include +#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(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(outer); + inner->foo(1); + inner->bar(1, "crazy"); + delete inner; + } + + printf("\n"); + return 0; +}