]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/common/unistr_case.cpp
ICU-64252.0.1.tar.gz
[apple/icu.git] / icuSources / common / unistr_case.cpp
index a048c04eff34db7adc88b7f1a46ba616cfc00e89..2138d60c01c2d17be8dae6c51f7e1a17403e2c43 100644 (file)
@@ -1,12 +1,14 @@
+// © 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
 /*
 *******************************************************************************
 *
 /*
 *******************************************************************************
 *
-*   Copyright (C) 1999-2010, International Business Machines
+*   Copyright (C) 1999-2014, International Business Machines
 *   Corporation and others.  All Rights Reserved.
 *
 *******************************************************************************
 *   file name:  unistr_case.cpp
 *   Corporation and others.  All Rights Reserved.
 *
 *******************************************************************************
 *   file name:  unistr_case.cpp
-*   encoding:   US-ASCII
+*   encoding:   UTF-8
 *   tab size:   8 (not used)
 *   indentation:2
 *
 *   tab size:   8 (not used)
 *   indentation:2
 *
 */
 
 #include "unicode/utypes.h"
 */
 
 #include "unicode/utypes.h"
+#include "unicode/brkiter.h"
+#include "unicode/casemap.h"
+#include "unicode/edits.h"
 #include "unicode/putil.h"
 #include "unicode/putil.h"
-#include "unicode/locid.h"
 #include "cstring.h"
 #include "cmemory.h"
 #include "unicode/ustring.h"
 #include "unicode/unistr.h"
 #include "unicode/uchar.h"
 #include "cstring.h"
 #include "cmemory.h"
 #include "unicode/ustring.h"
 #include "unicode/unistr.h"
 #include "unicode/uchar.h"
-#include "unicode/ubrk.h"
-#include "ustr_imp.h"
-#include "uhash.h"
+#include "uassert.h"
+#include "ucasemap_imp.h"
+#include "uelement.h"
 
 U_NAMESPACE_BEGIN
 
 
 U_NAMESPACE_BEGIN
 
