ICU-13597 Fixing safety of toUnicodeString() readonly aliases by moving that behavior to a new method, toTempUnicodeString().

X-SVN-Rev: 41164
This commit is contained in:
Shane Carr 2018-03-28 03:42:12 +00:00
parent 1b4670fd29
commit 2ede84ce47
15 changed files with 62 additions and 22 deletions

View File

@ -541,13 +541,14 @@ typedef enum UErrorCode {
U_FORMAT_INEXACT_ERROR, /**< Cannot format a number exactly and rounding mode is ROUND_UNNECESSARY @stable ICU 4.8 */
#ifndef U_HIDE_DRAFT_API
U_NUMBER_ARG_OUTOFBOUNDS_ERROR, /**< The argument to a NumberFormatter helper method was out of bounds; the bounds are usually 0 to 999. @draft ICU 61 */
U_NUMBER_SKELETON_SYNTAX_ERROR, /**< The number skeleton passed to C++ NumberFormatter or C UNumberFormatter was invalid or contained a syntax error. @draft ICU 62 */
#endif // U_HIDE_DRAFT_API
#ifndef U_HIDE_DEPRECATED_API
/**
* One more than the highest normal formatting API error code.
* @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420.
*/
U_FMT_PARSE_ERROR_LIMIT = 0x10113,
U_FMT_PARSE_ERROR_LIMIT = 0x10114,
#endif // U_HIDE_DEPRECATED_API
/*

View File

@ -126,7 +126,8 @@ _uFmtErrorName[U_FMT_PARSE_ERROR_LIMIT - U_FMT_PARSE_ERROR_START] = {
"U_DEFAULT_KEYWORD_MISSING",
"U_DECIMAL_NUMBER_SYNTAX_ERROR",
"U_FORMAT_INEXACT_ERROR",
"U_NUMBER_ARG_OUTOFBOUNDS_ERROR"
"U_NUMBER_ARG_OUTOFBOUNDS_ERROR",
"U_NUMBER_SKELETON_SYNTAX_ERROR",
};
static const char * const

View File

@ -155,7 +155,7 @@ unumf_resultToString(const UFormattedNumber* uresult, UChar* buffer, int32_t buf
return result->string.length();
}
return result->string.toUnicodeString().extract(buffer, bufferCapacity, *ec);
return result->string.toTempUnicodeString().extract(buffer, bufferCapacity, *ec);
}
U_CAPI void U_EXPORT2

View File

@ -774,7 +774,9 @@ bool
blueprint_helpers::parseExponentSignOption(const StringSegment& segment, MacroProps& macros, UErrorCode&) {
// Get the sign display type out of the CharsTrie data structure.
UCharsTrie tempStemTrie(kSerializedStemTrie);
UStringTrieResult result = tempStemTrie.next(segment.toUnicodeString().getBuffer(), segment.length());
UStringTrieResult result = tempStemTrie.next(
segment.toTempUnicodeString().getBuffer(),
segment.length());
if (result != USTRINGTRIE_INTERMEDIATE_VALUE && result != USTRINGTRIE_FINAL_VALUE) {
return false;
}
@ -788,6 +790,7 @@ blueprint_helpers::parseExponentSignOption(const StringSegment& segment, MacroPr
void blueprint_helpers::parseCurrencyOption(const StringSegment& segment, MacroProps& macros,
UErrorCode& status) {
// Can't use toTempUnicodeString() because getTerminatedBuffer is non-const
const UChar* currencyCode = segment.toUnicodeString().getTerminatedBuffer();
UErrorCode localStatus = U_ZERO_ERROR;
CurrencyUnit currency(currencyCode, localStatus);
@ -808,7 +811,7 @@ blueprint_helpers::generateCurrencyOption(const CurrencyUnit& currency, UnicodeS
void blueprint_helpers::parseMeasureUnitOption(const StringSegment& segment, MacroProps& macros,
UErrorCode& status) {
UnicodeString stemString = segment.toUnicodeString();
const UnicodeString stemString = segment.toTempUnicodeString();
// NOTE: The category (type) of the unit is guaranteed to be a valid subtag (alphanumeric)
// http://unicode.org/reports/tr35/#Validity_Data
@ -1043,7 +1046,7 @@ void blueprint_helpers::parseIncrementOption(const StringSegment& segment, Macro
UErrorCode& status) {
// Need to do char <-> UChar conversion...
CharString buffer;
SKELETON_UCHAR_TO_CHAR(buffer, segment.toUnicodeString(), 0, segment.length(), status);
SKELETON_UCHAR_TO_CHAR(buffer, segment.toTempUnicodeString(), 0, segment.length(), status);
// Utilize DecimalQuantity/decNumber to parse this for us.
DecimalQuantity dq;
@ -1155,7 +1158,7 @@ void blueprint_helpers::parseNumberingSystemOption(const StringSegment& segment,
UErrorCode& status) {
// Need to do char <-> UChar conversion...
CharString buffer;
SKELETON_UCHAR_TO_CHAR(buffer, segment.toUnicodeString(), 0, segment.length(), status);
SKELETON_UCHAR_TO_CHAR(buffer, segment.toTempUnicodeString(), 0, segment.length(), status);
NumberingSystem* ns = NumberingSystem::createInstanceByName(buffer.data(), status);
if (ns == nullptr) {

View File

@ -16,8 +16,6 @@ using icu::numparse::impl::StringSegment;
U_NAMESPACE_BEGIN namespace number {
namespace impl {
static constexpr UErrorCode U_NUMBER_SKELETON_SYNTAX_ERROR = U_ILLEGAL_ARGUMENT_ERROR;
// Forward-declaration
struct SeenMacroProps;

View File

@ -334,6 +334,10 @@ int32_t NumberStringBuilder::remove(int32_t index, int32_t count) {
}
UnicodeString NumberStringBuilder::toUnicodeString() const {
return UnicodeString(getCharPtr() + fZero, fLength);
}
const UnicodeString NumberStringBuilder::toTempUnicodeString() const {
// Readonly-alias constructor:
return UnicodeString(FALSE, getCharPtr() + fZero, fLength);
}

View File

@ -84,8 +84,17 @@ class U_I18N_API NumberStringBuilder : public UMemory {
int32_t insert(int32_t index, const NumberStringBuilder &other, UErrorCode &status);
/**
* Gets a "safe" UnicodeString that can be used even after the NumberStringBuilder is destructed.
* */
UnicodeString toUnicodeString() const;
/**
* Gets an "unsafe" UnicodeString that is valid only as long as the NumberStringBuilder is alive and
* unchanged. Slightly faster than toUnicodeString().
*/
const UnicodeString toTempUnicodeString() const;
UnicodeString toDebugString() const;
const char16_t *chars() const;

View File

@ -111,7 +111,16 @@ class U_I18N_API CharSequence {
}
}
/**
* Gets a "safe" UnicodeString that can be used even after the CharSequence is destructed.
* */
virtual UnicodeString toUnicodeString() const = 0;
/**
* Gets an "unsafe" UnicodeString that is valid only as long as the CharSequence is alive and
* unchanged. Slightly faster than toUnicodeString().
*/
virtual const UnicodeString toTempUnicodeString() const = 0;
};
class U_I18N_API AffixPatternProvider {

View File

@ -39,12 +39,13 @@ class UnicodeStringCharSequence : public CharSequence {
}
UnicodeString toUnicodeString() const U_OVERRIDE {
// Allocate a UnicodeString of the correct length
UnicodeString output(length(), 0, -1);
for (int32_t i = 0; i < length(); i++) {
output.append(charAt(i));
}
return output;
// Performs a copy:
return fStr;
}
const UnicodeString toTempUnicodeString() const U_OVERRIDE {
// Readonly alias:
return UnicodeString().fastCopyFrom(fStr);
}
private:

View File

@ -33,9 +33,8 @@ bool CurrencyNamesMatcher::match(StringSegment& segment, ParsedNumber& result, U
return false;
}
// NOTE: This requires a new UnicodeString to be allocated, instead of using the StringSegment.
// This should be fixed with #13584.
UnicodeString segmentString = segment.toUnicodeString();
// NOTE: This call site should be improved with #13584.
const UnicodeString segmentString = segment.toTempUnicodeString();
// Try to parse the currency
ParsePosition ppos(0);

View File

@ -61,6 +61,10 @@ UChar32 StringSegment::codePointAt(int32_t index) const {
}
UnicodeString StringSegment::toUnicodeString() const {
return UnicodeString(fStr.getBuffer() + fStart, fEnd - fStart);
}
const UnicodeString StringSegment::toTempUnicodeString() const {
// Use the readonly-aliasing constructor for efficiency.
return UnicodeString(FALSE, fStr.getBuffer() + fStart, fEnd - fStart);
}
@ -134,7 +138,7 @@ bool StringSegment::codePointsEqual(UChar32 cp1, UChar32 cp2, bool foldCase) {
}
bool StringSegment::operator==(const UnicodeString& other) const {
return toUnicodeString() == other;
return toTempUnicodeString() == other;
}

View File

@ -203,6 +203,8 @@ class StringSegment : public UMemory, public ::icu::number::impl::CharSequence {
UnicodeString toUnicodeString() const override;
const UnicodeString toTempUnicodeString() const override;
/**
* Returns the first code point in the string segment, or -1 if the string starts with an invalid
* code point.

View File

@ -29,12 +29,13 @@
/**
* \file
* \brief C API: NumberFormat
* \brief C API: Compatibility APIs for number formatting.
*
* <h2> Number Format C API </h2>
*
* <p><strong>IMPORTANT:</strong> New users with C++ capabilities are
* strongly encouraged to see if numberformatter.h fits their use case.
* <p><strong>IMPORTANT:</strong> New users with are strongly encouraged to
* see if unumberformatter.h fits their use case. Although not deprecated,
* this header is provided for backwards compatibility only.
*
* Number Format C API Provides functions for
* formatting and parsing a number. Also provides methods for
@ -399,6 +400,10 @@ typedef enum UNumberFormatFields {
* number format is opened using the given pattern, which must conform
* to the syntax described in DecimalFormat or RuleBasedNumberFormat,
* respectively.
*
* <p><strong>NOTE::</strong> New users with are strongly encouraged to
* use unumf_openWithSkeletonAndLocale instead of unum_open.
*
* @param pattern A pattern specifying the format to use.
* This parameter is ignored unless the style is
* UNUM_PATTERN_DECIMAL or UNUM_PATTERN_RULEBASED.

View File

@ -226,6 +226,7 @@ void AffixUtilsTest::testUnescapeWithSymbolProvider() {
AffixUtils::unescape(UnicodeStringCharSequence(input), sb, 0, provider, status);
assertSuccess("Spot 1", status);
assertEquals(input, expected, sb.toUnicodeString());
assertEquals(input, expected, sb.toTempUnicodeString());
}
// Test insertion position

View File

@ -50,10 +50,13 @@ void StringSegmentTest::testLength() {
void StringSegmentTest::testCharAt() {
StringSegment segment(SAMPLE_STRING, 0);
assertEquals("Initial", SAMPLE_STRING, segment.toUnicodeString());
assertEquals("Initial", SAMPLE_STRING, segment.toTempUnicodeString());
segment.adjustOffset(3);
assertEquals("After adjust-offset", UnicodeString(u"radio 📻"), segment.toUnicodeString());
assertEquals("After adjust-offset", UnicodeString(u"radio 📻"), segment.toTempUnicodeString());
segment.setLength(5);
assertEquals("After adjust-length", UnicodeString(u"radio"), segment.toUnicodeString());
assertEquals("After adjust-length", UnicodeString(u"radio"), segment.toTempUnicodeString());
}
void StringSegmentTest::testGetCodePoint() {