ICU-21087 Merge maint/maint-67 to master

This commit is contained in:
Shane F. Carr 2020-04-22 20:15:39 -05:00 committed by GitHub
commit a5c940dfd8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 830 additions and 223 deletions

View File

@ -1,6 +1,6 @@
{
"localeFilter": {
"filterType": "language",
"filterType": "locale",
"whitelist": [
"en",
"de",

View File

@ -202,7 +202,7 @@ summarizes the ICU data files and their corresponding features and categories:
| Region Display <br/> Names | `"region_tree"` | region/\*.txt | **1.1 MiB** |
| Rule-Based <br/> Number Formatting <br/> (Spellout, Ordinals) | `"rbnf_tree"` | rbnf/\*.txt | 538 KiB |
| StringPrep | `"stringprep"` | sprep/\*.txt | 193 KiB |
| Time Zones | `"misc"` <br/> `"zone_tree"` | misc/metaZones.txt <br/> misc/timezoneTypes.txt <br/> misc/windowsZones.txt <br/> misc/zoneinfo64.txt <br/> zone/\*.txt | 41 KiB <br/> 20 KiB <br/> 22 KiB <br/> 151 KiB <br/> **2.7 MiB** |
| Time Zones | `"misc"` <br/> `"zone_tree"` <br/> `"zone_supplemental"` | misc/metaZones.txt <br/> misc/timezoneTypes.txt <br/> misc/windowsZones.txt <br/> misc/zoneinfo64.txt <br/> zone/\*.txt <br/> zone/tzdbNames.txt | 41 KiB <br/> 20 KiB <br/> 22 KiB <br/> 151 KiB <br/> **2.7 MiB** <br/> 4.8 KiB |
| Transliteration | `"translit"` | translit/\*.txt | 685 KiB |
| Unicode Character <br/> Names | `"unames"` | in/unames.icu | 269 KiB |
| Unicode Text Layout | `"ulayout"` | in/ulayout.icu | 14 KiB |

View File

@ -466,6 +466,7 @@ LocaleMatcher::LocaleMatcher(LocaleMatcher &&src) U_NOEXCEPT :
thresholdDistance(src.thresholdDistance),
demotionPerDesiredLocale(src.demotionPerDesiredLocale),
favorSubtag(src.favorSubtag),
direction(src.direction),
supportedLocales(src.supportedLocales), lsrs(src.lsrs),
supportedLocalesLength(src.supportedLocalesLength),
supportedLsrToIndex(src.supportedLsrToIndex),
@ -502,6 +503,7 @@ LocaleMatcher &LocaleMatcher::operator=(LocaleMatcher &&src) U_NOEXCEPT {
thresholdDistance = src.thresholdDistance;
demotionPerDesiredLocale = src.demotionPerDesiredLocale;
favorSubtag = src.favorSubtag;
direction = src.direction;
supportedLocales = src.supportedLocales;
lsrs = src.lsrs;
supportedLocalesLength = src.supportedLocalesLength;

View File

@ -83,25 +83,6 @@ umutablecptrie_clone(const UMutableCPTrie *other, UErrorCode *pErrorCode);
U_CAPI void U_EXPORT2
umutablecptrie_close(UMutableCPTrie *trie);
#if U_SHOW_CPLUSPLUS_API
U_NAMESPACE_BEGIN
/**
* \class LocalUMutableCPTriePointer
* "Smart pointer" class, closes a UMutableCPTrie via umutablecptrie_close().
* For most methods see the LocalPointerBase base class.
*
* @see LocalPointerBase
* @see LocalPointer
* @stable ICU 63
*/
U_DEFINE_LOCAL_OPEN_POINTER(LocalUMutableCPTriePointer, UMutableCPTrie, umutablecptrie_close);
U_NAMESPACE_END
#endif
/**
* Creates a mutable trie with the same contents as the UCPMap.
* You must umutablecptrie_close() the mutable trie once you are done using it.
@ -235,4 +216,23 @@ umutablecptrie_buildImmutable(UMutableCPTrie *trie, UCPTrieType type, UCPTrieVal
U_CDECL_END
#if U_SHOW_CPLUSPLUS_API
U_NAMESPACE_BEGIN
/**
* \class LocalUMutableCPTriePointer
* "Smart pointer" class, closes a UMutableCPTrie via umutablecptrie_close().
* For most methods see the LocalPointerBase base class.
*
* @see LocalPointerBase
* @see LocalPointer
* @stable ICU 63
*/
U_DEFINE_LOCAL_OPEN_POINTER(LocalUMutableCPTriePointer, UMutableCPTrie, umutablecptrie_close);
U_NAMESPACE_END
#endif
#endif

View File

@ -183,25 +183,6 @@ typedef struct UText UText; /**< C typedef for struct UText. @stable ICU 3.6 */
U_STABLE UText * U_EXPORT2
utext_close(UText *ut);
#if U_SHOW_CPLUSPLUS_API
U_NAMESPACE_BEGIN
/**
* \class LocalUTextPointer
* "Smart pointer" class, closes a UText via utext_close().
* For most methods see the LocalPointerBase base class.
*
* @see LocalPointerBase
* @see LocalPointer
* @stable ICU 4.4
*/
U_DEFINE_LOCAL_OPEN_POINTER(LocalUTextPointer, UText, utext_close);
U_NAMESPACE_END
#endif
/**
* Open a read-only UText implementation for UTF-8 strings.
*
@ -1599,5 +1580,24 @@ enum {
U_CDECL_END
#if U_SHOW_CPLUSPLUS_API
U_NAMESPACE_BEGIN
/**
* \class LocalUTextPointer
* "Smart pointer" class, closes a UText via utext_close().
* For most methods see the LocalPointerBase base class.
*
* @see LocalPointerBase
* @see LocalPointer
* @stable ICU 4.4
*/
U_DEFINE_LOCAL_OPEN_POINTER(LocalUTextPointer, UText, utext_close);
U_NAMESPACE_END
#endif
#endif

View File

@ -33,6 +33,7 @@ def generate(config, io, common_vars):
requests += generate_unames(config, io, common_vars)
requests += generate_misc(config, io, common_vars)
requests += generate_curr_supplemental(config, io, common_vars)
requests += generate_zone_supplemental(config, io, common_vars)
requests += generate_translit(config, io, common_vars)
# Res Tree Files
@ -399,6 +400,29 @@ def generate_curr_supplemental(config, io, common_vars):
]
def generate_zone_supplemental(config, io, common_vars):
# tzdbNames Res File
input_file = InFile("zone/tzdbNames.txt")
input_basename = "tzdbNames.txt"
output_file = OutFile("zone/tzdbNames.res")
return [
SingleExecutionRequest(
name = "zone_supplemental_res",
category = "zone_supplemental",
dep_targets = [],
input_files = [input_file],
output_files = [output_file],
tool = IcuTool("genrb"),
args = "-s {IN_DIR}/zone -d {OUT_DIR}/zone -i {OUT_DIR} "
"-k "
"{INPUT_BASENAME}",
format_with = {
"INPUT_BASENAME": input_basename
}
)
]
def generate_translit(config, io, common_vars):
input_files = [
InFile("translit/root.txt"),
@ -444,10 +468,11 @@ def generate_tree(
requests = []
category = "%s_tree" % sub_dir
out_prefix = "%s/" % out_sub_dir if out_sub_dir else ""
# TODO: Clean this up for curr
input_files = [InFile(filename) for filename in io.glob("%s/*.txt" % sub_dir)]
if sub_dir == "curr":
input_files.remove(InFile("curr/supplementalData.txt"))
if sub_dir == "zone":
input_files.remove(InFile("zone/tzdbNames.txt"))
input_basenames = [v.filename[len(sub_dir)+1:] for v in input_files]
output_files = [
OutFile("%s%s.res" % (out_prefix, v[:-4]))

View File

@ -66,15 +66,23 @@ inline void abort_noreturn() { abort(); }
#endif
#endif
// Not all compilers support __has_attribute and combining a check for both
// ifdef and __has_attribute on the same preprocessor line isn't portable.
#ifdef __has_attribute
# define DOUBLE_CONVERSION_HAS_ATTRIBUTE(x) __has_attribute(x)
#else
# define DOUBLE_CONVERSION_HAS_ATTRIBUTE(x) 0
#endif
#ifndef DOUBLE_CONVERSION_UNUSED
#ifdef __GNUC__
#if DOUBLE_CONVERSION_HAS_ATTRIBUTE(unused)
#define DOUBLE_CONVERSION_UNUSED __attribute__((unused))
#else
#define DOUBLE_CONVERSION_UNUSED
#endif
#endif
#if defined(__clang__) && __has_attribute(uninitialized)
#if DOUBLE_CONVERSION_HAS_ATTRIBUTE(uninitialized)
#define DOUBLE_CONVERSION_STACK_UNINITIALIZED __attribute__((uninitialized))
#else
#define DOUBLE_CONVERSION_STACK_UNINITIALIZED

View File

@ -348,6 +348,7 @@ const ListFormatInternal* ListFormatter::getListFormatInternal(
return result;
}
#if !UCONFIG_NO_FORMATTING
static const char* typeWidthToStyleString(UListFormatterType type, UListFormatterWidth width) {
switch (type) {
case ULISTFMT_TYPE_AND:
@ -391,6 +392,7 @@ static const char* typeWidthToStyleString(UListFormatterType type, UListFormatte
return nullptr;
}
#endif
static const UChar solidus = 0x2F;
static const UChar aliasPrefix[] = { 0x6C,0x69,0x73,0x74,0x50,0x61,0x74,0x74,0x65,0x72,0x6E,0x2F }; // "listPattern/"
@ -511,9 +513,14 @@ ListFormatter* ListFormatter::createInstance(UErrorCode& errorCode) {
}
ListFormatter* ListFormatter::createInstance(const Locale& locale, UErrorCode& errorCode) {
#if !UCONFIG_NO_FORMATTING
return createInstance(locale, ULISTFMT_TYPE_AND, ULISTFMT_WIDTH_WIDE, errorCode);
#else
return createInstance(locale, "standard", errorCode);
#endif
}
#if !UCONFIG_NO_FORMATTING
ListFormatter* ListFormatter::createInstance(
const Locale& locale, UListFormatterType type, UListFormatterWidth width, UErrorCode& errorCode) {
const char* style = typeWidthToStyleString(type, width);
@ -523,6 +530,7 @@ ListFormatter* ListFormatter::createInstance(
}
return createInstance(locale, style, errorCode);
}
#endif
ListFormatter* ListFormatter::createInstance(const Locale& locale, const char *style, UErrorCode& errorCode) {
const ListFormatInternal* listFormatInternal = getListFormatInternal(locale, style, errorCode);

View File

@ -537,9 +537,9 @@ static const char * const gSubTypes[] = {
"solar-mass",
"stone",
"ton",
"one",
"percent",
"permille",
"", // TODO(ICU-21076): manual edit of what should have been generated by Java.
"percent", // TODO(ICU-21076): regenerate, deal with duplication.
"permille", // TODO(ICU-21076): regenerate, deal with duplication.
"gigawatt",
"horsepower",
"kilowatt",

View File

@ -12,6 +12,7 @@
// Helpful in toString methods and elsewhere.
#define UNISTR_FROM_STRING_EXPLICIT
#include <cstdlib>
#include "cstring.h"
#include "measunit_impl.h"
#include "uarrsort.h"
@ -34,17 +35,32 @@ namespace {
// TODO: Propose a new error code for this?
constexpr UErrorCode kUnitIdentifierSyntaxError = U_ILLEGAL_ARGUMENT_ERROR;
// This is to ensure we only insert positive integers into the trie
// Trie value offset for SI Prefixes. This is big enough to ensure we only
// insert positive integers into the trie.
constexpr int32_t kSIPrefixOffset = 64;
// Trie value offset for compound parts, e.g. "-per-", "-", "-and-".
constexpr int32_t kCompoundPartOffset = 128;
enum CompoundPart {
// Represents "-per-"
COMPOUND_PART_PER = kCompoundPartOffset,
// Represents "-"
COMPOUND_PART_TIMES,
COMPOUND_PART_PLUS,
// Represents "-and-"
COMPOUND_PART_AND,
};
// Trie value offset for "per-".
constexpr int32_t kInitialCompoundPartOffset = 192;
enum InitialCompoundPart {
// Represents "per-", the only compound part that can appear at the start of
// an identifier.
INITIAL_COMPOUND_PART_PER = kInitialCompoundPartOffset,
};
// Trie value offset for powers like "square-", "cubic-", "p2-" etc.
constexpr int32_t kPowerPartOffset = 256;
enum PowerPart {
@ -64,6 +80,8 @@ enum PowerPart {
POWER_PART_P15,
};
// Trie value offset for simple units, e.g. "gram", "nautical-mile",
// "fluid-ounce-imperial".
constexpr int32_t kSimpleUnitOffset = 512;
const struct SIPrefixStrings {
@ -94,7 +112,6 @@ const struct SIPrefixStrings {
// TODO(ICU-21059): Get this list from data
const char16_t* const gSimpleUnits[] = {
u"one", // note: expected to be index 0
u"candela",
u"carat",
u"gram",
@ -226,7 +243,8 @@ void U_CALLCONV initUnitExtras(UErrorCode& status) {
// Add syntax parts (compound, power prefixes)
b.add(u"-per-", COMPOUND_PART_PER, status);
b.add(u"-", COMPOUND_PART_TIMES, status);
b.add(u"-and-", COMPOUND_PART_PLUS, status);
b.add(u"-and-", COMPOUND_PART_AND, status);
b.add(u"per-", INITIAL_COMPOUND_PART_PER, status);
b.add(u"square-", POWER_PART_P2, status);
b.add(u"cubic-", POWER_PART_P3, status);
b.add(u"p2-", POWER_PART_P2, status);
@ -270,28 +288,30 @@ public:
enum Type {
TYPE_UNDEFINED,
TYPE_SI_PREFIX,
// Token type for "-per-", "-", and "-and-".
TYPE_COMPOUND_PART,
// Token type for "per-".
TYPE_INITIAL_COMPOUND_PART,
TYPE_POWER_PART,
TYPE_ONE,
TYPE_SIMPLE_UNIT,
};
// Calling getType() is invalid, resulting in an assertion failure, if Token
// value isn't positive.
Type getType() const {
if (fMatch <= 0) {
UPRV_UNREACHABLE;
}
U_ASSERT(fMatch > 0);
if (fMatch < kCompoundPartOffset) {
return TYPE_SI_PREFIX;
}
if (fMatch < kPowerPartOffset) {
if (fMatch < kInitialCompoundPartOffset) {
return TYPE_COMPOUND_PART;
}
if (fMatch < kPowerPartOffset) {
return TYPE_INITIAL_COMPOUND_PART;
}
if (fMatch < kSimpleUnitOffset) {
return TYPE_POWER_PART;
}
if (fMatch == kSimpleUnitOffset) {
return TYPE_ONE;
}
return TYPE_SIMPLE_UNIT;
}
@ -300,11 +320,22 @@ public:
return static_cast<UMeasureSIPrefix>(fMatch - kSIPrefixOffset);
}
// Valid only for tokens with type TYPE_COMPOUND_PART.
int32_t getMatch() const {
U_ASSERT(getType() == TYPE_COMPOUND_PART);
return fMatch;
}
int32_t getInitialCompoundPart() const {
// Even if there is only one InitialCompoundPart value, we have this
// function for the simplicity of code consistency.
U_ASSERT(getType() == TYPE_INITIAL_COMPOUND_PART);
// Defensive: if this assert fails, code using this function also needs
// to change.
U_ASSERT(fMatch == INITIAL_COMPOUND_PART_PER);
return fMatch;
}
int8_t getPower() const {
U_ASSERT(getType() == TYPE_POWER_PART);
return static_cast<int8_t>(fMatch - kPowerPartOffset);
@ -321,6 +352,14 @@ private:
class Parser {
public:
/**
* Factory function for parsing the given identifier.
*
* @param source The identifier to parse. This function does not make a copy
* of source: the underlying string that source points at, must outlive the
* parser.
* @param status ICU error code.
*/
static Parser from(StringPiece source, UErrorCode& status) {
if (U_FAILURE(status)) {
return Parser();
@ -339,10 +378,18 @@ public:
}
private:
// Tracks parser progress: the offset into fSource.
int32_t fIndex = 0;
// Since we're not owning this memory, whatever is passed to the constructor
// should live longer than this Parser - and the parser shouldn't return any
// references to that string.
StringPiece fSource;
UCharsTrie fTrie;
// Set to true when we've seen a "-per-" or a "per-", after which all units
// are in the denominator. Until we find an "-and-", at which point the
// identifier is invalid pending TODO(CLDR-13700).
bool fAfterPer = false;
Parser() : fSource(""), fTrie(u"") {}
@ -354,11 +401,17 @@ private:
return fIndex < fSource.length();
}
// Returns the next Token parsed from fSource, advancing fIndex to the end
// of that token in fSource. In case of U_FAILURE(status), the token
// returned will cause an abort if getType() is called on it.
Token nextToken(UErrorCode& status) {
fTrie.reset();
int32_t match = -1;
// Saves the position in the fSource string for the end of the most
// recent matching token.
int32_t previ = -1;
do {
// Find the longest token that matches a value in the trie:
while (fIndex < fSource.length()) {
auto result = fTrie.next(fSource.data()[fIndex++]);
if (result == USTRINGTRIE_NO_MATCH) {
break;
@ -373,7 +426,7 @@ private:
}
U_ASSERT(result == USTRINGTRIE_INTERMEDIATE_VALUE);
// continue;
} while (fIndex < fSource.length());
}
if (match < 0) {
status = kUnitIdentifierSyntaxError;
@ -383,62 +436,88 @@ private:
return Token(match);
}
void nextSingleUnit(SingleUnitImpl& result, bool& sawPlus, UErrorCode& status) {
sawPlus = false;
/**
* Returns the next "single unit" via result.
*
* If a "-per-" was parsed, the result will have appropriate negative
* dimensionality.
*
* Returns an error if we parse both compound units and "-and-", since mixed
* compound units are not yet supported - TODO(CLDR-13700).
*
* @param result Will be overwritten by the result, if status shows success.
* @param sawAnd If an "-and-" was parsed prior to finding the "single
* unit", sawAnd is set to true. If not, it is left as is.
* @param status ICU error code.
*/
void nextSingleUnit(SingleUnitImpl& result, bool& sawAnd, UErrorCode& status) {
if (U_FAILURE(status)) {
return;
}
if (!hasNext()) {
// probably "one"
return;
}
// state:
// 0 = no tokens seen yet (will accept power, SI prefix, or simple unit)
// 1 = power token seen (will not accept another power token)
// 2 = SI prefix token seen (will not accept a power or SI prefix token)
int32_t state = 0;
int32_t previ = fIndex;
// Maybe read a compound part
if (fIndex != 0) {
Token token = nextToken(status);
if (U_FAILURE(status)) {
return;
bool atStart = fIndex == 0;
Token token = nextToken(status);
if (U_FAILURE(status)) { return; }
if (atStart) {
// Identifiers optionally start with "per-".
if (token.getType() == Token::TYPE_INITIAL_COMPOUND_PART) {
U_ASSERT(token.getInitialCompoundPart() == INITIAL_COMPOUND_PART_PER);
fAfterPer = true;
result.dimensionality = -1;
token = nextToken(status);
if (U_FAILURE(status)) { return; }
}
} else {
// All other SingleUnit's are separated from previous SingleUnit's
// via a compound part:
if (token.getType() != Token::TYPE_COMPOUND_PART) {
status = kUnitIdentifierSyntaxError;
return;
}
switch (token.getMatch()) {
case COMPOUND_PART_PER:
if (fAfterPer) {
status = kUnitIdentifierSyntaxError;
return;
}
fAfterPer = true;
case COMPOUND_PART_PER:
if (sawAnd) {
// Mixed compound units not yet supported,
// TODO(CLDR-13700).
status = kUnitIdentifierSyntaxError;
return;
}
fAfterPer = true;
result.dimensionality = -1;
break;
case COMPOUND_PART_TIMES:
if (fAfterPer) {
result.dimensionality = -1;
break;
}
break;
case COMPOUND_PART_TIMES:
break;
case COMPOUND_PART_PLUS:
sawPlus = true;
fAfterPer = false;
break;
case COMPOUND_PART_AND:
if (fAfterPer) {
// Can't start with "-and-", and mixed compound units
// not yet supported, TODO(CLDR-13700).
status = kUnitIdentifierSyntaxError;
return;
}
sawAnd = true;
break;
}
previ = fIndex;
token = nextToken(status);
if (U_FAILURE(status)) { return; }
}
// Read a unit
while (hasNext()) {
Token token = nextToken(status);
if (U_FAILURE(status)) {
return;
}
// Read tokens until we have a complete SingleUnit or we reach the end.
while (true) {
switch (token.getType()) {
case Token::TYPE_POWER_PART:
if (state > 0) {
@ -446,7 +525,6 @@ private:
return;
}
result.dimensionality *= token.getPower();
previ = fIndex;
state = 1;
break;
@ -456,54 +534,62 @@ private:
return;
}
result.siPrefix = token.getSIPrefix();
previ = fIndex;
state = 2;
break;
case Token::TYPE_ONE:
// Skip "one" and go to the next unit
return nextSingleUnit(result, sawPlus, status);
case Token::TYPE_SIMPLE_UNIT:
result.index = token.getSimpleUnitIndex();
result.identifier = fSource.substr(previ, fIndex - previ);
return;
default:
status = kUnitIdentifierSyntaxError;
return;
}
}
// We ran out of tokens before finding a complete single unit.
status = kUnitIdentifierSyntaxError;
if (!hasNext()) {
// We ran out of tokens before finding a complete single unit.
status = kUnitIdentifierSyntaxError;
return;
}
token = nextToken(status);
if (U_FAILURE(status)) {
return;
}
}
}
/// @param result is modified, not overridden. Caller must pass in a
/// default-constructed (empty) MeasureUnitImpl instance.
void parseImpl(MeasureUnitImpl& result, UErrorCode& status) {
if (U_FAILURE(status)) {
return;
}
if (fSource.empty()) {
// The dimenionless unit: nothing to parse. leave result as is.
return;
}
int32_t unitNum = 0;
while (hasNext()) {
bool sawPlus;
bool sawAnd = false;
SingleUnitImpl singleUnit;
nextSingleUnit(singleUnit, sawPlus, status);
nextSingleUnit(singleUnit, sawAnd, status);
if (U_FAILURE(status)) {
return;
}
if (singleUnit.index == 0) {
continue;
}
U_ASSERT(!singleUnit.isDimensionless());
bool added = result.append(singleUnit, status);
if (sawPlus && !added) {
if (sawAnd && !added) {
// Two similar units are not allowed in a mixed unit
status = kUnitIdentifierSyntaxError;
return;
}
if ((++unitNum) >= 2) {
UMeasureUnitComplexity complexity = sawPlus
? UMEASURE_UNIT_MIXED
: UMEASURE_UNIT_COMPOUND;
// nextSingleUnit fails appropriately for "per" and "and" in the
// same identifier. It doesn't fail for other compound units
// (COMPOUND_PART_TIMES). Consequently we take care of that
// here.
UMeasureUnitComplexity complexity =
sawAnd ? UMEASURE_UNIT_MIXED : UMEASURE_UNIT_COMPOUND;
if (unitNum == 2) {
U_ASSERT(result.complexity == UMEASURE_UNIT_SINGLE);
result.complexity = complexity;
@ -526,15 +612,22 @@ compareSingleUnits(const void* /*context*/, const void* left, const void* right)
/**
* Generate the identifier string for a single unit in place.
*
* Does not support the dimensionless SingleUnitImpl: calling serializeSingle
* with the dimensionless unit results in an U_INTERNAL_PROGRAM_ERROR.
*
* @param first If singleUnit is part of a compound unit, and not its first
* single unit, set this to false. Otherwise: set to true.
*/
void serializeSingle(const SingleUnitImpl& singleUnit, bool first, CharString& output, UErrorCode& status) {
if (first && singleUnit.dimensionality < 0) {
output.append("one-per-", status);
// Essentially the "unary per". For compound units with a numerator, the
// caller takes care of the "binary per".
output.append("per-", status);
}
if (singleUnit.index == 0) {
// Don't propagate SI prefixes and powers on one
output.append("one", status);
if (singleUnit.isDimensionless()) {
status = U_INTERNAL_PROGRAM_ERROR;
return;
}
int8_t posPower = std::abs(singleUnit.dimensionality);
@ -573,7 +666,7 @@ void serializeSingle(const SingleUnitImpl& singleUnit, bool first, CharString& o
return;
}
output.append(singleUnit.identifier, status);
output.appendInvariantChars(gSimpleUnits[singleUnit.index], status);
}
/**
@ -585,7 +678,8 @@ void serialize(MeasureUnitImpl& impl, UErrorCode& status) {
}
U_ASSERT(impl.identifier.isEmpty());
if (impl.units.length() == 0) {
impl.identifier.append("one", status);
// Dimensionless, constructed by the default constructor: no appending
// to impl.identifier, we wish it to contain the zero-length string.
return;
}
if (impl.complexity == UMEASURE_UNIT_COMPOUND) {
@ -624,8 +718,17 @@ void serialize(MeasureUnitImpl& impl, UErrorCode& status) {
}
/** @return true if a new item was added */
/**
* Appends a SingleUnitImpl to a MeasureUnitImpl.
*
* @return true if a new item was added. If unit is the dimensionless unit, it
* is never added: the return value will always be false.
*/
bool appendImpl(MeasureUnitImpl& impl, const SingleUnitImpl& unit, UErrorCode& status) {
if (unit.isDimensionless()) {
// We don't append dimensionless units.
return false;
}
// Find a similar unit that already exists, to attempt to coalesce
SingleUnitImpl* oldUnit = nullptr;
for (int32_t i = 0; i < impl.units.length(); i++) {
@ -635,6 +738,8 @@ bool appendImpl(MeasureUnitImpl& impl, const SingleUnitImpl& unit, UErrorCode& s
}
}
if (oldUnit) {
// Both dimensionalities will be positive, or both will be negative, by
// virtue of isCompatibleWith().
oldUnit->dimensionality += unit.dimensionality;
} else {
SingleUnitImpl* destination = impl.units.emplaceBack();
@ -734,7 +839,12 @@ MeasureUnit MeasureUnit::withSIPrefix(UMeasureSIPrefix prefix, UErrorCode& statu
}
int32_t MeasureUnit::getDimensionality(UErrorCode& status) const {
return SingleUnitImpl::forMeasureUnit(*this, status).dimensionality;
SingleUnitImpl singleUnit = SingleUnitImpl::forMeasureUnit(*this, status);
if (U_FAILURE(status)) { return 0; }
if (singleUnit.isDimensionless()) {
return 0;
}
return singleUnit.dimensionality;
}
MeasureUnit MeasureUnit::withDimensionality(int32_t dimensionality, UErrorCode& status) const {

View File

@ -25,14 +25,25 @@ static const char kDefaultCurrency8[] = "XXX";
struct SingleUnitImpl : public UMemory {
/**
* Gets a single unit from the MeasureUnit. If there are multiple single units, sets an error
* code and return the base dimensionless unit. Parses if necessary.
* code and returns the base dimensionless unit. Parses if necessary.
*/
static SingleUnitImpl forMeasureUnit(const MeasureUnit& measureUnit, UErrorCode& status);
/** Transform this SingleUnitImpl into a MeasureUnit, simplifying if possible. */
MeasureUnit build(UErrorCode& status) const;
/** Compare this SingleUnitImpl to another SingleUnitImpl. */
/**
* Compare this SingleUnitImpl to another SingleUnitImpl for the sake of
* sorting and coalescing.
*
* Takes the sign of dimensionality into account, but not the absolute
* value: per-meter is not considered the same as meter, but meter is
* considered the same as square-meter.
*
* The dimensionless unit generally does not get compared, but if it did, it
* would sort before other units by virtue of index being < 0 and
* dimensionality not being negative.
*/
int32_t compareTo(const SingleUnitImpl& other) const {
if (dimensionality < 0 && other.dimensionality > 0) {
// Positive dimensions first
@ -66,16 +77,36 @@ struct SingleUnitImpl : public UMemory {
return (compareTo(other) == 0);
}
/** Simple unit index, unique for every simple unit. */
int32_t index = 0;
/**
* Returns true if this unit is the "dimensionless base unit", as produced
* by the MeasureUnit() default constructor. (This does not include the
* likes of concentrations or angles.)
*/
bool isDimensionless() const {
return index == -1;
}
/** Simple unit identifier; memory not owned by the SimpleUnit. */
StringPiece identifier;
/**
* Simple unit index, unique for every simple unit, -1 for the dimensionless
* unit. This is an index into a string list in measunit_extra.cpp.
*
* The default value is -1, meaning the dimensionless unit:
* isDimensionless() will return true, until index is changed.
*/
int32_t index = -1;
/** SI prefix. **/
/**
* SI prefix.
*
* This is ignored for the dimensionless unit.
*/
UMeasureSIPrefix siPrefix = UMEASURE_SI_PREFIX_ONE;
/** Dimensionality. **/
/**
* Dimensionality.
*
* This is meaningless for the dimensionless unit.
*/
int32_t dimensionality = 1;
};
@ -95,7 +126,8 @@ struct MeasureUnitImpl : public UMemory {
*
* @param identifier The unit identifier string.
* @param status Set if the identifier string is not valid.
* @return A newly parsed value object.
* @return A newly parsed value object. Behaviour of this unit is
* unspecified if an error is returned via status.
*/
static MeasureUnitImpl forIdentifier(StringPiece identifier, UErrorCode& status);
@ -148,15 +180,23 @@ struct MeasureUnitImpl : public UMemory {
/** Mutates this MeasureUnitImpl to take the reciprocal. */
void takeReciprocal(UErrorCode& status);
/** Mutates this MeasureUnitImpl to append a single unit. */
/**
* Mutates this MeasureUnitImpl to append a single unit.
*
* @return true if a new item was added. If unit is the dimensionless unit,
* it is never added: the return value will always be false.
*/
bool append(const SingleUnitImpl& singleUnit, UErrorCode& status);
/** The complexity, either SINGLE, COMPOUND, or MIXED. */
UMeasureUnitComplexity complexity = UMEASURE_UNIT_SINGLE;
/**
* The list of simple units. These may be summed or multiplied, based on the value of the
* complexity field.
* The list of simple units. These may be summed or multiplied, based on the
* value of the complexity field.
*
* The "dimensionless" unit (SingleUnitImpl default constructor) must not be
* added to this list.
*/
MaybeStackVector<SingleUnitImpl> units;

View File

@ -11,7 +11,7 @@ U_NAMESPACE_BEGIN
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NoUnit)
NoUnit U_EXPORT2 NoUnit::base() {
return NoUnit("one");
return NoUnit("");
}
NoUnit U_EXPORT2 NoUnit::percent() {

View File

@ -203,6 +203,9 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe,
patternStyle = CLDR_PATTERN_STYLE_CURRENCY;
}
pattern = utils::getPatternForStyle(macros.locale, nsName, patternStyle, status);
if (U_FAILURE(status)) {
return nullptr;
}
}
auto patternInfo = new ParsedPatternInfo();
if (patternInfo == nullptr) {
@ -211,6 +214,9 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe,
}
fPatternInfo.adoptInstead(patternInfo);
PatternParser::parseToPatternInfo(UnicodeString(pattern), *patternInfo, status);
if (U_FAILURE(status)) {
return nullptr;
}
/////////////////////////////////////////////////////////////////////////////////////
/// START POPULATING THE DEFAULT MICROPROPS AND BUILDING THE MICROPROPS GENERATOR ///
@ -241,6 +247,9 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe,
roundingMode = precision.fRoundingMode;
}
fMicros.rounder = {precision, roundingMode, currency, status};
if (U_FAILURE(status)) {
return nullptr;
}
// Grouping strategy
if (!macros.grouper.isBogus()) {
@ -323,6 +332,9 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe,
if (safe) {
fImmutablePatternModifier.adoptInstead(patternModifier->createImmutable(status));
}
if (U_FAILURE(status)) {
return nullptr;
}
// Outer modifier (CLDR units and currency long names)
if (isCldrUnit) {
@ -349,6 +361,9 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe,
// No outer modifier required
fMicros.modOuter = &fMicros.helpers.emptyWeakModifier;
}
if (U_FAILURE(status)) {
return nullptr;
}
// Compact notation
if (macros.notation.fType == Notation::NTN_COMPACT) {
@ -371,6 +386,9 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe,
fCompactHandler.adoptInstead(newCompactHandler);
chain = fCompactHandler.getAlias();
}
if (U_FAILURE(status)) {
return nullptr;
}
// Always add the pattern modifier as the last element of the chain.
if (safe) {

View File

@ -246,7 +246,8 @@ LongNameHandler::forCompoundUnit(const Locale &loc, const MeasureUnit &unit, con
if (U_FAILURE(status)) { return result; }
UnicodeString secondaryFormat = getWithPlural(secondaryData, StandardPlural::Form::ONE, status);
if (U_FAILURE(status)) { return result; }
SimpleFormatter secondaryCompiled(secondaryFormat, 1, 1, status);
// Some "one" pattern may not contain "{0}". For example in "ar" or "ne" locale.
SimpleFormatter secondaryCompiled(secondaryFormat, 0, 1, status);
if (U_FAILURE(status)) { return result; }
UnicodeString secondaryString = secondaryCompiled.getTextWithNoArguments().trim();
// TODO: Why does UnicodeString need to be explicit in the following line?

View File

@ -483,6 +483,8 @@ public:
*/
const UnicodeString& getDecimal() const;
#if !UCONFIG_NO_FORMATTING
#ifndef U_HIDE_DRAFT_API
/**
* Get the default hour cycle for a locale. Uses the locale that the
@ -499,6 +501,8 @@ public:
UDateFormatHourCycle getDefaultHourCycle(UErrorCode& status) const;
#endif /* U_HIDE_DRAFT_API */
#endif /* #if !UCONFIG_NO_FORMATTING */
/**
* ICU "poor man's RTTI", returns a UClassID for the actual class.
*

View File

@ -186,6 +186,7 @@ class U_I18N_API ListFormatter : public UObject{
static ListFormatter* createInstance(const Locale& locale, UErrorCode& errorCode);
#ifndef U_HIDE_DRAFT_API
#if !UCONFIG_NO_FORMATTING
/**
* Creates a ListFormatter for the given locale, list type, and style.
*
@ -198,8 +199,9 @@ class U_I18N_API ListFormatter : public UObject{
*/
static ListFormatter* createInstance(
const Locale& locale, UListFormatterType type, UListFormatterWidth width, UErrorCode& errorCode);
#endif /* !UCONFIG_NO_FORMATTING */
#endif /* U_HIDE_DRAFT_API */
#ifndef U_HIDE_INTERNAL_API
/**
* Creates a ListFormatter appropriate for a locale and style.

View File

@ -37,7 +37,7 @@ struct MeasureUnitImpl;
* Enumeration for unit complexity. There are three levels:
*
* - SINGLE: A single unit, optionally with a power and/or SI prefix. Examples: hectare,
* square-kilometer, kilojoule, one-per-second.
* square-kilometer, kilojoule, per-second.
* - COMPOUND: A unit composed of the product of multiple single units. Examples:
* meter-per-second, kilowatt-hour, kilogram-meter-per-square-second.
* - MIXED: A unit composed of the sum of multiple single units. Examples: foot+inch,
@ -387,6 +387,8 @@ class U_I18N_API MeasureUnit: public UObject {
* NOTE: Only works on SINGLE units. If this is a COMPOUND or MIXED unit, an error will
* occur. For more information, see UMeasureUnitComplexity.
*
* For the base dimensionless unit, withDimensionality does nothing.
*
* @param dimensionality The dimensionality (power).
* @param status Set if this is not a SINGLE unit or if another error occurs.
* @return A new SINGLE unit.
@ -401,6 +403,8 @@ class U_I18N_API MeasureUnit: public UObject {
* NOTE: Only works on SINGLE units. If this is a COMPOUND or MIXED unit, an error will
* occur. For more information, see UMeasureUnitComplexity.
*
* For the base dimensionless unit, getDimensionality returns 0.
*
* @param status Set if this is not a SINGLE unit or if another error occurs.
* @return The dimensionality (power) of this simple unit.
* @draft ICU 67
@ -447,7 +451,7 @@ class U_I18N_API MeasureUnit: public UObject {
*
* Examples:
* - Given "meter-kilogram-per-second", three units will be returned: "meter",
* "kilogram", and "one-per-second".
* "kilogram", and "per-second".
* - Given "hour+minute+second", three units will be returned: "hour", "minute",
* and "second".
*
@ -3375,11 +3379,15 @@ class U_I18N_API MeasureUnit: public UObject {
private:
// If non-null, fImpl is owned by the MeasureUnit.
// Used by new draft APIs in ICU 67. If non-null, fImpl is owned by the
// MeasureUnit.
MeasureUnitImpl* fImpl;
// These two ints are indices into static string lists in measunit.cpp
// An index into a static string list in measunit.cpp. If set to -1, fImpl
// is in use instead of fTypeId and fSubTypeId.
int16_t fSubTypeId;
// An index into a static string list in measunit.cpp. If set to -1, fImpl
// is in use instead of fTypeId and fSubTypeId.
int8_t fTypeId;
MeasureUnit(int32_t typeId, int32_t subTypeId);
@ -3389,7 +3397,11 @@ private:
static MeasureUnit *create(int typeId, int subTypeId, UErrorCode &status);
/**
* @return Whether subType is known to ICU.
* Sets output's typeId and subTypeId according to subType, if subType is a
* valid/known identifier.
*
* @return Whether subType is known to ICU. If false, output was not
* modified.
*/
static bool findBySubType(StringPiece subType, MeasureUnit* output);

View File

@ -652,6 +652,8 @@ udatpg_getPatternForSkeleton(const UDateTimePatternGenerator *dtpg,
const UChar *skeleton, int32_t skeletonLength,
int32_t *pLength);
#if !UCONFIG_NO_FORMATTING
#ifndef U_HIDE_DRAFT_API
/**
* Return the default hour cycle for a locale. Uses the locale that the
@ -670,4 +672,6 @@ U_DRAFT UDateFormatHourCycle U_EXPORT2
udatpg_getDefaultHourCycle(const UDateTimePatternGenerator *dtpg, UErrorCode* pErrorCode);
#endif /* U_HIDE_DRAFT_API */
#endif /* #if !UCONFIG_NO_FORMATTING */
#endif

View File

@ -217,7 +217,7 @@ class LocaleFilter(Filter):
return "root"
i = locale.rfind("_")
if i < 0:
assert locale == "root"
assert locale == "root", "Invalid locale: %s/%s" % (tree, locale)
return None
return locale[:i]

View File

@ -9,9 +9,11 @@
// Helpful in toString methods and elsewhere.
#define UNISTR_FROM_STRING_EXPLICIT
#include <stdio.h>
#include "unicode/unumberformatter.h"
#include "unicode/umisc.h"
#include "unicode/unum.h"
#include "unicode/ustring.h"
#include "cformtst.h"
#include "cintltst.h"
#include "cmemory.h"
@ -26,6 +28,8 @@ static void TestFormattedValue(void);
static void TestSkeletonParseError(void);
static void TestPerUnitInArabic(void);
void addUNumberFormatterTest(TestNode** root);
#define TESTCASE(x) addTest(root, &x, "tsformat/unumberformatter/" #x)
@ -36,6 +40,7 @@ void addUNumberFormatterTest(TestNode** root) {
TESTCASE(TestExampleCode);
TESTCASE(TestFormattedValue);
TESTCASE(TestSkeletonParseError);
TESTCASE(TestPerUnitInArabic);
}
@ -254,5 +259,88 @@ static void TestSkeletonParseError() {
unumf_close(uformatter);
}
static void TestPerUnitInArabic() {
const char* simpleMeasureUnits[] = {
"area-acre",
"digital-bit",
"digital-byte",
"temperature-celsius",
"length-centimeter",
"duration-day",
"angle-degree",
"temperature-fahrenheit",
"volume-fluid-ounce",
"length-foot",
"volume-gallon",
"digital-gigabit",
"digital-gigabyte",
"mass-gram",
"area-hectare",
"duration-hour",
"length-inch",
"digital-kilobit",
"digital-kilobyte",
"mass-kilogram",
"length-kilometer",
"volume-liter",
"digital-megabit",
"digital-megabyte",
"length-meter",
"length-mile",
"length-mile-scandinavian",
"volume-milliliter",
"length-millimeter",
"duration-millisecond",
"duration-minute",
"duration-month",
"mass-ounce",
"concentr-percent",
"digital-petabyte",
"mass-pound",
"duration-second",
"mass-stone",
"digital-terabit",
"digital-terabyte",
"duration-week",
"length-yard",
"duration-year"
};
#define BUFFER_LEN 256
char buffer[BUFFER_LEN];
UChar ubuffer[BUFFER_LEN];
const char* locale = "ar";
UErrorCode status = U_ZERO_ERROR;
UFormattedNumber* formatted = unumf_openResult(&status);
if (U_FAILURE(status)) {
log_err("FAIL: unumf_openResult failed");
return;
}
for(int32_t i=0; i < UPRV_LENGTHOF(simpleMeasureUnits); ++i) {
for(int32_t j=0; j < UPRV_LENGTHOF(simpleMeasureUnits); ++j) {
status = U_ZERO_ERROR;
sprintf(buffer, "measure-unit/%s per-measure-unit/%s",
simpleMeasureUnits[i], simpleMeasureUnits[j]);
int32_t outputlen = 0;
u_strFromUTF8(ubuffer, BUFFER_LEN, &outputlen, buffer, strlen(buffer), &status);
if (U_FAILURE(status)) {
log_err("FAIL u_strFromUTF8: %s = %s ( %s )\n", locale, buffer,
u_errorName(status));
}
UNumberFormatter* nf = unumf_openForSkeletonAndLocale(
ubuffer, outputlen, locale, &status);
if (U_FAILURE(status)) {
log_err("FAIL unumf_openForSkeletonAndLocale: %s = %s ( %s )\n",
locale, buffer, u_errorName(status));
} else {
unumf_formatDouble(nf, 1, formatted, &status);
if (U_FAILURE(status)) {
log_err("FAIL unumf_formatDouble: %s = %s ( %s )\n",
locale, buffer, u_errorName(status));
}
}
unumf_close(nf);
}
}
unumf_closeResult(formatted);
}
#endif /* #if !UCONFIG_NO_FORMATTING */

View File

@ -1943,6 +1943,9 @@ void DateIntervalFormatTest::testTicket20707() {
int32_t j = 0;
for (const UnicodeString skeleton : {u"hh", u"HH", u"kk", u"KK", u"jj", u"JJs", u"CC"}) {
LocalPointer<DateIntervalFormat> dtifmt(DateIntervalFormat::createInstance(skeleton, locale, status));
if (status.errDataIfFailureAndReset()) {
continue;
}
FieldPosition fposition;
UnicodeString result;
LocalPointer<Calendar> calendar(Calendar::createInstance(TimeZone::createTimeZone(timeZone), status));

View File

@ -6,6 +6,7 @@
#include <string>
#include <vector>
#include <utility>
#include "unicode/utypes.h"
#include "unicode/localematcher.h"
@ -333,7 +334,9 @@ void LocaleMatcherTest::testDirection() {
{
// arz is a close one-way match to ar, and the region matches.
// (Egyptian Arabic vs. Arabic)
LocaleMatcher withOneWay = builder.build(errorCode);
// Also explicitly exercise the move copy constructor.
LocaleMatcher built = builder.build(errorCode);
LocaleMatcher withOneWay(std::move(built));
Locale::RangeIterator<Locale *> desiredIter(ARRAY_RANGE(desired));
assertEquals("with one-way", "ar",
locString(withOneWay.getBestMatch(desiredIter, errorCode)));
@ -341,8 +344,11 @@ void LocaleMatcherTest::testDirection() {
{
// nb is a less close two-way match to nn, and the regions differ.
// (Norwegian Bokmal vs. Nynorsk)
LocaleMatcher onlyTwoWay =
// Also explicitly exercise the move assignment operator.
LocaleMatcher onlyTwoWay = builder.build(errorCode);
LocaleMatcher built =
builder.setDirection(ULOCMATCH_DIRECTION_ONLY_TWO_WAY).build(errorCode);
onlyTwoWay = std::move(built);
Locale::RangeIterator<Locale *> desiredIter(ARRAY_RANGE(desired));
assertEquals("only two-way", "nn",
locString(onlyTwoWay.getBestMatch(desiredIter, errorCode)));

View File

@ -79,9 +79,11 @@ private:
void Test20332_PersonUnits();
void TestNumericTime();
void TestNumericTimeSomeSpecialFormats();
void TestIdentifiers();
void TestInvalidIdentifiers();
void TestCompoundUnitOperations();
void TestIdentifiers();
void TestDimensionlessBehaviour();
void Test21060_AddressSanitizerProblem();
void verifyFormat(
const char *description,
@ -201,9 +203,11 @@ void MeasureFormatTest::runIndexedTest(
TESTCASE_AUTO(Test20332_PersonUnits);
TESTCASE_AUTO(TestNumericTime);
TESTCASE_AUTO(TestNumericTimeSomeSpecialFormats);
TESTCASE_AUTO(TestIdentifiers);
TESTCASE_AUTO(TestInvalidIdentifiers);
TESTCASE_AUTO(TestCompoundUnitOperations);
TESTCASE_AUTO(TestIdentifiers);
TESTCASE_AUTO(TestDimensionlessBehaviour);
TESTCASE_AUTO(Test21060_AddressSanitizerProblem);
TESTCASE_AUTO_END;
}
@ -3237,10 +3241,43 @@ void MeasureFormatTest::TestNumericTimeSomeSpecialFormats() {
verifyFormat("Danish fhoursFminutes", fmtDa, fhoursFminutes, 2, "2.03,877");
}
void MeasureFormatTest::TestIdentifiers() {
IcuTestErrorCode status(*this, "TestIdentifiers");
struct TestCase {
const char* id;
const char* normalized;
} cases[] = {
// Correctly normalized identifiers should not change
{"", ""},
{"square-meter-per-square-meter", "square-meter-per-square-meter"},
{"kilogram-meter-per-square-meter-square-second",
"kilogram-meter-per-square-meter-square-second"},
{"square-mile-and-square-foot", "square-mile-and-square-foot"},
{"square-foot-and-square-mile", "square-foot-and-square-mile"},
{"per-cubic-centimeter", "per-cubic-centimeter"},
{"per-kilometer", "per-kilometer"},
// Normalization of power and per
{"p2-foot-and-p2-mile", "square-foot-and-square-mile"},
{"gram-square-gram-per-dekagram", "cubic-gram-per-dekagram"},
{"kilogram-per-meter-per-second", "kilogram-per-meter-second"},
// TODO(ICU-20920): Add more test cases once the proper ranking is available.
};
for (const auto &cas : cases) {
status.setScope(cas.id);
MeasureUnit unit = MeasureUnit::forIdentifier(cas.id, status);
status.errIfFailureAndReset();
const char* actual = unit.getIdentifier();
assertEquals(cas.id, cas.normalized, actual);
status.errIfFailureAndReset();
}
}
void MeasureFormatTest::TestInvalidIdentifiers() {
IcuTestErrorCode status(*this, "TestInvalidIdentifiers");
const char* const inputs[] = {
const char *const inputs[] = {
"kilo",
"kilokilo",
"onekilo",
@ -3256,7 +3293,23 @@ void MeasureFormatTest::TestInvalidIdentifiers() {
"-p2-meter",
"+p2-meter",
"+",
"-"
"-",
"-mile",
"-and-mile",
"-per-mile",
"one",
"one-one",
"one-per-mile",
"one-per-cubic-centimeter",
"square--per-meter",
"metersecond", // Must have compound part in between single units
// Negative powers not supported in mixed units yet. TODO(CLDR-13701).
"per-hour-and-hertz",
"hertz-and-per-hour",
// Compound units not supported in mixed units yet. TODO(CLDR-13700).
"kilonewton-meter-and-newton-meter",
};
for (const auto& input : inputs) {
@ -3293,9 +3346,9 @@ void MeasureFormatTest::TestCompoundUnitOperations() {
MeasureUnit overQuarticKilometer1 = kilometer.withDimensionality(-4, status);
verifySingleUnit(squareMeter, UMEASURE_SI_PREFIX_ONE, 2, "square-meter");
verifySingleUnit(overCubicCentimeter, UMEASURE_SI_PREFIX_CENTI, -3, "one-per-cubic-centimeter");
verifySingleUnit(overCubicCentimeter, UMEASURE_SI_PREFIX_CENTI, -3, "per-cubic-centimeter");
verifySingleUnit(quarticKilometer, UMEASURE_SI_PREFIX_KILO, 4, "p4-kilometer");
verifySingleUnit(overQuarticKilometer1, UMEASURE_SI_PREFIX_KILO, -4, "one-per-p4-kilometer");
verifySingleUnit(overQuarticKilometer1, UMEASURE_SI_PREFIX_KILO, -4, "per-p4-kilometer");
assertTrue("power inequality", quarticKilometer != overQuarticKilometer1);
@ -3308,9 +3361,9 @@ void MeasureFormatTest::TestCompoundUnitOperations() {
.reciprocal(status)
.withSIPrefix(UMEASURE_SI_PREFIX_KILO, status);
verifySingleUnit(overQuarticKilometer2, UMEASURE_SI_PREFIX_KILO, -4, "one-per-p4-kilometer");
verifySingleUnit(overQuarticKilometer3, UMEASURE_SI_PREFIX_KILO, -4, "one-per-p4-kilometer");
verifySingleUnit(overQuarticKilometer4, UMEASURE_SI_PREFIX_KILO, -4, "one-per-p4-kilometer");
verifySingleUnit(overQuarticKilometer2, UMEASURE_SI_PREFIX_KILO, -4, "per-p4-kilometer");
verifySingleUnit(overQuarticKilometer3, UMEASURE_SI_PREFIX_KILO, -4, "per-p4-kilometer");
verifySingleUnit(overQuarticKilometer4, UMEASURE_SI_PREFIX_KILO, -4, "per-p4-kilometer");
assertTrue("reciprocal equality", overQuarticKilometer1 == overQuarticKilometer2);
assertTrue("reciprocal equality", overQuarticKilometer1 == overQuarticKilometer3);
@ -3341,7 +3394,7 @@ void MeasureFormatTest::TestCompoundUnitOperations() {
const char* secondCentimeterSub[] = {"centimeter", "square-kilosecond"};
verifyCompoundUnit(secondCentimeter, "centimeter-square-kilosecond",
secondCentimeterSub, UPRV_LENGTHOF(secondCentimeterSub));
const char* secondCentimeterPerKilometerSub[] = {"centimeter", "square-kilosecond", "one-per-kilometer"};
const char* secondCentimeterPerKilometerSub[] = {"centimeter", "square-kilosecond", "per-kilometer"};
verifyCompoundUnit(secondCentimeterPerKilometer, "centimeter-square-kilosecond-per-kilometer",
secondCentimeterPerKilometerSub, UPRV_LENGTHOF(secondCentimeterPerKilometerSub));
@ -3376,31 +3429,16 @@ void MeasureFormatTest::TestCompoundUnitOperations() {
assertTrue("order matters inequality", footInch != inchFoot);
MeasureUnit one1;
MeasureUnit one2 = MeasureUnit::forIdentifier("one", status);
MeasureUnit one3 = MeasureUnit::forIdentifier("", status);
MeasureUnit squareOne = one2.withDimensionality(2, status);
MeasureUnit onePerOne = one2.reciprocal(status);
MeasureUnit squareKiloOne = squareOne.withSIPrefix(UMEASURE_SI_PREFIX_KILO, status);
MeasureUnit onePerSquareKiloOne = squareKiloOne.reciprocal(status);
MeasureUnit oneOne = MeasureUnit::forIdentifier("one-one", status);
MeasureUnit onePlusOne = MeasureUnit::forIdentifier("one-and-one", status);
MeasureUnit kilometer2 = one2.product(kilometer, status);
MeasureUnit dimensionless;
MeasureUnit dimensionless2 = MeasureUnit::forIdentifier("", status);
status.errIfFailureAndReset("Dimensionless MeasureUnit.");
assertTrue("dimensionless equality", dimensionless == dimensionless2);
verifySingleUnit(one1, UMEASURE_SI_PREFIX_ONE, 1, "one");
verifySingleUnit(one2, UMEASURE_SI_PREFIX_ONE, 1, "one");
verifySingleUnit(one3, UMEASURE_SI_PREFIX_ONE, 1, "one");
verifySingleUnit(squareOne, UMEASURE_SI_PREFIX_ONE, 1, "one");
verifySingleUnit(onePerOne, UMEASURE_SI_PREFIX_ONE, 1, "one");
verifySingleUnit(squareKiloOne, UMEASURE_SI_PREFIX_ONE, 1, "one");
verifySingleUnit(onePerSquareKiloOne, UMEASURE_SI_PREFIX_ONE, 1, "one");
verifySingleUnit(oneOne, UMEASURE_SI_PREFIX_ONE, 1, "one");
verifySingleUnit(onePlusOne, UMEASURE_SI_PREFIX_ONE, 1, "one");
// We support starting from an "identity" MeasureUnit and then combining it
// with others via product:
MeasureUnit kilometer2 = dimensionless.product(kilometer, status);
status.errIfFailureAndReset("dimensionless.product(kilometer, status)");
verifySingleUnit(kilometer2, UMEASURE_SI_PREFIX_KILO, 1, "kilometer");
assertTrue("one equality", one1 == one2);
assertTrue("one equality", one2 == one3);
assertTrue("one-per-one equality", onePerOne == onePerSquareKiloOne);
assertTrue("kilometer equality", kilometer == kilometer2);
// Test out-of-range powers
@ -3411,36 +3449,102 @@ void MeasureFormatTest::TestCompoundUnitOperations() {
status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR);
MeasureUnit power16b = power15.product(kilometer, status);
status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR);
MeasureUnit powerN15 = MeasureUnit::forIdentifier("one-per-p15-kilometer", status);
verifySingleUnit(powerN15, UMEASURE_SI_PREFIX_KILO, -15, "one-per-p15-kilometer");
MeasureUnit powerN15 = MeasureUnit::forIdentifier("per-p15-kilometer", status);
verifySingleUnit(powerN15, UMEASURE_SI_PREFIX_KILO, -15, "per-p15-kilometer");
status.errIfFailureAndReset();
MeasureUnit powerN16a = MeasureUnit::forIdentifier("one-per-p16-kilometer", status);
MeasureUnit powerN16a = MeasureUnit::forIdentifier("per-p16-kilometer", status);
status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR);
MeasureUnit powerN16b = powerN15.product(overQuarticKilometer1, status);
status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR);
}
void MeasureFormatTest::TestIdentifiers() {
IcuTestErrorCode status(*this, "TestIdentifiers");
struct TestCase {
bool valid;
const char* id;
const char* normalized;
} cases[] = {
{ true, "square-meter-per-square-meter", "square-meter-per-square-meter" },
// TODO(ICU-20920): Add more test cases once the proper ranking is available.
};
for (const auto& cas : cases) {
status.setScope(cas.id);
MeasureUnit unit = MeasureUnit::forIdentifier(cas.id, status);
if (!cas.valid) {
status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR);
continue;
}
const char* actual = unit.getIdentifier();
assertEquals(cas.id, cas.normalized, actual);
status.errIfFailureAndReset();
}
void MeasureFormatTest::TestDimensionlessBehaviour() {
IcuTestErrorCode status(*this, "TestDimensionlessBehaviour");
MeasureUnit dimensionless;
MeasureUnit modified;
// At the time of writing, each of the seven groups below caused
// Parser::from("") to be called:
// splitToSingleUnits
int32_t count;
LocalArray<MeasureUnit> singles = dimensionless.splitToSingleUnits(count, status);
status.errIfFailureAndReset("dimensionless.splitToSingleUnits(...)");
assertEquals("no singles in dimensionless", 0, count);
// product(dimensionless)
MeasureUnit mile = MeasureUnit::getMile();
mile = mile.product(dimensionless, status);
status.errIfFailureAndReset("mile.product(dimensionless, ...)");
verifySingleUnit(mile, UMEASURE_SI_PREFIX_ONE, 1, "mile");
// dimensionless.getSIPrefix()
UMeasureSIPrefix siPrefix = dimensionless.getSIPrefix(status);
status.errIfFailureAndReset("dimensionless.getSIPrefix(...)");
assertEquals("dimensionless SIPrefix", UMEASURE_SI_PREFIX_ONE, siPrefix);
// dimensionless.withSIPrefix()
modified = dimensionless.withSIPrefix(UMEASURE_SI_PREFIX_KILO, status);
status.errIfFailureAndReset("dimensionless.withSIPrefix(...)");
singles = modified.splitToSingleUnits(count, status);
assertEquals("no singles in modified", 0, count);
siPrefix = modified.getSIPrefix(status);
status.errIfFailureAndReset("modified.getSIPrefix(...)");
assertEquals("modified SIPrefix", UMEASURE_SI_PREFIX_ONE, siPrefix);
// dimensionless.getComplexity()
UMeasureUnitComplexity complexity = dimensionless.getComplexity(status);
status.errIfFailureAndReset("dimensionless.getComplexity(...)");
assertEquals("dimensionless complexity", UMEASURE_UNIT_SINGLE, complexity);
// Dimensionality is mostly meaningless for dimensionless units, but it's
// still considered a SINGLE unit, so this code doesn't throw errors:
// dimensionless.getDimensionality()
int32_t dimensionality = dimensionless.getDimensionality(status);
status.errIfFailureAndReset("dimensionless.getDimensionality(...)");
assertEquals("dimensionless dimensionality", 0, dimensionality);
// dimensionless.withDimensionality()
dimensionless.withDimensionality(-1, status);
status.errIfFailureAndReset("dimensionless.withDimensionality(...)");
dimensionality = dimensionless.getDimensionality(status);
status.errIfFailureAndReset("dimensionless.getDimensionality(...)");
assertEquals("dimensionless dimensionality", 0, dimensionality);
}
// ICU-21060
void MeasureFormatTest::Test21060_AddressSanitizerProblem() {
IcuTestErrorCode status(*this, "Test21060_AddressSanitizerProblem");
MeasureUnit first = MeasureUnit::forIdentifier("", status);
status.errIfFailureAndReset();
// Experimentally, a compound unit like "kilogram-meter" failed. A single
// unit like "kilogram" or "meter" did not fail, did not trigger the
// problem.
MeasureUnit crux = MeasureUnit::forIdentifier("per-meter", status);
// Heap allocation of a new CharString for first.identifier happens here:
first = first.product(crux, status);
// Constructing second from first's identifier resulted in a failure later,
// as second held a reference to a substring of first's identifier:
MeasureUnit second = MeasureUnit::forIdentifier(first.getIdentifier(), status);
// Heap is freed here, as an old first.identifier CharString is deallocated
// and a new CharString is allocated:
first = first.product(crux, status);
// Proving we've had no failure yet:
status.errIfFailureAndReset();
// heap-use-after-free failure happened here, since a SingleUnitImpl had
// held onto a StringPiece pointing at a substring of an identifier that was
// freed above:
second = second.product(crux, status);
status.errIfFailureAndReset();
}

View File

@ -258,6 +258,7 @@ class NumberSkeletonTest : public IntlTest {
void defaultTokens();
void flexibleSeparators();
void wildcardCharacters();
void perUnitInArabic();
void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par = 0);

View File

@ -30,6 +30,7 @@ void NumberSkeletonTest::runIndexedTest(int32_t index, UBool exec, const char*&
TESTCASE_AUTO(defaultTokens);
TESTCASE_AUTO(flexibleSeparators);
TESTCASE_AUTO(wildcardCharacters);
TESTCASE_AUTO(perUnitInArabic);
TESTCASE_AUTO_END;
}
@ -362,5 +363,77 @@ void NumberSkeletonTest::expectedErrorSkeleton(const char16_t** cases, int32_t c
}
}
void NumberSkeletonTest::perUnitInArabic() {
IcuTestErrorCode status(*this, "perUnitInArabic");
struct TestCase {
const char16_t* type;
const char16_t* subtype;
} cases[] = {
{u"area", u"acre"},
{u"digital", u"bit"},
{u"digital", u"byte"},
{u"temperature", u"celsius"},
{u"length", u"centimeter"},
{u"duration", u"day"},
{u"angle", u"degree"},
{u"temperature", u"fahrenheit"},
{u"volume", u"fluid-ounce"},
{u"length", u"foot"},
{u"volume", u"gallon"},
{u"digital", u"gigabit"},
{u"digital", u"gigabyte"},
{u"mass", u"gram"},
{u"area", u"hectare"},
{u"duration", u"hour"},
{u"length", u"inch"},
{u"digital", u"kilobit"},
{u"digital", u"kilobyte"},
{u"mass", u"kilogram"},
{u"length", u"kilometer"},
{u"volume", u"liter"},
{u"digital", u"megabit"},
{u"digital", u"megabyte"},
{u"length", u"meter"},
{u"length", u"mile"},
{u"length", u"mile-scandinavian"},
{u"volume", u"milliliter"},
{u"length", u"millimeter"},
{u"duration", u"millisecond"},
{u"duration", u"minute"},
{u"duration", u"month"},
{u"mass", u"ounce"},
{u"concentr", u"percent"},
{u"digital", u"petabyte"},
{u"mass", u"pound"},
{u"duration", u"second"},
{u"mass", u"stone"},
{u"digital", u"terabit"},
{u"digital", u"terabyte"},
{u"duration", u"week"},
{u"length", u"yard"},
{u"duration", u"year"},
};
for (const auto& cas1 : cases) {
for (const auto& cas2 : cases) {
UnicodeString skeleton(u"measure-unit/");
skeleton += cas1.type;
skeleton += u"-";
skeleton += cas1.subtype;
skeleton += u" ";
skeleton += u"per-measure-unit/";
skeleton += cas2.type;
skeleton += u"-";
skeleton += cas2.subtype;
status.setScope(skeleton);
UnicodeString actual = NumberFormatter::forSkeleton(skeleton, status).locale("ar")
.formatDouble(5142.3, status)
.toString(status);
status.errIfFailureAndReset();
}
}
}
#endif /* #if !UCONFIG_NO_FORMATTING */

View File

@ -9679,6 +9679,9 @@ void NumberFormatTest::Test20956_MonetarySymbolGetters() {
IcuTestErrorCode status(*this, "Test20956_MonetarySymbolGetters");
LocalPointer<DecimalFormat> decimalFormat(static_cast<DecimalFormat*>(
NumberFormat::createCurrencyInstance("et", status)));
if (status.errDataIfFailureAndReset()) {
return;
}
decimalFormat->setCurrency(u"EEK");
@ -9823,6 +9826,9 @@ void NumberFormatTest::Test20961_CurrencyPluralPattern() {
{
LocalPointer<DecimalFormat> decimalFormat(static_cast<DecimalFormat*>(
NumberFormat::createInstance("en-US", UNUM_CURRENCY_PLURAL, status)));
if (status.errDataIfFailureAndReset()) {
return;
}
UnicodeString result;
decimalFormat->toPattern(result);
assertEquals("Currency pattern", u"#,##0.00 ¤¤¤", result);

View File

@ -1395,6 +1395,14 @@ void NewResourceBundleTest::TestFilter() {
}
}
/*
* The following test for ICU-20706 has infinite loops on certain inputs for
* locales and calendars. In order to unblock the build (ICU-21055), those
* specific values are temporarily removed.
* The issue of the infinite loops and its blocking dependencies were captured
* in ICU-21080.
*/
void NewResourceBundleTest::TestIntervalAliasFallbacks() {
const char* locales[] = {
// Thee will not cause infinity loop
@ -1402,6 +1410,7 @@ void NewResourceBundleTest::TestIntervalAliasFallbacks() {
"ja",
// These will cause infinity loop
#if 0
"fr_CA",
"en_150",
"es_419",
@ -1413,6 +1422,7 @@ void NewResourceBundleTest::TestIntervalAliasFallbacks() {
"zh_Hant",
"zh_Hant_TW",
"zh_TW",
#endif
};
const char* calendars[] = {
// These won't cause infinity loop
@ -1420,6 +1430,7 @@ void NewResourceBundleTest::TestIntervalAliasFallbacks() {
"chinese",
// These will cause infinity loop
#if 0
"islamic",
"islamic-civil",
"islamic-tbla",
@ -1428,6 +1439,7 @@ void NewResourceBundleTest::TestIntervalAliasFallbacks() {
"islamic-rgsa",
"japanese",
"roc",
#endif
};
for (int lidx = 0; lidx < UPRV_LENGTHOF(locales); lidx++) {

View File

@ -39,6 +39,7 @@ public:
void TestGetByFallback(void);
void TestFilter(void);
void TestIntervalAliasFallbacks(void);
#if U_ENABLE_TRACING

View File

@ -1562,6 +1562,7 @@ void TransliteratorTest::TestBasicTransliteratorEvenWithoutData() {
BASIC_TRANSLITERATOR_ID[i], UTRANS_FORWARD, parseError, status));
if (translit.get() == nullptr || !U_SUCCESS(status)) {
dataerrln("FAIL: createInstance %s failed", BASIC_TRANSLITERATOR_ID[i]);
continue;
}
UnicodeString data(TEST_DATA);
UnicodeString expected(EXPECTED_RESULTS[i]);
@ -1570,6 +1571,7 @@ void TransliteratorTest::TestBasicTransliteratorEvenWithoutData() {
dataerrln(UnicodeString("FAIL: expected translit(") +
BASIC_TRANSLITERATOR_ID[i] + ") = '" +
EXPECTED_RESULTS[i] + "' but got '" + data);
continue;
}
}
for (int32_t i=0; BASIC_TRANSLITERATOR_RULES[i]; i++) {
@ -1580,6 +1582,7 @@ void TransliteratorTest::TestBasicTransliteratorEvenWithoutData() {
BASIC_TRANSLITERATOR_RULES[i], UTRANS_FORWARD, parseError, status));
if (translit.get() == nullptr || !U_SUCCESS(status)) {
dataerrln("FAIL: createFromRules %s failed", BASIC_TRANSLITERATOR_RULES[i]);
continue;
}
}
}

View File

@ -6,7 +6,7 @@
#*******************************************************************************
api.report.version = 67
api.report.prev.version = 66
release.file.ver = 67rc
api.doc.version = 67 Release Candidate
maven.pom.ver = 67.1-SNAPSHOT
release.file.ver = 67_1
api.doc.version = 67.1
maven.pom.ver = 67.1

View File

@ -241,8 +241,10 @@ public class LongNameHandler implements MicroPropsGenerator, ModifierStore {
String compiled = SimpleFormatterImpl
.compileToStringMinMaxArguments(rawPerUnitFormat, sb, 2, 2);
String secondaryFormat = getWithPlural(secondaryData, StandardPlural.ONE);
// Some "one" pattern may not contain "{0}". For example in "ar" or "ne" locale.
String secondaryCompiled = SimpleFormatterImpl
.compileToStringMinMaxArguments(secondaryFormat, sb, 1, 1);
.compileToStringMinMaxArguments(secondaryFormat, sb, 0, 1);
String secondaryString = SimpleFormatterImpl.getTextWithNoArguments(secondaryCompiled)
.trim();
perUnitFormat = SimpleFormatterImpl.formatCompiledPattern(compiled, "{0}", secondaryString);

View File

@ -349,4 +349,65 @@ public class NumberSkeletonTest {
assertEquals(mode.toString(), modeString, skeleton.substring(14));
}
}
@Test
public void perUnitInArabic() {
String[][] cases = {
{"area", "acre"},
{"digital", "bit"},
{"digital", "byte"},
{"temperature", "celsius"},
{"length", "centimeter"},
{"duration", "day"},
{"angle", "degree"},
{"temperature", "fahrenheit"},
{"volume", "fluid-ounce"},
{"length", "foot"},
{"volume", "gallon"},
{"digital", "gigabit"},
{"digital", "gigabyte"},
{"mass", "gram"},
{"area", "hectare"},
{"duration", "hour"},
{"length", "inch"},
{"digital", "kilobit"},
{"digital", "kilobyte"},
{"mass", "kilogram"},
{"length", "kilometer"},
{"volume", "liter"},
{"digital", "megabit"},
{"digital", "megabyte"},
{"length", "meter"},
{"length", "mile"},
{"length", "mile-scandinavian"},
{"volume", "milliliter"},
{"length", "millimeter"},
{"duration", "millisecond"},
{"duration", "minute"},
{"duration", "month"},
{"mass", "ounce"},
{"concentr", "percent"},
{"digital", "petabyte"},
{"mass", "pound"},
{"duration", "second"},
{"mass", "stone"},
{"digital", "terabit"},
{"digital", "terabyte"},
{"duration", "week"},
{"length", "yard"},
{"duration", "year"},
};
ULocale arabic = new ULocale("ar");
for (String[] cas1 : cases) {
for (String[] cas2 : cases) {
String skeleton = "measure-unit/";
skeleton += cas1[0] + "-" + cas1[1] + " per-measure-unit/" + cas2[0] + "-" + cas2[1];
String actual = NumberFormatter.forSkeleton(skeleton).locale(arabic).format(5142.3)
.toString();
// Just make sure it won't throw exception
}
}
}
}

View File

@ -56,15 +56,23 @@ inline void abort_noreturn() { abort(); }
#endif
#endif
// Not all compilers support __has_attribute and combining a check for both
// ifdef and __has_attribute on the same preprocessor line isn't portable.
#ifdef __has_attribute
# define DOUBLE_CONVERSION_HAS_ATTRIBUTE(x) __has_attribute(x)
#else
# define DOUBLE_CONVERSION_HAS_ATTRIBUTE(x) 0
#endif
#ifndef DOUBLE_CONVERSION_UNUSED
#ifdef __GNUC__
#if DOUBLE_CONVERSION_HAS_ATTRIBUTE(unused)
#define DOUBLE_CONVERSION_UNUSED __attribute__((unused))
#else
#define DOUBLE_CONVERSION_UNUSED
#endif
#endif
#if defined(__clang__) && __has_attribute(uninitialized)
#if DOUBLE_CONVERSION_HAS_ATTRIBUTE(uninitialized)
#define DOUBLE_CONVERSION_STACK_UNINITIALIZED __attribute__((uninitialized))
#else
#define DOUBLE_CONVERSION_STACK_UNINITIALIZED

View File

@ -147,24 +147,25 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\double-conversion\bignum-dtoa.cc" />
<ClCompile Include="..\double-conversion\bignum.cc" />
<ClCompile Include="..\double-conversion\bignum-dtoa.cc" />
<ClCompile Include="..\double-conversion\cached-powers.cc" />
<ClCompile Include="..\double-conversion\diy-fp.cc" />
<ClCompile Include="..\double-conversion\double-conversion.cc" />
<ClCompile Include="..\double-conversion\double-to-string.cc" />
<ClCompile Include="..\double-conversion\fast-dtoa.cc" />
<ClCompile Include="..\double-conversion\fixed-dtoa.cc" />
<ClCompile Include="..\double-conversion\string-to-double.cc" />
<ClCompile Include="..\double-conversion\strtod.cc" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\double-conversion\bignum-dtoa.h" />
<ClInclude Include="..\double-conversion\bignum.h" />
<ClInclude Include="..\double-conversion\cached-powers.h" />
<ClInclude Include="..\double-conversion\diy-fp.h" />
<ClInclude Include="..\double-conversion\double-conversion.h" />
<ClInclude Include="..\double-conversion\double-to-string.h" />
<ClInclude Include="..\double-conversion\fast-dtoa.h" />
<ClInclude Include="..\double-conversion\fixed-dtoa.h" />
<ClInclude Include="..\double-conversion\ieee.h" />
<ClInclude Include="..\double-conversion\string-to-double.h" />
<ClInclude Include="..\double-conversion\strtod.h" />
<ClInclude Include="..\double-conversion\utils.h" />
</ItemGroup>

View File

@ -24,12 +24,6 @@
<ClCompile Include="..\double-conversion\cached-powers.cc">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\double-conversion\diy-fp.cc">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\double-conversion\double-conversion.cc">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\double-conversion\fast-dtoa.cc">
<Filter>Source Files</Filter>
</ClCompile>
@ -39,14 +33,17 @@
<ClCompile Include="..\double-conversion\strtod.cc">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\double-conversion\double-to-string.cc">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\double-conversion\string-to-double.cc">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\double-conversion\bignum.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\double-conversion\bignum-dtoa.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\double-conversion\cached-powers.h">
<Filter>Header Files</Filter>
</ClInclude>
@ -71,5 +68,11 @@
<ClInclude Include="..\double-conversion\utils.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\double-conversion\double-to-string.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\double-conversion\string-to-double.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@ -109,6 +109,7 @@
<PreprocessorDefinitions>_SCL_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>false</SDLCheck>
<AdditionalIncludeDirectories>$(SolutionDir)..</AdditionalIncludeDirectories>
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>