diff --git a/icu4c/source/common/ucnv.c b/icu4c/source/common/ucnv.c index 8cfa0d16c95..15dc0f0450a 100644 --- a/icu4c/source/common/ucnv.c +++ b/icu4c/source/common/ucnv.c @@ -148,6 +148,27 @@ ucnv_safeClone(const UConverter* cnv, void *stackBuffer, int32_t *pBufferSize, U UConverter * localConverter; int32_t bufferSizeNeeded; char *stackBufferChars = (char *)stackBuffer; + UErrorCode cbErr; + UConverterToUnicodeArgs toUArgs = { + sizeof(UConverterToUnicodeArgs), + TRUE, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL + }; + UConverterFromUnicodeArgs fromUArgs = { + sizeof(UConverterFromUnicodeArgs), + TRUE, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL + }; if (status == NULL || U_FAILURE(*status)){ return 0; @@ -164,6 +185,7 @@ ucnv_safeClone(const UConverter* cnv, void *stackBuffer, int32_t *pBufferSize, U *pBufferSize -= offsetUp; stackBufferChars += offsetUp; } + stackBuffer = (void *)stackBufferChars; if (cnv->sharedData->impl->safeClone != NULL) { @@ -173,6 +195,7 @@ ucnv_safeClone(const UConverter* cnv, void *stackBuffer, int32_t *pBufferSize, U } else { + /* inherent sizing */ bufferSizeNeeded = sizeof(UConverter); } @@ -181,26 +204,59 @@ ucnv_safeClone(const UConverter* cnv, void *stackBuffer, int32_t *pBufferSize, U return 0; } + + /* Now, see if we must allocate any memory */ if (*pBufferSize < bufferSizeNeeded || stackBuffer == NULL) { /* allocate one here...*/ - localConverter = ucnv_createConverter (ucnv_getName (cnv, status), status); - if (U_SUCCESS(*status)) - { + localConverter = (UConverter *) uprv_malloc (bufferSizeNeeded); + + if(localConverter == NULL) { + *status = U_MEMORY_ALLOCATION_ERROR; + return NULL; + } + + if (U_SUCCESS(*status)) { *status = U_SAFECLONE_ALLOCATED_ERROR; } + + /* record the fact that memory was allocated */ + *pBufferSize = bufferSizeNeeded; } else { - if (cnv->sharedData->impl->safeClone != NULL) { - /* call the custom safeClone function */ - localConverter = cnv->sharedData->impl->safeClone(cnv, stackBuffer, pBufferSize, status); - } - else - { - localConverter = (UConverter *)stackBuffer; - uprv_memcpy(localConverter, cnv, sizeof(UConverter)); - localConverter->isCopyLocal = TRUE; - } + /* just use the stack buffer */ + localConverter = (UConverter*) stackBuffer; } + + /* increment refcount of shared data if needed */ + if (cnv->sharedData->referenceCounter != ~0) { + umtx_lock (NULL); + if (cnv->sharedData->referenceCounter != ~0) { + cnv->sharedData->referenceCounter++; + } + umtx_unlock (NULL); + } + + /* Copy initial state */ + uprv_memcpy(localConverter, cnv, sizeof(UConverter)); + + /* now either call the safeclone fcn or not */ + if (cnv->sharedData->impl->safeClone != NULL) { + /* call the custom safeClone function */ + localConverter = cnv->sharedData->impl->safeClone(cnv, localConverter, pBufferSize, status); + } + + if(localConverter == (UConverter*)stackBuffer) { + /* we're using user provided data - set to not destroy */ + localConverter->isCopyLocal = TRUE; + } + + /* allow callback functions to handle any memory allocation */ + toUArgs.converter = fromUArgs.converter = localConverter; + cbErr = U_ZERO_ERROR; + cnv->fromCharErrorBehaviour(cnv->toUContext, &toUArgs, NULL, 0, UCNV_CLONE, &cbErr); + cbErr = U_ZERO_ERROR; + cnv->fromUCharErrorBehaviour(cnv->fromUContext, &fromUArgs, NULL, 0, 0, UCNV_CLONE, &cbErr); + return localConverter; } diff --git a/icu4c/source/common/ucnv2022.c b/icu4c/source/common/ucnv2022.c index 96c34b771b6..12ece4d2f10 100644 --- a/icu4c/source/common/ucnv2022.c +++ b/icu4c/source/common/ucnv2022.c @@ -3306,8 +3306,6 @@ _ISO_2022_SafeClone( uprv_memcpy(&localClone->mydata, cnv->extraInfo, sizeof(UConverterDataISO2022)); - localClone->cnv.isCopyLocal = TRUE; - /* clone the current converter if it is open in ISO-2022 converter and since it * preserves conversion state in currentConverter object we need to clone it */ diff --git a/icu4c/source/common/ucnv_cnv.h b/icu4c/source/common/ucnv_cnv.h index 0ebb4f70c5a..6d98100771c 100644 --- a/icu4c/source/common/ucnv_cnv.h +++ b/icu4c/source/common/ucnv_cnv.h @@ -87,6 +87,10 @@ typedef void (*UConverterWriteSub) (UConverterFromUnicodeArgs *pArgs, int32_t of * For converter-specific safeClone processing * If this function is not set, then ucnv_safeClone assumes that the converter has no private data that changes * after the converter is done opening. + * If this function is set, then it is called just after a memcpy() of + * converter data to the new, empty converter, and is expected to set up + * the initial state of the converter. It is not expected to increment the + * reference counts of the standard data types such as the shared data. */ typedef UConverter * (*UConverterSafeClone) (const UConverter *cnv, void *stackBuffer,