From f7e715daeeee351bade5aacd6e53c1754f5cea6d Mon Sep 17 00:00:00 2001 From: Markus Scherer Date: Thu, 28 May 2015 17:03:45 +0000 Subject: [PATCH] ICU-10086 move & swap for UnicodeString, LocalPointer, LocalArray X-SVN-Rev: 37469 --- icu4c/source/common/cmemory.h | 53 +++++ icu4c/source/common/unicode/localpointer.h | 232 ++++++++++++++++++++- icu4c/source/common/unicode/platform.h | 64 +++++- icu4c/source/common/unicode/umachine.h | 4 +- icu4c/source/common/unicode/unistr.h | 62 ++++++ icu4c/source/common/unistr.cpp | 48 +++++ icu4c/source/i18n/brktrans.cpp | 8 +- icu4c/source/test/intltest/itutil.cpp | 187 ++++++++++++++++- icu4c/source/test/intltest/ustrtest.cpp | 125 +++++++---- icu4c/source/test/intltest/ustrtest.h | 3 +- 10 files changed, 733 insertions(+), 53 deletions(-) diff --git a/icu4c/source/common/cmemory.h b/icu4c/source/common/cmemory.h index d182b5aa7d1..f997d9224f8 100644 --- a/icu4c/source/common/cmemory.h +++ b/icu4c/source/common/cmemory.h @@ -158,12 +158,55 @@ public: * @param p simple pointer to an array of T items that is adopted */ explicit LocalMemory(T *p=NULL) : LocalPointerBase(p) {} +#if U_HAVE_RVALUE_REFERENCES + /** + * Move constructor, leaves src with isNull(). + * @param src source smart pointer + */ + LocalMemory(LocalMemory &&src) U_NOEXCEPT : LocalPointerBase(src.ptr) { + src.ptr=NULL; + } +#endif /** * Destructor deletes the memory it owns. */ ~LocalMemory() { uprv_free(LocalPointerBase::ptr); } +#if U_HAVE_RVALUE_REFERENCES + /** + * Move assignment operator, leaves src with isNull(). + * The behavior is undefined if *this and src are the same object. + * @param src source smart pointer + * @return *this + */ + LocalMemory &operator=(LocalMemory &&src) U_NOEXCEPT { + return moveFrom(src); + } +#endif + /** + * Move assignment, leaves src with isNull(). + * The behavior is undefined if *this and src are the same object. + * + * Can be called explicitly, does not need C++11 support. + * @param src source smart pointer + * @return *this + */ + LocalMemory &moveFrom(LocalMemory &src) U_NOEXCEPT { + delete[] LocalPointerBase::ptr; + LocalPointerBase::ptr=src.ptr; + src.ptr=NULL; + return *this; + } + /** + * Swap pointers. + * @param other other smart pointer + */ + void swap(LocalMemory &other) U_NOEXCEPT { + T *temp=LocalPointerBase::ptr; + LocalPointerBase::ptr=other.ptr; + other.ptr=temp; + } /** * Deletes the array it owns, * and adopts (takes ownership of) the one passed in. @@ -203,6 +246,16 @@ public: T &operator[](ptrdiff_t i) const { return LocalPointerBase::ptr[i]; } }; +/** + * Non-member LocalMemory swap function. + * @param p1 will get p2's pointer + * @param p2 will get p1's pointer + */ +template +inline void swap(LocalMemory &p1, LocalMemory &p2) U_NOEXCEPT { + p1.swap(p2); +} + template inline T *LocalMemory::allocateInsteadAndReset(int32_t newCapacity) { if(newCapacity>0) { diff --git a/icu4c/source/common/unicode/localpointer.h b/icu4c/source/common/unicode/localpointer.h index e3aabf65c38..87df956ca0b 100644 --- a/icu4c/source/common/unicode/localpointer.h +++ b/icu4c/source/common/unicode/localpointer.h @@ -1,7 +1,7 @@ /* ******************************************************************************* * -* Copyright (C) 2009-2014, International Business Machines +* Copyright (C) 2009-2015, International Business Machines * Corporation and others. All Rights Reserved. * ******************************************************************************* @@ -151,11 +151,11 @@ protected: T *ptr; private: // No comparison operators with other LocalPointerBases. - bool operator==(const LocalPointerBase &other); - bool operator!=(const LocalPointerBase &other); - // No ownership transfer: No copy constructor, no assignment operator. - LocalPointerBase(const LocalPointerBase &other); - void operator=(const LocalPointerBase &other); + bool operator==(const LocalPointerBase &other); + bool operator!=(const LocalPointerBase &other); + // No ownership sharing: No copy constructor, no assignment operator. + LocalPointerBase(const LocalPointerBase &other); + void operator=(const LocalPointerBase &other); // No heap allocation. Use only on the stack. static void * U_EXPORT2 operator new(size_t size); static void * U_EXPORT2 operator new[](size_t size); @@ -210,6 +210,16 @@ public: errorCode=U_MEMORY_ALLOCATION_ERROR; } } +#if U_HAVE_RVALUE_REFERENCES + /** + * Move constructor, leaves src with isNull(). + * @param src source smart pointer + * @draft ICU 56 + */ + LocalPointer(LocalPointer &&src) U_NOEXCEPT : LocalPointerBase(src.ptr) { + src.ptr=NULL; + } +#endif #endif /* U_HIDE_DRAFT_API */ /** * Destructor deletes the object it owns. @@ -218,6 +228,45 @@ public: ~LocalPointer() { delete LocalPointerBase::ptr; } +#ifndef U_HIDE_DRAFT_API +#if U_HAVE_RVALUE_REFERENCES + /** + * Move assignment operator, leaves src with isNull(). + * The behavior is undefined if *this and src are the same object. + * @param src source smart pointer + * @return *this + * @draft ICU 56 + */ + LocalPointer &operator=(LocalPointer &&src) U_NOEXCEPT { + return moveFrom(src); + } +#endif + /** + * Move assignment, leaves src with isNull(). + * The behavior is undefined if *this and src are the same object. + * + * Can be called explicitly, does not need C++11 support. + * @param src source smart pointer + * @return *this + * @draft ICU 56 + */ + LocalPointer &moveFrom(LocalPointer &src) U_NOEXCEPT { + delete LocalPointerBase::ptr; + LocalPointerBase::ptr=src.ptr; + src.ptr=NULL; + return *this; + } + /** + * Swap pointers. + * @param other other smart pointer + * @draft ICU 56 + */ + void swap(LocalPointer &other) U_NOEXCEPT { + T *temp=LocalPointerBase::ptr; + LocalPointerBase::ptr=other.ptr; + other.ptr=temp; + } +#endif /* U_HIDE_DRAFT_API */ /** * Deletes the object it owns, * and adopts (takes ownership of) the one passed in. @@ -258,6 +307,19 @@ public: #endif /* U_HIDE_DRAFT_API */ }; +#ifndef U_HIDE_DRAFT_API +/** + * Non-member LocalPointer swap function. + * @param p1 will get p2's pointer + * @param p2 will get p1's pointer + * @draft ICU 56 + */ +template +inline void swap(LocalPointer &p1, LocalPointer &p2) U_NOEXCEPT { + p1.swap(p2); +} +#endif /* U_HIDE_DRAFT_API */ + /** * "Smart pointer" class, deletes objects via the C++ array delete[] operator. * For most methods see the LocalPointerBase base class. @@ -285,6 +347,36 @@ public: * @stable ICU 4.4 */ explicit LocalArray(T *p=NULL) : LocalPointerBase(p) {} +#ifndef U_HIDE_DRAFT_API + /** + * Constructor takes ownership and reports an error if NULL. + * + * This constructor is intended to be used with other-class constructors + * that may report a failure UErrorCode, + * so that callers need to check only for U_FAILURE(errorCode) + * and not also separately for isNull(). + * + * @param p simple pointer to an array of T objects that is adopted + * @param errorCode in/out UErrorCode, set to U_MEMORY_ALLOCATION_ERROR + * if p==NULL and no other failure code had been set + * @draft ICU 56 + */ + LocalArray(T *p, UErrorCode &errorCode) : LocalPointerBase(p) { + if(p==NULL && U_SUCCESS(errorCode)) { + errorCode=U_MEMORY_ALLOCATION_ERROR; + } + } +#if U_HAVE_RVALUE_REFERENCES + /** + * Move constructor, leaves src with isNull(). + * @param src source smart pointer + * @draft ICU 56 + */ + LocalArray(LocalArray &&src) U_NOEXCEPT : LocalPointerBase(src.ptr) { + src.ptr=NULL; + } +#endif +#endif /* U_HIDE_DRAFT_API */ /** * Destructor deletes the array it owns. * @stable ICU 4.4 @@ -292,6 +384,45 @@ public: ~LocalArray() { delete[] LocalPointerBase::ptr; } +#ifndef U_HIDE_DRAFT_API +#if U_HAVE_RVALUE_REFERENCES + /** + * Move assignment operator, leaves src with isNull(). + * The behavior is undefined if *this and src are the same object. + * @param src source smart pointer + * @return *this + * @draft ICU 56 + */ + LocalArray &operator=(LocalArray &&src) U_NOEXCEPT { + return moveFrom(src); + } +#endif + /** + * Move assignment, leaves src with isNull(). + * The behavior is undefined if *this and src are the same object. + * + * Can be called explicitly, does not need C++11 support. + * @param src source smart pointer + * @return *this + * @draft ICU 56 + */ + LocalArray &moveFrom(LocalArray &src) U_NOEXCEPT { + delete[] LocalPointerBase::ptr; + LocalPointerBase::ptr=src.ptr; + src.ptr=NULL; + return *this; + } + /** + * Swap pointers. + * @param other other smart pointer + * @draft ICU 56 + */ + void swap(LocalArray &other) U_NOEXCEPT { + T *temp=LocalPointerBase::ptr; + LocalPointerBase::ptr=other.ptr; + other.ptr=temp; + } +#endif /* U_HIDE_DRAFT_API */ /** * Deletes the array it owns, * and adopts (takes ownership of) the one passed in. @@ -302,6 +433,34 @@ public: delete[] LocalPointerBase::ptr; LocalPointerBase::ptr=p; } +#ifndef U_HIDE_DRAFT_API + /** + * Deletes the array it owns, + * and adopts (takes ownership of) the one passed in. + * + * If U_FAILURE(errorCode), then the current array is retained and the new one deleted. + * + * If U_SUCCESS(errorCode) but the input pointer is NULL, + * then U_MEMORY_ALLOCATION_ERROR is set, + * the current array is deleted, and NULL is set. + * + * @param p simple pointer to an array of T objects that is adopted + * @param errorCode in/out UErrorCode, set to U_MEMORY_ALLOCATION_ERROR + * if p==NULL and no other failure code had been set + * @draft ICU 56 + */ + void adoptInsteadAndCheckErrorCode(T *p, UErrorCode &errorCode) { + if(U_SUCCESS(errorCode)) { + delete[] LocalPointerBase::ptr; + LocalPointerBase::ptr=p; + if(p==NULL) { + errorCode=U_MEMORY_ALLOCATION_ERROR; + } + } else { + delete[] p; + } + } +#endif /* U_HIDE_DRAFT_API */ /** * Array item access (writable). * No index bounds check. @@ -312,6 +471,19 @@ public: T &operator[](ptrdiff_t i) const { return LocalPointerBase::ptr[i]; } }; +#ifndef U_HIDE_DRAFT_API +/** + * Non-member LocalArray swap function. + * @param p1 will get p2's pointer + * @param p2 will get p1's pointer + * @draft ICU 56 + */ +template +inline void swap(LocalArray &p1, LocalArray &p2) U_NOEXCEPT { + p1.swap(p2); +} +#endif /* U_HIDE_DRAFT_API */ + /** * \def U_DEFINE_LOCAL_OPEN_POINTER * "Smart pointer" definition macro, deletes objects via the closeFunction. @@ -335,16 +507,64 @@ public: * @see LocalPointer * @stable ICU 4.4 */ +#if U_HAVE_RVALUE_REFERENCES +#define U_DEFINE_LOCAL_OPEN_POINTER(LocalPointerClassName, Type, closeFunction) \ + class LocalPointerClassName : public LocalPointerBase { \ + public: \ + explicit LocalPointerClassName(Type *p=NULL) : LocalPointerBase(p) {} \ + LocalPointerClassName(LocalPointerClassName &&src) U_NOEXCEPT \ + : LocalPointerBase(src.ptr) { \ + src.ptr=NULL; \ + } \ + ~LocalPointerClassName() { closeFunction(ptr); } \ + LocalPointerClassName &operator=(LocalPointerClassName &&src) U_NOEXCEPT { \ + return moveFrom(src); \ + } \ + LocalPointerClassName &moveFrom(LocalPointerClassName &src) U_NOEXCEPT { \ + closeFunction(ptr); \ + LocalPointerBase::ptr=src.ptr; \ + src.ptr=NULL; \ + return *this; \ + } \ + void swap(LocalPointerClassName &other) U_NOEXCEPT { \ + Type *temp=LocalPointerBase::ptr; \ + LocalPointerBase::ptr=other.ptr; \ + other.ptr=temp; \ + } \ + void adoptInstead(Type *p) { \ + closeFunction(ptr); \ + ptr=p; \ + } \ + }; \ + inline void swap(LocalPointerClassName &p1, LocalPointerClassName &p2) U_NOEXCEPT { \ + p1.swap(p2); \ + } +#else #define U_DEFINE_LOCAL_OPEN_POINTER(LocalPointerClassName, Type, closeFunction) \ class LocalPointerClassName : public LocalPointerBase { \ public: \ explicit LocalPointerClassName(Type *p=NULL) : LocalPointerBase(p) {} \ ~LocalPointerClassName() { closeFunction(ptr); } \ + LocalPointerClassName &moveFrom(LocalPointerClassName &src) U_NOEXCEPT { \ + closeFunction(ptr); \ + LocalPointerBase::ptr=src.ptr; \ + src.ptr=NULL; \ + return *this; \ + } \ + void swap(LocalPointerClassName &other) U_NOEXCEPT { \ + Type *temp=LocalPointerBase::ptr; \ + LocalPointerBase::ptr=other.ptr; \ + other.ptr=temp; \ + } \ void adoptInstead(Type *p) { \ closeFunction(ptr); \ ptr=p; \ } \ + }; \ + inline void swap(LocalPointerClassName &p1, LocalPointerClassName &p2) U_NOEXCEPT { \ + p1.swap(p2); \ } +#endif U_NAMESPACE_END diff --git a/icu4c/source/common/unicode/platform.h b/icu4c/source/common/unicode/platform.h index 482900069d1..d040ba4e03f 100644 --- a/icu4c/source/common/unicode/platform.h +++ b/icu4c/source/common/unicode/platform.h @@ -429,6 +429,12 @@ #ifndef __has_attribute # define __has_attribute(x) 0 #endif +#ifndef __has_feature +# define __has_feature(x) 0 +#endif +#ifndef __has_extension +# define __has_extension(x) 0 +#endif /** * \def U_MALLOC_ATTR @@ -454,6 +460,62 @@ # define U_ALLOC_SIZE_ATTR2(X,Y) #endif +/** + * \def U_CPLUSPLUS_VERSION + * 0 if no C++; 1, 11, 14, ... if C++. + * Support for specific features cannot always be determined by the C++ version alone. + * @internal + */ +#ifdef U_CPLUSPLUS_VERSION +# if U_CPLUSPLUS_VERSION != 0 && !defined(__cplusplus) +# undef U_CPLUSPLUS_VERSION +# define U_CPLUSPLUS_VERSION 0 +# endif + /* Otherwise use the predefined value. */ +#elif !defined(__cplusplus) +# define U_CPLUSPLUS_VERSION 0 +#elif __cplusplus >= 201402L +# define U_CPLUSPLUS_VERSION 14 +#elif __cplusplus >= 201103L +# define U_CPLUSPLUS_VERSION 11 +#else + // C++98 or C++03 +# define U_CPLUSPLUS_VERSION 1 +#endif + +/** + * \def U_HAVE_RVALUE_REFERENCES + * Set to 1 if the compiler supports rvalue references. + * C++11 feature, necessary for move constructor & move assignment. + * @internal + */ +#ifdef U_HAVE_RVALUE_REFERENCES + /* Use the predefined value. */ +#elif U_CPLUSPLUS_VERSION >= 11 || __has_feature(cxx_rvalue_references) || __has_extension(cxx_rvalue_references) \ + || defined(__GXX_EXPERIMENTAL_CXX0X__) \ + || (defined(_MSC_VER) && _MSC_VER >= 1600) /* Visual Studio 2010 */ +# define U_HAVE_RVALUE_REFERENCES 1 +#else +# define U_HAVE_RVALUE_REFERENCES 0 +#endif + +/** + * \def U_NOEXCEPT + * "noexcept" if supported, otherwise empty. + * Some code, especially STL containers, uses move semantics of objects only + * if the move constructor and the move operator are declared as not throwing exceptions. + * @internal + */ +#ifdef U_NOEXCEPT + /* Use the predefined value. */ +#elif U_CPLUSPLUS_VERSION >= 11 || __has_feature(cxx_noexcept) || __has_extension(cxx_noexcept) \ + || U_GCC_MAJOR_MINOR >= 406 \ + || (defined(_MSC_VER) && _MSC_VER >= 1900) /* Visual Studio 2015 */ +# define U_NOEXCEPT noexcept +#else +# define U_NOEXCEPT +#endif + /** @} */ /*===========================================================================*/ @@ -670,7 +732,7 @@ * does not support u"abc" string literals. * C++11 and C11 require support for UTF-16 literals */ -# if (defined(__cplusplus) && __cplusplus >= 201103L) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) +# if U_CPLUSPLUS_VERSION >= 11 || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) # define U_HAVE_CHAR16_T 1 # else # define U_HAVE_CHAR16_T 0 diff --git a/icu4c/source/common/unicode/umachine.h b/icu4c/source/common/unicode/umachine.h index 532159216a5..939a11706ad 100644 --- a/icu4c/source/common/unicode/umachine.h +++ b/icu4c/source/common/unicode/umachine.h @@ -1,7 +1,7 @@ /* ****************************************************************************** * -* Copyright (C) 1999-2014, International Business Machines +* Copyright (C) 1999-2015, International Business Machines * Corporation and others. All Rights Reserved. * ****************************************************************************** @@ -132,7 +132,7 @@ * @internal */ -#if defined(__cplusplus) && __cplusplus>=201103L +#if U_CPLUSPLUS_VERSION >= 11 /* C++11 */ #ifndef U_OVERRIDE #define U_OVERRIDE override diff --git a/icu4c/source/common/unicode/unistr.h b/icu4c/source/common/unicode/unistr.h index f841594abbe..179b9c2666b 100644 --- a/icu4c/source/common/unicode/unistr.h +++ b/icu4c/source/common/unicode/unistr.h @@ -1877,6 +1877,40 @@ public: */ UnicodeString &fastCopyFrom(const UnicodeString &src); +#ifndef U_HIDE_DRAFT_API +#if U_HAVE_RVALUE_REFERENCES + /** + * Move assignment operator, might leave src in bogus state. + * This string will have the same contents and state that the source string had. + * The behavior is undefined if *this and src are the same object. + * @param src source string + * @return *this + * @draft ICU 56 + */ + UnicodeString &operator=(UnicodeString &&src) U_NOEXCEPT { + return moveFrom(src); + } +#endif + /** + * Move assignment, might leave src in bogus state. + * This string will have the same contents and state that the source string had. + * The behavior is undefined if *this and src are the same object. + * + * Can be called explicitly, does not need C++11 support. + * @param src source string + * @return *this + * @draft ICU 56 + */ + UnicodeString &moveFrom(UnicodeString &src) U_NOEXCEPT; + + /** + * Swap strings. + * @param other other string + * @draft ICU 56 + */ + void swap(UnicodeString &other) U_NOEXCEPT; +#endif /* U_HIDE_DRAFT_API */ + /** * Assignment operator. Replace the characters in this UnicodeString * with the code unit ch. @@ -3131,6 +3165,18 @@ public: */ UnicodeString(const UnicodeString& that); +#ifndef U_HIDE_DRAFT_API +#if U_HAVE_RVALUE_REFERENCES + /** + * Move constructor, might leave src in bogus state. + * This string will have the same contents and state that the source string had. + * @param src source string + * @draft ICU 56 + */ + UnicodeString(UnicodeString &&src) U_NOEXCEPT; +#endif +#endif /* U_HIDE_DRAFT_API */ + /** * 'Substring' constructor from tail of source string. * @param src The UnicodeString object to copy. @@ -3440,6 +3486,9 @@ private: // implements assigment operator, copy constructor, and fastCopyFrom() UnicodeString ©From(const UnicodeString &src, UBool fastCopy=FALSE); + // Copies just the fields without memory management. + void copyFieldsFrom(UnicodeString &src, UBool setSrcToBogus) U_NOEXCEPT; + // Pin start and limit to acceptable values. inline void pinIndex(int32_t& start) const; inline void pinIndices(int32_t& start, @@ -3619,6 +3668,19 @@ private: U_COMMON_API UnicodeString U_EXPORT2 operator+ (const UnicodeString &s1, const UnicodeString &s2); +#ifndef U_HIDE_DRAFT_API +/** + * Non-member UnicodeString swap function. + * @param s1 will get s2's contents and state + * @param s2 will get s1's contents and state + * @draft ICU 56 + */ +U_COMMON_API inline void U_EXPORT2 +swap(UnicodeString &s1, UnicodeString &s2) U_NOEXCEPT { + s1.swap(s2); +} +#endif /* U_HIDE_DRAFT_API */ + //======================================== // Inline members //======================================== diff --git a/icu4c/source/common/unistr.cpp b/icu4c/source/common/unistr.cpp index a995d76dacc..bd53c5a77c5 100644 --- a/icu4c/source/common/unistr.cpp +++ b/icu4c/source/common/unistr.cpp @@ -306,6 +306,13 @@ UnicodeString::UnicodeString(const UnicodeString& that) { copyFrom(that); } +#if U_HAVE_RVALUE_REFERENCES +UnicodeString::UnicodeString(UnicodeString &&src) U_NOEXCEPT { + fUnion.fFields.fLengthAndFlags = kShortString; + moveFrom(src); +} +#endif + UnicodeString::UnicodeString(const UnicodeString& that, int32_t srcStart) { fUnion.fFields.fLengthAndFlags = kShortString; @@ -536,6 +543,47 @@ UnicodeString::copyFrom(const UnicodeString &src, UBool fastCopy) { return *this; } +UnicodeString &UnicodeString::moveFrom(UnicodeString &src) U_NOEXCEPT { + // No explicit check for self move assignment, consistent with standard library. + // Self move assignment causes no crash nor leak but might make the object bogus. + releaseArray(); + copyFieldsFrom(src, TRUE); + return *this; +} + +// Same as moveFrom() except without memory management. +void UnicodeString::copyFieldsFrom(UnicodeString &src, UBool setSrcToBogus) U_NOEXCEPT { + int16_t lengthAndFlags = fUnion.fFields.fLengthAndFlags = src.fUnion.fFields.fLengthAndFlags; + if(lengthAndFlags & kUsingStackBuffer) { + // Short string using the stack buffer, copy the contents. + uprv_memcpy(fUnion.fStackFields.fBuffer, src.fUnion.fStackFields.fBuffer, + getShortLength() * U_SIZEOF_UCHAR); + } else { + // In all other cases, copy all fields. + fUnion.fFields.fArray = src.fUnion.fFields.fArray; + fUnion.fFields.fCapacity = src.fUnion.fFields.fCapacity; + if(!hasShortLength()) { + fUnion.fFields.fLength = src.fUnion.fFields.fLength; + } + if(setSrcToBogus) { + // Set src to bogus without releasing any memory. + src.fUnion.fFields.fLengthAndFlags = kIsBogus; + src.fUnion.fFields.fArray = NULL; + src.fUnion.fFields.fCapacity = 0; + } + } +} + +void UnicodeString::swap(UnicodeString &other) U_NOEXCEPT { + UnicodeString temp; // Empty short string: Known not to need releaseArray(). + // Copy fields without resetting source values in between. + temp.copyFieldsFrom(*this, FALSE); + this->copyFieldsFrom(other, FALSE); + other.copyFieldsFrom(temp, FALSE); + // Set temp to an empty string so that other's memory is not released twice. + temp.fUnion.fFields.fLengthAndFlags = kShortString; +} + //======================================== // Miscellaneous operations //======================================== diff --git a/icu4c/source/i18n/brktrans.cpp b/icu4c/source/i18n/brktrans.cpp index 10505494fd4..dafac399f85 100644 --- a/icu4c/source/i18n/brktrans.cpp +++ b/icu4c/source/i18n/brktrans.cpp @@ -77,8 +77,8 @@ void BreakTransliterator::handleTransliterate(Replaceable& text, UTransPosition& { Mutex m; BreakTransliterator *nonConstThis = const_cast(this); - boundaries.adoptInstead(nonConstThis->cachedBoundaries.orphan()); - bi.adoptInstead(nonConstThis->cachedBI.orphan()); + boundaries.moveFrom(nonConstThis->cachedBoundaries); + bi.moveFrom(nonConstThis->cachedBI); } if (bi.isNull()) { bi.adoptInstead(BreakIterator::createWordInstance(Locale::getEnglish(), status)); @@ -143,10 +143,10 @@ void BreakTransliterator::handleTransliterate(Replaceable& text, UTransPosition& Mutex m; BreakTransliterator *nonConstThis = const_cast(this); if (nonConstThis->cachedBI.isNull()) { - nonConstThis->cachedBI.adoptInstead(bi.orphan()); + nonConstThis->cachedBI.moveFrom(bi); } if (nonConstThis->cachedBoundaries.isNull()) { - nonConstThis->cachedBoundaries.adoptInstead(boundaries.orphan()); + nonConstThis->cachedBoundaries.moveFrom(boundaries); } } diff --git a/icu4c/source/test/intltest/itutil.cpp b/icu4c/source/test/intltest/itutil.cpp index ce2c5a85d7a..40ff1d28072 100644 --- a/icu4c/source/test/intltest/itutil.cpp +++ b/icu4c/source/test/intltest/itutil.cpp @@ -1,6 +1,6 @@ /******************************************************************** * COPYRIGHT: - * Copyright (c) 1997-2014, International Business Machines Corporation and + * Copyright (c) 1997-2015, International Business Machines Corporation and * others. All Rights Reserved. ********************************************************************/ @@ -250,8 +250,11 @@ public: void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par=NULL); void TestLocalPointer(); + void TestLocalPointerMoveSwap(); void TestLocalArray(); + void TestLocalArrayMoveSwap(); void TestLocalXyzPointer(); + void TestLocalXyzPointerMoveSwap(); void TestLocalXyzPointerNull(); }; @@ -265,13 +268,16 @@ void LocalPointerTest::runIndexedTest(int32_t index, UBool exec, const char *&na } TESTCASE_AUTO_BEGIN; TESTCASE_AUTO(TestLocalPointer); + TESTCASE_AUTO(TestLocalPointerMoveSwap); TESTCASE_AUTO(TestLocalArray); + TESTCASE_AUTO(TestLocalArrayMoveSwap); TESTCASE_AUTO(TestLocalXyzPointer); + TESTCASE_AUTO(TestLocalXyzPointerMoveSwap); TESTCASE_AUTO(TestLocalXyzPointerNull); TESTCASE_AUTO_END; } -// Exercise every LocalPointer and LocalPointerBase method. +// Exercise almost every LocalPointer and LocalPointerBase method. void LocalPointerTest::TestLocalPointer() { // constructor LocalPointer s(new UnicodeString((UChar32)0x50005)); @@ -294,7 +300,6 @@ void LocalPointerTest::TestLocalPointer() { errln("LocalPointer orphan() failure"); } delete orphan; - // destructor s.adoptInstead(new UnicodeString()); if(s->length()!=0) { errln("LocalPointer adoptInstead(empty) failure"); @@ -336,9 +341,50 @@ void LocalPointerTest::TestLocalPointer() { errln("LocalPointer(NULL, errorCode) did not set U_MEMORY_ALLOCATION_ERROR"); return; } + + // destructor } -// Exercise every LocalArray method (but not LocalPointerBase). +void LocalPointerTest::TestLocalPointerMoveSwap() { + UnicodeString *p1 = new UnicodeString((UChar)0x61); + UnicodeString *p2 = new UnicodeString((UChar)0x62); + LocalPointer s1(p1); + LocalPointer s2(p2); + s1.swap(s2); + if(s1.getAlias() != p2 || s2.getAlias() != p1) { + errln("LocalPointer.swap() did not swap"); + } + swap(s1, s2); + if(s1.getAlias() != p1 || s2.getAlias() != p2) { + errln("swap(LocalPointer) did not swap back"); + } + LocalPointer s3; + s3.moveFrom(s1); + if(s3.getAlias() != p1 || s1.isValid()) { + errln("LocalPointer.moveFrom() did not move"); + } +#if U_HAVE_RVALUE_REFERENCES + infoln("TestLocalPointerMoveSwap() with rvalue references"); + s1 = static_cast &&>(s3); + if(s1.getAlias() != p1 || s3.isValid()) { + errln("LocalPointer move assignment operator did not move"); + } + LocalPointer s4(static_cast &&>(s2)); + if(s4.getAlias() != p2 || s2.isValid()) { + errln("LocalPointer move constructor did not move"); + } +#else + infoln("TestLocalPointerMoveSwap() without rvalue references"); +#endif + + // Move self assignment leaves the object valid but in an undefined state. + // Do it to make sure there is no crash, + // but do not check for any particular resulting value. + s1.moveFrom(s1); + s3.moveFrom(s3); +} + +// Exercise almost every LocalArray method (but not LocalPointerBase). void LocalPointerTest::TestLocalArray() { // constructor LocalArray a(new UnicodeString[2]); @@ -354,9 +400,87 @@ void LocalPointerTest::TestLocalArray() { if(a[3].length()!=2 || a[3][1]!=0x62) { errln("LocalArray adoptInstead() failure"); } + + // LocalArray(p, errorCode) sets U_MEMORY_ALLOCATION_ERROR if p==NULL. + UErrorCode errorCode = U_ZERO_ERROR; + LocalArray ua(new UnicodeString[3], errorCode); + if(ua.isNull() && U_SUCCESS(errorCode)) { + errln("LocalArray(p, errorCode) failure"); + return; + } + errorCode = U_ZERO_ERROR; + UnicodeString *u4 = new UnicodeString[4]; + ua.adoptInsteadAndCheckErrorCode(u4, errorCode); + if(ua.isNull() && U_SUCCESS(errorCode)) { + errln("adoptInsteadAndCheckErrorCode(p, errorCode) failure"); + return; + } + // Incoming failure: Keep the current object and delete the input object. + errorCode = U_ILLEGAL_ARGUMENT_ERROR; + ua.adoptInsteadAndCheckErrorCode(new UnicodeString[5], errorCode); + if(ua.isValid() && ua.getAlias() != u4) { + errln("adoptInsteadAndCheckErrorCode(p, U_FAILURE) did not retain the old array"); + return; + } + errorCode = U_ZERO_ERROR; + ua.adoptInsteadAndCheckErrorCode(NULL, errorCode); + if(errorCode != U_MEMORY_ALLOCATION_ERROR) { + errln("adoptInsteadAndCheckErrorCode(NULL, errorCode) did not set U_MEMORY_ALLOCATION_ERROR"); + return; + } + if(ua.isValid()) { + errln("adoptInsteadAndCheckErrorCode(NULL, errorCode) kept the array"); + return; + } + errorCode = U_ZERO_ERROR; + LocalArray null(NULL, errorCode); + if(errorCode != U_MEMORY_ALLOCATION_ERROR) { + errln("LocalArray(NULL, errorCode) did not set U_MEMORY_ALLOCATION_ERROR"); + return; + } + // destructor } +void LocalPointerTest::TestLocalArrayMoveSwap() { + UnicodeString *p1 = new UnicodeString[2]; + UnicodeString *p2 = new UnicodeString[3]; + LocalArray a1(p1); + LocalArray a2(p2); + a1.swap(a2); + if(a1.getAlias() != p2 || a2.getAlias() != p1) { + errln("LocalArray.swap() did not swap"); + } + swap(a1, a2); + if(a1.getAlias() != p1 || a2.getAlias() != p2) { + errln("swap(LocalArray) did not swap back"); + } + LocalArray a3; + a3.moveFrom(a1); + if(a3.getAlias() != p1 || a1.isValid()) { + errln("LocalArray.moveFrom() did not move"); + } +#if U_HAVE_RVALUE_REFERENCES + infoln("TestLocalArrayMoveSwap() with rvalue references"); + a1 = static_cast &&>(a3); + if(a1.getAlias() != p1 || a3.isValid()) { + errln("LocalArray move assignment operator did not move"); + } + LocalArray a4(static_cast &&>(a2)); + if(a4.getAlias() != p2 || a2.isValid()) { + errln("LocalArray move constructor did not move"); + } +#else + infoln("TestLocalArrayMoveSwap() without rvalue references"); +#endif + + // Move self assignment leaves the object valid but in an undefined state. + // Do it to make sure there is no crash, + // but do not check for any particular resulting value. + a1.moveFrom(a1); + a3.moveFrom(a3); +} + #include "unicode/ucnvsel.h" #include "unicode/ucal.h" #include "unicode/udatpg.h" @@ -475,6 +599,61 @@ void LocalPointerTest::TestLocalXyzPointer() { // destructors } +void LocalPointerTest::TestLocalXyzPointerMoveSwap() { +#if !UCONFIG_NO_NORMALIZATION + IcuTestErrorCode errorCode(*this, "TestLocalXyzPointerMoveSwap"); + const UNormalizer2 *nfc=unorm2_getNFCInstance(errorCode); + const UNormalizer2 *nfd=unorm2_getNFDInstance(errorCode); + if(errorCode.logIfFailureAndReset("unorm2_getNF[CD]Instance()")) { + return; + } + UnicodeSet emptySet; + UNormalizer2 *p1 = unorm2_openFiltered(nfc, emptySet.toUSet(), errorCode); + UNormalizer2 *p2 = unorm2_openFiltered(nfd, emptySet.toUSet(), errorCode); + LocalUNormalizer2Pointer f1(p1); + LocalUNormalizer2Pointer f2(p2); + if(errorCode.logIfFailureAndReset("unorm2_openFiltered()")) { + return; + } + if(f1.isNull() || f2.isNull()) { + errln("LocalUNormalizer2Pointer failure"); + return; + } + f1.swap(f2); + if(f1.getAlias() != p2 || f2.getAlias() != p1) { + errln("LocalUNormalizer2Pointer.swap() did not swap"); + } + swap(f1, f2); + if(f1.getAlias() != p1 || f2.getAlias() != p2) { + errln("swap(LocalUNormalizer2Pointer) did not swap back"); + } + LocalUNormalizer2Pointer f3; + f3.moveFrom(f1); + if(f3.getAlias() != p1 || f1.isValid()) { + errln("LocalUNormalizer2Pointer.moveFrom() did not move"); + } +#if U_HAVE_RVALUE_REFERENCES + infoln("TestLocalXyzPointerMoveSwap() with rvalue references"); + f1 = static_cast(f3); + if(f1.getAlias() != p1 || f3.isValid()) { + errln("LocalUNormalizer2Pointer move assignment operator did not move"); + } + LocalUNormalizer2Pointer f4(static_cast(f2)); + if(f4.getAlias() != p2 || f2.isValid()) { + errln("LocalUNormalizer2Pointer move constructor did not move"); + } +#else + infoln("TestLocalXyzPointerMoveSwap() without rvalue references"); +#endif +#endif /* !UCONFIG_NO_NORMALIZATION */ + + // Move self assignment leaves the object valid but in an undefined state. + // Do it to make sure there is no crash, + // but do not check for any particular resulting value. + f1.moveFrom(f1); + f3.moveFrom(f3); +} + // Try LocalXyzPointer types with NULL pointers. void LocalPointerTest::TestLocalXyzPointerNull() { { diff --git a/icu4c/source/test/intltest/ustrtest.cpp b/icu4c/source/test/intltest/ustrtest.cpp index a730be86f50..4f5623d3364 100644 --- a/icu4c/source/test/intltest/ustrtest.cpp +++ b/icu4c/source/test/intltest/ustrtest.cpp @@ -30,41 +30,33 @@ UnicodeStringTest::~UnicodeStringTest() {} void UnicodeStringTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char *par) { if (exec) logln("TestSuite UnicodeStringTest: "); - switch (index) { - case 0: - name = "StringCaseTest"; - if (exec) { - logln("StringCaseTest---"); logln(""); - StringCaseTest test; - callTest(test, par); - } - break; - case 1: name = "TestBasicManipulation"; if (exec) TestBasicManipulation(); break; - case 2: name = "TestCompare"; if (exec) TestCompare(); break; - case 3: name = "TestExtract"; if (exec) TestExtract(); break; - case 4: name = "TestRemoveReplace"; if (exec) TestRemoveReplace(); break; - case 5: name = "TestSearching"; if (exec) TestSearching(); break; - case 6: name = "TestSpacePadding"; if (exec) TestSpacePadding(); break; - case 7: name = "TestPrefixAndSuffix"; if (exec) TestPrefixAndSuffix(); break; - case 8: name = "TestFindAndReplace"; if (exec) TestFindAndReplace(); break; - case 9: name = "TestBogus"; if (exec) TestBogus(); break; - case 10: name = "TestReverse"; if (exec) TestReverse(); break; - case 11: name = "TestMiscellaneous"; if (exec) TestMiscellaneous(); break; - case 12: name = "TestStackAllocation"; if (exec) TestStackAllocation(); break; - case 13: name = "TestUnescape"; if (exec) TestUnescape(); break; - case 14: name = "TestCountChar32"; if (exec) TestCountChar32(); break; - case 15: name = "TestStringEnumeration"; if (exec) TestStringEnumeration(); break; - case 16: name = "TestNameSpace"; if (exec) TestNameSpace(); break; - case 17: name = "TestUTF32"; if (exec) TestUTF32(); break; - case 18: name = "TestUTF8"; if (exec) TestUTF8(); break; - case 19: name = "TestReadOnlyAlias"; if (exec) TestReadOnlyAlias(); break; - case 20: name = "TestAppendable"; if (exec) TestAppendable(); break; - case 21: name = "TestUnicodeStringImplementsAppendable"; if (exec) TestUnicodeStringImplementsAppendable(); break; - case 22: name = "TestSizeofUnicodeString"; if (exec) TestSizeofUnicodeString(); break; - case 23: name = "TestStartsWithAndEndsWithNulTerminated"; if (exec) TestStartsWithAndEndsWithNulTerminated(); break; - - default: name = ""; break; //needed to end loop - } + TESTCASE_AUTO_BEGIN; + TESTCASE_AUTO_CLASS(StringCaseTest); + TESTCASE_AUTO(TestBasicManipulation); + TESTCASE_AUTO(TestCompare); + TESTCASE_AUTO(TestExtract); + TESTCASE_AUTO(TestRemoveReplace); + TESTCASE_AUTO(TestSearching); + TESTCASE_AUTO(TestSpacePadding); + TESTCASE_AUTO(TestPrefixAndSuffix); + TESTCASE_AUTO(TestFindAndReplace); + TESTCASE_AUTO(TestBogus); + TESTCASE_AUTO(TestReverse); + TESTCASE_AUTO(TestMiscellaneous); + TESTCASE_AUTO(TestStackAllocation); + TESTCASE_AUTO(TestUnescape); + TESTCASE_AUTO(TestCountChar32); + TESTCASE_AUTO(TestStringEnumeration); + TESTCASE_AUTO(TestNameSpace); + TESTCASE_AUTO(TestUTF32); + TESTCASE_AUTO(TestUTF8); + TESTCASE_AUTO(TestReadOnlyAlias); + TESTCASE_AUTO(TestAppendable); + TESTCASE_AUTO(TestUnicodeStringImplementsAppendable); + TESTCASE_AUTO(TestSizeofUnicodeString); + TESTCASE_AUTO(TestStartsWithAndEndsWithNulTerminated); + TESTCASE_AUTO(TestMoveSwap); + TESTCASE_AUTO_END; } void @@ -2130,3 +2122,66 @@ UnicodeStringTest::TestSizeofUnicodeString() { expectedStackBufferLength); } } + +void +UnicodeStringTest::TestMoveSwap() { + static const UChar abc[3] = { 0x61, 0x62, 0x63 }; // "abc" + UnicodeString s1(FALSE, abc, UPRV_LENGTHOF(abc)); // read-only alias + UnicodeString s2(100, 0x7a, 100); // 100 * 'z' should be on the heap + UnicodeString s3("defg", 4, US_INV); // in stack buffer + const UChar *p = s2.getBuffer(); + s1.swap(s2); + if(s1.getBuffer() != p || s1.length() != 100 || s2.getBuffer() != abc || s2.length() != 3) { + errln("UnicodeString.swap() did not swap"); + } + swap(s2, s3); + if(s2 != UNICODE_STRING_SIMPLE("defg") || s3.getBuffer() != abc || s3.length() != 3) { + errln("swap(UnicodeString) did not swap back"); + } + UnicodeString s4; + s4.moveFrom(s1); + if(s4.getBuffer() != p || s4.length() != 100 || !s1.isBogus()) { + errln("UnicodeString.moveFrom(heap) did not move"); + } + UnicodeString s5; + s5.moveFrom(s2); + if(s5 != UNICODE_STRING_SIMPLE("defg")) { + errln("UnicodeString.moveFrom(stack) did not move"); + } + UnicodeString s6; + s6.moveFrom(s3); + if(s6.getBuffer() != abc || s6.length() != 3) { + errln("UnicodeString.moveFrom(alias) did not move"); + } +#if U_HAVE_RVALUE_REFERENCES + infoln("TestMoveSwap() with rvalue references"); + s1 = static_cast(s6); + if(s1.getBuffer() != abc || s1.length() != 3) { + errln("UnicodeString move assignment operator did not move"); + } + UnicodeString s7(static_cast(s4)); + if(s7.getBuffer() != p || s7.length() != 100 || !s4.isBogus()) { + errln("UnicodeString move constructor did not move"); + } +#else + infoln("TestMoveSwap() without rvalue references"); + UnicodeString s7; +#endif + + // Move self assignment leaves the object valid but in an undefined state. + // Do it to make sure there is no crash, + // but do not check for any particular resulting value. + s1.moveFrom(s1); + s2.moveFrom(s2); + s3.moveFrom(s3); + s4.moveFrom(s4); + s5.moveFrom(s5); + s6.moveFrom(s6); + s7.moveFrom(s7); + // Simple copy assignment must work. + UnicodeString simple = UNICODE_STRING_SIMPLE("simple"); + s1 = s6 = s4 = s7 = simple; + if(s1 != simple || s4 != simple || s6 != simple || s7 != simple) { + errln("UnicodeString copy after self-move did not work"); + } +} diff --git a/icu4c/source/test/intltest/ustrtest.h b/icu4c/source/test/intltest/ustrtest.h index 02476c93fb7..655af1c9dae 100644 --- a/icu4c/source/test/intltest/ustrtest.h +++ b/icu4c/source/test/intltest/ustrtest.h @@ -1,6 +1,6 @@ /******************************************************************** * COPYRIGHT: - * Copyright (c) 1997-2012, International Business Machines Corporation and + * Copyright (c) 1997-2015, International Business Machines Corporation and * others. All Rights Reserved. ********************************************************************/ @@ -88,6 +88,7 @@ public: void TestAppendable(); void TestUnicodeStringImplementsAppendable(); void TestSizeofUnicodeString(); + void TestMoveSwap(); }; class StringCaseTest: public IntlTest {