]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/common/ucnv_bld.c
ICU-6.2.22.tar.gz
[apple/icu.git] / icuSources / common / ucnv_bld.c
index cd59fa368c34ddca750a7e5e6edf376586063237..f9f99c9b599911d5b14375967064227ddb887145 100644 (file)
@@ -1,7 +1,7 @@
 /*
  ********************************************************************
- * COPYRIGHT: 
- * Copyright (c) 1996-2003, International Business Machines Corporation and
+ * COPYRIGHT:
+ * Copyright (c) 1996-2004, International Business Machines Corporation and
  * others. All Rights Reserved.
  ********************************************************************
  *
  *  uses uconv_io.h routines to access disk information
  *  is used by ucnv.h to implement public API create/delete/flushCache routines
  * Modification History:
- * 
+ *
  *   Date        Name        Description
- * 
+ *
  *   06/20/2000  helena      OS/400 port changes; mostly typecast.
  *   06/29/2000  helena      Major rewrite of the callback interface.
 */
 
+#include "unicode/utypes.h"
+
+#if !UCONFIG_NO_CONVERSION
 
 #include "unicode/udata.h"
 #include "unicode/ucnv.h"
-#include "unicode/ucnv_err.h"
 #include "unicode/uloc.h"
+#include "utracimp.h"
 #include "ucnv_io.h"
 #include "ucnv_bld.h"
+#include "ucnvmbcs.h"
+#include "ucnv_ext.h"
 #include "ucnv_cnv.h"
 #include "ucnv_imp.h"
 #include "uhash.h"
@@ -33,7 +38,7 @@
 #include "cstring.h"
 #include "cmemory.h"
 #include "ucln_cmn.h"
-#include "ustr_imp.h"
+#include "ustr_cnv.h"
 
 
 