@@ -59,7 +63,9 @@ UnicodeString::doCaseCompare(int32_t start,
   const UChar *chars = getArrayStart();
 
   chars += start;
   const UChar *chars = getArrayStart();
 
   chars += start;
-  srcChars += srcStart;
+  if(srcStart!=0) {
+    srcChars += srcStart;
+  }
 
   if(chars != srcChars) {
     UErrorCode errorCode=U_ZERO_ERROR;
 
   if(chars != srcChars) {
     UErrorCode errorCode=U_ZERO_ERROR;
@@ -84,141 +90,139 @@ UnicodeString::doCaseCompare(int32_t start,
 // Write implementation
 //========================================
 
 // Write implementation
 //========================================
 
-/*
- * Implement argument checking and buffer handling
- * for string case mapping as a common function.
- */
-
 UnicodeString &
 UnicodeString &
-UnicodeString::caseMap(BreakIterator *titleIter,
-                       const char *locale,
-                       uint32_t options,
-                       int32_t toWhichCase) {
+UnicodeString::caseMap(int32_t caseLocale, uint32_t options, UCASEMAP_BREAK_ITERATOR_PARAM
+                       UStringCaseMapper *stringCaseMapper) {
   if(isEmpty() || !isWritable()) {
     // nothing to do
     return *this;
   }
 
   if(isEmpty() || !isWritable()) {
     // nothing to do
     return *this;
   }
 
-  const UCaseProps *csp=ucase_getSingleton();
-
-  // We need to allocate a new buffer for the internal string case mapping function.
-  // This is very similar to how doReplace() keeps the old array pointer
-  // and deletes the old array itself after it is done.
-  // In addition, we are forcing cloneArrayIfNeeded() to always allocate a new array.
-  UChar oldStackBuffer[US_STACKBUF_SIZE];
+  UChar oldBuffer[2 * US_STACKBUF_SIZE];
   UChar *oldArray;
   UChar *oldArray;
-  int32_t oldLength;
+  int32_t oldLength = length();
+  int32_t newLength;
+  UBool writable = isBufferWritable();
+  UErrorCode errorCode = U_ZERO_ERROR;
 
 
-  if(fFlags&kUsingStackBuffer) {
-    // copy the stack buffer contents because it will be overwritten
-    u_memcpy(oldStackBuffer, fUnion.fStackBuffer, fShortLength);
-    oldArray = oldStackBuffer;
-    oldLength = fShortLength;
+#if !UCONFIG_NO_BREAK_ITERATION
+  // Read-only alias to the original string contents for the titlecasing BreakIterator.
+  // We cannot set the iterator simply to *this because *this is being modified.
+  UnicodeString oldString;
+#endif
+
+  // Try to avoid heap-allocating a new character array for this string.
+  if (writable ? oldLength <= UPRV_LENGTHOF(oldBuffer) : oldLength < US_STACKBUF_SIZE) {
+    // Short string: Copy the contents into a temporary buffer and
+    // case-map back into the current array, or into the stack buffer.
+    UChar *buffer = getArrayStart();
+    int32_t capacity;
+    oldArray = oldBuffer;
+    u_memcpy(oldBuffer, buffer, oldLength);
+    if (writable) {
+      capacity = getCapacity();
+    } else {
+      // Switch from the read-only alias or shared heap buffer to the stack buffer.
+      if (!cloneArrayIfNeeded(US_STACKBUF_SIZE, US_STACKBUF_SIZE, /* doCopyArray= */ FALSE)) {
+        return *this;
+      }
+      U_ASSERT(fUnion.fFields.fLengthAndFlags & kUsingStackBuffer);
+      buffer = fUnion.fStackFields.fBuffer;
+      capacity = US_STACKBUF_SIZE;
+    }
+#if !UCONFIG_NO_BREAK_ITERATION
+    if (iter != nullptr) {
+      oldString.setTo(FALSE, oldArray, oldLength);
+      iter->setText(oldString);
+    }
+#endif
+    newLength = stringCaseMapper(caseLocale, options, UCASEMAP_BREAK_ITERATOR
+                                 buffer, capacity,
+                                 oldArray, oldLength, NULL, errorCode);
+    if (U_SUCCESS(errorCode)) {
+      setLength(newLength);
+      return *this;
+    } else if (errorCode == U_BUFFER_OVERFLOW_ERROR) {
+      // common overflow handling below
+    } else {
+      setToBogus();
+      return *this;
+    }
   } else {
   } else {
+    // Longer string or read-only buffer:
+    // Collect only changes and then apply them to this string.
+    // Case mapping often changes only small parts of a string,
+    // and often does not change its length.
     oldArray = getArrayStart();
     oldArray = getArrayStart();
-    oldLength = length();
+    Edits edits;
+    UChar replacementChars[200];
+#if !UCONFIG_NO_BREAK_ITERATION
+    if (iter != nullptr) {
+      oldString.setTo(FALSE, oldArray, oldLength);
+      iter->setText(oldString);
+    }
+#endif
+    stringCaseMapper(caseLocale, options | U_OMIT_UNCHANGED_TEXT, UCASEMAP_BREAK_ITERATOR
+                     replacementChars, UPRV_LENGTHOF(replacementChars),
+                     oldArray, oldLength, &edits, errorCode);
+    if (U_SUCCESS(errorCode)) {
+      // Grow the buffer at most once, not for multiple doReplace() calls.
+      newLength = oldLength + edits.lengthDelta();
+      if (newLength > oldLength && !cloneArrayIfNeeded(newLength, newLength)) {
+        return *this;
+      }
+      for (Edits::Iterator ei = edits.getCoarseChangesIterator(); ei.next(errorCode);) {
+        doReplace(ei.destinationIndex(), ei.oldLength(),
+                  replacementChars, ei.replacementIndex(), ei.newLength());
+      }
+      if (U_FAILURE(errorCode)) {
+        setToBogus();
+      }
+      return *this;
+    } else if (errorCode == U_BUFFER_OVERFLOW_ERROR) {
+      // common overflow handling below
+      newLength = oldLength + edits.lengthDelta();
+    } else {
+      setToBogus();
+      return *this;
+    }
   }
 
   }
 
-  int32_t capacity;
-  if(oldLength <= US_STACKBUF_SIZE) {
-    capacity = US_STACKBUF_SIZE;
-  } else {
-    capacity = oldLength + 20;
-  }
+  // Handle buffer overflow, newLength is known.
+  // We need to allocate a new buffer for the internal string case mapping function.
+  // This is very similar to how doReplace() keeps the old array pointer
+  // and deletes the old array itself after it is done.
+  // In addition, we are forcing cloneArrayIfNeeded() to always allocate a new array.
   int32_t *bufferToDelete = 0;
   int32_t *bufferToDelete = 0;
-  if(!cloneArrayIfNeeded(capacity, capacity, FALSE, &bufferToDelete, TRUE)) {
+  if (!cloneArrayIfNeeded(newLength, newLength, FALSE, &bufferToDelete, TRUE)) {
     return *this;
   }
     return *this;
   }
-
-  // Case-map, and if the result is too long, then reallocate and repeat.
-  UErrorCode errorCode;
-  int32_t newLength;
-  do {
-    errorCode = U_ZERO_ERROR;
-    if(toWhichCase==TO_LOWER) {
-      newLength = ustr_toLower(csp, getArrayStart(), getCapacity(),
-                               oldArray, oldLength,
-                               locale, &errorCode);
-    } else if(toWhichCase==TO_UPPER) {
-      newLength = ustr_toUpper(csp, getArrayStart(), getCapacity(),
-                               oldArray, oldLength,
-                               locale, &errorCode);
-    } else if(toWhichCase==TO_TITLE) {
-#if UCONFIG_NO_BREAK_ITERATION
-        errorCode=U_UNSUPPORTED_ERROR;
-#else
-      newLength = ustr_toTitle(csp, getArrayStart(), getCapacity(),
-                               oldArray, oldLength,
-                               (UBreakIterator *)titleIter, locale, options, &errorCode);
-#endif
-    } else {
-      newLength = ustr_foldCase(csp, getArrayStart(), getCapacity(),
-                                oldArray, oldLength,
-                                options,
-                                &errorCode);
-    }
-    setLength(newLength);
-  } while(errorCode==U_BUFFER_OVERFLOW_ERROR && cloneArrayIfNeeded(newLength, newLength, FALSE));
-
+  errorCode = U_ZERO_ERROR;
+  // No need to iter->setText() again: The case mapper restarts via iter->first().
+  newLength = stringCaseMapper(caseLocale, options, UCASEMAP_BREAK_ITERATOR
+                               getArrayStart(), getCapacity(),
+                               oldArray, oldLength, NULL, errorCode);
   if (bufferToDelete) {
     uprv_free(bufferToDelete);
   }
   if (bufferToDelete) {
     uprv_free(bufferToDelete);
   }
-  if(U_FAILURE(errorCode)) {
+  if (U_SUCCESS(errorCode)) {
+    setLength(newLength);
+  } else {
     setToBogus();
   }
   return *this;
 }
 
     setToBogus();
   }
   return *this;
 }
 
-UnicodeString &
-UnicodeString::toLower() {
-  return caseMap(0, Locale::getDefault().getName(), 0, TO_LOWER);
-}
-
-UnicodeString &
-UnicodeString::toLower(const Locale &locale) {
-  return caseMap(0, locale.getName(), 0, TO_LOWER);
-}
-
-UnicodeString &
-UnicodeString::toUpper() {
-  return caseMap(0, Locale::getDefault().getName(), 0, TO_UPPER);
-}
-
-UnicodeString &
-UnicodeString::toUpper(const Locale &locale) {
-  return caseMap(0, locale.getName(), 0, TO_UPPER);
-}
-
-#if !UCONFIG_NO_BREAK_ITERATION
-
-UnicodeString &
-UnicodeString::toTitle(BreakIterator *titleIter) {
-  return caseMap(titleIter, Locale::getDefault().getName(), 0, TO_TITLE);
-}
-
-UnicodeString &
-UnicodeString::toTitle(BreakIterator *titleIter, const Locale &locale) {
-  return caseMap(titleIter, locale.getName(), 0, TO_TITLE);
-}
-
-UnicodeString &
-UnicodeString::toTitle(BreakIterator *titleIter, const Locale &locale, uint32_t options) {
-  return caseMap(titleIter, locale.getName(), options, TO_TITLE);
-}
-
-#endif
-
 UnicodeString &
 UnicodeString::foldCase(uint32_t options) {
 UnicodeString &
 UnicodeString::foldCase(uint32_t options) {
-    /* The Locale parameter isn't used. Use "" instead. */
-    return caseMap(0, "", options, FOLD_CASE);
+  return caseMap(UCASE_LOC_ROOT, options, UCASEMAP_BREAK_ITERATOR_NULL ustrcase_internalFold);
 }
 
 U_NAMESPACE_END
 
 // Defined here to reduce dependencies on break iterator
 U_CAPI int32_t U_EXPORT2
 }
 
 U_NAMESPACE_END
 
 // Defined here to reduce dependencies on break iterator
 U_CAPI int32_t U_EXPORT2
-uhash_hashCaselessUnicodeString(const UHashTok key) {
+uhash_hashCaselessUnicodeString(const UElement key) {
     U_NAMESPACE_USE
     const UnicodeString *str = (const UnicodeString*) key.pointer;
     if (str == NULL) {
     U_NAMESPACE_USE
     const UnicodeString *str = (const UnicodeString*) key.pointer;
     if (str == NULL) {
@@ -232,7 +236,7 @@ uhash_hashCaselessUnicodeString(const UHashTok key) {
 
 // Defined here to reduce dependencies on break iterator
 U_CAPI UBool U_EXPORT2
 
 // Defined here to reduce dependencies on break iterator
 U_CAPI UBool U_EXPORT2
-uhash_compareCaselessUnicodeString(const UHashTok key1, const UHashTok key2) {
+uhash_compareCaselessUnicodeString(const UElement key1, const UElement key2) {
     U_NAMESPACE_USE
     const UnicodeString *str1 = (const UnicodeString*) key1.pointer;
     const UnicodeString *str2 = (const UnicodeString*) key2.pointer;
     U_NAMESPACE_USE
     const UnicodeString *str1 = (const UnicodeString*) key1.pointer;
     const UnicodeString *str2 = (const UnicodeString*) key2.pointer;
@@ -244,4 +248,3 @@ uhash_compareCaselessUnicodeString(const UHashTok key1, const UHashTok key2) {
     }
     return str1->caseCompare(*str2, U_FOLD_CASE_DEFAULT) == 0;
 }
     }
     return str1->caseCompare(*str2, U_FOLD_CASE_DEFAULT) == 0;
 }
-