From c0340c47b071adcad155a41d568e3b7fc3aa4c49 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Thu, 6 Apr 2023 14:04:56 +0200 Subject: Release version 65.1.0+7 Patch upstream issue with operator== ambiguity described in GH issue #1. --- libicui18n/libicui18n/buildfile | 2 +- libicui18n/libicui18n/fmtable.cpp | 1043 +++++++++++++++++++++++++++++++ libicui18n/libicui18n/fmtable.cpp.patch | 12 + libicui18n/manifest | 2 +- libicuio/manifest | 2 +- libicuuc/manifest | 2 +- 6 files changed, 1059 insertions(+), 4 deletions(-) create mode 100644 libicui18n/libicui18n/fmtable.cpp create mode 100644 libicui18n/libicui18n/fmtable.cpp.patch diff --git a/libicui18n/libicui18n/buildfile b/libicui18n/libicui18n/buildfile index c1b1965..fcddedf 100644 --- a/libicui18n/libicui18n/buildfile +++ b/libicui18n/libicui18n/buildfile @@ -3,7 +3,7 @@ import intf_libs = libicuuc%lib{icuuc} -patched = stsearch olsontz vtzone rbtz dtitvfmt basictz +patched = stsearch olsontz vtzone rbtz dtitvfmt basictz fmtable lib{icui18n}: {hxx}{**} i18n/cxx{* -{$patched}} cxx{$patched} $intf_libs diff --git a/libicui18n/libicui18n/fmtable.cpp b/libicui18n/libicui18n/fmtable.cpp new file mode 100644 index 0000000..4024dc9 --- /dev/null +++ b/libicui18n/libicui18n/fmtable.cpp @@ -0,0 +1,1043 @@ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* +******************************************************************************* +* Copyright (C) 1997-2016, International Business Machines Corporation and +* others. All Rights Reserved. +******************************************************************************* +* +* File FMTABLE.CPP +* +* Modification History: +* +* Date Name Description +* 03/25/97 clhuang Initial Implementation. +******************************************************************************** +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include +#include +#include "unicode/fmtable.h" +#include "unicode/ustring.h" +#include "unicode/measure.h" +#include "unicode/curramt.h" +#include "unicode/uformattable.h" +#include "charstr.h" +#include "cmemory.h" +#include "cstring.h" +#include "fmtableimp.h" +#include "number_decimalquantity.h" + +// ***************************************************************************** +// class Formattable +// ***************************************************************************** + +U_NAMESPACE_BEGIN + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(Formattable) + +using number::impl::DecimalQuantity; + + +//-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. + +// NOTE: As of 3.0, there are limitations to the UObject API. It does +// not (yet) support cloning, operator=, nor operator==. To +// work around this, I implement some simple inlines here. Later +// these can be modified or removed. [alan] + +// NOTE: These inlines assume that all fObjects are in fact instances +// of the Measure class, which is true as of 3.0. [alan] + +// Return TRUE if *a == *b. +static inline UBool objectEquals(const UObject* a, const UObject* b) { + // LATER: return *a == *b; + //return *((const Measure*) a) == *((const Measure*) b); + return ((const Measure*) a)->operator== (*((const Measure*) b)); +} + +// Return a clone of *a. +static inline UObject* objectClone(const UObject* a) { + // LATER: return a->clone(); + return ((const Measure*) a)->clone(); +} + +// Return TRUE if *a is an instance of Measure. +static inline UBool instanceOfMeasure(const UObject* a) { + return dynamic_cast(a) != NULL; +} + +/** + * Creates a new Formattable array and copies the values from the specified + * original. + * @param array the original array + * @param count the original array count + * @return the new Formattable array. + */ +static Formattable* createArrayCopy(const Formattable* array, int32_t count) { + Formattable *result = new Formattable[count]; + if (result != NULL) { + for (int32_t i=0; i INT32_MAX) { + status = U_INVALID_FORMAT_ERROR; + return INT32_MAX; + } else if (fValue.fInt64 < INT32_MIN) { + status = U_INVALID_FORMAT_ERROR; + return INT32_MIN; + } else { + return (int32_t)fValue.fInt64; + } + case Formattable::kDouble: + if (fValue.fDouble > INT32_MAX) { + status = U_INVALID_FORMAT_ERROR; + return INT32_MAX; + } else if (fValue.fDouble < INT32_MIN) { + status = U_INVALID_FORMAT_ERROR; + return INT32_MIN; + } else { + return (int32_t)fValue.fDouble; // loses fraction + } + case Formattable::kObject: + if (fValue.fObject == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + return 0; + } + // TODO Later replace this with instanceof call + if (instanceOfMeasure(fValue.fObject)) { + return ((const Measure*) fValue.fObject)-> + getNumber().getLong(status); + } + U_FALLTHROUGH; + default: + status = U_INVALID_FORMAT_ERROR; + return 0; + } +} + +// ------------------------------------- +// Maximum int that can be represented exactly in a double. (53 bits) +// Larger ints may be rounded to a near-by value as not all are representable. +// TODO: move this constant elsewhere, possibly configure it for different +// floating point formats, if any non-standard ones are still in use. +static const int64_t U_DOUBLE_MAX_EXACT_INT = 9007199254740992LL; + +int64_t +Formattable::getInt64(UErrorCode& status) const +{ + if (U_FAILURE(status)) { + return 0; + } + + switch (fType) { + case Formattable::kLong: + case Formattable::kInt64: + return fValue.fInt64; + case Formattable::kDouble: + if (fValue.fDouble > (double)U_INT64_MAX) { + status = U_INVALID_FORMAT_ERROR; + return U_INT64_MAX; + } else if (fValue.fDouble < (double)U_INT64_MIN) { + status = U_INVALID_FORMAT_ERROR; + return U_INT64_MIN; + } else if (fabs(fValue.fDouble) > U_DOUBLE_MAX_EXACT_INT && fDecimalQuantity != NULL) { + if (fDecimalQuantity->fitsInLong(true)) { + return fDecimalQuantity->toLong(); + } else { + // Unexpected + status = U_INVALID_FORMAT_ERROR; + return fDecimalQuantity->isNegative() ? U_INT64_MIN : U_INT64_MAX; + } + } else { + return (int64_t)fValue.fDouble; + } + case Formattable::kObject: + if (fValue.fObject == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + return 0; + } + if (instanceOfMeasure(fValue.fObject)) { + return ((const Measure*) fValue.fObject)-> + getNumber().getInt64(status); + } + U_FALLTHROUGH; + default: + status = U_INVALID_FORMAT_ERROR; + return 0; + } +} + +// ------------------------------------- +double +Formattable::getDouble(UErrorCode& status) const +{ + if (U_FAILURE(status)) { + return 0; + } + + switch (fType) { + case Formattable::kLong: + case Formattable::kInt64: // loses precision + return (double)fValue.fInt64; + case Formattable::kDouble: + return fValue.fDouble; + case Formattable::kObject: + if (fValue.fObject == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + return 0; + } + // TODO Later replace this with instanceof call + if (instanceOfMeasure(fValue.fObject)) { + return ((const Measure*) fValue.fObject)-> + getNumber().getDouble(status); + } + U_FALLTHROUGH; + default: + status = U_INVALID_FORMAT_ERROR; + return 0; + } +} + +const UObject* +Formattable::getObject() const { + return (fType == kObject) ? fValue.fObject : NULL; +} + +// ------------------------------------- +// Sets the value to a double value d. + +void +Formattable::setDouble(double d) +{ + dispose(); + fType = kDouble; + fValue.fDouble = d; +} + +// ------------------------------------- +// Sets the value to a long value l. + +void +Formattable::setLong(int32_t l) +{ + dispose(); + fType = kLong; + fValue.fInt64 = l; +} + +// ------------------------------------- +// Sets the value to an int64 value ll. + +void +Formattable::setInt64(int64_t ll) +{ + dispose(); + fType = kInt64; + fValue.fInt64 = ll; +} + +// ------------------------------------- +// Sets the value to a Date instance d. + +void +Formattable::setDate(UDate d) +{ + dispose(); + fType = kDate; + fValue.fDate = d; +} + +// ------------------------------------- +// Sets the value to a string value stringToCopy. + +void +Formattable::setString(const UnicodeString& stringToCopy) +{ + dispose(); + fType = kString; + fValue.fString = new UnicodeString(stringToCopy); +} + +// ------------------------------------- +// Sets the value to an array of Formattable objects. + +void +Formattable::setArray(const Formattable* array, int32_t count) +{ + dispose(); + fType = kArray; + fValue.fArrayAndCount.fArray = createArrayCopy(array, count); + fValue.fArrayAndCount.fCount = count; +} + +// ------------------------------------- +// Adopts the stringToAdopt value. + +void +Formattable::adoptString(UnicodeString* stringToAdopt) +{ + dispose(); + fType = kString; + fValue.fString = stringToAdopt; +} + +// ------------------------------------- +// Adopts the array value and its count. + +void +Formattable::adoptArray(Formattable* array, int32_t count) +{ + dispose(); + fType = kArray; + fValue.fArrayAndCount.fArray = array; + fValue.fArrayAndCount.fCount = count; +} + +void +Formattable::adoptObject(UObject* objectToAdopt) { + dispose(); + fType = kObject; + fValue.fObject = objectToAdopt; +} + +// ------------------------------------- +UnicodeString& +Formattable::getString(UnicodeString& result, UErrorCode& status) const +{ + if (fType != kString) { + setError(status, U_INVALID_FORMAT_ERROR); + result.setToBogus(); + } else { + if (fValue.fString == NULL) { + setError(status, U_MEMORY_ALLOCATION_ERROR); + } else { + result = *fValue.fString; + } + } + return result; +} + +// ------------------------------------- +const UnicodeString& +Formattable::getString(UErrorCode& status) const +{ + if (fType != kString) { + setError(status, U_INVALID_FORMAT_ERROR); + return *getBogus(); + } + if (fValue.fString == NULL) { + setError(status, U_MEMORY_ALLOCATION_ERROR); + return *getBogus(); + } + return *fValue.fString; +} + +// ------------------------------------- +UnicodeString& +Formattable::getString(UErrorCode& status) +{ + if (fType != kString) { + setError(status, U_INVALID_FORMAT_ERROR); + return *getBogus(); + } + if (fValue.fString == NULL) { + setError(status, U_MEMORY_ALLOCATION_ERROR); + return *getBogus(); + } + return *fValue.fString; +} + +// ------------------------------------- +const Formattable* +Formattable::getArray(int32_t& count, UErrorCode& status) const +{ + if (fType != kArray) { + setError(status, U_INVALID_FORMAT_ERROR); + count = 0; + return NULL; + } + count = fValue.fArrayAndCount.fCount; + return fValue.fArrayAndCount.fArray; +} + +// ------------------------------------- +// Gets the bogus string, ensures mondo bogosity. + +UnicodeString* +Formattable::getBogus() const +{ + return (UnicodeString*)&fBogus; /* cast away const :-( */ +} + + +// -------------------------------------- +StringPiece Formattable::getDecimalNumber(UErrorCode &status) { + if (U_FAILURE(status)) { + return ""; + } + if (fDecimalStr != NULL) { + return fDecimalStr->toStringPiece(); + } + + CharString *decimalStr = internalGetCharString(status); + if(decimalStr == NULL) { + return ""; // getDecimalNumber returns "" for error cases + } else { + return decimalStr->toStringPiece(); + } +} + +CharString *Formattable::internalGetCharString(UErrorCode &status) { + if(fDecimalStr == NULL) { + if (fDecimalQuantity == NULL) { + // No decimal number for the formattable yet. Which means the value was + // set directly by the user as an int, int64 or double. If the value came + // from parsing, or from the user setting a decimal number, fDecimalNum + // would already be set. + // + LocalPointer dq(new DecimalQuantity(), status); + if (U_FAILURE(status)) { return nullptr; } + populateDecimalQuantity(*dq, status); + if (U_FAILURE(status)) { return nullptr; } + fDecimalQuantity = dq.orphan(); + } + + fDecimalStr = new CharString(); + if (fDecimalStr == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + return NULL; + } + // Older ICUs called uprv_decNumberToString here, which is not exactly the same as + // DecimalQuantity::toScientificString(). The biggest difference is that uprv_decNumberToString does + // not print scientific notation for magnitudes greater than -5 and smaller than some amount (+5?). + if (fDecimalQuantity->isInfinite()) { + fDecimalStr->append("Infinity", status); + } else if (fDecimalQuantity->isNaN()) { + fDecimalStr->append("NaN", status); + } else if (fDecimalQuantity->isZeroish()) { + fDecimalStr->append("0", -1, status); + } else if (fType==kLong || fType==kInt64 || // use toPlainString for integer types + (fDecimalQuantity->getMagnitude() != INT32_MIN && std::abs(fDecimalQuantity->getMagnitude()) < 5)) { + fDecimalStr->appendInvariantChars(fDecimalQuantity->toPlainString(), status); + } else { + fDecimalStr->appendInvariantChars(fDecimalQuantity->toScientificString(), status); + } + } + return fDecimalStr; +} + +void +Formattable::populateDecimalQuantity(number::impl::DecimalQuantity& output, UErrorCode& status) const { + if (fDecimalQuantity != nullptr) { + output = *fDecimalQuantity; + return; + } + + switch (fType) { + case kDouble: + output.setToDouble(this->getDouble()); + output.roundToInfinity(); + break; + case kLong: + output.setToInt(this->getLong()); + break; + case kInt64: + output.setToLong(this->getInt64()); + break; + default: + // The formattable's value is not a numeric type. + status = U_INVALID_STATE_ERROR; + } +} + +// --------------------------------------- +void +Formattable::adoptDecimalQuantity(DecimalQuantity *dq) { + if (fDecimalQuantity != NULL) { + delete fDecimalQuantity; + } + fDecimalQuantity = dq; + if (dq == NULL) { // allow adoptDigitList(NULL) to clear + return; + } + + // Set the value into the Union of simple type values. + // Cannot use the set() functions because they would delete the fDecimalNum value. + if (fDecimalQuantity->fitsInLong()) { + fValue.fInt64 = fDecimalQuantity->toLong(); + if (fValue.fInt64 <= INT32_MAX && fValue.fInt64 >= INT32_MIN) { + fType = kLong; + } else { + fType = kInt64; + } + } else { + fType = kDouble; + fValue.fDouble = fDecimalQuantity->toDouble(); + } +} + + +// --------------------------------------- +void +Formattable::setDecimalNumber(StringPiece numberString, UErrorCode &status) { + if (U_FAILURE(status)) { + return; + } + dispose(); + + auto* dq = new DecimalQuantity(); + dq->setToDecNumber(numberString, status); + adoptDecimalQuantity(dq); + + // Note that we do not hang on to the caller's input string. + // If we are asked for the string, we will regenerate one from fDecimalQuantity. +} + +#if 0 +//---------------------------------------------------- +// console I/O +//---------------------------------------------------- +#ifdef _DEBUG + +#include +using namespace std; + +#include "unicode/datefmt.h" +#include "unistrm.h" + +class FormattableStreamer /* not : public UObject because all methods are static */ { +public: + static void streamOut(ostream& stream, const Formattable& obj); + +private: + FormattableStreamer() {} // private - forbid instantiation +}; + +// This is for debugging purposes only. This will send a displayable +// form of the Formattable object to the output stream. + +void +FormattableStreamer::streamOut(ostream& stream, const Formattable& obj) +{ + static DateFormat *defDateFormat = 0; + + UnicodeString buffer; + switch(obj.getType()) { + case Formattable::kDate : + // Creates a DateFormat instance for formatting the + // Date instance. + if (defDateFormat == 0) { + defDateFormat = DateFormat::createInstance(); + } + defDateFormat->format(obj.getDate(), buffer); + stream << buffer; + break; + case Formattable::kDouble : + // Output the double as is. + stream << obj.getDouble() << 'D'; + break; + case Formattable::kLong : + // Output the double as is. + stream << obj.getLong() << 'L'; + break; + case Formattable::kString: + // Output the double as is. Please see UnicodeString console + // I/O routine for more details. + stream << '"' << obj.getString(buffer) << '"'; + break; + case Formattable::kArray: + int32_t i, count; + const Formattable* array; + array = obj.getArray(count); + stream << '['; + // Recursively calling the console I/O routine for each element in the array. + for (i=0; itoUFormattable(); + + if( fmt == NULL ) { + *status = U_MEMORY_ALLOCATION_ERROR; + } + return fmt; +} + +U_DRAFT void U_EXPORT2 +ufmt_close(UFormattable *fmt) { + Formattable *obj = Formattable::fromUFormattable(fmt); + + delete obj; +} + +U_INTERNAL UFormattableType U_EXPORT2 +ufmt_getType(const UFormattable *fmt, UErrorCode *status) { + if(U_FAILURE(*status)) { + return (UFormattableType)UFMT_COUNT; + } + const Formattable *obj = Formattable::fromUFormattable(fmt); + return (UFormattableType)obj->getType(); +} + + +U_INTERNAL UBool U_EXPORT2 +ufmt_isNumeric(const UFormattable *fmt) { + const Formattable *obj = Formattable::fromUFormattable(fmt); + return obj->isNumeric(); +} + +U_DRAFT UDate U_EXPORT2 +ufmt_getDate(const UFormattable *fmt, UErrorCode *status) { + const Formattable *obj = Formattable::fromUFormattable(fmt); + + return obj->getDate(*status); +} + +U_DRAFT double U_EXPORT2 +ufmt_getDouble(UFormattable *fmt, UErrorCode *status) { + Formattable *obj = Formattable::fromUFormattable(fmt); + + return obj->getDouble(*status); +} + +U_DRAFT int32_t U_EXPORT2 +ufmt_getLong(UFormattable *fmt, UErrorCode *status) { + Formattable *obj = Formattable::fromUFormattable(fmt); + + return obj->getLong(*status); +} + + +U_DRAFT const void *U_EXPORT2 +ufmt_getObject(const UFormattable *fmt, UErrorCode *status) { + const Formattable *obj = Formattable::fromUFormattable(fmt); + + const void *ret = obj->getObject(); + if( ret==NULL && + (obj->getType() != Formattable::kObject) && + U_SUCCESS( *status )) { + *status = U_INVALID_FORMAT_ERROR; + } + return ret; +} + +U_DRAFT const UChar* U_EXPORT2 +ufmt_getUChars(UFormattable *fmt, int32_t *len, UErrorCode *status) { + Formattable *obj = Formattable::fromUFormattable(fmt); + + // avoid bogosity by checking the type first. + if( obj->getType() != Formattable::kString ) { + if( U_SUCCESS(*status) ){ + *status = U_INVALID_FORMAT_ERROR; + } + return NULL; + } + + // This should return a valid string + UnicodeString &str = obj->getString(*status); + if( U_SUCCESS(*status) && len != NULL ) { + *len = str.length(); + } + return str.getTerminatedBuffer(); +} + +U_DRAFT int32_t U_EXPORT2 +ufmt_getArrayLength(const UFormattable* fmt, UErrorCode *status) { + const Formattable *obj = Formattable::fromUFormattable(fmt); + + int32_t count; + (void)obj->getArray(count, *status); + return count; +} + +U_DRAFT UFormattable * U_EXPORT2 +ufmt_getArrayItemByIndex(UFormattable* fmt, int32_t n, UErrorCode *status) { + Formattable *obj = Formattable::fromUFormattable(fmt); + int32_t count; + (void)obj->getArray(count, *status); + if(U_FAILURE(*status)) { + return NULL; + } else if(n<0 || n>=count) { + setError(*status, U_INDEX_OUTOFBOUNDS_ERROR); + return NULL; + } else { + return (*obj)[n].toUFormattable(); // returns non-const Formattable + } +} + +U_DRAFT const char * U_EXPORT2 +ufmt_getDecNumChars(UFormattable *fmt, int32_t *len, UErrorCode *status) { + if(U_FAILURE(*status)) { + return ""; + } + Formattable *obj = Formattable::fromUFormattable(fmt); + CharString *charString = obj->internalGetCharString(*status); + if(U_FAILURE(*status)) { + return ""; + } + if(charString == NULL) { + *status = U_MEMORY_ALLOCATION_ERROR; + return ""; + } else { + if(len!=NULL) { + *len = charString->length(); + } + return charString->data(); + } +} + +U_DRAFT int64_t U_EXPORT2 +ufmt_getInt64(UFormattable *fmt, UErrorCode *status) { + Formattable *obj = Formattable::fromUFormattable(fmt); + return obj->getInt64(*status); +} + +#endif /* #if !UCONFIG_NO_FORMATTING */ + +//eof diff --git a/libicui18n/libicui18n/fmtable.cpp.patch b/libicui18n/libicui18n/fmtable.cpp.patch new file mode 100644 index 0000000..e753db1 --- /dev/null +++ b/libicui18n/libicui18n/fmtable.cpp.patch @@ -0,0 +1,12 @@ +--- i18n/fmtable.cpp 2023-04-05 17:32:46.385668953 +0200 ++++ fmtable.cpp 2023-04-06 14:01:47.787233628 +0200 +@@ -56,7 +56,8 @@ + // Return TRUE if *a == *b. + static inline UBool objectEquals(const UObject* a, const UObject* b) { + // LATER: return *a == *b; +- return *((const Measure*) a) == *((const Measure*) b); ++ //return *((const Measure*) a) == *((const Measure*) b); ++ return ((const Measure*) a)->operator== (*((const Measure*) b)); + } + + // Return a clone of *a. diff --git a/libicui18n/manifest b/libicui18n/manifest index f29c3e5..0bd114a 100644 --- a/libicui18n/manifest +++ b/libicui18n/manifest @@ -1,6 +1,6 @@ : 1 name: libicui18n -version: 65.1.0+6 +version: 65.1.0+7 upstream-version: 65.1 project: icu summary: ICU high-level internationalization C/C++ library diff --git a/libicuio/manifest b/libicuio/manifest index c0eac20..cb5c784 100644 --- a/libicuio/manifest +++ b/libicuio/manifest @@ -1,6 +1,6 @@ : 1 name: libicuio -version: 65.1.0+6 +version: 65.1.0+7 upstream-version: 65.1 project: icu summary: ICU input/output C/C++ library diff --git a/libicuuc/manifest b/libicuuc/manifest index d53df67..317dbfc 100644 --- a/libicuuc/manifest +++ b/libicuuc/manifest @@ -1,6 +1,6 @@ : 1 name: libicuuc -version: 65.1.0+6 +version: 65.1.0+7 upstream-version: 65.1 project: icu summary: ICU basic internationalization C/C++ library -- cgit v1.1