@@ -95,7 +100,9 @@ static struct {
   { "cesu8", UCNV_CESU8 },
 #if !UCONFIG_NO_LEGACY_CONVERSION
   { "hz",UCNV_HZ },
+#endif
   { "imapmailboxname", UCNV_IMAP_MAILBOX },
+#if !UCONFIG_NO_LEGACY_CONVERSION
   { "iscii", UCNV_ISCII },
   { "iso2022", UCNV_ISO_2022 },
 #endif
@@ -154,7 +161,7 @@ static const char DATA_TYPE[] = "cnv";
 /*                by open converters.                                                  */
 /*                Not thread safe.                                                     */
 /*                Not supported  API.  Marked U_CAPI only for use by test programs.    */
-U_CFUNC UBool U_EXPORT2 ucnv_cleanup(void) {
+static UBool U_CALLCONV ucnv_cleanup(void) {
     if (SHARED_DATA_HASHTABLE != NULL) {
         ucnv_flushCache();
         if (SHARED_DATA_HASHTABLE != NULL && uhash_count(SHARED_DATA_HASHTABLE) == 0) {
@@ -169,10 +176,6 @@ U_CFUNC UBool U_EXPORT2 ucnv_cleanup(void) {
     return (SHARED_DATA_HASHTABLE == NULL);
 }
 
-U_CFUNC void ucnv_init(UErrorCode *status) {
-    umtx_init(&cnvCacheMutex);
-}
-
 static UBool U_CALLCONV
 isCnvAcceptable(void *context,
              const char *type, const char *name,
@@ -193,7 +196,7 @@ isCnvAcceptable(void *context,
  * Un flatten shared data from a UDATA..
  */
 static UConverterSharedData*
-ucnv_data_unFlattenClone(UDataMemory *pData, UErrorCode *status)
+ucnv_data_unFlattenClone(UConverterLoadArgs *pArgs, UDataMemory *pData, UErrorCode *status)
 {
     /* UDataInfo info; -- necessary only if some converters have different formatVersion */
     const uint8_t *raw = (const uint8_t *)udata_getMemory(pData);
@@ -222,12 +225,16 @@ ucnv_data_unFlattenClone(UDataMemory *pData, UErrorCode *status)
     /* copy initial values from the static structure for this type */
     uprv_memcpy(data, converterData[type], sizeof(UConverterSharedData));
 
+#if 0 /* made UConverterMBCSTable part of UConverterSharedData -- markus 20031107 */
     /*
      * It would be much more efficient if the table were a direct member, not a pointer.
      * However, that would add to the size of all UConverterSharedData objects
      * even if they do not use this table (especially algorithmic ones).
      * If this changes, then the static templates from converterData[type]
      * need more entries.
+     *
+     * In principle, it would be cleaner if the load() function below
+     * allocated the table.
      */
     data->table = (UConverterTable *)uprv_malloc(sizeof(UConverterTable));
     if(data->table == NULL) {
@@ -236,16 +243,17 @@ ucnv_data_unFlattenClone(UDataMemory *pData, UErrorCode *status)
         return NULL;
     }
     uprv_memset(data->table, 0, sizeof(UConverterTable));
-    
+#endif
+
     data->staticData = source;
-    
+
     data->sharedDataCached = FALSE;
 
     /* fill in fields from the loaded data */
     data->dataMemory = (void*)pData; /* for future use */
 
     if(data->impl->load != NULL) {
-        data->impl->load(data, raw + source->structSize, status);
+        data->impl->load(data, pArgs, raw + source->structSize, status);
         if(U_FAILURE(*status)) {
             uprv_free(data->table);
             uprv_free(data);
@@ -259,32 +267,47 @@ ucnv_data_unFlattenClone(UDataMemory *pData, UErrorCode *status)
  *goes to disk and opens it.
  *allocates the memory and returns a new UConverter object
  */
-static UConverterSharedData *createConverterFromFile(const char* pkg, const char *fileName, UErrorCode * err)
+static UConverterSharedData *createConverterFromFile(UConverterLoadArgs *pArgs, UErrorCode * err)
 {
     UDataMemory *data;
     UConverterSharedData *sharedData;
 
+    UTRACE_ENTRY_OC(UTRACE_UCNV_LOAD);
+
     if (err == NULL || U_FAILURE (*err)) {
+        UTRACE_EXIT_STATUS(*err);
         return NULL;
     }
 
-    data = udata_openChoice(pkg, DATA_TYPE, fileName, isCnvAcceptable, NULL, err);
+    UTRACE_DATA2(UTRACE_OPEN_CLOSE, "load converter %s from package %s", pArgs->name, pArgs->pkg);
+
+    data = udata_openChoice(pArgs->pkg, DATA_TYPE, pArgs->name, isCnvAcceptable, NULL, err);
     if(U_FAILURE(*err))
     {
+        UTRACE_EXIT_STATUS(*err);
         return NULL;
     }
 
-    sharedData = ucnv_data_unFlattenClone(data, err);
+    sharedData = ucnv_data_unFlattenClone(pArgs, data, err);
     if(U_FAILURE(*err))
     {
         udata_close(data);
+        UTRACE_EXIT_STATUS(*err);
         return NULL;
     }
 
+    /*
+     * TODO Store pkg in a field in the shared data so that delta-only converters
+     * can load base converters from the same package.
+     * If the pkg name is longer than the field, then either do not load the converter
+     * in the first place, or just set the pkg field to "".
+     */
+
+    UTRACE_EXIT_PTR_STATUS(sharedData, *err);
     return sharedData;
 }
 
-int32_t 
+int32_t
 ucnv_copyPlatformString(char *platformString, UConverterPlatform pltfrm)
 {
     switch (pltfrm)
@@ -307,7 +330,7 @@ static const UConverterSharedData *
 getAlgorithmicTypeFromName(const char *realName)
 {
     uint32_t mid, start, limit;
-       uint32_t lastMid;
+    uint32_t lastMid;
     int result;
     char strippedName[UCNV_MAX_CONVERTER_NAME_LENGTH];
 
@@ -318,14 +341,14 @@ getAlgorithmicTypeFromName(const char *realName)
     start = 0;
     limit = sizeof(cnvNameType)/sizeof(cnvNameType[0]);
     mid = limit;
-       lastMid = UINT32_MAX;
+    lastMid = UINT32_MAX;
 
     for (;;) {
         mid = (uint32_t)((start + limit) / 2);
-               if (lastMid == mid) {   /* Have we moved? */
-                       break;  /* We haven't moved, and it wasn't found. */
-               }
-               lastMid = mid;
+        if (lastMid == mid) {   /* Have we moved? */
+            break;  /* We haven't moved, and it wasn't found. */
+        }
+        lastMid = mid;
         result = uprv_strcmp(strippedName, cnvNameType[mid].name);
 
         if (result < 0) {
@@ -358,7 +381,9 @@ ucnv_shareConverterData(UConverterSharedData * data)
         SHARED_DATA_HASHTABLE = uhash_openSize(uhash_hashChars, uhash_compareChars,
                             ucnv_io_countAvailableAliases(&err),
                             &err);
-        if (U_FAILURE(err)) 
+        ucln_common_registerCleanup(UCLN_COMMON_UCNV, ucnv_cleanup);
+
+        if (U_FAILURE(err))
             return;
     }
 
@@ -372,7 +397,7 @@ ucnv_shareConverterData(UConverterSharedData * data)
     }
     UCNV_DEBUG_LOG("put:chk",data->staticData->name,sanity);
     */
-    
+
     /* Mark it shared */
     data->sharedDataCached = TRUE;
 
@@ -423,13 +448,18 @@ ucnv_getSharedConverterData(const char *name)
 static UBool
 ucnv_deleteSharedConverterData(UConverterSharedData * deadSharedData)
 {
-    if (deadSharedData->referenceCounter > 0)
+    UTRACE_ENTRY_OC(UTRACE_UCNV_UNLOAD);
+    UTRACE_DATA2(UTRACE_OPEN_CLOSE, "unload converter %s shared data %p", deadSharedData->staticData->name, deadSharedData);
+
+    if (deadSharedData->referenceCounter > 0) {
+        UTRACE_EXIT_VALUE((int32_t)FALSE);
         return FALSE;
+    }
 
     if (deadSharedData->impl->unload != NULL) {
         deadSharedData->impl->unload(deadSharedData);
     }
-    
+
     if(deadSharedData->dataMemory != NULL)
     {
         UDataMemory *data = (UDataMemory*)deadSharedData->dataMemory;
@@ -456,42 +486,94 @@ ucnv_deleteSharedConverterData(UConverterSharedData * deadSharedData)
 #endif
 
     uprv_free(deadSharedData);
-    
+
+    UTRACE_EXIT_VALUE((int32_t)TRUE);
     return TRUE;
 }
 
+/**
+ * Load a non-algorithmic converter.
+ * If pkg==NULL, then this function must be called inside umtx_lock(&cnvCacheMutex).
+ */
+UConverterSharedData *
+ucnv_load(UConverterLoadArgs *pArgs, UErrorCode *err) {
+    UConverterSharedData *mySharedConverterData;
+
+    if(err == NULL || U_FAILURE(*err)) {
+        return NULL;
+    }
+
+    if(pArgs->pkg != NULL && *pArgs->pkg != 0) {
+        /* application-provided converters are not currently cached */
+        return createConverterFromFile(pArgs, err);
+    }
+
+    mySharedConverterData = ucnv_getSharedConverterData(pArgs->name);
+    if (mySharedConverterData == NULL)
+    {
+        /*Not cached, we need to stream it in from file */
+        mySharedConverterData = createConverterFromFile(pArgs, err);
+        if (U_FAILURE (*err) || (mySharedConverterData == NULL))
+        {
+            return NULL;
+        }
+        else
+        {
+            /* share it with other library clients */
+            ucnv_shareConverterData(mySharedConverterData);
+        }
+    }
+    else
+    {
+        /* The data for this converter was already in the cache.            */
+        /* Update the reference counter on the shared data: one more client */
+        mySharedConverterData->referenceCounter++;
+    }
+
+    return mySharedConverterData;
+}
+
+/**
+ * Unload a non-algorithmic converter.
+ * It must be sharedData->referenceCounter != ~0
+ * and this function must be called inside umtx_lock(&cnvCacheMutex).
+ */
 void
-ucnv_unloadSharedDataIfReady(UConverterSharedData *sharedData)
-{
-    umtx_lock(&cnvCacheMutex);
-    /*
-    Double checking doesn't work on some platforms.
-    Don't check referenceCounter outside of a mutex block.
-    */
-    if (sharedData->referenceCounter != ~0) {
+ucnv_unload(UConverterSharedData *sharedData) {
+    if(sharedData != NULL) {
         if (sharedData->referenceCounter > 0) {
             sharedData->referenceCounter--;
         }
-        
+
         if((sharedData->referenceCounter <= 0)&&(sharedData->sharedDataCached == FALSE)) {
             ucnv_deleteSharedConverterData(sharedData);
         }
     }
-    umtx_unlock(&cnvCacheMutex);
 }
 
 void
-ucnv_incrementRefCount(UConverterSharedData *sharedData)
+ucnv_unloadSharedDataIfReady(UConverterSharedData *sharedData)
 {
-    umtx_lock(&cnvCacheMutex);
     /*
-    Double checking doesn't work on some platforms.
-    Don't check referenceCounter outside of a mutex block.
+    Checking whether it's an algorithic converter is okay
+    in multithreaded applications because the value never changes.
+    Don't check referenceCounter for any other value.
     */
-    if (sharedData->referenceCounter != ~0) {
+    if(sharedData != NULL && sharedData->referenceCounter != ~0) {
+        umtx_lock(&cnvCacheMutex);
+        ucnv_unload(sharedData);
+        umtx_unlock(&cnvCacheMutex);
+    }
+}
+
+void
+ucnv_incrementRefCount(UConverterSharedData *sharedData)
+{
+    if(sharedData != NULL && sharedData->referenceCounter != ~0) {
+        umtx_lock(&cnvCacheMutex);
         sharedData->referenceCounter++;
+        umtx_unlock(&cnvCacheMutex);
     }
-    umtx_unlock(&cnvCacheMutex);
 }
 
 static void
@@ -575,54 +657,58 @@ parseConverterOptions(const char *inName,
  * -Call dataConverter initializer (Data=TRUE, Cached=TRUE)
  * -Call AlgorithmicConverter initializer (Data=FALSE, Cached=TRUE)
  */
-UConverter *
-ucnv_createConverter(UConverter *myUConverter, const char *converterName, UErrorCode * err)
-{
-    char cnvName[UCNV_MAX_CONVERTER_NAME_LENGTH], locale[ULOC_FULLNAME_CAPACITY];
-    const char *realName;
+UConverterSharedData *
+ucnv_loadSharedData(const char *converterName, UConverterLookupData *lookup, UErrorCode * err) {
+    UConverterLookupData stackLookup;
     UConverterSharedData *mySharedConverterData = NULL;
     UErrorCode internalErrorCode = U_ZERO_ERROR;
-    uint32_t options = 0;
-    if (U_FAILURE (*err))
+
+    if (U_FAILURE (*err)) {
         return NULL;
+    }
+
+    if(lookup == NULL) {
+        lookup = &stackLookup;
+    }
 
-    locale[0] = 0;
+    lookup->locale[0] = 0;
+    lookup->options = 0;
 
     /* In case "name" is NULL we want to open the default converter. */
     if (converterName == NULL) {
-        realName = ucnv_io_getDefaultConverterName();
-        if (realName == NULL) {
+        lookup->realName = ucnv_io_getDefaultConverterName();
+        if (lookup->realName == NULL) {
             *err = U_MISSING_RESOURCE_ERROR;
             return NULL;
         }
         /* the default converter name is already canonical */
     } else {
         /* separate the converter name from the options */
-        parseConverterOptions(converterName, cnvName, locale, &options, err);
+        parseConverterOptions(converterName, lookup->cnvName, lookup->locale, &lookup->options, err);
         if (U_FAILURE(*err)) {
             /* Very bad name used. */
             return NULL;
         }
 
         /* get the canonical converter name */
-        realName = ucnv_io_getConverterName(cnvName, &internalErrorCode);
-        if (U_FAILURE(internalErrorCode) || realName == NULL) {
+        lookup->realName = ucnv_io_getConverterName(lookup->cnvName, &internalErrorCode);
+        if (U_FAILURE(internalErrorCode) || lookup->realName == NULL) {
             /*
             * set the input name in case the converter was added
             * without updating the alias table, or when there is no alias table
             */
-            realName = cnvName;
+            lookup->realName = lookup->cnvName;
         }
     }
 
     /* separate the converter name from the options */
-    if(realName != cnvName) {
-        parseConverterOptions(realName, cnvName, locale, &options, err);
-        realName = cnvName;
+    if(lookup->realName != lookup->cnvName) {
+        parseConverterOptions(lookup->realName, lookup->cnvName, lookup->locale, &lookup->options, err);
+        lookup->realName = lookup->cnvName;
     }
-    
+
     /* get the shared data for an algorithmic converter, if it is one */
-    mySharedConverterData = (UConverterSharedData *)getAlgorithmicTypeFromName(realName);
+    mySharedConverterData = (UConverterSharedData *)getAlgorithmicTypeFromName(lookup->realName);
     if (mySharedConverterData == NULL)
     {
         /* it is a data-based converter, get its shared data.               */
@@ -630,50 +716,57 @@ ucnv_createConverter(UConverter *myUConverter, const char *converterName, UError
         /*   converter data cache, and adding new entries to the cache      */
         /*   to prevent other threads from modifying the cache during the   */
         /*   process.                                                       */
+        UConverterLoadArgs args={ 0 };
+
+        args.size=sizeof(UConverterLoadArgs);
+        args.nestedLoads=1;
+        args.options=lookup->options;
+        args.pkg=NULL;
+        args.name=lookup->realName;
+
         umtx_lock(&cnvCacheMutex);
-        mySharedConverterData = ucnv_getSharedConverterData(realName);
-        if (mySharedConverterData == NULL)
-        {
-            /*Not cached, we need to stream it in from file */
-            mySharedConverterData = createConverterFromFile(NULL, realName, err);
-            if (U_FAILURE (*err) || (mySharedConverterData == NULL))
-            {
-                umtx_unlock(&cnvCacheMutex);
-                return NULL;
-            }
-            else
-            {
-                /* share it with other library clients */
-                ucnv_shareConverterData(mySharedConverterData);
-            }
-        }
-        else
+        mySharedConverterData = ucnv_load(&args, err);
+        umtx_unlock(&cnvCacheMutex);
+        if (U_FAILURE (*err) || (mySharedConverterData == NULL))
         {
-            /* The data for this converter was already in the cache.            */
-            /* Update the reference counter on the shared data: one more client */
-            mySharedConverterData->referenceCounter++;
+            return NULL;
         }
-        umtx_unlock(&cnvCacheMutex);
     }
 
-    myUConverter = ucnv_createConverterFromSharedData(myUConverter, mySharedConverterData, realName, locale, options, err);
+    return mySharedConverterData;
+}
 
-    if (U_FAILURE(*err))
-    {
-        /*
-        Checking whether it's an algorithic converter is okay
-        in multithreaded applications because the value never changes.
-        Don't check referenceCounter for any other value.
-        */
-        if (mySharedConverterData->referenceCounter != ~0) {
-            umtx_lock(&cnvCacheMutex);
-            --mySharedConverterData->referenceCounter;
-            umtx_unlock(&cnvCacheMutex);
+UConverter *
+ucnv_createConverter(UConverter *myUConverter, const char *converterName, UErrorCode * err)
+{
+    UConverterLookupData stackLookup;
+    UConverterSharedData *mySharedConverterData;
+
+    UTRACE_ENTRY_OC(UTRACE_UCNV_OPEN);
+
+    if(U_SUCCESS(*err)) {
+        UTRACE_DATA1(UTRACE_OPEN_CLOSE, "open converter %s", converterName);
+
+        mySharedConverterData = ucnv_loadSharedData(converterName, &stackLookup, err);
+
+        if(U_SUCCESS(*err)) {
+            myUConverter = ucnv_createConverterFromSharedData(
+                myUConverter, mySharedConverterData,
+                stackLookup.realName, stackLookup.locale, stackLookup.options,
+                err);
+
+            if(U_SUCCESS(*err)) {
+                UTRACE_EXIT_PTR_STATUS(myUConverter, *err);
+                return myUConverter;
+            } else {
+                ucnv_unloadSharedDataIfReady(mySharedConverterData);
+            }
         }
-        return NULL;
     }
 
-    return myUConverter;
+    /* exit with error */
+    UTRACE_EXIT_STATUS(*err);
+    return NULL;
 }
 
 UConverter *
@@ -681,11 +774,16 @@ ucnv_createAlgorithmicConverter(UConverter *myUConverter,
                                 UConverterType type,
                                 const char *locale, uint32_t options,
                                 UErrorCode *err) {
+    UConverter *cnv;
     const UConverterSharedData *sharedData;
     UBool isAlgorithmicConverter;
 
+    UTRACE_ENTRY_OC(UTRACE_UCNV_OPEN_ALGORITHMIC);
+    UTRACE_DATA1(UTRACE_OPEN_CLOSE, "open algorithmic converter type %d", (int32_t)type);
+
     if(type<0 || UCNV_NUMBER_OF_SUPPORTED_CONVERTER_TYPES<=type) {
         *err = U_ILLEGAL_ARGUMENT_ERROR;
+        UTRACE_EXIT_STATUS(U_ILLEGAL_ARGUMENT_ERROR);
         return NULL;
     }
 
@@ -696,47 +794,66 @@ ucnv_createAlgorithmicConverter(UConverter *myUConverter,
     if (isAlgorithmicConverter) {
         /* not a valid type, or not an algorithmic converter */
         *err = U_ILLEGAL_ARGUMENT_ERROR;
+        UTRACE_EXIT_STATUS(U_ILLEGAL_ARGUMENT_ERROR);
         return NULL;
     }
 
-    return ucnv_createConverterFromSharedData(myUConverter, (UConverterSharedData *)sharedData, "",
+    cnv = ucnv_createConverterFromSharedData(myUConverter, (UConverterSharedData *)sharedData, "",
                 locale != NULL ? locale : "", options, err);
+
+    UTRACE_EXIT_PTR_STATUS(cnv, *err);
+    return cnv;
 }
 
 UConverter*
 ucnv_createConverterFromPackage(const char *packageName, const char *converterName, UErrorCode * err)
 {
     char cnvName[UCNV_MAX_CONVERTER_NAME_LENGTH], locale[ULOC_FULLNAME_CAPACITY];
-    uint32_t options=0;
     UConverter *myUConverter;
-    UConverterSharedData *mySharedConverterData = NULL;
+    UConverterSharedData *mySharedConverterData;
+
+    UConverterLoadArgs args={ 0 };
+
+    UTRACE_ENTRY_OC(UTRACE_UCNV_OPEN_PACKAGE);
 
     if(U_FAILURE(*err)) {
-        return NULL; 
+        UTRACE_EXIT_STATUS(*err);
+        return NULL;
     }
 
-    /* first, get the options out of the convertername string */
-    parseConverterOptions(converterName, cnvName, locale, &options, err);
+    UTRACE_DATA2(UTRACE_OPEN_CLOSE, "open converter %s from package %s", converterName, packageName);
+
+    args.size=sizeof(UConverterLoadArgs);
+    args.nestedLoads=1;
+    args.pkg=packageName;
+
+    /* first, get the options out of the converterName string */
+    parseConverterOptions(converterName, cnvName, locale, &args.options, err);
     if (U_FAILURE(*err)) {
         /* Very bad name used. */
+        UTRACE_EXIT_STATUS(*err);
         return NULL;
     }
-    
+    args.name=cnvName;
+
     /* open the data, unflatten the shared structure */
-    mySharedConverterData = createConverterFromFile(packageName, cnvName, err);
-    
+    mySharedConverterData = createConverterFromFile(&args, err);
+
     if (U_FAILURE(*err)) {
-        return NULL; 
+        UTRACE_EXIT_STATUS(*err);
+        return NULL;
     }
 
     /* create the actual converter */
-    myUConverter = ucnv_createConverterFromSharedData(NULL, mySharedConverterData, cnvName, locale, options, err);
-    
+    myUConverter = ucnv_createConverterFromSharedData(NULL, mySharedConverterData, cnvName, locale, args.options, err);
+
     if (U_FAILURE(*err)) {
         ucnv_close(myUConverter);
-        return NULL; 
+        UTRACE_EXIT_STATUS(*err);
+        return NULL;
     }
-    
+
+    UTRACE_EXIT_PTR_STATUS(myUConverter, *err);
     return myUConverter;
 }
 
@@ -768,13 +885,14 @@ ucnv_createConverterFromSharedData(UConverter *myUConverter,
     myUConverter->isExtraLocal = FALSE;
     myUConverter->sharedData = mySharedConverterData;
     myUConverter->options = options;
-    myUConverter->mode = UCNV_SI;
     myUConverter->fromCharErrorBehaviour = (UConverterToUCallback) UCNV_TO_U_CALLBACK_SUBSTITUTE;
     myUConverter->fromUCharErrorBehaviour = (UConverterFromUCallback) UCNV_FROM_U_CALLBACK_SUBSTITUTE;
     myUConverter->toUnicodeStatus = myUConverter->sharedData->toUnicodeStatus;
+    myUConverter->maxBytesPerUChar = myUConverter->sharedData->staticData->maxBytesPerChar;
     myUConverter->subChar1 = myUConverter->sharedData->staticData->subChar1;
     myUConverter->subCharLen = myUConverter->sharedData->staticData->subCharLen;
     uprv_memcpy (myUConverter->subChar, myUConverter->sharedData->staticData->subChar, myUConverter->subCharLen);
+    myUConverter->preFromUFirstCP = U_SENTINEL;
 
     if(myUConverter != NULL && myUConverter->sharedData->impl->open != NULL) {
         myUConverter->sharedData->impl->open(myUConverter, realName, locale,options, err);
@@ -793,10 +911,13 @@ U_CAPI int32_t U_EXPORT2
 ucnv_flushCache ()
 {
     UConverterSharedData *mySharedData = NULL;
-    int32_t pos = -1;
+    int32_t pos;
     int32_t tableDeletedNum = 0;
     const UHashElement *e;
     UErrorCode status = U_ILLEGAL_ARGUMENT_ERROR;
+    int32_t i, remaining;
+
+    UTRACE_ENTRY_OC(UTRACE_UCNV_FLUSH_CACHE);
 
     /* Close the default converter without creating a new one so that everything will be flushed. */
     ucnv_close(u_getDefaultConverter(&status));
@@ -804,8 +925,10 @@ ucnv_flushCache ()
     /*if shared data hasn't even been lazy evaluated yet
     * return 0
     */
-    if (SHARED_DATA_HASHTABLE == NULL)
+    if (SHARED_DATA_HASHTABLE == NULL) {
+        UTRACE_EXIT_VALUE((int32_t)0);
         return 0;
+    }
 
     /*creates an enumeration to iterate through every element in the
     * table
@@ -819,25 +942,356 @@ ucnv_flushCache ()
     *                   is protected by cnvCacheMutex.
     */
     umtx_lock(&cnvCacheMutex);
-    while ((e = uhash_nextElement (SHARED_DATA_HASHTABLE, &pos)) != NULL)
-    {
-        mySharedData = (UConverterSharedData *) e->value.pointer;
-        /*deletes only if reference counter == 0 */
-        if (mySharedData->referenceCounter == 0)
+    /*
+     * double loop: A delta/extension-only converter has a pointer to its base table's
+     * shared data; the first iteration of the outer loop may see the delta converter
+     * before the base converter, and unloading the delta converter may get the base
+     * converter's reference counter down to 0.
+     */
+    i = 0;
+    do {
+        remaining = 0;
+        pos = -1;
+        while ((e = uhash_nextElement (SHARED_DATA_HASHTABLE, &pos)) != NULL)
         {
-            tableDeletedNum++;
-            
-            UCNV_DEBUG_LOG("del",mySharedData->staticData->name,mySharedData);
-            
-            uhash_removeElement(SHARED_DATA_HASHTABLE, e);
-            mySharedData->sharedDataCached = FALSE;
-            ucnv_deleteSharedConverterData (mySharedData);
+            mySharedData = (UConverterSharedData *) e->value.pointer;
+            /*deletes only if reference counter == 0 */
+            if (mySharedData->referenceCounter == 0)
+            {
+                tableDeletedNum++;
+
+                UCNV_DEBUG_LOG("del",mySharedData->staticData->name,mySharedData);
+
+                uhash_removeElement(SHARED_DATA_HASHTABLE, e);
+                mySharedData->sharedDataCached = FALSE;
+                ucnv_deleteSharedConverterData (mySharedData);
+            } else {
+                ++remaining;
+            }
         }
-    }
+    } while(++i == 1 && remaining > 0);
     umtx_unlock(&cnvCacheMutex);
 
+    UTRACE_DATA1(UTRACE_INFO, "ucnv_flushCache() exits with %d converters remaining", remaining);
+
     ucnv_io_flushAvailableConverterCache();
 
+    UTRACE_EXIT_VALUE(tableDeletedNum);
     return tableDeletedNum;
 }
 
+/* data swapping ------------------------------------------------------------ */
+
+/* most of this might belong more properly into ucnvmbcs.c, but that is so large */
+
+#if !UCONFIG_NO_LEGACY_CONVERSION
+
+U_CAPI int32_t U_EXPORT2
+ucnv_swap(const UDataSwapper *ds,
+          const void *inData, int32_t length, void *outData,
+          UErrorCode *pErrorCode) {
+    const UDataInfo *pInfo;
+    int32_t headerSize;
+
+    const uint8_t *inBytes;
+    uint8_t *outBytes;
+
+    uint32_t offset, count, staticDataSize;
+    int32_t size;
+
+    const UConverterStaticData *inStaticData;
+    UConverterStaticData *outStaticData;
+
+    const _MBCSHeader *inMBCSHeader;
+    _MBCSHeader *outMBCSHeader;
+    _MBCSHeader mbcsHeader;
+    uint8_t outputType;
+
+    const int32_t *inExtIndexes;
+    int32_t extOffset;
+
+    /* udata_swapDataHeader checks the arguments */
+    headerSize=udata_swapDataHeader(ds, inData, length, outData, pErrorCode);
+    if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) {
+        return 0;
+    }
+
+    /* check data format and format version */
+    pInfo=(const UDataInfo *)((const char *)inData+4);
+    if(!(
+        pInfo->dataFormat[0]==0x63 &&   /* dataFormat="cnvt" */
+        pInfo->dataFormat[1]==0x6e &&
+        pInfo->dataFormat[2]==0x76 &&
+        pInfo->dataFormat[3]==0x74 &&
+        pInfo->formatVersion[0]==6 &&
+        pInfo->formatVersion[1]>=2
+    )) {
+        udata_printError(ds, "ucnv_swap(): data format %02x.%02x.%02x.%02x (format version %02x.%02x) is not recognized as an ICU .cnv conversion table\n",
+                         pInfo->dataFormat[0], pInfo->dataFormat[1],
+                         pInfo->dataFormat[2], pInfo->dataFormat[3],
+                         pInfo->formatVersion[0], pInfo->formatVersion[1]);
+        *pErrorCode=U_UNSUPPORTED_ERROR;
+        return 0;
+    }
+
+    inBytes=(const uint8_t *)inData+headerSize;
+    outBytes=(uint8_t *)outData+headerSize;
+
+    /* read the initial UConverterStaticData structure after the UDataInfo header */
+    inStaticData=(const UConverterStaticData *)inBytes;
+    outStaticData=(UConverterStaticData *)outBytes;
+
+    if(length<0) {
+        staticDataSize=ds->readUInt32(inStaticData->structSize);
+    } else {
+        length-=headerSize;
+        if( length<sizeof(UConverterStaticData) ||
+            (uint32_t)length<(staticDataSize=ds->readUInt32(inStaticData->structSize))
+        ) {
+            udata_printError(ds, "ucnv_swap(): too few bytes (%d after header) for an ICU .cnv conversion table\n",
+                             length);
+            *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR;
+            return 0;
+        }
+    }
+
+    if(length>=0) {
+        /* swap the static data */
+        if(inStaticData!=outStaticData) {
+            uprv_memcpy(outStaticData, inStaticData, staticDataSize);
+        }
+
+        ds->swapArray32(ds, &inStaticData->structSize, 4,
+                           &outStaticData->structSize, pErrorCode);
+        ds->swapArray32(ds, &inStaticData->codepage, 4,
+                           &outStaticData->codepage, pErrorCode);
+
+        ds->swapInvChars(ds, inStaticData->name, uprv_strlen(inStaticData->name),
+                            outStaticData->name, pErrorCode);
+        if(U_FAILURE(*pErrorCode)) {
+            udata_printError(ds, "ucnv_swap(): error swapping converter name - %s\n",
+                             u_errorName(*pErrorCode));
+            return 0;
+        }
+    }
+
+    inBytes+=staticDataSize;
+    outBytes+=staticDataSize;
+    if(length>=0) {
+        length-=(int32_t)staticDataSize;
+    }
+
+    /* check for supported conversionType values */
+    if(inStaticData->conversionType==UCNV_MBCS) {
+        /* swap MBCS data */
+        inMBCSHeader=(const _MBCSHeader *)inBytes;
+        outMBCSHeader=(_MBCSHeader *)outBytes;
+
+        if(!(inMBCSHeader->version[0]==4 || inMBCSHeader->version[1]>=1)) {
+            udata_printError(ds, "ucnv_swap(): unsupported _MBCSHeader.version %d.%d\n",
+                             inMBCSHeader->version[0], inMBCSHeader->version[1]);
+            *pErrorCode=U_UNSUPPORTED_ERROR;
+            return 0;
+        }
+
+        uprv_memcpy(mbcsHeader.version, inMBCSHeader->version, 4);
+        mbcsHeader.countStates=         ds->readUInt32(inMBCSHeader->countStates);
+        mbcsHeader.countToUFallbacks=   ds->readUInt32(inMBCSHeader->countToUFallbacks);
+        mbcsHeader.offsetToUCodeUnits=  ds->readUInt32(inMBCSHeader->offsetToUCodeUnits);
+        mbcsHeader.offsetFromUTable=    ds->readUInt32(inMBCSHeader->offsetFromUTable);
+        mbcsHeader.offsetFromUBytes=    ds->readUInt32(inMBCSHeader->offsetFromUBytes);
+        mbcsHeader.flags=               ds->readUInt32(inMBCSHeader->flags);
+        mbcsHeader.fromUBytesLength=    ds->readUInt32(inMBCSHeader->fromUBytesLength);
+
+        extOffset=(int32_t)mbcsHeader.flags>>8;
+        outputType=(uint8_t)mbcsHeader.flags;
+
+        /* make sure that the output type is known */
+        switch(outputType) {
+        case MBCS_OUTPUT_1:
+        case MBCS_OUTPUT_2:
+        case MBCS_OUTPUT_3:
+        case MBCS_OUTPUT_4:
+        case MBCS_OUTPUT_3_EUC:
+        case MBCS_OUTPUT_4_EUC:
+        case MBCS_OUTPUT_2_SISO:
+        case MBCS_OUTPUT_EXT_ONLY:
+            /* OK */
+            break;
+        default:
+            udata_printError(ds, "ucnv_swap(): unsupported MBCS output type 0x%x\n",
+                             outputType);
+            *pErrorCode=U_UNSUPPORTED_ERROR;
+            return 0;
+        }
+
+        /* calculate the length of the MBCS data */
+        if(extOffset==0) {
+            size=(int32_t)(mbcsHeader.offsetFromUBytes+mbcsHeader.fromUBytesLength);
+
+            /* avoid compiler warnings - not otherwise necessary, and the value does not matter */
+            inExtIndexes=NULL;
+        } else {
+            /* there is extension data after the base data, see ucnv_ext.h */
+            if(length>=0 && length<(extOffset+UCNV_EXT_INDEXES_MIN_LENGTH*4)) {
+                udata_printError(ds, "ucnv_swap(): too few bytes (%d after headers) for an ICU MBCS .cnv conversion table with extension data\n",
+                                 length);
+                *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR;
+                return 0;
+            }
+
+            inExtIndexes=(const int32_t *)(inBytes+extOffset);
+            size=extOffset+udata_readInt32(ds, inExtIndexes[UCNV_EXT_SIZE]);
+        }
+
+        if(length>=0) {
+            if(length<size) {
+                udata_printError(ds, "ucnv_swap(): too few bytes (%d after headers) for an ICU MBCS .cnv conversion table\n",
+                                 length);
+                *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR;
+                return 0;
+            }
+
+            /* copy the data for inaccessible bytes */
+            if(inBytes!=outBytes) {
+                uprv_memcpy(outBytes, inBytes, size);
+            }
+
+            /* swap the MBCSHeader */
+            ds->swapArray32(ds, &inMBCSHeader->countStates, 7*4,
+                               &outMBCSHeader->countStates, pErrorCode);
+
+            if(outputType==MBCS_OUTPUT_EXT_ONLY) {
+                /*
+                 * extension-only file,
+                 * contains a base name instead of normal base table data
+                 */
+
+                /* swap the base name, between the header and the extension data */
+                ds->swapInvChars(ds, inMBCSHeader+1, uprv_strlen((const char *)(inMBCSHeader+1)),
+                                    outMBCSHeader+1, pErrorCode);
+            } else {
+                /* normal file with base table data */
+
+                /* swap the state table, 1kB per state */
+                ds->swapArray32(ds, inMBCSHeader+1, (int32_t)(mbcsHeader.countStates*1024),
+                                   outMBCSHeader+1, pErrorCode);
+
+                /* swap the toUFallbacks[] */
+                offset=sizeof(_MBCSHeader)+mbcsHeader.countStates*1024;
+                ds->swapArray32(ds, inBytes+offset, (int32_t)(mbcsHeader.countToUFallbacks*8),
+                                   outBytes+offset, pErrorCode);
+
+                /* swap the unicodeCodeUnits[] */
+                offset=mbcsHeader.offsetToUCodeUnits;
+                count=mbcsHeader.offsetFromUTable-offset;
+                ds->swapArray16(ds, inBytes+offset, (int32_t)count,
+                                   outBytes+offset, pErrorCode);
+
+                /* offset to the stage 1 table, independent of the outputType */
+                offset=mbcsHeader.offsetFromUTable;
+
+                if(outputType==MBCS_OUTPUT_1) {
+                    /* SBCS: swap the fromU tables, all 16 bits wide */
+                    count=(mbcsHeader.offsetFromUBytes-offset)+mbcsHeader.fromUBytesLength;
+                    ds->swapArray16(ds, inBytes+offset, (int32_t)count,
+                                       outBytes+offset, pErrorCode);
+                } else {
+                    /* otherwise: swap the stage tables separately */
+
+                    /* stage 1 table: uint16_t[0x440 or 0x40] */
+                    if(inStaticData->unicodeMask&UCNV_HAS_SUPPLEMENTARY) {
+                        count=0x440*2; /* for all of Unicode */
+                    } else {
+                        count=0x40*2; /* only BMP */
+                    }
+                    ds->swapArray16(ds, inBytes+offset, (int32_t)count,
+                                       outBytes+offset, pErrorCode);
+
+                    /* stage 2 table: uint32_t[] */
+                    offset+=count;
+                    count=mbcsHeader.offsetFromUBytes-offset;
+                    ds->swapArray32(ds, inBytes+offset, (int32_t)count,
+                                       outBytes+offset, pErrorCode);
+
+                    /* stage 3/result bytes: sometimes uint16_t[] or uint32_t[] */
+                    offset=mbcsHeader.offsetFromUBytes;
+                    count=mbcsHeader.fromUBytesLength;
+                    switch(outputType) {
+                    case MBCS_OUTPUT_2:
+                    case MBCS_OUTPUT_3_EUC:
+                    case MBCS_OUTPUT_2_SISO:
+                        ds->swapArray16(ds, inBytes+offset, (int32_t)count,
+                                           outBytes+offset, pErrorCode);
+                        break;
+                    case MBCS_OUTPUT_4:
+                        ds->swapArray32(ds, inBytes+offset, (int32_t)count,
+                                           outBytes+offset, pErrorCode);
+                        break;
+                    default:
+                        /* just uint8_t[], nothing to swap */
+                        break;
+                    }
+                }
+            }
+
+            if(extOffset!=0) {
+                /* swap the extension data */
+                inBytes+=extOffset;
+                outBytes+=extOffset;
+
+                /* swap toUTable[] */
+                offset=udata_readInt32(ds, inExtIndexes[UCNV_EXT_TO_U_INDEX]);
+                length=udata_readInt32(ds, inExtIndexes[UCNV_EXT_TO_U_LENGTH]);
+                ds->swapArray32(ds, inBytes+offset, length*4, outBytes+offset, pErrorCode);
+
+                /* swap toUUChars[] */
+                offset=udata_readInt32(ds, inExtIndexes[UCNV_EXT_TO_U_UCHARS_INDEX]);
+                length=udata_readInt32(ds, inExtIndexes[UCNV_EXT_TO_U_UCHARS_LENGTH]);
+                ds->swapArray16(ds, inBytes+offset, length*2, outBytes+offset, pErrorCode);
+
+                /* swap fromUTableUChars[] */
+                offset=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_UCHARS_INDEX]);
+                length=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_LENGTH]);
+                ds->swapArray16(ds, inBytes+offset, length*2, outBytes+offset, pErrorCode);
+
+                /* swap fromUTableValues[] */
+                offset=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_VALUES_INDEX]);
+                /* same length as for fromUTableUChars[] */
+                ds->swapArray32(ds, inBytes+offset, length*4, outBytes+offset, pErrorCode);
+
+                /* no need to swap fromUBytes[] */
+
+                /* swap fromUStage12[] */
+                offset=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_STAGE_12_INDEX]);
+                length=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_STAGE_12_LENGTH]);
+                ds->swapArray16(ds, inBytes+offset, length*2, outBytes+offset, pErrorCode);
+
+                /* swap fromUStage3[] */
+                offset=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_STAGE_3_INDEX]);
+                length=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_STAGE_3_LENGTH]);
+                ds->swapArray16(ds, inBytes+offset, length*2, outBytes+offset, pErrorCode);
+
+                /* swap fromUStage3b[] */
+                offset=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_STAGE_3B_INDEX]);
+                length=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_STAGE_3B_LENGTH]);
+                ds->swapArray32(ds, inBytes+offset, length*4, outBytes+offset, pErrorCode);
+
+                /* swap indexes[] */
+                length=udata_readInt32(ds, inExtIndexes[UCNV_EXT_INDEXES_LENGTH]);
+                ds->swapArray32(ds, inBytes, length*4, outBytes, pErrorCode);
+            }
+        }
+    } else {
+        udata_printError(ds, "ucnv_swap(): unknown conversionType=%d!=UCNV_MBCS\n",
+                         inStaticData->conversionType);
+        *pErrorCode=U_UNSUPPORTED_ERROR;
+        return 0;
+    }
+
+    return headerSize+(int32_t)staticDataSize+size;
+}
+
+#endif /* #if !UCONFIG_NO_LEGACY_CONVERSION */
+
+#endif