diff --git a/.eslintrc.js b/.eslintrc.js index c076ba7979a2b9..98568dd9bc4311 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -40,6 +40,11 @@ module.exports = { ], parserOptions: { sourceType: 'module' }, }, + { + files: ['**/*.md'], + parserOptions: { ecmaFeatures: { impliedStrict: true } }, + rules: { strict: 'off' }, + }, ], rules: { // ESLint built-in rules @@ -93,6 +98,7 @@ module.exports = { 'no-dupe-class-members': 'error', 'no-dupe-keys': 'error', 'no-duplicate-case': 'error', + 'no-duplicate-imports': 'error', 'no-empty-character-class': 'error', 'no-ex-assign': 'error', 'no-extra-boolean-cast': 'error', @@ -241,6 +247,7 @@ module.exports = { // Custom rules from eslint-plugin-node-core 'node-core/no-unescaped-regexp-dot': 'error', + 'node-core/no-duplicate-requires': 'error', }, globals: { Atomics: false, diff --git a/BUILDING.md b/BUILDING.md index 48c287a319273e..a37ecb701ef04e 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -41,14 +41,14 @@ platforms in production. |--------------|--------------|----------------------------------|----------------------|------------------| | GNU/Linux | Tier 1 | kernel >= 2.6.32, glibc >= 2.12 | x64, arm | | | GNU/Linux | Tier 1 | kernel >= 3.10, glibc >= 2.17 | arm64 | | -| macOS | Tier 1 | >= 10.10 | x64 | | +| macOS/OS X | Tier 1 | >= 10.10 | x64 | | | Windows | Tier 1 | >= Windows 7/2008 R2/2012 R2 | x86, x64 | vs2017 | | SmartOS | Tier 2 | >= 15 < 16.4 | x86, x64 | see note1 | | FreeBSD | Tier 2 | >= 10 | x64 | | | GNU/Linux | Tier 2 | kernel >= 3.13.0, glibc >= 2.19 | ppc64le >=power8 | | | AIX | Tier 2 | >= 7.1 TL04 | ppc64be >=power7 | | | GNU/Linux | Tier 2 | kernel >= 3.10, glibc >= 2.17 | s390x | | -| macOS | Experimental | >= 10.8 < 10.10 | x64 | no test coverage | +| OS X | Experimental | >= 10.8 < 10.10 | x64 | no test coverage | | GNU/Linux | Experimental | kernel >= 2.6.32, glibc >= 2.12 | x86 | limited CI | | Linux (musl) | Experimental | musl >= 1.0 | x64 | | @@ -408,7 +408,7 @@ This version of Node.js does not support FIPS. It is possible to specify one or more JavaScript text files to be bundled in the binary as builtin modules when building Node.js. -### Unix / macOS +### Unix/macOS This command will make `/root/myModule.js` available via `require('/root/myModule')` and `./myModule2.js` available via diff --git a/CHANGELOG.md b/CHANGELOG.md index 2bc1a7eaf53e81..b93976dde5d728 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,16 +8,16 @@ release lines. Select a Node.js version below to view the changelog history: -* [Node.js 10](doc/changelogs/CHANGELOG_V10.md) -* [Node.js 9](doc/changelogs/CHANGELOG_V9.md) -* [Node.js 8](doc/changelogs/CHANGELOG_V8.md) -* [Node.js 7](doc/changelogs/CHANGELOG_V7.md) -* [Node.js 6](doc/changelogs/CHANGELOG_V6.md) -* [Node.js 5](doc/changelogs/CHANGELOG_V5.md) -* [Node.js 4](doc/changelogs/CHANGELOG_V4.md) -* [io.js](doc/changelogs/CHANGELOG_IOJS.md) -* [Node.js 0.12](doc/changelogs/CHANGELOG_V012.md) -* [Node.js 0.10](doc/changelogs/CHANGELOG_V010.md) +* [Node.js 10](doc/changelogs/CHANGELOG_V10.md) — **Current** +* [Node.js 9](doc/changelogs/CHANGELOG_V9.md) — End-of-Life +* [Node.js 8](doc/changelogs/CHANGELOG_V8.md) — **Long Term Support** +* [Node.js 7](doc/changelogs/CHANGELOG_V7.md) — End-of-Life +* [Node.js 6](doc/changelogs/CHANGELOG_V6.md) — Long Term Support +* [Node.js 5](doc/changelogs/CHANGELOG_V5.md) — End-of-Life +* [Node.js 4](doc/changelogs/CHANGELOG_V4.md) — End-of-Life +* [io.js](doc/changelogs/CHANGELOG_IOJS.md) — End-of-Life +* [Node.js 0.12](doc/changelogs/CHANGELOG_V012.md) — End-of-Life +* [Node.js 0.10](doc/changelogs/CHANGELOG_V010.md) — End-of-Life * [Archive](doc/changelogs/CHANGELOG_ARCHIVE.md) Please use the following table to find the changelog for a specific Node.js @@ -26,14 +26,13 @@ release. - - - -
10Current9 8LTS 6LTS4EOL
-10.6.0
+10.7.0
+10.6.0
10.5.0
10.4.1
10.4.0
@@ -44,25 +43,6 @@ release. 10.0.0
-9.11.1
-9.11.0
-9.10.1
-9.10.0
-9.9.0
-9.8.0
-9.7.1
-9.7.0
-9.6.1
-9.6.0
-9.5.0
-9.4.0
-9.3.0
-9.2.1
-9.2.0
-9.1.0
-9.0.0
-
8.11.1
8.11.0
8.10.0
@@ -126,55 +106,13 @@ release. 6.1.0
6.0.0
-4.9.1
-4.9.0
-4.8.7
-4.8.6
-4.8.5
-4.8.4
-4.8.3
-4.8.2
-4.8.1
-4.8.0
-4.7.3
-4.7.2
-4.7.1
-4.7.0
-4.6.2
-4.6.1
-4.6.0
-4.5.0
-4.4.7
-4.4.6
-4.4.5
-4.4.4
-4.4.3
-4.4.2
-4.4.1
-4.4.0
-4.3.2
-4.3.1
-4.3.0
-4.2.6
-4.2.5
-4.2.4
-4.2.3
-4.2.2
-4.2.1
-4.2.0
-4.1.2
-4.1.1
-4.1.0
-4.0.0
-
### Notes * Release streams marked with `LTS` are currently covered by the - [Node.js Long Term Support plan](https://github.com/nodejs/LTS). + [Node.js Long Term Support plan](https://github.com/nodejs/Release). * Release versions displayed in **bold** text represent the most recent actively supported release. diff --git a/COLLABORATOR_GUIDE.md b/COLLABORATOR_GUIDE.md index 238a72547247b6..8d50589afdd11d 100644 --- a/COLLABORATOR_GUIDE.md +++ b/COLLABORATOR_GUIDE.md @@ -199,14 +199,10 @@ status indicator. Do not land any Pull Requests without passing (green or yellow) CI runs. If you believe any failed (red or grey) CI sub-tasks are unrelated to the change in the -Pull Request, you may re-run the sub-task to try to see if it passes (just open -the failed sub-task page and press the "Rebuild" button; be sure you are still -logged in for this action). If re-runs of all failed sub-tasks pass (do not -forget to provide the links for successfully rerun sub-tasks), it is permissible -to land the Pull Request but only if the initial failures are believed in good -faith to be unrelated to the changes in the Pull Request. Otherwise, reasonable -steps must be taken to confirm that the changes are not resulting in an -unreliable test. +Pull Request, use "Resume Build" in the left navigation of the relevant +`node-test-pull-request` job. It will create a new `node-test-pull-request` run +that preserves all the green results from the current job but re-runs everything +else. #### Useful CI Jobs diff --git a/Makefile b/Makefile index c75194dbaa1566..168dd27ea03d6d 100644 --- a/Makefile +++ b/Makefile @@ -305,6 +305,15 @@ benchmark/napi/function_call/build/Release/binding.node: all \ --directory="$(shell pwd)/benchmark/napi/function_call" \ --nodedir="$(shell pwd)" +benchmark/napi/function_args/build/Release/binding.node: all \ + benchmark/napi/function_args/napi_binding.c \ + benchmark/napi/function_args/binding.cc \ + benchmark/napi/function_args/binding.gyp + $(NODE) deps/npm/node_modules/node-gyp/bin/node-gyp rebuild \ + --python="$(PYTHON)" \ + --directory="$(shell pwd)/benchmark/napi/function_args" \ + --nodedir="$(shell pwd)" + # Implicitly depends on $(NODE_EXE). We don't depend on it explicitly because # it always triggers a rebuild due to it being a .PHONY rule. See the comment # near the build-addons rule for more background. @@ -361,6 +370,7 @@ ADDONS_NAPI_BINDING_GYPS := \ $(wildcard test/addons-napi/*/binding.gyp)) ADDONS_NAPI_BINDING_SOURCES := \ + $(filter-out test/addons-napi/??_*/*.c, $(wildcard test/addons-napi/*/*.c)) \ $(filter-out test/addons-napi/??_*/*.cc, $(wildcard test/addons-napi/*/*.cc)) \ $(filter-out test/addons-napi/??_*/*.h, $(wildcard test/addons-napi/*/*.h)) @@ -613,7 +623,7 @@ doc-only: $(apidoc_dirs) $(apiassets) ## Builds the docs with the local or the if [ ! -d doc/api/assets ]; then \ $(MAKE) tools/doc/node_modules/js-yaml/package.json; \ fi; - @$(MAKE) $(apidocs_html) $(apidocs_json) + @$(MAKE) out/doc/api/all.html out/doc/api/all.json .PHONY: doc doc: $(NODE_EXE) doc-only @@ -663,10 +673,12 @@ out/doc/api/%.json: doc/api/%.md tools/doc/generate.js tools/doc/json.js out/doc/api/%.html: doc/api/%.md tools/doc/generate.js tools/doc/html.js $(call available-node, $(gen-html)) -out/doc/api/all.html: $(filter-out out/doc/api/all.html, $(apidocs_html)) \ - tools/doc/allhtml.js +out/doc/api/all.html: $(apidocs_html) tools/doc/allhtml.js $(call available-node, tools/doc/allhtml.js) +out/doc/api/all.json: $(apidocs_json) tools/doc/alljson.js + $(call available-node, tools/doc/alljson.js) + .PHONY: docopen docopen: $(apidocs_html) @$(PYTHON) -mwebbrowser file://$(PWD)/out/doc/api/all.html diff --git a/README.md b/README.md index cd30238a94f293..ce03c7d4c04269 100644 --- a/README.md +++ b/README.md @@ -332,7 +332,7 @@ For more information about the governance of the Node.js project, see * [brendanashworth](https://github.com/brendanashworth) - **Brendan Ashworth** <brendan.ashworth@me.com> * [BridgeAR](https://github.com/BridgeAR) - -**Ruben Bridgewater** <ruben@bridgewater.de> +**Ruben Bridgewater** <ruben@bridgewater.de> (he/him) * [bzoz](https://github.com/bzoz) - **Bartosz Sosnowski** <bartosz@janeasystems.com> * [calvinmetcalf](https://github.com/calvinmetcalf) - @@ -345,6 +345,8 @@ For more information about the governance of the Node.js project, see **Colin Ihrig** <cjihrig@gmail.com> * [claudiorodriguez](https://github.com/claudiorodriguez) - **Claudio Rodriguez** <cjrodr@yahoo.com> +* [codebytere](https://github.com/codebytere) - +**Shelley Vohr** <codebytere@gmail.com> (she/her) * [danbev](https://github.com/danbev) - **Daniel Bevenius** <daniel.bevenius@gmail.com> * [DavidCai1993](https://github.com/DavidCai1993) - @@ -420,7 +422,7 @@ For more information about the governance of the Node.js project, see * [kunalspathak](https://github.com/kunalspathak) - **Kunal Pathak** <kunal.pathak@microsoft.com> * [lance](https://github.com/lance) - -**Lance Ball** <lball@redhat.com> +**Lance Ball** <lball@redhat.com> (he/him) * [Leko](https://github.com/Leko) - **Shingo Inoue** <leko.noor@gmail.com> (he/him) * [lpinca](https://github.com/lpinca) - diff --git a/benchmark/napi/function_args/.gitignore b/benchmark/napi/function_args/.gitignore new file mode 100644 index 00000000000000..567609b1234a9b --- /dev/null +++ b/benchmark/napi/function_args/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/benchmark/napi/function_args/binding.cc b/benchmark/napi/function_args/binding.cc new file mode 100644 index 00000000000000..11eb394a6a98f5 --- /dev/null +++ b/benchmark/napi/function_args/binding.cc @@ -0,0 +1,142 @@ +#include +#include +#include + +using v8::Isolate; +using v8::Context; +using v8::Local; +using v8::MaybeLocal; +using v8::Value; +using v8::Number; +using v8::String; +using v8::Object; +using v8::Array; +using v8::ArrayBufferView; +using v8::ArrayBuffer; +using v8::FunctionCallbackInfo; + +void CallWithString(const FunctionCallbackInfo& args) { + assert(args.Length() == 1 && args[0]->IsString()); + if (args.Length() == 1 && args[0]->IsString()) { + Local str = args[0].As(); + const int32_t length = str->Utf8Length() + 1; + char* buf = new char[length]; + str->WriteUtf8(buf, length); + delete [] buf; + } +} + +void CallWithArray(const FunctionCallbackInfo& args) { + assert(args.Length() == 1 && args[0]->IsArray()); + if (args.Length() == 1 && args[0]->IsArray()) { + const Local array = args[0].As(); + uint32_t length = array->Length(); + for (uint32_t i = 0; i < length; ++ i) { + Local v; + v = array->Get(i); + } + } +} + +void CallWithNumber(const FunctionCallbackInfo& args) { + assert(args.Length() == 1 && args[0]->IsNumber()); + if (args.Length() == 1 && args[0]->IsNumber()) { + args[0].As()->Value(); + } +} + +void CallWithObject(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + Local context = isolate->GetCurrentContext(); + + assert(args.Length() == 1 && args[0]->IsObject()); + if (args.Length() == 1 && args[0]->IsObject()) { + Local obj = args[0].As(); + + MaybeLocal map_key = String::NewFromUtf8(isolate, + "map", v8::NewStringType::kNormal); + assert(!map_key.IsEmpty()); + MaybeLocal map_maybe = obj->Get(context, + map_key.ToLocalChecked()); + assert(!map_maybe.IsEmpty()); + Local map; + map = map_maybe.ToLocalChecked(); + + MaybeLocal operand_key = String::NewFromUtf8(isolate, + "operand", v8::NewStringType::kNormal); + assert(!operand_key.IsEmpty()); + MaybeLocal operand_maybe = obj->Get(context, + operand_key.ToLocalChecked()); + assert(!operand_maybe.IsEmpty()); + Local operand; + operand = operand_maybe.ToLocalChecked(); + + MaybeLocal data_key = String::NewFromUtf8(isolate, + "data", v8::NewStringType::kNormal); + assert(!data_key.IsEmpty()); + MaybeLocal data_maybe = obj->Get(context, + data_key.ToLocalChecked()); + assert(!data_maybe.IsEmpty()); + Local data; + data = data_maybe.ToLocalChecked(); + + MaybeLocal reduce_key = String::NewFromUtf8(isolate, + "reduce", v8::NewStringType::kNormal); + assert(!reduce_key.IsEmpty()); + MaybeLocal reduce_maybe = obj->Get(context, + reduce_key.ToLocalChecked()); + assert(!reduce_maybe.IsEmpty()); + Local reduce; + reduce = reduce_maybe.ToLocalChecked(); + } +} + +void CallWithTypedarray(const FunctionCallbackInfo& args) { + assert(args.Length() == 1 && args[0]->IsArrayBufferView()); + if (args.Length() == 1 && args[0]->IsArrayBufferView()) { + assert(args[0]->IsArrayBufferView()); + Local view = args[0].As(); + const size_t byte_offset = view->ByteOffset(); + const size_t byte_length = view->ByteLength(); + assert(byte_length > 0); + assert(view->HasBuffer()); + Local buffer; + buffer = view->Buffer(); + ArrayBuffer::Contents contents; + contents = buffer->GetContents(); + const uint32_t* data = reinterpret_cast( + static_cast(contents.Data()) + byte_offset); + assert(data); + } +} + +void CallWithArguments(const FunctionCallbackInfo& args) { + assert(args.Length() > 1 && args[0]->IsNumber()); + if (args.Length() > 1 && args[0]->IsNumber()) { + int32_t loop = args[0].As()->Value(); + for (int32_t i = 1; i < loop; ++i) { + assert(i < args.Length()); + assert(args[i]->IsUint32()); + args[i].As()->Value(); + } + } +} + +void Initialize(Local target) { + NODE_SET_METHOD(target, "callWithString", CallWithString); + NODE_SET_METHOD(target, "callWithLongString", CallWithString); + + NODE_SET_METHOD(target, "callWithArray", CallWithArray); + NODE_SET_METHOD(target, "callWithLargeArray", CallWithArray); + NODE_SET_METHOD(target, "callWithHugeArray", CallWithArray); + + NODE_SET_METHOD(target, "callWithNumber", CallWithNumber); + NODE_SET_METHOD(target, "callWithObject", CallWithObject); + NODE_SET_METHOD(target, "callWithTypedarray", CallWithTypedarray); + + NODE_SET_METHOD(target, "callWith10Numbers", CallWithArguments); + NODE_SET_METHOD(target, "callWith100Numbers", CallWithArguments); + NODE_SET_METHOD(target, "callWith1000Numbers", CallWithArguments); +} + +NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize) diff --git a/benchmark/napi/function_args/binding.gyp b/benchmark/napi/function_args/binding.gyp new file mode 100644 index 00000000000000..ac122ed1a07962 --- /dev/null +++ b/benchmark/napi/function_args/binding.gyp @@ -0,0 +1,12 @@ +{ + 'targets': [ + { + 'target_name': 'napi_binding', + 'sources': [ 'napi_binding.c' ] + }, + { + 'target_name': 'binding', + 'sources': [ 'binding.cc' ] + } + ] +} diff --git a/benchmark/napi/function_args/index.js b/benchmark/napi/function_args/index.js new file mode 100644 index 00000000000000..c8f281a3429fde --- /dev/null +++ b/benchmark/napi/function_args/index.js @@ -0,0 +1,99 @@ +// show the difference between calling a V8 binding C++ function +// relative to a comparable N-API C++ function, +// in various types/numbers of arguments. +// Reports n of calls per second. +'use strict'; + +const common = require('../../common.js'); + +let v8; +let napi; + +try { + v8 = require('./build/Release/binding'); +} catch (err) { + // eslint-disable-next-line no-path-concat + console.error(__filename + ': V8 Binding failed to load'); + process.exit(0); +} + +try { + napi = require('./build/Release/napi_binding'); +} catch (err) { + // eslint-disable-next-line no-path-concat + console.error(__filename + ': NAPI-Binding failed to load'); + process.exit(0); +} + +const argsTypes = ['String', 'Number', 'Object', 'Array', 'Typedarray', + '10Numbers', '100Numbers', '1000Numbers']; + +const generateArgs = (argType) => { + let args = []; + + if (argType === 'String') { + args.push('The quick brown fox jumps over the lazy dog'); + } else if (argType === 'LongString') { + args.push(Buffer.alloc(32768, '42').toString()); + } else if (argType === 'Number') { + args.push(Math.floor(314158964 * Math.random())); + } else if (argType === 'Object') { + args.push({ + map: 'add', + operand: 10, + data: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + reduce: 'add', + }); + } else if (argType === 'Array') { + const arr = []; + for (let i = 0; i < 50; ++i) { + arr.push(Math.random() * 10e9); + } + args.push(arr); + } else if (argType === 'Typedarray') { + const arr = new Uint32Array(1000); + for (let i = 0; i < 1000; ++i) { + arr[i] = Math.random() * 4294967296; + } + args.push(arr); + } else if (argType === '10Numbers') { + args.push(10); + for (let i = 0; i < 9; ++i) { + args = [...args, ...generateArgs('Number')]; + } + } else if (argType === '100Numbers') { + args.push(100); + for (let i = 0; i < 99; ++i) { + args = [...args, ...generateArgs('Number')]; + } + } else if (argType === '1000Numbers') { + args.push(1000); + for (let i = 0; i < 999; ++i) { + args = [...args, ...generateArgs('Number')]; + } + } + + return args; +}; + +const bench = common.createBenchmark(main, { + type: argsTypes, + engine: ['v8', 'napi'], + n: [1, 1e1, 1e2, 1e3, 1e4, 1e5], +}); + +function main({ n, engine, type }) { + const bindings = engine === 'v8' ? v8 : napi; + const methodName = 'callWith' + type; + const fn = bindings[methodName]; + + if (fn) { + const args = generateArgs(type); + + bench.start(); + for (var i = 0; i < n; i++) { + fn.apply(null, args); + } + bench.end(n); + } +} diff --git a/benchmark/napi/function_args/napi_binding.c b/benchmark/napi/function_args/napi_binding.c new file mode 100644 index 00000000000000..b697644ca441e9 --- /dev/null +++ b/benchmark/napi/function_args/napi_binding.c @@ -0,0 +1,229 @@ +#include +#include +#include +#include +#include + +static napi_value CallWithString(napi_env env, napi_callback_info info) { + napi_status status; + + size_t argc = 1; + napi_value args[1]; + status = napi_get_cb_info(env, info, &argc, args, NULL, NULL); + assert(status == napi_ok); + + napi_valuetype types[1]; + status = napi_typeof(env, args[0], types); + assert(status == napi_ok); + + assert(types[0] == napi_string); + if (types[0] == napi_string) { + size_t len = 0; + // Get the length + status = napi_get_value_string_utf8(env, args[0], NULL, 0, &len); + assert(status == napi_ok); + char* buf = (char*)malloc(len + 1); + status = napi_get_value_string_utf8(env, args[0], buf, len + 1, &len); + assert(status == napi_ok); + free(buf); + } + + return NULL; +} + +static napi_value CallWithArray(napi_env env, napi_callback_info info) { + napi_status status; + + size_t argc = 1; + napi_value args[1]; + status = napi_get_cb_info(env, info, &argc, args, NULL, NULL); + assert(status == napi_ok); + + napi_value array = args[0]; + bool is_array = false; + status = napi_is_array(env, array, &is_array); + assert(status == napi_ok); + + assert(is_array); + if (is_array) { + uint32_t length; + status = napi_get_array_length(env, array, &length); + assert(status == napi_ok); + + for (uint32_t i = 0; i < length; ++i) { + napi_value v; + status = napi_get_element(env, array, i, &v); + assert(status == napi_ok); + } + } + + return NULL; +} + +static napi_value CallWithNumber(napi_env env, napi_callback_info info) { + napi_status status; + + size_t argc = 1; + napi_value args[1]; + status = napi_get_cb_info(env, info, &argc, args, NULL, NULL); + assert(status == napi_ok); + + napi_valuetype types[1]; + status = napi_typeof(env, args[0], types); + assert(status == napi_ok); + + assert(types[0] == napi_number); + if (types[0] == napi_number) { + double value = 0.0; + status = napi_get_value_double(env, args[0], &value); + assert(status == napi_ok); + } + + return NULL; +} + +static napi_value CallWithObject(napi_env env, napi_callback_info info) { + napi_status status; + + size_t argc = 1; + napi_value args[1]; + status = napi_get_cb_info(env, info, &argc, args, NULL, NULL); + assert(status == napi_ok); + + napi_valuetype types[1]; + status = napi_typeof(env, args[0], types); + assert(status == napi_ok); + + assert(argc == 1 && types[0] == napi_object); + if (argc == 1 && types[0] == napi_object) { + napi_value value; + + status = napi_get_named_property(env, args[0], "map", &value); + assert(status == napi_ok); + + status = napi_get_named_property(env, args[0], "operand", &value); + assert(status == napi_ok); + + status = napi_get_named_property(env, args[0], "data", &value); + assert(status == napi_ok); + + status = napi_get_named_property(env, args[0], "reduce", &value); + assert(status == napi_ok); + } + + return NULL; +} + +static napi_value CallWithTypedarray(napi_env env, napi_callback_info info) { + napi_status status; + + size_t argc = 1; + napi_value args[1]; + status = napi_get_cb_info(env, info, &argc, args, NULL, NULL); + assert(status == napi_ok); + + bool is_typedarray = false; + status = napi_is_typedarray(env, args[0], &is_typedarray); + assert(status == napi_ok); + + assert(is_typedarray); + if (is_typedarray) { + napi_typedarray_type type; + napi_value input_buffer; + size_t byte_offset = 0; + size_t length = 0; + status = napi_get_typedarray_info(env, args[0], &type, &length, + NULL, &input_buffer, &byte_offset); + assert(status == napi_ok); + assert(length > 0); + + void* data = NULL; + size_t byte_length = 0; + status = napi_get_arraybuffer_info(env, + input_buffer, &data, &byte_length); + assert(status == napi_ok); + + uint32_t* input_integers = (uint32_t*)((uint8_t*)(data) + byte_offset); + assert(input_integers); + } + + return NULL; +} + +static napi_value CallWithArguments(napi_env env, napi_callback_info info) { + napi_status status; + + size_t argc = 1; + napi_value args[1000]; + // Get the length + status = napi_get_cb_info(env, info, &argc, NULL, NULL, NULL); + assert(status == napi_ok); + + status = napi_get_cb_info(env, info, &argc, args, NULL, NULL); + assert(status == napi_ok); + assert(argc <= 1000); + + napi_valuetype types[1]; + status = napi_typeof(env, args[0], types); + assert(status == napi_ok); + + assert(argc > 1 && types[0] == napi_number); + if (argc > 1 && types[0] == napi_number) { + uint32_t loop = 0; + status = napi_get_value_uint32(env, args[0], &loop); + assert(status == napi_ok); + + for (uint32_t i = 1; i < loop; ++i) { + assert(i < argc); + status = napi_typeof(env, args[i], types); + assert(status == napi_ok); + assert(types[0] == napi_number); + + uint32_t value = 0; + status = napi_get_value_uint32(env, args[i], &value); + assert(status == napi_ok); + } + } + + return NULL; +} + + +#define EXPORT_FUNC(env, exports, name, func) \ + do { \ + napi_status status; \ + napi_value js_func; \ + status = napi_create_function((env), \ + (name), \ + NAPI_AUTO_LENGTH, \ + (func), \ + NULL, \ + &js_func); \ + assert(status == napi_ok); \ + status = napi_set_named_property((env), \ + (exports), \ + (name), \ + js_func); \ + assert(status == napi_ok); \ + } while (0); + + +NAPI_MODULE_INIT() { + EXPORT_FUNC(env, exports, "callWithString", CallWithString); + EXPORT_FUNC(env, exports, "callWithLongString", CallWithString); + + EXPORT_FUNC(env, exports, "callWithArray", CallWithArray); + EXPORT_FUNC(env, exports, "callWithLargeArray", CallWithArray); + EXPORT_FUNC(env, exports, "callWithHugeArray", CallWithArray); + + EXPORT_FUNC(env, exports, "callWithNumber", CallWithNumber); + + EXPORT_FUNC(env, exports, "callWithObject", CallWithObject); + EXPORT_FUNC(env, exports, "callWithTypedarray", CallWithTypedarray); + + EXPORT_FUNC(env, exports, "callWith10Numbers", CallWithArguments); + EXPORT_FUNC(env, exports, "callWith100Numbers", CallWithArguments); + EXPORT_FUNC(env, exports, "callWith1000Numbers", CallWithArguments); + + return exports; +} diff --git a/benchmark/streams/creation.js b/benchmark/streams/creation.js index 67187f91bd9cb1..46a0a547907c45 100644 --- a/benchmark/streams/creation.js +++ b/benchmark/streams/creation.js @@ -1,9 +1,11 @@ 'use strict'; const common = require('../common.js'); -const Duplex = require('stream').Duplex; -const Readable = require('stream').Readable; -const Transform = require('stream').Transform; -const Writable = require('stream').Writable; +const { + Duplex, + Readable, + Transform, + Writable, +} = require('stream'); const bench = common.createBenchmark(main, { n: [50e6], diff --git a/benchmark/util/normalize-encoding.js b/benchmark/util/normalize-encoding.js index 96eab1912d0761..65cf5b1b0ce18c 100644 --- a/benchmark/util/normalize-encoding.js +++ b/benchmark/util/normalize-encoding.js @@ -4,18 +4,25 @@ const common = require('../common.js'); const assert = require('assert'); const groupedInputs = { - group_common: ['undefined', 'utf8', 'utf-8', 'base64', 'binary', 'latin1'], - group_upper: ['UTF-8', 'UTF8', 'UCS2', 'UTF-16LE', 'UTF16LE', 'BASE64'], - group_uncommon: [ 'foo', '1', 'false', 'undefined', '[]'], + group_common: ['undefined', 'utf8', 'utf-8', 'base64', + 'binary', 'latin1', 'ucs-2', 'usc-2'], + group_upper: ['UTF-8', 'UTF8', 'UCS2', 'UTF-16LE', + 'UTF16LE', 'BASE64', 'UCS-2', 'USC-2'], + group_uncommon: ['foo', '1', 'false', 'undefined', '[]', '{}'], group_misc: ['', 'utf16le', 'usc2', 'hex', 'HEX', 'BINARY'] }; const inputs = [ - '', 'utf8', 'utf-8', 'UTF-8', - 'UTF8', 'Utf8', 'uTf-8', 'utF-8', 'ucs2', - 'UCS2', 'utf16le', 'utf-16le', 'UTF-16LE', 'UTF16LE', + '', + 'utf8', 'utf-8', 'UTF-8', + 'UTF8', 'Utf8', 'uTf-8', 'utF-8', + 'ucs2', 'UCS2', 'UcS2', + 'USC2', 'usc2', 'uSc2', + 'ucs-2', 'UCS-2', 'UcS-2', + 'usc-2', 'USC-2', 'uSc-2', + 'utf16le', 'utf-16le', 'UTF-16LE', 'UTF16LE', 'binary', 'BINARY', 'latin1', 'base64', 'BASE64', - 'hex', 'HEX', 'foo', '1', 'false', 'undefined', '[]']; + 'hex', 'HEX', 'foo', '1', 'false', 'undefined', '[]', '{}']; const bench = common.createBenchmark(main, { input: inputs.concat(Object.keys(groupedInputs)), @@ -42,6 +49,8 @@ function getInput(input) { return [undefined]; case '[]': return [[]]; + case '{}': + return [{}]; default: return [input]; } @@ -53,7 +62,7 @@ function main({ input, n }) { var noDead = ''; bench.start(); - for (var i = 0; i < n; i += 1) { + for (var i = 0; i < n; ++i) { for (var j = 0; j < inputs.length; ++j) { noDead = normalizeEncoding(inputs[j]); } diff --git a/common.gypi b/common.gypi index d25a434b03d433..a782cfbecb85e2 100644 --- a/common.gypi +++ b/common.gypi @@ -28,7 +28,7 @@ # Reset this number to 0 on major V8 upgrades. # Increment by one for each non-official patch applied to deps/v8. - 'v8_embedder_string': '-node.13', + 'v8_embedder_string': '-node.15', # Enable disassembler for `--print-code` v8 options 'v8_enable_disassembler': 1, @@ -176,6 +176,17 @@ ['OS!="mac" and OS!="win"', { 'cflags': [ '-fno-omit-frame-pointer' ], }], + ['OS=="linux"', { + 'variables': { + 'lto': ' -flto=4 -fuse-linker-plugin -ffat-lto-objects ', + }, + 'conditions': [ + ['enable_lto=="true"', { + 'cflags': ['<(lto)'], + 'ldflags': ['<(lto)'], + },], + ], + },], ['OS == "android"', { 'cflags': [ '-fPIE' ], 'ldflags': [ '-fPIE', '-pie' ] diff --git a/configure b/configure index 3a9ab42414569e..068fb8a76999a1 100755 --- a/configure +++ b/configure @@ -157,6 +157,11 @@ parser.add_option("--enable-vtune-profiling", "JavaScript code executed in nodejs. This feature is only available " "for x32, x86, and x64 architectures.") +parser.add_option("--enable-lto", + action="store_true", + dest="enable_lto", + help="Enable compiling with lto of a binary. This feature is only available " + "on linux with gcc and g++.") parser.add_option("--link-module", action="append", @@ -932,6 +937,25 @@ def configure_node(o): else: o['variables']['node_enable_v8_vtunejit'] = 'false' + if flavor != 'linux' and (options.enable_lto): + raise Exception( + 'The lto option is supported only on linux.') + + if flavor == 'linux': + if options.enable_lto: + version_checked = (5, 4, 1) + for compiler in [(CC, 'c'), (CXX, 'c++')]: + ok, is_clang, clang_version, compiler_version = \ + try_check_compiler(compiler[0], compiler[1]) + compiler_version_num = tuple(map(int, compiler_version)) + if is_clang or compiler_version_num < version_checked: + version_checked_str = ".".join(map(str, version_checked)) + raise Exception( + 'The option --enable-lto is supported for gcc and gxx %s' + ' or newer only.' % (version_checked_str)) + + o['variables']['enable_lto'] = b(options.enable_lto) + if flavor in ('solaris', 'mac', 'linux', 'freebsd'): use_dtrace = not options.without_dtrace # Don't enable by default on linux and freebsd @@ -1185,8 +1209,8 @@ def glob_to_var(dir_base, dir_sub, patch_dir): def configure_intl(o): icus = [ { - 'url': 'https://ssl.icu-project.org/files/icu4c/61.1/icu4c-61_1-src.zip', - 'md5': '780d8524c8a860ed8d8f6fe75cb7ce3f', + 'url': 'https://sourceforge.net/projects/icu/files/ICU4C/62.1/icu4c-62_1-src.zip', + 'md5': '408854f7b9b58311b68fab4b4dfc80be', }, ] def icu_download(path): diff --git a/deps/icu-small/README-SMALL-ICU.txt b/deps/icu-small/README-SMALL-ICU.txt index b3919ec52b21ae..b98a528485653b 100644 --- a/deps/icu-small/README-SMALL-ICU.txt +++ b/deps/icu-small/README-SMALL-ICU.txt @@ -1,8 +1,8 @@ Small ICU sources - auto generated by shrink-icu-src.py This directory contains the ICU subset used by --with-intl=small-icu (the default) -It is a strict subset of ICU 61 source files with the following exception(s): -* deps/icu-small/source/data/in/icudt61l.dat : Reduced-size data file +It is a strict subset of ICU 62 source files with the following exception(s): +* deps/icu-small/source/data/in/icudt62l.dat : Reduced-size data file To rebuild this directory, see ../../tools/icu/README.md diff --git a/deps/icu-small/source/common/charstr.cpp b/deps/icu-small/source/common/charstr.cpp index 8bacd20ddc7a68..353f1d52542fa2 100644 --- a/deps/icu-small/source/common/charstr.cpp +++ b/deps/icu-small/source/common/charstr.cpp @@ -23,6 +23,18 @@ U_NAMESPACE_BEGIN +CharString::CharString(CharString&& src) U_NOEXCEPT + : buffer(std::move(src.buffer)), len(src.len) { + src.len = 0; // not strictly necessary because we make no guarantees on the source string +} + +CharString& CharString::operator=(CharString&& src) U_NOEXCEPT { + buffer = std::move(src.buffer); + len = src.len; + src.len = 0; // not strictly necessary because we make no guarantees on the source string + return *this; +} + CharString &CharString::copyFrom(const CharString &s, UErrorCode &errorCode) { if(U_SUCCESS(errorCode) && this!=&s && ensureCapacity(s.len+1, 0, errorCode)) { len=s.len; diff --git a/deps/icu-small/source/common/charstr.h b/deps/icu-small/source/common/charstr.h index 3cfdf6a897a497..86f69c383a0b37 100644 --- a/deps/icu-small/source/common/charstr.h +++ b/deps/icu-small/source/common/charstr.h @@ -55,6 +55,18 @@ class U_COMMON_API CharString : public UMemory { } ~CharString() {} + /** + * Move constructor; might leave src in an undefined state. + * This string will have the same contents and state that the source string had. + */ + CharString(CharString &&src) U_NOEXCEPT; + /** + * Move assignment operator; might leave src in an undefined state. + * This string will have the same contents and state that the source string had. + * The behavior is undefined if *this and src are the same object. + */ + CharString &operator=(CharString &&src) U_NOEXCEPT; + /** * Replaces this string's contents with the other string's contents. * CharString does not support the standard copy constructor nor diff --git a/deps/icu-small/source/common/cmemory.h b/deps/icu-small/source/common/cmemory.h index a44f9a190293bd..f98e13efc0ffbe 100644 --- a/deps/icu-small/source/common/cmemory.h +++ b/deps/icu-small/source/common/cmemory.h @@ -299,6 +299,14 @@ class MaybeStackArray { * Destructor deletes the array (if owned). */ ~MaybeStackArray() { releaseArray(); } + /** + * Move constructor: transfers ownership or copies the stack array. + */ + MaybeStackArray(MaybeStackArray &&src) U_NOEXCEPT; + /** + * Move assignment: transfers ownership or copies the stack array. + */ + MaybeStackArray &operator=(MaybeStackArray &&src) U_NOEXCEPT; /** * Returns the array capacity (number of T items). * @return array capacity @@ -376,6 +384,11 @@ class MaybeStackArray { uprv_free(ptr); } } + void resetToStackArray() { + ptr=stackArray; + capacity=stackCapacity; + needToRelease=FALSE; + } /* No comparison operators with other MaybeStackArray's. */ bool operator==(const MaybeStackArray & /*other*/) {return FALSE;} bool operator!=(const MaybeStackArray & /*other*/) {return TRUE;} @@ -398,6 +411,34 @@ class MaybeStackArray { #endif }; +template +icu::MaybeStackArray::MaybeStackArray( + MaybeStackArray && src) U_NOEXCEPT + : ptr(src.ptr), capacity(src.capacity), needToRelease(src.needToRelease) { + if (src.ptr == src.stackArray) { + ptr = stackArray; + uprv_memcpy(stackArray, src.stackArray, sizeof(T) * src.capacity); + } else { + src.resetToStackArray(); // take ownership away from src + } +} + +template +inline MaybeStackArray & +MaybeStackArray::operator=(MaybeStackArray && src) U_NOEXCEPT { + releaseArray(); // in case this instance had its own memory allocated + capacity = src.capacity; + needToRelease = src.needToRelease; + if (src.ptr == src.stackArray) { + ptr = stackArray; + uprv_memcpy(stackArray, src.stackArray, sizeof(T) * src.capacity); + } else { + ptr = src.ptr; + src.resetToStackArray(); // take ownership away from src + } + return *this; +} + template inline T *MaybeStackArray::resize(int32_t newCapacity, int32_t length) { if(newCapacity>0) { @@ -447,9 +488,7 @@ inline T *MaybeStackArray::orphanOrClone(int32_t length, int32 uprv_memcpy(p, ptr, (size_t)length*sizeof(T)); } resultCapacity=length; - ptr=stackArray; - capacity=stackCapacity; - needToRelease=FALSE; + resetToStackArray(); return p; } diff --git a/deps/icu-small/source/common/edits.cpp b/deps/icu-small/source/common/edits.cpp index 9ec005624fef0c..3b3611fcf80cfe 100644 --- a/deps/icu-small/source/common/edits.cpp +++ b/deps/icu-small/source/common/edits.cpp @@ -4,10 +4,12 @@ // edits.cpp // created: 2017feb08 Markus W. Scherer -#include "unicode/utypes.h" #include "unicode/edits.h" +#include "unicode/unistr.h" +#include "unicode/utypes.h" #include "cmemory.h" #include "uassert.h" +#include "util.h" U_NAMESPACE_BEGIN @@ -773,4 +775,29 @@ int32_t Edits::Iterator::sourceIndexFromDestinationIndex(int32_t i, UErrorCode & } } +UnicodeString& Edits::Iterator::toString(UnicodeString& sb) const { + sb.append(u"{ src[", -1); + ICU_Utility::appendNumber(sb, srcIndex); + sb.append(u"..", -1); + ICU_Utility::appendNumber(sb, srcIndex + oldLength_); + if (changed) { + sb.append(u"] ⇝ dest[", -1); + } else { + sb.append(u"] ≡ dest[", -1); + } + ICU_Utility::appendNumber(sb, destIndex); + sb.append(u"..", -1); + ICU_Utility::appendNumber(sb, destIndex + newLength_); + if (changed) { + sb.append(u"], repl[", -1); + ICU_Utility::appendNumber(sb, replIndex); + sb.append(u"..", -1); + ICU_Utility::appendNumber(sb, replIndex + newLength_); + sb.append(u"] }", -1); + } else { + sb.append(u"] (no-change) }", -1); + } + return sb; +} + U_NAMESPACE_END diff --git a/deps/icu-small/source/common/locmap.cpp b/deps/icu-small/source/common/locmap.cpp index 029c1edf032a00..a3cf2d5eb2dbcb 100644 --- a/deps/icu-small/source/common/locmap.cpp +++ b/deps/icu-small/source/common/locmap.cpp @@ -1015,7 +1015,7 @@ static const char* getPosixID(const ILcidPosixMap *this_0, uint32_t hostID) { uint32_t i; - for (i = 0; i <= this_0->numRegions; i++) + for (i = 0; i < this_0->numRegions; i++) { if (this_0->regionMaps[i].hostID == hostID) { diff --git a/deps/icu-small/source/common/norm2_nfc_data.h b/deps/icu-small/source/common/norm2_nfc_data.h index 8f5c4346db5ffe..c8bf440ae10819 100644 --- a/deps/icu-small/source/common/norm2_nfc_data.h +++ b/deps/icu-small/source/common/norm2_nfc_data.h @@ -12,625 +12,638 @@ #ifdef INCLUDED_FROM_NORMALIZER2_CPP static const UVersionInfo norm2_nfc_data_formatVersion={3,0,0,0}; -static const UVersionInfo norm2_nfc_data_dataVersion={0xa,0,0,0}; +static const UVersionInfo norm2_nfc_data_dataVersion={0xb,0,0,0}; static const int32_t norm2_nfc_data_indexes[Normalizer2Impl::IX_COUNT]={ -0x50,0x4cc0,0x8918,0x8a18,0x8a18,0x8a18,0x8a18,0x8a18,0xc0,0x300,0xadc,0x29d0,0x3c56,0xfc00,0x1282,0x3b8c, +0x50,0x4e50,0x8aa8,0x8ba8,0x8ba8,0x8ba8,0x8ba8,0x8ba8,0xc0,0x300,0xadc,0x29d0,0x3c56,0xfc00,0x1282,0x3b8c, 0x3c24,0x3c56,0x300,0 }; -static const uint16_t norm2_nfc_data_trieIndex[9776]={ -0x2a8,0x2b0,0x2b8,0x2c0,0x2ce,0x2d6,0x2de,0x2e6,0x2ee,0x2f6,0x2fe,0x306,0x30e,0x316,0x31c,0x324, -0x32c,0x334,0x2c7,0x2cf,0x339,0x341,0x2c7,0x2cf,0x349,0x351,0x359,0x361,0x369,0x371,0x379,0x381, -0x389,0x391,0x399,0x3a1,0x3a9,0x3b1,0x3b9,0x3c1,0x2c7,0x2cf,0x2c7,0x2cf,0x3c8,0x3d0,0x3d8,0x3e0, -0x3e4,0x3ec,0x3f2,0x3fa,0x2c7,0x2cf,0x402,0x40a,0x40e,0x416,0x41e,0x426,0x2c7,0x2cf,0x424,0x42c, -0x431,0x438,0x43c,0x2c7,0x2c7,0x2c7,0x443,0x44b,0x2c7,0x453,0x45b,0x2c7,0x2c7,0x463,0x46b,0x2c7, -0x2c7,0x473,0x47b,0x2c7,0x2c7,0x483,0x48b,0x2c7,0x2c7,0x463,0x492,0x2c7,0x49a,0x4a0,0x4a8,0x2c7, -0x2c7,0x2c7,0x4af,0x2c7,0x2c7,0x4b5,0x4bd,0x2c7,0x2c7,0x4c3,0x4cb,0x2c7,0x2c7,0x2c7,0x4d1,0x2c7, -0x2c7,0x4d9,0x4e0,0x2c7,0x2c7,0x4e3,0x4ea,0x2c7,0x4ed,0x4f4,0x4fc,0x504,0x50c,0x514,0x51b,0x2c7, -0x2c7,0x522,0x2c7,0x2c7,0x529,0x2c7,0x2c7,0x2c7,0x93b,0x2c7,0x2c7,0x943,0x2c7,0x949,0x951,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x52d,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x535,0x535,0x2c7,0x2c7,0x2c7,0x2c7,0x53b,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x543,0x2c7,0x2c7,0x2c7,0x546,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x54d,0x2c7,0x2c7,0x555,0x2c7,0x55d,0x2c7,0x2c7,0x565,0x56a,0x572,0x578,0x2c7,0x57e,0x2c7,0x585, -0x2c7,0x58a,0x2c7,0x2c7,0x2c7,0x2c7,0x590,0x598,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x5a0,0x5a5, -0x5ad,0x5b5,0x5bd,0x5c5,0x5cd,0x5d5,0x5dd,0x5e5,0x5ed,0x5f5,0x5fd,0x605,0x60d,0x615,0x61d,0x625, -0x62d,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x631,0x639,0x2c7,0x640,0x2c7,0x2c7,0x644,0x64b,0x650,0x2c7, -0x658,0x660,0x668,0x670,0x678,0x680,0x2c7,0x688,0x2c7,0x68e,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x691,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x699,0x2c7,0x2c7,0x2c7,0x69e,0x2c7,0x2c7,0x2c7,0x6a6, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x6ae,0x6b5,0x6bd,0x6c5,0x6cd,0x6d5,0x6dd,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x6e5,0x6ed,0x2c7,0x2c7,0x6f5,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x6fc,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x703,0x70b,0x2c7,0x711,0x715,0x2c7,0x2c7,0x58b,0x71d,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x721,0x729,0x72c,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x48b, -0x959,0x95a,0x95b,0x95c,0x95d,0x95e,0x96d,0x959,0x95a,0x95b,0x95c,0x95d,0x95e,0x96d,0x959,0x95a, -0x95b,0x95c,0x95d,0x95e,0x96d,0x959,0x95a,0x95b,0x95c,0x95d,0x95e,0x96d,0x959,0x95a,0x95b,0x95c, -0x95d,0x95e,0x96d,0x959,0x95a,0x95b,0x95c,0x95d,0x95e,0x96d,0x959,0x95a,0x95b,0x95c,0x95d,0x95e, -0x96d,0x959,0x95a,0x95b,0x95c,0x95d,0x95e,0x96d,0x959,0x95a,0x95b,0x95c,0x95d,0x95e,0x96d,0x959, -0x95a,0x95b,0x95c,0x95d,0x95e,0x96d,0x959,0x95a,0x95b,0x95c,0x95d,0x95e,0x96d,0x959,0x95a,0x95b, -0x95c,0x95d,0x95e,0x96d,0x959,0x95a,0x95b,0x95c,0x95d,0x95e,0x96d,0x959,0x95a,0x95b,0x95c,0x95d, -0x95e,0x96d,0x959,0x95a,0x95b,0x95c,0x95d,0x95e,0x96d,0x959,0x95a,0x95b,0x95c,0x95d,0x95e,0x96d, -0x959,0x95a,0x95b,0x95c,0x95d,0x95e,0x96d,0x959,0x95a,0x95b,0x95c,0x95d,0x95e,0x96d,0x959,0x95a, -0x95b,0x95c,0x95d,0x95e,0x96d,0x959,0x95a,0x95b,0x95c,0x95d,0x95e,0x96d,0x959,0x95a,0x95b,0x95c, -0x95d,0x95e,0x96d,0x959,0x95a,0x95b,0x95c,0x95d,0x95e,0x96d,0x959,0x95a,0x95b,0x95c,0x95d,0x95e, -0x96d,0x959,0x95a,0x95b,0x95c,0x95d,0x95e,0x96d,0x959,0x95a,0x95b,0x95c,0x95d,0x95e,0x96d,0x959, -0x95a,0x95b,0x95c,0x95d,0x95e,0x96d,0x959,0x95a,0x95b,0x95c,0x95d,0x95e,0x96d,0x959,0x95a,0x95b, -0x95c,0x95d,0x95e,0x96d,0x959,0x95a,0x95b,0x95c,0x95d,0x95e,0x96d,0x959,0x95a,0x95b,0x95c,0x95d, -0x95e,0x96d,0x959,0x95a,0x95b,0x95c,0x95d,0x95e,0x96d,0x959,0x95a,0x95b,0x95c,0x95d,0x95e,0x96d, -0x959,0x95a,0x95b,0x95c,0x95d,0x95e,0x96d,0x959,0x95a,0x95b,0x95c,0x95d,0x95e,0x96d,0x959,0x95a, -0x95b,0x95c,0x95d,0x95e,0x96d,0x959,0x95a,0x95b,0x95c,0x95d,0x95e,0x96d,0x959,0x95a,0x95b,0x95c, -0x95d,0x95e,0x96d,0x959,0x95a,0x95b,0x95c,0x95d,0x95e,0x96d,0x959,0x95a,0x95b,0x95c,0x95d,0x95e, -0x96d,0x959,0x95a,0x95b,0x95c,0x95d,0x95e,0x96d,0x959,0x95a,0x95b,0x95c,0x95d,0x95e,0x96d,0x959, -0x95a,0x95b,0x95c,0x95d,0x95e,0x96d,0x959,0x95a,0x95b,0x95c,0x95d,0x95e,0x96d,0x959,0x95a,0x95b, -0x95c,0x95d,0x95e,0x96d,0x959,0x95a,0x95b,0x95c,0x95d,0x95e,0x96d,0x959,0x95a,0x95b,0x95c,0x95d, -0x95e,0x96d,0x959,0x95a,0x95b,0x95c,0x95d,0x95e,0x96d,0x959,0x95a,0x95b,0x95c,0x95d,0x95e,0x96d, -0x959,0x95a,0x95b,0x95c,0x95d,0x95e,0x96d,0x959,0x95a,0x95b,0x95c,0x95d,0x95e,0x965,0x2c7,0x2c7, -0x975,0x97c,0x2a8,0x983,0x2a8,0x2a8,0x2a8,0x2a8,0x2a8,0x2a8,0x2a8,0x2a8,0x2a8,0x2a8,0x2a8,0x2a8, -0x2a8,0x2a8,0x2a8,0x2a8,0x2a8,0x2a8,0x2a8,0x2a8,0x2a8,0x2a8,0x2a8,0x2a8,0x2a8,0x2a8,0x2a8,0x2a8, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x734,0x73c,0x744,0x74c,0x754,0x75c,0x764,0x76c, -0x774,0x77c,0x784,0x78c,0x794,0x79c,0x7a4,0x2c7,0x7ab,0x7b3,0x7bb,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x7c3,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0xb20,0xb20,0xb38,0xb78,0xbb8,0xbf8,0xc38,0xc70,0xcb0,0xb1c,0xce4,0xb1c,0xd24,0xd64,0xda4,0xde4, -0xe24,0xe64,0xea4,0xee4,0xb1c,0xb1c,0xf20,0xf60,0xf90,0xfc8,0xb1c,0x1008,0x1038,0x1078,0xb1c,0x1090, -0x880,0x8b0,0x8ee,0x928,0x188,0x188,0x188,0x188,0x188,0x188,0x188,0x188,0x188,0x953,0x188,0x188, -0x188,0x188,0x188,0x188,0x188,0x188,0x188,0x96f,0x188,0x188,0x9a5,0x188,0x9e5,0xa1f,0x188,0x188, +static const uint16_t norm2_nfc_data_trieIndex[9976]={ +0x2aa,0x2b2,0x2ba,0x2c2,0x2d0,0x2d8,0x2e0,0x2e8,0x2f0,0x2f8,0x300,0x308,0x310,0x318,0x31e,0x326, +0x32e,0x336,0x2c9,0x2d1,0x33b,0x343,0x2c9,0x2d1,0x34b,0x353,0x35b,0x363,0x36b,0x373,0x37b,0x383, +0x38b,0x393,0x39b,0x3a3,0x3ab,0x3b3,0x3bb,0x3c3,0x2c9,0x2d1,0x2c9,0x2d1,0x3ca,0x3d2,0x3da,0x3e2, +0x3e6,0x3ee,0x3f4,0x3fc,0x2c9,0x2d1,0x404,0x40c,0x410,0x418,0x420,0x428,0x2c9,0x2d1,0x426,0x42e, +0x436,0x43d,0x441,0x2c9,0x2c9,0x2c9,0x448,0x450,0x2c9,0x458,0x460,0x2c9,0x2c9,0x468,0x470,0x478, +0x2c9,0x480,0x488,0x2c9,0x2c9,0x490,0x498,0x2c9,0x2c9,0x468,0x49f,0x2c9,0x4a7,0x4ad,0x4b5,0x2c9, +0x2c9,0x2c9,0x4bc,0x2c9,0x2c9,0x4c2,0x4ca,0x2c9,0x2c9,0x4d0,0x4d8,0x2c9,0x2c9,0x2c9,0x4de,0x2c9, +0x2c9,0x4e6,0x4ed,0x2c9,0x2c9,0x4f0,0x4f7,0x2c9,0x4fa,0x501,0x509,0x511,0x519,0x521,0x528,0x2c9, +0x2c9,0x52f,0x2c9,0x2c9,0x536,0x2c9,0x2c9,0x2c9,0x96d,0x2c9,0x2c9,0x975,0x2c9,0x97b,0x983,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x53a,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x542,0x542,0x2c9,0x2c9,0x2c9,0x2c9,0x548,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x550,0x2c9,0x2c9,0x2c9,0x553,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x55a,0x2c9,0x2c9,0x562,0x2c9,0x56a,0x2c9,0x2c9,0x572,0x577,0x57f,0x585,0x2c9,0x58b,0x2c9,0x592, +0x2c9,0x597,0x2c9,0x2c9,0x2c9,0x2c9,0x59d,0x5a5,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x5ad,0x5b2, +0x5ba,0x5c2,0x5ca,0x5d2,0x5da,0x5e2,0x5ea,0x5f2,0x5fa,0x602,0x60a,0x612,0x61a,0x622,0x62a,0x632, +0x63a,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x63e,0x646,0x2c9,0x64d,0x2c9,0x2c9,0x651,0x658,0x65d,0x2c9, +0x665,0x66d,0x675,0x67d,0x685,0x68d,0x2c9,0x695,0x2c9,0x69b,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x69e,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x6a6,0x2c9,0x2c9,0x2c9,0x6ab,0x2c9,0x2c9,0x2c9,0x6b3, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x6bb,0x6c2,0x6ca,0x6d2,0x6da,0x6e2,0x6ea,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x6f2,0x6fa,0x2c9,0x2c9,0x702,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x709,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x710,0x718,0x2c9,0x71e,0x722,0x2c9,0x2c9,0x598,0x72a,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x72e,0x736,0x739,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x498, +0x98b,0x98c,0x98d,0x98e,0x98f,0x990,0x99f,0x98b,0x98c,0x98d,0x98e,0x98f,0x990,0x99f,0x98b,0x98c, +0x98d,0x98e,0x98f,0x990,0x99f,0x98b,0x98c,0x98d,0x98e,0x98f,0x990,0x99f,0x98b,0x98c,0x98d,0x98e, +0x98f,0x990,0x99f,0x98b,0x98c,0x98d,0x98e,0x98f,0x990,0x99f,0x98b,0x98c,0x98d,0x98e,0x98f,0x990, +0x99f,0x98b,0x98c,0x98d,0x98e,0x98f,0x990,0x99f,0x98b,0x98c,0x98d,0x98e,0x98f,0x990,0x99f,0x98b, +0x98c,0x98d,0x98e,0x98f,0x990,0x99f,0x98b,0x98c,0x98d,0x98e,0x98f,0x990,0x99f,0x98b,0x98c,0x98d, +0x98e,0x98f,0x990,0x99f,0x98b,0x98c,0x98d,0x98e,0x98f,0x990,0x99f,0x98b,0x98c,0x98d,0x98e,0x98f, +0x990,0x99f,0x98b,0x98c,0x98d,0x98e,0x98f,0x990,0x99f,0x98b,0x98c,0x98d,0x98e,0x98f,0x990,0x99f, +0x98b,0x98c,0x98d,0x98e,0x98f,0x990,0x99f,0x98b,0x98c,0x98d,0x98e,0x98f,0x990,0x99f,0x98b,0x98c, +0x98d,0x98e,0x98f,0x990,0x99f,0x98b,0x98c,0x98d,0x98e,0x98f,0x990,0x99f,0x98b,0x98c,0x98d,0x98e, +0x98f,0x990,0x99f,0x98b,0x98c,0x98d,0x98e,0x98f,0x990,0x99f,0x98b,0x98c,0x98d,0x98e,0x98f,0x990, +0x99f,0x98b,0x98c,0x98d,0x98e,0x98f,0x990,0x99f,0x98b,0x98c,0x98d,0x98e,0x98f,0x990,0x99f,0x98b, +0x98c,0x98d,0x98e,0x98f,0x990,0x99f,0x98b,0x98c,0x98d,0x98e,0x98f,0x990,0x99f,0x98b,0x98c,0x98d, +0x98e,0x98f,0x990,0x99f,0x98b,0x98c,0x98d,0x98e,0x98f,0x990,0x99f,0x98b,0x98c,0x98d,0x98e,0x98f, +0x990,0x99f,0x98b,0x98c,0x98d,0x98e,0x98f,0x990,0x99f,0x98b,0x98c,0x98d,0x98e,0x98f,0x990,0x99f, +0x98b,0x98c,0x98d,0x98e,0x98f,0x990,0x99f,0x98b,0x98c,0x98d,0x98e,0x98f,0x990,0x99f,0x98b,0x98c, +0x98d,0x98e,0x98f,0x990,0x99f,0x98b,0x98c,0x98d,0x98e,0x98f,0x990,0x99f,0x98b,0x98c,0x98d,0x98e, +0x98f,0x990,0x99f,0x98b,0x98c,0x98d,0x98e,0x98f,0x990,0x99f,0x98b,0x98c,0x98d,0x98e,0x98f,0x990, +0x99f,0x98b,0x98c,0x98d,0x98e,0x98f,0x990,0x99f,0x98b,0x98c,0x98d,0x98e,0x98f,0x990,0x99f,0x98b, +0x98c,0x98d,0x98e,0x98f,0x990,0x99f,0x98b,0x98c,0x98d,0x98e,0x98f,0x990,0x99f,0x98b,0x98c,0x98d, +0x98e,0x98f,0x990,0x99f,0x98b,0x98c,0x98d,0x98e,0x98f,0x990,0x99f,0x98b,0x98c,0x98d,0x98e,0x98f, +0x990,0x99f,0x98b,0x98c,0x98d,0x98e,0x98f,0x990,0x99f,0x98b,0x98c,0x98d,0x98e,0x98f,0x990,0x99f, +0x98b,0x98c,0x98d,0x98e,0x98f,0x990,0x99f,0x98b,0x98c,0x98d,0x98e,0x98f,0x990,0x997,0x2c9,0x2c9, +0x9a7,0x9ae,0x2aa,0x9b5,0x2aa,0x2aa,0x2aa,0x2aa,0x2aa,0x2aa,0x2aa,0x2aa,0x2aa,0x2aa,0x2aa,0x2aa, +0x2aa,0x2aa,0x2aa,0x2aa,0x2aa,0x2aa,0x2aa,0x2aa,0x2aa,0x2aa,0x2aa,0x2aa,0x2aa,0x2aa,0x2aa,0x2aa, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x741,0x749,0x751,0x759,0x761,0x769,0x771,0x779, +0x781,0x789,0x791,0x799,0x7a1,0x7a9,0x7b1,0x2c9,0x7b8,0x7c0,0x7c8,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x7d0,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0xb28,0xb28,0xb40,0xb80,0xbc0,0xc00,0xc40,0xc78,0xcb8,0xb24,0xcec,0xb24,0xd2c,0xd6c,0xdac,0xdec, +0xe2c,0xe6c,0xeac,0xeec,0xb24,0xb24,0xf28,0xf68,0xf98,0xfd0,0xb24,0x1010,0x1040,0x1080,0xb24,0x1098, +0x880,0x8b0,0x8ee,0x92d,0x188,0x188,0x188,0x188,0x188,0x188,0x188,0x188,0x188,0x95a,0x188,0x188, +0x188,0x188,0x188,0x188,0x188,0x188,0x188,0x976,0x188,0x188,0x9ac,0x188,0x9ec,0xa26,0x188,0x188, 0x188,0x188,0x188,0x188,0x188,0x188,0x188,0x188,0x188,0x188,0x188,0x188,0x188,0x188,0x188,0x188, -0x188,0x188,0x188,0x188,0x188,0x188,0x188,0x188,0x188,0x188,0x188,0x188,0x188,0x188,0x188,0xa5f, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x7c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x7cf,0x2c7,0x2c7,0x2c7,0x7d2,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x7d9,0x7dd,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x7e5,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x6fc,0x69e,0x7e7,0x7ef,0x2c7,0x2c7,0x7f7,0x7fe,0x2c7,0x58b,0x2c7,0x2c7,0x806,0x2c7,0x2c7,0x809, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x80f,0x2c7,0x463,0x816,0x81d,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x825,0x2c7,0x2c7,0x829,0x831,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x836,0x83e,0x2c7,0x2c7,0x69e, -0x2c7,0x2c7,0x2c7,0x841,0x2c7,0x2c7,0x2c7,0x847,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x535,0x848,0x2c7,0x84a,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x69e,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x852,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x856,0x2c7,0x85c,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x862,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x86a, -0x872,0x87a,0x880,0x888,0x2c7,0x2c7,0x2c7,0x890,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x898,0x8a0,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x8a4,0x2c7,0x2c7,0x2c7,0x8ab,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x8b3, -0x8bb,0x8c3,0x8cb,0x8d3,0x8db,0x8e3,0x8eb,0x8f3,0x8fb,0x903,0x90b,0x913,0x91b,0x923,0x92b,0x933, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7, -0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2c7,0x2a7, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,4,8,0xc,1, -1,0x10,0x50,0x5c,0x70,0x88,0xcc,0xd0,0xec,0x108,0x144,0x148,0x15c,0x174,0x180,0x1a4, -0x1e4,1,0x1ec,0x20c,0x228,0x244,0x290,0x298,0x2b0,0x2b8,0x2dc,1,1,1,1,1, -1,0x2f4,0x334,0x340,0x354,0x36c,0x3b0,0x3b4,0x3d0,0x3f0,0x428,0x430,0x444,0x45c,0x468,0x48c, -0x4cc,1,0x4d4,0x4f4,0x510,0x530,0x57c,0x584,0x5a0,0x5a8,0x5d0,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -0x5e8,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,0x1284,0x128a,0xade,0x1290,0xaf4,0xafe,0x5f4,0xb08, -0x1296,0x129c,0xb12,0x12a2,0x12a8,0x12ae,0x12b4,0xb28,1,0x12ba,0x12c0,0x12c6,0xb32,0xb48,0xb5a,1, -0x5fc,0x12cc,0x12d2,0x12d8,0xb64,0x12de,1,1,0x12e4,0x12ea,0xb7a,0x12f0,0xb90,0xb9a,0x600,0xba4, -0x12f6,0x12fc,0xbae,0x1302,0x1308,0x130e,0x1314,0xbc4,1,0x131a,0x1320,0x1326,0xbce,0xbe4,0xbf6,1, -0x608,0x132c,0x1332,0x1338,0xc00,0x133e,1,0x1344,0x134a,0x1350,0xc16,0xc2c,0x1357,0x135d,0x1362,0x1368, -0x136e,0x1374,0x137a,0x1380,0x1386,0x138c,0x1392,0x1398,1,1,0xc42,0xc50,0x139e,0x13a4,0x13aa,0x13b0, -0x13b7,0x13bd,0x13c2,0x13c8,0x13ce,0x13d4,0x13da,0x13e0,0x13e6,0x13ec,0x13f3,0x13f9,0x13fe,0x1404,1,1, -0x140a,0x1410,0x1416,0x141c,0x1422,0x1428,0x142f,0x1435,0x143a,1,1,1,0x1441,0x1447,0x144d,0x1453, -1,0x1458,0x145e,0x1465,0x146b,0x1470,0x1476,1,1,1,1,0x147c,0x1482,0x1489,0x148f,0x1494, -0x149a,1,1,1,0xc5e,0xc6c,0x14a0,0x14a6,0x14ac,0x14b2,1,1,0x14b8,0x14be,0x14c5,0x14cb, -0x14d0,0x14d6,0xc7a,0xc84,0x14dc,0x14e2,0x14e9,0x14ef,0xc8e,0xc98,0x14f5,0x14fb,0x1500,0x1506,1,1, -0xca2,0xcac,0xcb6,0xcc0,0x150c,0x1512,0x1518,0x151e,0x1524,0x152a,0x1531,0x1537,0x153c,0x1542,0x1548,0x154e, -0x1554,0x155a,0x1560,0x1566,0x156c,0x1572,0x1578,0x60c,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,0xcca,0xce4,1,1,1,1,1,1, -1,1,1,1,1,1,1,0xcfe,0xd18,1,1,1,1,1,1,0x610, -1,1,1,1,1,1,1,1,1,1,1,1,1,0x157e,0x1584,0x158a, -0x1590,0x1596,0x159c,0x15a2,0x15a8,0x15b0,0x15ba,0x15c4,0x15ce,0x15d8,0x15e2,0x15ec,0x15f6,1,0x1600,0x160a, -0x1614,0x161e,0x1627,0x162d,1,1,0x1632,0x1638,0x163e,0x1644,0xd32,0xd3c,0x164d,0x1657,0x165f,0x1665, -0x166b,1,1,1,0x1670,0x1676,1,1,0x167c,0x1682,0x168a,0x1694,0x169d,0x16a3,0x16a9,0x16af, -0x16b4,0x16ba,0x16c0,0x16c6,0x16cc,0x16d2,0x16d8,0x16de,0x16e4,0x16ea,0x16f0,0x16f6,0x16fc,0x1702,0x1708,0x170e, -0x1714,0x171a,0x1720,0x1726,0x172c,0x1732,0x1738,0x173e,0x1744,0x174a,0x1750,0x1756,1,1,0x175c,0x1762, -1,1,1,1,1,1,0xd46,0xd50,0xd5a,0xd64,0x176a,0x1774,0x177e,0x1788,0xd6e,0xd78, -0x1792,0x179c,0x17a4,0x17aa,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,0x614,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,0xfdcc,0xfdcc,0xfdcc,0xfdcc,0xfdcc,0xffcc,0xfdcc,0xfdcc,0xfdcc,0xfdcc,0xfdcc,0xfdcc, -0xfdcc,0xffcc,0xffcc,0xfdcc,0xffcc,0xfdcc,0xffcc,0xfdcc,0xfdcc,0xffd0,0xffb8,0xffb8,0xffb8,0xffb8,0xffd0,0xfdb0, -0xffb8,0xffb8,0xffb8,0xffb8,0xffb8,0xff94,0xff94,0xfdb8,0xfdb8,0xfdb8,0xfdb8,0xfd94,0xfd94,0xffb8,0xffb8,0xffb8, -0xffb8,0xfdb8,0xfdb8,0xffb8,0xfdb8,0xfdb8,0xffb8,0xffb8,0xfe02,0xfe02,0xfe02,0xfe02,0xfc02,0xffb8,0xffb8,0xffb8, -0xffb8,0xffcc,0xffcc,0xffcc,0x3c26,0x3c2c,0xfdcc,0x3c32,0x3c38,0xfde0,0xffcc,0xffb8,0xffb8,0xffb8,0xffcc,0xffcc, -0xffcc,0xffb8,0xffb8,1,0xffcc,0xffcc,0xffcc,0xffb8,0xffb8,0xffb8,0xffb8,0xffcc,0xffd0,0xffb8,0xffb8,0xffcc, -0xffd2,0xffd4,0xffd4,0xffd2,0xffd4,0xffd4,0xffd2,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc, -0xffcc,0xffcc,0xffcc,0xffcc,1,1,1,1,0x29d1,1,1,1,1,1,1,1, -1,1,0x29d5,1,1,1,1,1,1,0x17b1,0x17b7,0x29d9,0x17bd,0x17c3,0x17c9,1, -0x17cf,1,0x17d5,0x17db,0x17e3,0x618,1,1,1,0x634,1,0x644,1,0x658,1,1, -1,1,1,0x674,1,0x684,1,1,1,0x688,1,1,1,0x6a0,0x17eb,0x17f1, -0xd82,0x17f7,0xd8c,0x17fd,0x1805,0x6b4,1,1,1,0x6d4,1,0x6e4,1,0x6fc,1,1, -1,1,1,0x71c,1,0x72c,1,1,1,0x734,1,1,1,0x754,0xd96,0xda8, -0x180d,0x1813,0xdba,1,1,1,0x76c,0x1819,0x181f,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,0x1825,0x182b,1,0x1831,1,1,0x774,0x1837,1,1,1,1, -0x183d,0x1843,0x1849,1,0x778,1,1,0x780,1,0x784,0x790,0x798,0x79c,0x184f,0x7ac,1, -1,1,0x7b0,1,1,1,1,0x7b4,1,1,1,0x7c4,1,1,1,0x7c8, -1,0x7cc,1,1,0x7d0,1,1,0x7d8,1,0x7dc,0x7e8,0x7f0,0x7f4,0x1855,0x804,1, -1,1,0x808,1,1,1,1,0x80c,1,1,1,0x81c,1,1,1,0x820, -1,0x824,1,1,0x185b,0x1861,1,0x1867,1,1,0x828,0x186d,1,1,1,1, -0x1873,0x1879,0x187f,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,0x82c,0x830,0x1885,0x188b,1,1,1,1, -1,1,1,1,1,1,1,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,0x1891,0x1897,1,1,1,1,1,1,1,1,1, -1,1,1,1,0x189d,0x18a3,0x18a9,0x18af,1,1,0x18b5,0x18bb,0x834,0x838,0x18c1,0x18c7, -0x18cd,0x18d3,0x18d9,0x18df,1,1,0x18e5,0x18eb,0x18f1,0x18f7,0x18fd,0x1903,0x83c,0x840,0x1909,0x190f, -0x1915,0x191b,0x1921,0x1927,0x192d,0x1933,0x1939,0x193f,0x1945,0x194b,1,1,0x1951,0x1957,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,0xffb8,0xffcc,0xffcc,0xffcc,0xffcc,0xffb8,0xffcc,0xffcc,0xffcc,0xffbc,0xffb8,0xffcc,0xffcc,0xffcc,0xffcc, -0xffcc,0xffcc,0xffb8,0xffb8,0xffb8,0xffb8,0xffb8,0xffb8,0xffcc,0xffcc,0xffb8,0xffcc,0xffcc,0xffbc,0xffc8,0xffcc, -0xfe14,0xfe16,0xfe18,0xfe1a,0xfe1c,0xfe1e,0xfe20,0xfe22,0xfe24,0xfe26,0xfe26,0xfe28,0xfe2a,0xfe2c,1,0xfe2e, -1,0xfe30,0xfe32,1,0xffcc,0xffb8,1,0xfe24,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xfe3c,0xfe3e,0xfe40,1,1,1,1,1, -1,1,0x195c,0x1962,0x1969,0x196f,0x1975,0x844,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -0x850,1,0x854,0xfe36,0xfe38,0xfe3a,0xfe3c,0xfe3e,0xfe40,0xfe42,0xfe44,0xfdcc,0xfdcc,0xfdb8,0xffb8,0xffcc, -0xffcc,0xffcc,0xffcc,0xffcc,0xffb8,0xffcc,0xffcc,0xffb8,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,0xfe46,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,0x197b,0x858,0x1981,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,0x85c,0x1987,1,0x860,0xffcc,0xffcc, -0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,1,1,0xffcc,0xffcc,0xffcc,0xffcc,0xffb8,0xffcc,1,1,0xffcc, -0xffcc,1,0xffb8,0xffcc,0xffcc,0xffb8,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,0xfe48,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,0xffcc,0xffb8,0xffcc,0xffcc,0xffb8,0xffcc,0xffcc,0xffb8, -0xffb8,0xffb8,0xffcc,0xffb8,0xffb8,0xffcc,0xffb8,0xffcc,0xffcc,0xffcc,0xffb8,0xffcc,0xffb8,0xffcc,0xffb8,0xffcc, -0xffb8,0xffcc,0xffcc,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc, -0xffcc,0xffcc,0xffb8,0xffcc,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,0xffcc,0xffcc,0xffcc,0xffcc,1,0xffcc, -0xffcc,0xffcc,0xffcc,0xffcc,1,0xffcc,0xffcc,0xffcc,1,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,0xffb8,0xffb8,0xffb8,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,1,0xffb8, -0xffcc,0xffcc,0xffb8,0xffcc,0xffcc,0xffb8,0xffcc,0xffcc,0xffcc,0xffb8,0xffb8,0xffb8,0xfe36,0xfe38,0xfe3a,0xffcc, -0xffcc,0xffcc,0xffb8,0xffcc,0xffcc,0xffb8,0xffb8,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,1,1,1,1, -1,1,1,1,0x864,0x198d,1,1,1,1,1,1,0x868,0x1993,1,0x86c, -0x1999,1,1,1,1,1,1,1,0xfc0e,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,0xfe12,1,1,1,0xffcc,0xffb8,0xffcc, -0xffcc,1,1,1,0x29dc,0x29e2,0x29e8,0x29ee,0x29f4,0x29fa,0x2a00,0x2a06,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,0xfe0e,1,0xfc00,1,1,1,1,1, -1,1,1,0x870,1,1,1,0x199f,0x19a5,0xfe12,1,1,1,1,1,1, -1,1,1,0xfc00,1,1,1,1,0x2a0c,0x2a12,1,0x2a18,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0x2a1e, -1,1,0x2a24,1,1,1,1,1,0xfe0e,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,0xfe12,1,1,1,1,1,1, -1,1,1,1,1,0x2a2a,0x2a30,0x2a36,1,1,0x2a3c,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,0xfe0e,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,0xfe12,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0x878, -0x19ab,1,1,0x19b1,0x19b7,0xfe12,1,1,1,1,1,1,1,1,0xfc00,0xfc00, -1,1,1,1,0x2a42,0x2a48,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,0x884,1,0x19bd,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,0xfc00,1, -1,1,1,1,1,1,0x888,0x890,1,1,0x19c3,0x19c9,0x19cf,0xfe12,1,1, -1,1,1,1,1,1,1,0xfc00,1,1,1,1,1,1,1,1, -1,1,0x894,1,0x19d5,1,1,1,1,0xfe12,1,1,1,1,1,1, -1,0xfea8,0xfcb6,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -0xfe0e,1,1,0x898,0x19db,1,0xfc00,1,1,1,0x89c,0x19e1,0x19e7,1,0xdc4,0x19ef, -1,0xfe12,1,1,1,1,1,1,1,0xfc00,0xfc00,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,0xfe12,0xfe12,1,0xfc00,1,1,1,1,1, -1,1,0x8a8,0x8b0,1,1,0x19f7,0x19fd,0x1a03,0xfe12,1,1,1,1,1,1, -1,1,1,0xfc00,1,1,1,1,1,1,1,1,1,1,0xfc12,1, -1,1,1,0xfc00,1,1,1,1,1,1,1,1,1,0x8b4,0x1a09,1, -0xdce,0x1a11,0x1a19,0xfc00,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,0xfece,0xfece,0xfe12,1, -1,1,1,1,1,1,1,1,0xfed6,0xfed6,0xfed6,0xfed6,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,0xfeec,0xfeec,1,1,1,1,1,1,1,1,1,1, -0xfef4,0xfef4,0xfef4,0xfef4,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,0xffb8,0xffb8,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,0xffb8,1,0xffb8,1,0xffb0,1,1,1,1,1,1, -1,1,1,0x2a4f,1,1,1,1,1,1,1,1,1,0x2a55,1,1, -1,1,0x2a5b,1,1,1,1,0x2a61,1,1,1,1,0x2a67,1,1,1, -1,1,1,1,1,1,1,1,1,0x2a6d,1,1,1,1,1,1, -1,0xff02,0xff04,0x3c40,0xff08,0x3c48,0x2a72,1,0x2a78,1,0xff04,0xff04,0xff04,0xff04,1,1, -0xff04,0x3c50,0xffcc,0xffcc,0xfe12,1,0xffcc,0xffcc,1,1,1,1,1,1,1,1, -1,1,1,0x2a7f,1,1,1,1,1,1,1,1,1,0x2a85,1,1, -1,1,0x2a8b,1,1,1,1,0x2a91,1,1,1,1,0x2a97,1,1,1, -1,1,1,1,1,1,1,1,1,0x2a9d,1,1,1,1,1,1, -1,1,0xffb8,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,0x8c0,0x1a1f,1, -1,1,1,1,1,1,0xfc00,1,1,1,1,1,1,1,1,0xfe0e, -1,0xfe12,0xfe12,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,0xffb8,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,0xffcc,0xffcc,0xffcc,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,0xfe12,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,0xfe12,1, -1,1,1,1,1,1,1,1,1,0xffcc,1,1,1,1,1,1, -1,1,1,1,1,0xffc8,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,0xffbc,0xffcc,0xffb8,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,0xffcc,0xffb8,1,1,1, -1,1,1,1,0xfe12,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc, -0xffcc,1,1,0xffb8,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffb8,0xffb8,0xffb8,0xffb8,0xffb8,0xffb8,0xffcc, -0xffcc,0xffb8,1,1,1,1,1,1,1,0x8c4,0x1a25,0x8c8,0x1a2b,0x8cc,0x1a31,0x8d0, -0x1a37,0x8d4,0x1a3d,1,1,0x8d8,0x1a43,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,0xfe0e,0xfc00,1,1, -1,1,0x8dc,0x1a49,0x8e0,0x1a4f,0x8e4,0x8e8,0x1a55,0x1a5b,0x8ec,0x1a61,0xfe12,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,0xffcc,0xffb8,0xffcc,0xffcc,0xffcc, +0x188,0x188,0x188,0x188,0x188,0x188,0x188,0x188,0x188,0x188,0x188,0x188,0x188,0x188,0x188,0xa66, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x7d4, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x7dc,0x2c9,0x2c9,0x2c9,0x7df,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x7e6,0x7ea,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x7f2,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x7f9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x800,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x709,0x6ab,0x805,0x80d,0x2c9,0x2c9,0x815,0x81c,0x2c9,0x598,0x2c9,0x2c9,0x824,0x2c9,0x2c9,0x827, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x82d,0x2c9,0x830,0x838,0x83f,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x847,0x2c9,0x2c9,0x84f,0x857,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x85c,0x864,0x2c9,0x2c9,0x6ab, +0x2c9,0x2c9,0x2c9,0x867,0x2c9,0x2c9,0x2c9,0x86d,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x870,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x542,0x86e, +0x2c9,0x877,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x6ab,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x87f,0x2c9,0x882,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x888,0x2c9,0x88e,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x894,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x89c,0x8a4,0x8ac,0x8b2,0x8ba,0x2c9,0x2c9,0x2c9,0x8c2,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x8ca,0x8d2,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x8d6,0x2c9,0x2c9,0x2c9, +0x8dd,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x8e5,0x8ed,0x8f5,0x8fd,0x905,0x90d,0x915,0x91d,0x925,0x92d, +0x935,0x93d,0x945,0x94d,0x955,0x95d,0x965,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9, +0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2c9,0x2a9,0x2a9,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,4,8,0xc,1,1,0x10,0x50,0x5c,0x70,0x88,0xcc,0xd0, +0xec,0x108,0x144,0x148,0x15c,0x174,0x180,0x1a4,0x1e4,1,0x1ec,0x20c,0x228,0x244,0x290,0x298, +0x2b0,0x2b8,0x2dc,1,1,1,1,1,1,0x2f4,0x334,0x340,0x354,0x36c,0x3b0,0x3b4, +0x3d0,0x3f0,0x428,0x430,0x444,0x45c,0x468,0x48c,0x4cc,1,0x4d4,0x4f4,0x510,0x530,0x57c,0x584, +0x5a0,0x5a8,0x5d0,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,0x5e8,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +0x1284,0x128a,0xade,0x1290,0xaf4,0xafe,0x5f4,0xb08,0x1296,0x129c,0xb12,0x12a2,0x12a8,0x12ae,0x12b4,0xb28, +1,0x12ba,0x12c0,0x12c6,0xb32,0xb48,0xb5a,1,0x5fc,0x12cc,0x12d2,0x12d8,0xb64,0x12de,1,1, +0x12e4,0x12ea,0xb7a,0x12f0,0xb90,0xb9a,0x600,0xba4,0x12f6,0x12fc,0xbae,0x1302,0x1308,0x130e,0x1314,0xbc4, +1,0x131a,0x1320,0x1326,0xbce,0xbe4,0xbf6,1,0x608,0x132c,0x1332,0x1338,0xc00,0x133e,1,0x1344, +0x134a,0x1350,0xc16,0xc2c,0x1357,0x135d,0x1362,0x1368,0x136e,0x1374,0x137a,0x1380,0x1386,0x138c,0x1392,0x1398, +1,1,0xc42,0xc50,0x139e,0x13a4,0x13aa,0x13b0,0x13b7,0x13bd,0x13c2,0x13c8,0x13ce,0x13d4,0x13da,0x13e0, +0x13e6,0x13ec,0x13f3,0x13f9,0x13fe,0x1404,1,1,0x140a,0x1410,0x1416,0x141c,0x1422,0x1428,0x142f,0x1435, +0x143a,1,1,1,0x1441,0x1447,0x144d,0x1453,1,0x1458,0x145e,0x1465,0x146b,0x1470,0x1476,1, +1,1,1,0x147c,0x1482,0x1489,0x148f,0x1494,0x149a,1,1,1,0xc5e,0xc6c,0x14a0,0x14a6, +0x14ac,0x14b2,1,1,0x14b8,0x14be,0x14c5,0x14cb,0x14d0,0x14d6,0xc7a,0xc84,0x14dc,0x14e2,0x14e9,0x14ef, +0xc8e,0xc98,0x14f5,0x14fb,0x1500,0x1506,1,1,0xca2,0xcac,0xcb6,0xcc0,0x150c,0x1512,0x1518,0x151e, +0x1524,0x152a,0x1531,0x1537,0x153c,0x1542,0x1548,0x154e,0x1554,0x155a,0x1560,0x1566,0x156c,0x1572,0x1578,0x60c, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +0xcca,0xce4,1,1,1,1,1,1,1,1,1,1,1,1,1,0xcfe, +0xd18,1,1,1,1,1,1,0x610,1,1,1,1,1,1,1,1, +1,1,1,1,1,0x157e,0x1584,0x158a,0x1590,0x1596,0x159c,0x15a2,0x15a8,0x15b0,0x15ba,0x15c4, +0x15ce,0x15d8,0x15e2,0x15ec,0x15f6,1,0x1600,0x160a,0x1614,0x161e,0x1627,0x162d,1,1,0x1632,0x1638, +0x163e,0x1644,0xd32,0xd3c,0x164d,0x1657,0x165f,0x1665,0x166b,1,1,1,0x1670,0x1676,1,1, +0x167c,0x1682,0x168a,0x1694,0x169d,0x16a3,0x16a9,0x16af,0x16b4,0x16ba,0x16c0,0x16c6,0x16cc,0x16d2,0x16d8,0x16de, +0x16e4,0x16ea,0x16f0,0x16f6,0x16fc,0x1702,0x1708,0x170e,0x1714,0x171a,0x1720,0x1726,0x172c,0x1732,0x1738,0x173e, +0x1744,0x174a,0x1750,0x1756,1,1,0x175c,0x1762,1,1,1,1,1,1,0xd46,0xd50, +0xd5a,0xd64,0x176a,0x1774,0x177e,0x1788,0xd6e,0xd78,0x1792,0x179c,0x17a4,0x17aa,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,0x614,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,0xfdcc,0xfdcc,0xfdcc,0xfdcc, +0xfdcc,0xffcc,0xfdcc,0xfdcc,0xfdcc,0xfdcc,0xfdcc,0xfdcc,0xfdcc,0xffcc,0xffcc,0xfdcc,0xffcc,0xfdcc,0xffcc,0xfdcc, +0xfdcc,0xffd0,0xffb8,0xffb8,0xffb8,0xffb8,0xffd0,0xfdb0,0xffb8,0xffb8,0xffb8,0xffb8,0xffb8,0xff94,0xff94,0xfdb8, +0xfdb8,0xfdb8,0xfdb8,0xfd94,0xfd94,0xffb8,0xffb8,0xffb8,0xffb8,0xfdb8,0xfdb8,0xffb8,0xfdb8,0xfdb8,0xffb8,0xffb8, +0xfe02,0xfe02,0xfe02,0xfe02,0xfc02,0xffb8,0xffb8,0xffb8,0xffb8,0xffcc,0xffcc,0xffcc,0x3c26,0x3c2c,0xfdcc,0x3c32, +0x3c38,0xfde0,0xffcc,0xffb8,0xffb8,0xffb8,0xffcc,0xffcc,0xffcc,0xffb8,0xffb8,1,0xffcc,0xffcc,0xffcc,0xffb8, +0xffb8,0xffb8,0xffb8,0xffcc,0xffd0,0xffb8,0xffb8,0xffcc,0xffd2,0xffd4,0xffd4,0xffd2,0xffd4,0xffd4,0xffd2,0xffcc, +0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,1,1,1,1, +0x29d1,1,1,1,1,1,1,1,1,1,0x29d5,1,1,1,1,1, +1,0x17b1,0x17b7,0x29d9,0x17bd,0x17c3,0x17c9,1,0x17cf,1,0x17d5,0x17db,0x17e3,0x618,1,1, +1,0x634,1,0x644,1,0x658,1,1,1,1,1,0x674,1,0x684,1,1, +1,0x688,1,1,1,0x6a0,0x17eb,0x17f1,0xd82,0x17f7,0xd8c,0x17fd,0x1805,0x6b4,1,1, +1,0x6d4,1,0x6e4,1,0x6fc,1,1,1,1,1,0x71c,1,0x72c,1,1, +1,0x734,1,1,1,0x754,0xd96,0xda8,0x180d,0x1813,0xdba,1,1,1,0x76c,0x1819, +0x181f,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,0x1825,0x182b,1,0x1831, +1,1,0x774,0x1837,1,1,1,1,0x183d,0x1843,0x1849,1,0x778,1,1,0x780, +1,0x784,0x790,0x798,0x79c,0x184f,0x7ac,1,1,1,0x7b0,1,1,1,1,0x7b4, +1,1,1,0x7c4,1,1,1,0x7c8,1,0x7cc,1,1,0x7d0,1,1,0x7d8, +1,0x7dc,0x7e8,0x7f0,0x7f4,0x1855,0x804,1,1,1,0x808,1,1,1,1,0x80c, +1,1,1,0x81c,1,1,1,0x820,1,0x824,1,1,0x185b,0x1861,1,0x1867, +1,1,0x828,0x186d,1,1,1,1,0x1873,0x1879,0x187f,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +0x82c,0x830,0x1885,0x188b,1,1,1,1,1,1,1,1,1,1,1,0xffcc, 0xffcc,0xffcc,0xffcc,0xffcc,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,0xfe12,0xfe12,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,0xfe0e,1,1,1,1,1, -1,1,1,1,1,1,0xfe12,0xfe12,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0xfe0e, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -0xffcc,0xffcc,0xffcc,1,0xfe02,0xffb8,0xffb8,0xffb8,0xffb8,0xffb8,0xffcc,0xffcc,0xffb8,0xffb8,0xffb8,0xffb8, -0xffcc,1,0xfe02,0xfe02,0xfe02,0xfe02,0xfe02,0xfe02,0xfe02,1,1,1,1,0xffb8,1,1, -1,1,1,1,0xffcc,1,1,1,0xffcc,0xffcc,1,1,1,1,1,1, -0xffcc,0xffcc,0xffb8,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffb8,0xffcc,0xffcc,0xffd4,0xffac,0xffb8, -0xff94,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc, -0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffd0,0xffc8,0xffc8,0xffb8,1,0xffcc, -0xffd2,0xffb8,0xffcc,0xffb8,0x1a66,0x1a6c,0x1a72,0x1a78,0x1a7f,0x1a85,0x1a8b,0x1a91,0x1a99,0x1aa3,0x1aaa,0x1ab0, -0x1ab6,0x1abc,0x1ac2,0x1ac8,0x1acf,0x1ad5,0x1ada,0x1ae0,0x1ae8,0x1af2,0x1afc,0x1b06,0x1b0e,0x1b14,0x1b1a,0x1b20, -0x1b29,0x1b33,0x1b3b,0x1b41,0x1b46,0x1b4c,0x1b52,0x1b58,0x1b5e,0x1b64,0x1b6a,0x1b70,0x1b77,0x1b7d,0x1b82,0x1b88, -0x1b8e,0x1b94,0x1b9c,0x1ba6,0x1bae,0x1bb4,0x1bba,0x1bc0,0x1bc6,0x1bcc,0xdd8,0xde2,0x1bd4,0x1bde,0x1be6,0x1bec, -0x1bf2,0x1bf8,0x1bfe,0x1c04,0x1c0a,0x1c10,0x1c17,0x1c1d,0x1c22,0x1c28,0x1c2e,0x1c34,0x1c3a,0x1c40,0x1c46,0x1c4c, -0x1c54,0x1c5e,0x1c68,0x1c72,0x1c7c,0x1c86,0x1c90,0x1c9a,0x1ca3,0x1ca9,0x1caf,0x1cb5,0x1cba,0x1cc0,0xdec,0xdf6, -0x1cc8,0x1cd2,0x1cda,0x1ce0,0x1ce6,0x1cec,0xe00,0xe0a,0x1cf4,0x1cfe,0x1d08,0x1d12,0x1d1c,0x1d26,0x1d2e,0x1d34, -0x1d3a,0x1d40,0x1d46,0x1d4c,0x1d52,0x1d58,0x1d5e,0x1d64,0x1d6a,0x1d70,0x1d76,0x1d7c,0x1d84,0x1d8e,0x1d98,0x1da2, -0x1daa,0x1db0,0x1db7,0x1dbd,0x1dc2,0x1dc8,0x1dce,0x1dd4,0x1dda,0x1de0,0x1de6,0x1dec,0x1df3,0x1df9,0x1dff,0x1e05, -0x1e0b,0x1e11,0x1e16,0x1e1c,0x1e22,0x1e28,0x1e2f,0x1e35,0x1e3b,0x1e41,0x1e46,0x1e4c,0x1e52,0x1e58,1,0x1e5f, -1,1,1,1,0xe14,0xe22,0x1e64,0x1e6a,0x1e72,0x1e7c,0x1e86,0x1e90,0x1e9a,0x1ea4,0x1eae,0x1eb8, -0x1ec2,0x1ecc,0x1ed6,0x1ee0,0x1eea,0x1ef4,0x1efe,0x1f08,0x1f12,0x1f1c,0x1f26,0x1f30,0xe30,0xe3a,0x1f38,0x1f3e, -0x1f44,0x1f4a,0x1f52,0x1f5c,0x1f66,0x1f70,0x1f7a,0x1f84,0x1f8e,0x1f98,0x1fa2,0x1fac,0x1fb4,0x1fba,0x1fc0,0x1fc6, -0xe44,0xe4e,0x1fcc,0x1fd2,0x1fda,0x1fe4,0x1fee,0x1ff8,0x2002,0x200c,0x2016,0x2020,0x202a,0x2034,0x203e,0x2048, -0x2052,0x205c,0x2066,0x2070,0x207a,0x2084,0x208e,0x2098,0x20a0,0x20a6,0x20ac,0x20b2,0x20ba,0x20c4,0x20ce,0x20d8, -0x20e2,0x20ec,0x20f6,0x2100,0x210a,0x2114,0x211c,0x2122,0x2129,0x212f,0x2134,0x213a,0x2140,0x2146,1,1, -1,1,1,1,0xe58,0xe6e,0xe86,0xe94,0xea2,0xeb0,0xebe,0xecc,0xed8,0xeee,0xf06,0xf14, -0xf22,0xf30,0xf3e,0xf4c,0xf58,0xf66,0x214f,0x2159,0x2163,0x216d,1,1,0xf74,0xf82,0x2177,0x2181, -0x218b,0x2195,1,1,0xf90,0xfa6,0xfbe,0xfcc,0xfda,0xfe8,0xff6,0x1004,0x1010,0x1026,0x103e,0x104c, -0x105a,0x1068,0x1076,0x1084,0x1090,0x10a2,0x219f,0x21a9,0x21b3,0x21bd,0x21c7,0x21d1,0x10b4,0x10c6,0x21db,0x21e5, -0x21ef,0x21f9,0x2203,0x220d,0x10d8,0x10e6,0x2217,0x2221,0x222b,0x2235,1,1,0x10f4,0x1102,0x223f,0x2249, -0x2253,0x225d,1,1,0x1110,0x1122,0x2267,0x2271,0x227b,0x2285,0x228f,0x2299,1,0x1134,1,0x22a3, -1,0x22ad,1,0x22b7,0x1146,0x115c,0x1174,0x1182,0x1190,0x119e,0x11ac,0x11ba,0x11c6,0x11dc,0x11f4,0x1202, -0x1210,0x121e,0x122c,0x123a,0x1246,0x3b8e,0x22bf,0x3b96,0x1250,0x3b9e,0x22c5,0x3ba6,0x22cb,0x3bae,0x22d1,0x3bb6, -0x125a,0x3bbe,1,1,0x22d8,0x22e2,0x22f1,0x2301,0x2311,0x2321,0x2331,0x2341,0x234c,0x2356,0x2365,0x2375, -0x2385,0x2395,0x23a5,0x23b5,0x23c0,0x23ca,0x23d9,0x23e9,0x23f9,0x2409,0x2419,0x2429,0x2434,0x243e,0x244d,0x245d, -0x246d,0x247d,0x248d,0x249d,0x24a8,0x24b2,0x24c1,0x24d1,0x24e1,0x24f1,0x2501,0x2511,0x251c,0x2526,0x2535,0x2545, -0x2555,0x2565,0x2575,0x2585,0x258f,0x2595,0x259d,0x25a4,0x25ad,1,0x1264,0x25b7,0x25bf,0x25c5,0x25cb,0x3bc6, -0x25d0,1,0x2aa2,0x8f0,1,0x25d7,0x25df,0x25e6,0x25ef,1,0x126e,0x25f9,0x2601,0x3bce,0x2607,0x3bd6, -0x260c,0x2613,0x2619,0x261f,0x2625,0x262b,0x2633,0x3be0,1,1,0x263b,0x2643,0x264b,0x2651,0x2657,0x3bea, -1,0x265d,0x2663,0x2669,0x266f,0x2675,0x267d,0x3bf4,0x2685,0x268b,0x2691,0x2699,0x26a1,0x26a7,0x26ad,0x3bfe, -0x26b3,0x26b9,0x3c06,0x2aa7,1,1,0x26c1,0x26c8,0x26d1,1,0x1278,0x26db,0x26e3,0x3c0e,0x26e9,0x3c16, -0x26ee,0x2aab,0x8fc,1,0xfa09,0xfa09,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,0xffcc,0xffcc,0xfe02,0xfe02,0xffcc,0xffcc,0xffcc,0xffcc,0xfe02,0xfe02,0xfe02,0xffcc, -0xffcc,1,1,1,1,0xffcc,1,1,1,0xfe02,0xfe02,0xffcc,0xffb8,0xffcc,0xfe02,0xfe02, -0xffb8,0xffb8,0xffb8,0xffb8,0xffcc,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,0x2aae,1,1,1,0x2ab2,0x3c1e,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -0x908,1,0x90c,1,0x910,1,1,1,1,1,0x26f5,0x26fb,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,0x2701,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,0x2707,0x270d,0x2713, -0x914,1,0x918,1,0x91c,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,0x920,0x2719,1,1,1,0x924,0x271f,1,0x928,0x2725,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,0x92c,0x272b,0x930,0x2731,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,0x934,1,1,1, -1,0x2737,1,0x938,0x273d,0x93c,1,0x2743,0x940,0x2749,1,1,1,0x944,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -0x274f,0x948,0x2755,1,0x94c,0x950,1,1,1,1,1,1,1,0x275b,0x2761,0x2767, -0x276d,0x2773,0x954,0x958,0x2779,0x277f,0x95c,0x960,0x2785,0x278b,0x964,0x968,0x96c,0x970,1,1, -0x2791,0x2797,0x974,0x978,0x279d,0x27a3,0x97c,0x980,0x27a9,0x27af,1,1,1,1,1,1, -1,0x984,0x988,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,0x98c,1,1,1,1,1,0x990,0x994,1,0x998,0x27b5,0x27bb,0x27c1,0x27c7, -1,1,0x99c,0x9a0,0x9a4,0x9a8,1,1,1,1,1,1,1,1,1,1, -0x27cd,0x27d3,0x27d9,0x27df,1,1,1,1,1,1,0x27e5,0x27eb,0x27f1,0x27f7,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,0x2ab7,0x2abb,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -0x2abf,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,0xffcc,0xffcc,0xffcc,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,0xfe12,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc, -0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc, -0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,1,1,1,1,1,1,1,1, -1,1,0xffb4,0xffc8,0xffd0,0xffbc,0xffc0,0xffc0,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,0x9ac,1,1,1,1,0x9b0, -0x27fd,0x9b4,0x2803,0x9b8,0x2809,0x9bc,0x280f,0x9c0,0x2815,0x9c4,0x281b,0x9c8,0x2821,0x9cc,0x2827,0x9d0, -0x282d,0x9d4,0x2833,0x9d8,0x2839,0x9dc,0x283f,1,0x9e0,0x2845,0x9e4,0x284b,0x9e8,0x2851,1,1, -1,1,1,0x9ec,0x2857,0x285d,0x9f4,0x2863,0x2869,0x9fc,0x286f,0x2875,0xa04,0x287b,0x2881,0xa0c, -0x2887,0x288d,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,0x2893,1,1,1,1,0xfc10,0xfc10,1, -1,0xa14,0x2899,1,1,1,1,1,1,1,0xa18,1,1,1,1,0xa1c, -0x289f,0xa20,0x28a5,0xa24,0x28ab,0xa28,0x28b1,0xa2c,0x28b7,0xa30,0x28bd,0xa34,0x28c3,0xa38,0x28c9,0xa3c, -0x28cf,0xa40,0x28d5,0xa44,0x28db,0xa48,0x28e1,1,0xa4c,0x28e7,0xa50,0x28ed,0xa54,0x28f3,1,1, -1,1,1,0xa58,0x28f9,0x28ff,0xa60,0x2905,0x290b,0xa68,0x2911,0x2917,0xa70,0x291d,0x2923,0xa78, -0x2929,0x292f,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,0xa80,0xa84,0xa88,0xa8c,1,0x2935,1,1,0x293b,0x2941,0x2947,0x294d,1, -1,0xa90,0x2953,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,0xffcc,1,1,1,1,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc, -0xffcc,0xffcc,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,0xffcc,0xffcc,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,0xffcc,0xffcc,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,0xfe12,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -0xfe12,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,0xffcc,0xffcc,0xffcc,0xffcc, -0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0xffb8, -0xffb8,0xffb8,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,0xfe12,1,1,1,1,1,1,1,1, -1,1,1,1,0xfe12,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,0x1891,0x1897,1, +1,1,1,1,1,1,1,1,1,1,1,1,0x189d,0x18a3,0x18a9,0x18af, +1,1,0x18b5,0x18bb,0x834,0x838,0x18c1,0x18c7,0x18cd,0x18d3,0x18d9,0x18df,1,1,0x18e5,0x18eb, +0x18f1,0x18f7,0x18fd,0x1903,0x83c,0x840,0x1909,0x190f,0x1915,0x191b,0x1921,0x1927,0x192d,0x1933,0x1939,0x193f, +0x1945,0x194b,1,1,0x1951,0x1957,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,0xffb8,0xffcc,0xffcc,0xffcc,0xffcc,0xffb8,0xffcc, +0xffcc,0xffcc,0xffbc,0xffb8,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffb8,0xffb8,0xffb8,0xffb8,0xffb8,0xffb8, +0xffcc,0xffcc,0xffb8,0xffcc,0xffcc,0xffbc,0xffc8,0xffcc,0xfe14,0xfe16,0xfe18,0xfe1a,0xfe1c,0xfe1e,0xfe20,0xfe22, +0xfe24,0xfe26,0xfe26,0xfe28,0xfe2a,0xfe2c,1,0xfe2e,1,0xfe30,0xfe32,1,0xffcc,0xffb8,1,0xfe24, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc, +0xfe3c,0xfe3e,0xfe40,1,1,1,1,1,1,1,0x195c,0x1962,0x1969,0x196f,0x1975,0x844, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,0x850,1,0x854,0xfe36,0xfe38,0xfe3a,0xfe3c,0xfe3e, +0xfe40,0xfe42,0xfe44,0xfdcc,0xfdcc,0xfdb8,0xffb8,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffb8,0xffcc,0xffcc,0xffb8, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +0xfe46,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +0x197b,0x858,0x1981,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,0x85c,0x1987,1,0x860,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,1,1,0xffcc, +0xffcc,0xffcc,0xffcc,0xffb8,0xffcc,1,1,0xffcc,0xffcc,1,0xffb8,0xffcc,0xffcc,0xffb8,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,0xffcc,1,0xffcc,0xffcc,0xffb8,1,1,0xffcc,0xffcc,1,1,1, -1,1,0xffcc,0xffcc,1,0xffcc,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,0xfe12,1,1,1,1,1,1,1,1,1, -0x2ac5,0x2ac9,0x2acd,0x2ad1,0x2ad5,0x2ad9,0x2add,0x2ae1,0x2ae1,0x2ae5,0x2ae9,0x2aed,0x2af1,0x2af5,0x2af9,0x2afd, -0x2b01,0x2b05,0x2b09,0x2b0d,0x2b11,0x2b15,0x2b19,0x2b1d,0x2b21,0x2b25,0x2b29,0x2b2d,0x2b31,0x2b35,0x2b39,0x2b3d, -0x2b41,0x2b45,0x2b49,0x2b4d,0x2b51,0x2b55,0x2b59,0x2b5d,0x2b61,0x2b65,0x2b69,0x2b6d,0x2b71,0x2b75,0x2b79,0x2b7d, -0x2b81,0x2b85,0x2b89,0x2b8d,0x2b91,0x2b95,0x2b99,0x2b9d,0x2ba1,0x2ba5,0x2ba9,0x2bad,0x2bb1,0x2bb5,0x2bb9,0x2bbd, -0x2bc1,0x2bc5,0x2bc9,0x2bcd,0x2bd1,0x2bd5,0x2bd9,0x2bdd,0x2be1,0x2be5,0x2be9,0x2bed,0x2bf1,0x2bf5,0x2bf9,0x2bfd, -0x2c01,0x2c05,0x2c09,0x2c0d,0x2c11,0x2c15,0x2c19,0x2c1d,0x2c21,0x2c25,0x2c29,0x2c2d,0x2b11,0x2c31,0x2c35,0x2c39, -0x2c3d,0x2c41,0x2c45,0x2c49,0x2c4d,0x2c51,0x2c55,0x2c59,0x2c5d,0x2c61,0x2c65,0x2c69,0x2c6d,0x2c71,0x2c75,0x2c79, -0x2c7d,0x2c81,0x2c85,0x2c89,0x2c8d,0x2c91,0x2c95,0x2c99,0x2c9d,0x2ca1,0x2ca5,0x2ca9,0x2cad,0x2cb1,0x2cb5,0x2cb9, -0x2cbd,0x2cc1,0x2cc5,0x2cc9,0x2ccd,0x2cd1,0x2cd5,0x2cd9,0x2cdd,0x2ce1,0x2ce5,0x2ce9,0x2ced,0x2cf1,0x2cf5,0x2cf9, -0x2cfd,0x2d01,0x2d05,0x2d09,0x2d0d,0x2d11,0x2d15,0x2d19,0x2d1d,0x2d21,0x2d25,0x2d29,0x2d2d,0x2d31,0x2d35,0x2d39, -0x2d3d,0x2c79,0x2d41,0x2d45,0x2d49,0x2d4d,0x2d51,0x2d55,0x2d59,0x2d5d,0x2c39,0x2d61,0x2d65,0x2d69,0x2d6d,0x2d71, -0x2d75,0x2d79,0x2d7d,0x2d81,0x2d85,0x2d89,0x2d8d,0x2d91,0x2d95,0x2d99,0x2d9d,0x2da1,0x2da5,0x2da9,0x2dad,0x2b11, -0x2db1,0x2db5,0x2db9,0x2dbd,0x2dc1,0x2dc5,0x2dc9,0x2dcd,0x2dd1,0x2dd5,0x2dd9,0x2ddd,0x2de1,0x2de5,0x2de9,0x2ded, -0x2df1,0x2df5,0x2df9,0x2dfd,0x2e01,0x2e05,0x2e09,0x2e0d,0x2e11,0x2e15,0x2e19,0x2c41,0x2e1d,0x2e21,0x2e25,0x2e29, -0x2e2d,0x2e31,0x2e35,0x2e39,0x2e3d,0x2e41,0x2e45,0x2e49,0x2e4d,0x2e51,0x2e55,0x2e59,0x2e5d,0x2e61,0x2e65,0x2e69, -0x2e6d,0x2e71,0x2e75,0x2e79,0x2e7d,0x2e81,0x2e85,0x2e89,0x2e8d,0x2e91,0x2e95,0x2e99,0x2e9d,0x2ea1,0x2ea5,0x2ea9, -0x2ead,0x2eb1,0x2eb5,0x2eb9,0x2ebd,0x2ec1,0x2ec5,0x2ec9,0x2ecd,0x2ed1,0x2ed5,0x2ed9,0x2edd,0x2ee1,1,1, -0x2ee5,1,0x2ee9,1,1,0x2eed,0x2ef1,0x2ef5,0x2ef9,0x2efd,0x2f01,0x2f05,0x2f09,0x2f0d,0x2f11,1, -0x2f15,1,0x2f19,1,1,0x2f1d,0x2f21,1,1,1,0x2f25,0x2f29,0x2f2d,0x2f31,0x2f35,0x2f39, -0x2f3d,0x2f41,0x2f45,0x2f49,0x2f4d,0x2f51,0x2f55,0x2f59,0x2f5d,0x2f61,0x2f65,0x2f69,0x2f6d,0x2f71,0x2f75,0x2f79, -0x2f7d,0x2f81,0x2f85,0x2f89,0x2f8d,0x2f91,0x2f95,0x2f99,0x2f9d,0x2fa1,0x2fa5,0x2fa9,0x2fad,0x2fb1,0x2fb5,0x2fb9, -0x2fbd,0x2fc1,0x2fc5,0x2fc9,0x2fcd,0x2fd1,0x2fd5,0x2d15,0x2fd9,0x2fdd,0x2fe1,0x2fe5,0x2fe9,0x2fed,0x2fed,0x2ff1, -0x2ff5,0x2ff9,0x2ffd,0x3001,0x3005,0x3009,0x300d,0x2f1d,0x3011,0x3015,0x3019,0x301d,0x3021,0x3027,1,1, -0x302b,0x302f,0x3033,0x3037,0x303b,0x303f,0x3043,0x3047,0x2f55,0x304b,0x304f,0x3053,0x2ee5,0x3057,0x305b,0x305f, -0x3063,0x3067,0x306b,0x306f,0x3073,0x3077,0x307b,0x307f,0x3083,0x2f79,0x3087,0x2f7d,0x308b,0x308f,0x3093,0x3097, -0x309b,0x2ee9,0x2b65,0x309f,0x30a3,0x30a7,0x2c7d,0x2dd9,0x30ab,0x30af,0x2f99,0x30b3,0x2f9d,0x30b7,0x30bb,0x30bf, -0x2ef1,0x30c3,0x30c7,0x30cb,0x30cf,0x30d3,0x2ef5,0x30d7,0x30db,0x30df,0x30e3,0x30e7,0x30eb,0x2fd5,0x30ef,0x30f3, -0x2d15,0x30f7,0x2fe5,0x30fb,0x30ff,0x3103,0x3107,0x310b,0x2ff9,0x310f,0x2f19,0x3113,0x2ffd,0x2c31,0x3117,0x3001, -0x311b,0x3009,0x311f,0x3123,0x3127,0x312b,0x312f,0x3011,0x2f09,0x3133,0x3015,0x3137,0x3019,0x313b,0x2ae1,0x313f, -0x3145,0x314b,0x3151,0x3155,0x3159,0x315d,0x3163,0x3169,0x316f,0x3173,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,0x3176,0xfe34,0x317c,1,1,1,1, -1,1,1,1,1,1,0x3182,0x3188,0x3190,0x319a,0x31a2,0x31a8,0x31ae,0x31b4,0x31ba,0x31c0, -0x31c6,0x31cc,0x31d2,1,0x31d8,0x31de,0x31e4,0x31ea,0x31f0,1,0x31f6,1,0x31fc,0x3202,1,0x3208, -0x320e,1,0x3214,0x321a,0x3220,0x3226,0x322c,0x3232,0x3238,0x323e,0x3244,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,0xffcc,0xffcc,0xffcc,0xffcc, -0xffcc,0xffcc,0xffcc,0xffb8,0xffb8,0xffb8,0xffb8,0xffb8,0xffb8,0xffb8,0xffcc,0xffcc,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,0xffb8,1,1,0xffb8,1,1,1, +1,0xfe48,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +0xffcc,0xffb8,0xffcc,0xffcc,0xffb8,0xffcc,0xffcc,0xffb8,0xffb8,0xffb8,0xffcc,0xffb8,0xffb8,0xffcc,0xffb8,0xffcc, +0xffcc,0xffcc,0xffb8,0xffcc,0xffb8,0xffcc,0xffb8,0xffcc,0xffb8,0xffcc,0xffcc,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffb8,0xffcc,1,1,1,1, +1,1,1,1,1,0xffb8,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,0xffcc,0xffcc, -0xffcc,0xffcc,0xffcc,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,0xffb8,1,0xffcc,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,0xffcc,0xfe02,0xffb8,1, -1,1,1,0xfe12,1,1,1,1,1,0xffcc,0xffb8,1,1,1,1,1, +0xffcc,0xffcc,1,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,1,0xffcc,0xffcc,0xffcc,1,0xffcc,0xffcc,0xffcc, +0xffcc,0xffcc,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,0xffb8,0xffb8,0xffb8, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,0xa94,0x2959,0xa9a,0x2963,1,1,1,1,1,1,1, -1,0xaa0,1,1,1,1,1,0x296d,1,1,1,1,1,1,1,1, -1,1,1,1,1,0xfe12,0xfc0e,1,1,1,1,1,0xffcc,0xffcc,0xffcc,1, +1,1,1,0xffb8,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc, +0xffcc,0xffcc,1,0xffb8,0xffcc,0xffcc,0xffb8,0xffcc,0xffcc,0xffb8,0xffcc,0xffcc,0xffcc,0xffb8,0xffb8,0xffb8, +0xfe36,0xfe38,0xfe3a,0xffcc,0xffcc,0xffcc,0xffb8,0xffcc,0xffcc,0xffb8,0xffb8,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc, +1,1,1,1,1,1,1,1,0x864,0x198d,1,1,1,1,1,1, +0x868,0x1993,1,0x86c,0x1999,1,1,1,1,1,1,1,0xfc0e,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,0xfe12,1,1, +1,0xffcc,0xffb8,0xffcc,0xffcc,1,1,1,0x29dc,0x29e2,0x29e8,0x29ee,0x29f4,0x29fa,0x2a00,0x2a06, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0xfc00, -1,1,1,1,1,1,0x2977,0x2981,1,0xaa6,0xaac,0xfe12,0xfe12,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,0xfe0e,1,0xfc00,1, +1,1,1,1,1,1,1,0x870,1,1,1,0x199f,0x19a5,0xfe12,1,1, +1,1,1,1,1,1,1,0xfc00,1,1,1,1,0x2a0c,0x2a12,1,0x2a18, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,0xffcc,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,0x2a1e,1,1,0x2a24,1,1,1,1,1,0xfe0e,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,0xfe12,1,1, +1,1,1,1,1,1,1,1,1,0x2a2a,0x2a30,0x2a36,1,1,0x2a3c,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,0xfe0e,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,0xfe12,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,0x878,0x19ab,1,1,0x19b1,0x19b7,0xfe12,1,1,1,1,1,1, +1,1,0xfc00,0xfc00,1,1,1,1,0x2a42,0x2a48,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,0x884,1, +0x19bd,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,0xfc00,1,1,1,1,1,1,1,0x888,0x890,1,1,0x19c3,0x19c9, +0x19cf,0xfe12,1,1,1,1,1,1,1,1,1,0xfc00,1,1,1,1, +1,1,1,1,1,1,0x894,1,0x19d5,1,1,1,1,0xfe12,1,1, +1,1,1,1,1,0xfea8,0xfcb6,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,0xfe0e,1,1,0x898,0x19db,1,0xfc00,1,1,1,0x89c,0x19e1, +0x19e7,1,0xdc4,0x19ef,1,0xfe12,1,1,1,1,1,1,1,0xfc00,0xfc00,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,0xfe12,0xfe12,1,0xfc00,1, +1,1,1,1,1,1,0x8a8,0x8b0,1,1,0x19f7,0x19fd,0x1a03,0xfe12,1,1, +1,1,1,1,1,1,1,0xfc00,1,1,1,1,1,1,1,1, +1,1,0xfc12,1,1,1,1,0xfc00,1,1,1,1,1,1,1,1, +1,0x8b4,0x1a09,1,0xdce,0x1a11,0x1a19,0xfc00,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +0xfece,0xfece,0xfe12,1,1,1,1,1,1,1,1,1,0xfed6,0xfed6,0xfed6,0xfed6, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,0xfeec,0xfeec,1,1,1,1,1,1, +1,1,1,1,0xfef4,0xfef4,0xfef4,0xfef4,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +0xffb8,0xffb8,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,0xffb8,1,0xffb8,1,0xffb0,1,1, +1,1,1,1,1,1,1,0x2a4f,1,1,1,1,1,1,1,1, +1,0x2a55,1,1,1,1,0x2a5b,1,1,1,1,0x2a61,1,1,1,1, +0x2a67,1,1,1,1,1,1,1,1,1,1,1,1,0x2a6d,1,1, +1,1,1,1,1,0xff02,0xff04,0x3c40,0xff08,0x3c48,0x2a72,1,0x2a78,1,0xff04,0xff04, +0xff04,0xff04,1,1,0xff04,0x3c50,0xffcc,0xffcc,0xfe12,1,0xffcc,0xffcc,1,1,1,1, +1,1,1,1,1,1,1,0x2a7f,1,1,1,1,1,1,1,1, +1,0x2a85,1,1,1,1,0x2a8b,1,1,1,1,0x2a91,1,1,1,1, +0x2a97,1,1,1,1,1,1,1,1,1,1,1,1,0x2a9d,1,1, +1,1,1,1,1,1,0xffb8,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,0x8c0,0x1a1f,1,1,1,1,1,1,1,0xfc00,1,1,1,1,1, +1,1,1,0xfe0e,1,0xfe12,0xfe12,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,0xffb8,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,0xffcc,0xffcc,0xffcc,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,0xfe12,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,0xfe12,1,1,1,1,1,1,1,1,1,1,0xffcc,1,1, +1,1,1,1,1,1,1,1,1,0xffc8,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,0xffbc,0xffcc,0xffb8,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0xffcc, +0xffb8,1,1,1,1,1,1,1,0xfe12,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,0xffcc,0xffcc,0xffcc, +0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,1,1,0xffb8,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffb8,0xffb8,0xffb8, +0xffb8,0xffb8,0xffb8,0xffcc,0xffcc,0xffb8,1,1,1,1,1,1,1,0x8c4,0x1a25,0x8c8, +0x1a2b,0x8cc,0x1a31,0x8d0,0x1a37,0x8d4,0x1a3d,1,1,0x8d8,0x1a43,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +0xfe0e,0xfc00,1,1,1,1,0x8dc,0x1a49,0x8e0,0x1a4f,0x8e4,0x8e8,0x1a55,0x1a5b,0x8ec,0x1a61, +0xfe12,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0xffcc, +0xffb8,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,0xfe12,0xfe12,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,0xfe0e,1, +1,1,1,1,1,1,1,1,1,1,0xfe12,0xfe12,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,0xfe0e,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,0xffcc,0xffcc,0xffcc,1,0xfe02,0xffb8,0xffb8,0xffb8,0xffb8,0xffb8,0xffcc,0xffcc, +0xffb8,0xffb8,0xffb8,0xffb8,0xffcc,1,0xfe02,0xfe02,0xfe02,0xfe02,0xfe02,0xfe02,0xfe02,1,1,1, +1,0xffb8,1,1,1,1,1,1,0xffcc,1,1,1,0xffcc,0xffcc,1,1, +1,1,1,1,0xffcc,0xffcc,0xffb8,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffb8,0xffcc, +0xffcc,0xffd4,0xffac,0xffb8,0xff94,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc, +0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffd0,0xffc8, +0xffc8,0xffb8,1,0xffcc,0xffd2,0xffb8,0xffcc,0xffb8,0x1a66,0x1a6c,0x1a72,0x1a78,0x1a7f,0x1a85,0x1a8b,0x1a91, +0x1a99,0x1aa3,0x1aaa,0x1ab0,0x1ab6,0x1abc,0x1ac2,0x1ac8,0x1acf,0x1ad5,0x1ada,0x1ae0,0x1ae8,0x1af2,0x1afc,0x1b06, +0x1b0e,0x1b14,0x1b1a,0x1b20,0x1b29,0x1b33,0x1b3b,0x1b41,0x1b46,0x1b4c,0x1b52,0x1b58,0x1b5e,0x1b64,0x1b6a,0x1b70, +0x1b77,0x1b7d,0x1b82,0x1b88,0x1b8e,0x1b94,0x1b9c,0x1ba6,0x1bae,0x1bb4,0x1bba,0x1bc0,0x1bc6,0x1bcc,0xdd8,0xde2, +0x1bd4,0x1bde,0x1be6,0x1bec,0x1bf2,0x1bf8,0x1bfe,0x1c04,0x1c0a,0x1c10,0x1c17,0x1c1d,0x1c22,0x1c28,0x1c2e,0x1c34, +0x1c3a,0x1c40,0x1c46,0x1c4c,0x1c54,0x1c5e,0x1c68,0x1c72,0x1c7c,0x1c86,0x1c90,0x1c9a,0x1ca3,0x1ca9,0x1caf,0x1cb5, +0x1cba,0x1cc0,0xdec,0xdf6,0x1cc8,0x1cd2,0x1cda,0x1ce0,0x1ce6,0x1cec,0xe00,0xe0a,0x1cf4,0x1cfe,0x1d08,0x1d12, +0x1d1c,0x1d26,0x1d2e,0x1d34,0x1d3a,0x1d40,0x1d46,0x1d4c,0x1d52,0x1d58,0x1d5e,0x1d64,0x1d6a,0x1d70,0x1d76,0x1d7c, +0x1d84,0x1d8e,0x1d98,0x1da2,0x1daa,0x1db0,0x1db7,0x1dbd,0x1dc2,0x1dc8,0x1dce,0x1dd4,0x1dda,0x1de0,0x1de6,0x1dec, +0x1df3,0x1df9,0x1dff,0x1e05,0x1e0b,0x1e11,0x1e16,0x1e1c,0x1e22,0x1e28,0x1e2f,0x1e35,0x1e3b,0x1e41,0x1e46,0x1e4c, +0x1e52,0x1e58,1,0x1e5f,1,1,1,1,0xe14,0xe22,0x1e64,0x1e6a,0x1e72,0x1e7c,0x1e86,0x1e90, +0x1e9a,0x1ea4,0x1eae,0x1eb8,0x1ec2,0x1ecc,0x1ed6,0x1ee0,0x1eea,0x1ef4,0x1efe,0x1f08,0x1f12,0x1f1c,0x1f26,0x1f30, +0xe30,0xe3a,0x1f38,0x1f3e,0x1f44,0x1f4a,0x1f52,0x1f5c,0x1f66,0x1f70,0x1f7a,0x1f84,0x1f8e,0x1f98,0x1fa2,0x1fac, +0x1fb4,0x1fba,0x1fc0,0x1fc6,0xe44,0xe4e,0x1fcc,0x1fd2,0x1fda,0x1fe4,0x1fee,0x1ff8,0x2002,0x200c,0x2016,0x2020, +0x202a,0x2034,0x203e,0x2048,0x2052,0x205c,0x2066,0x2070,0x207a,0x2084,0x208e,0x2098,0x20a0,0x20a6,0x20ac,0x20b2, +0x20ba,0x20c4,0x20ce,0x20d8,0x20e2,0x20ec,0x20f6,0x2100,0x210a,0x2114,0x211c,0x2122,0x2129,0x212f,0x2134,0x213a, +0x2140,0x2146,1,1,1,1,1,1,0xe58,0xe6e,0xe86,0xe94,0xea2,0xeb0,0xebe,0xecc, +0xed8,0xeee,0xf06,0xf14,0xf22,0xf30,0xf3e,0xf4c,0xf58,0xf66,0x214f,0x2159,0x2163,0x216d,1,1, +0xf74,0xf82,0x2177,0x2181,0x218b,0x2195,1,1,0xf90,0xfa6,0xfbe,0xfcc,0xfda,0xfe8,0xff6,0x1004, +0x1010,0x1026,0x103e,0x104c,0x105a,0x1068,0x1076,0x1084,0x1090,0x10a2,0x219f,0x21a9,0x21b3,0x21bd,0x21c7,0x21d1, +0x10b4,0x10c6,0x21db,0x21e5,0x21ef,0x21f9,0x2203,0x220d,0x10d8,0x10e6,0x2217,0x2221,0x222b,0x2235,1,1, +0x10f4,0x1102,0x223f,0x2249,0x2253,0x225d,1,1,0x1110,0x1122,0x2267,0x2271,0x227b,0x2285,0x228f,0x2299, +1,0x1134,1,0x22a3,1,0x22ad,1,0x22b7,0x1146,0x115c,0x1174,0x1182,0x1190,0x119e,0x11ac,0x11ba, +0x11c6,0x11dc,0x11f4,0x1202,0x1210,0x121e,0x122c,0x123a,0x1246,0x3b8e,0x22bf,0x3b96,0x1250,0x3b9e,0x22c5,0x3ba6, +0x22cb,0x3bae,0x22d1,0x3bb6,0x125a,0x3bbe,1,1,0x22d8,0x22e2,0x22f1,0x2301,0x2311,0x2321,0x2331,0x2341, +0x234c,0x2356,0x2365,0x2375,0x2385,0x2395,0x23a5,0x23b5,0x23c0,0x23ca,0x23d9,0x23e9,0x23f9,0x2409,0x2419,0x2429, +0x2434,0x243e,0x244d,0x245d,0x246d,0x247d,0x248d,0x249d,0x24a8,0x24b2,0x24c1,0x24d1,0x24e1,0x24f1,0x2501,0x2511, +0x251c,0x2526,0x2535,0x2545,0x2555,0x2565,0x2575,0x2585,0x258f,0x2595,0x259d,0x25a4,0x25ad,1,0x1264,0x25b7, +0x25bf,0x25c5,0x25cb,0x3bc6,0x25d0,1,0x2aa2,0x8f0,1,0x25d7,0x25df,0x25e6,0x25ef,1,0x126e,0x25f9, +0x2601,0x3bce,0x2607,0x3bd6,0x260c,0x2613,0x2619,0x261f,0x2625,0x262b,0x2633,0x3be0,1,1,0x263b,0x2643, +0x264b,0x2651,0x2657,0x3bea,1,0x265d,0x2663,0x2669,0x266f,0x2675,0x267d,0x3bf4,0x2685,0x268b,0x2691,0x2699, +0x26a1,0x26a7,0x26ad,0x3bfe,0x26b3,0x26b9,0x3c06,0x2aa7,1,1,0x26c1,0x26c8,0x26d1,1,0x1278,0x26db, +0x26e3,0x3c0e,0x26e9,0x3c16,0x26ee,0x2aab,0x8fc,1,0xfa09,0xfa09,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,0xffcc,0xffcc,0xfe02,0xfe02,0xffcc,0xffcc,0xffcc,0xffcc, +0xfe02,0xfe02,0xfe02,0xffcc,0xffcc,1,1,1,1,0xffcc,1,1,1,0xfe02,0xfe02,0xffcc, +0xffb8,0xffcc,0xfe02,0xfe02,0xffb8,0xffb8,0xffb8,0xffb8,0xffcc,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,0x2aae,1,1,1,0x2ab2,0x3c1e, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,0x908,1,0x90c,1,0x910,1,1,1,1,1,0x26f5,0x26fb, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,0x2701,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,0x2707,0x270d,0x2713,0x914,1,0x918,1,0x91c,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,0x920,0x2719,1,1,1,0x924,0x271f,1,0x928, +0x2725,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,0x92c,0x272b,0x930,0x2731,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +0x934,1,1,1,1,0x2737,1,0x938,0x273d,0x93c,1,0x2743,0x940,0x2749,1,1, +1,0x944,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,0x274f,0x948,0x2755,1,0x94c,0x950,1,1,1,1,1,1, +1,0x275b,0x2761,0x2767,0x276d,0x2773,0x954,0x958,0x2779,0x277f,0x95c,0x960,0x2785,0x278b,0x964,0x968, +0x96c,0x970,1,1,0x2791,0x2797,0x974,0x978,0x279d,0x27a3,0x97c,0x980,0x27a9,0x27af,1,1, +1,1,1,1,1,0x984,0x988,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,0x98c,1,1,1,1,1,0x990,0x994,1,0x998, +0x27b5,0x27bb,0x27c1,0x27c7,1,1,0x99c,0x9a0,0x9a4,0x9a8,1,1,1,1,1,1, +1,1,1,1,0x27cd,0x27d3,0x27d9,0x27df,1,1,1,1,1,1,0x27e5,0x27eb, +0x27f1,0x27f7,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,0x2ab7,0x2abb,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,0x2abf,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,0xffcc,0xffcc,0xffcc,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,0xfe12,0xffcc,0xffcc,0xffcc,0xffcc, +0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc, +0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,1,1,1,1, +1,1,1,1,1,1,0xffb4,0xffc8,0xffd0,0xffbc,0xffc0,0xffc0,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,0x9ac,1, +1,1,1,0x9b0,0x27fd,0x9b4,0x2803,0x9b8,0x2809,0x9bc,0x280f,0x9c0,0x2815,0x9c4,0x281b,0x9c8, +0x2821,0x9cc,0x2827,0x9d0,0x282d,0x9d4,0x2833,0x9d8,0x2839,0x9dc,0x283f,1,0x9e0,0x2845,0x9e4,0x284b, +0x9e8,0x2851,1,1,1,1,1,0x9ec,0x2857,0x285d,0x9f4,0x2863,0x2869,0x9fc,0x286f,0x2875, +0xa04,0x287b,0x2881,0xa0c,0x2887,0x288d,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,0x2893,1,1,1, +1,0xfc10,0xfc10,1,1,0xa14,0x2899,1,1,1,1,1,1,1,0xa18,1, +1,1,1,0xa1c,0x289f,0xa20,0x28a5,0xa24,0x28ab,0xa28,0x28b1,0xa2c,0x28b7,0xa30,0x28bd,0xa34, +0x28c3,0xa38,0x28c9,0xa3c,0x28cf,0xa40,0x28d5,0xa44,0x28db,0xa48,0x28e1,1,0xa4c,0x28e7,0xa50,0x28ed, +0xa54,0x28f3,1,1,1,1,1,0xa58,0x28f9,0x28ff,0xa60,0x2905,0x290b,0xa68,0x2911,0x2917, +0xa70,0x291d,0x2923,0xa78,0x2929,0x292f,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,0xa80,0xa84,0xa88,0xa8c,1,0x2935,1,1,0x293b, +0x2941,0x2947,0x294d,1,1,0xa90,0x2953,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,0xffcc,1,1,1,1,0xffcc,0xffcc,0xffcc,0xffcc, +0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,0xffcc,0xffcc,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,0xffcc,0xffcc,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,0xfe12,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,0xfe12,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc, +0xffcc,0xffcc,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,0xffb8,0xffb8,0xffb8,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,0xfe12,1,1,1,1, 1,1,1,1,1,1,1,1,0xfe12,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,0xffcc,1,0xffcc,0xffcc,0xffb8,1,1,0xffcc, +0xffcc,1,1,1,1,1,0xffcc,0xffcc,1,0xffcc,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,0xfe12,1,1,1,1,1, +1,1,1,1,0x2ac5,0x2ac9,0x2acd,0x2ad1,0x2ad5,0x2ad9,0x2add,0x2ae1,0x2ae1,0x2ae5,0x2ae9,0x2aed, +0x2af1,0x2af5,0x2af9,0x2afd,0x2b01,0x2b05,0x2b09,0x2b0d,0x2b11,0x2b15,0x2b19,0x2b1d,0x2b21,0x2b25,0x2b29,0x2b2d, +0x2b31,0x2b35,0x2b39,0x2b3d,0x2b41,0x2b45,0x2b49,0x2b4d,0x2b51,0x2b55,0x2b59,0x2b5d,0x2b61,0x2b65,0x2b69,0x2b6d, +0x2b71,0x2b75,0x2b79,0x2b7d,0x2b81,0x2b85,0x2b89,0x2b8d,0x2b91,0x2b95,0x2b99,0x2b9d,0x2ba1,0x2ba5,0x2ba9,0x2bad, +0x2bb1,0x2bb5,0x2bb9,0x2bbd,0x2bc1,0x2bc5,0x2bc9,0x2bcd,0x2bd1,0x2bd5,0x2bd9,0x2bdd,0x2be1,0x2be5,0x2be9,0x2bed, +0x2bf1,0x2bf5,0x2bf9,0x2bfd,0x2c01,0x2c05,0x2c09,0x2c0d,0x2c11,0x2c15,0x2c19,0x2c1d,0x2c21,0x2c25,0x2c29,0x2c2d, +0x2b11,0x2c31,0x2c35,0x2c39,0x2c3d,0x2c41,0x2c45,0x2c49,0x2c4d,0x2c51,0x2c55,0x2c59,0x2c5d,0x2c61,0x2c65,0x2c69, +0x2c6d,0x2c71,0x2c75,0x2c79,0x2c7d,0x2c81,0x2c85,0x2c89,0x2c8d,0x2c91,0x2c95,0x2c99,0x2c9d,0x2ca1,0x2ca5,0x2ca9, +0x2cad,0x2cb1,0x2cb5,0x2cb9,0x2cbd,0x2cc1,0x2cc5,0x2cc9,0x2ccd,0x2cd1,0x2cd5,0x2cd9,0x2cdd,0x2ce1,0x2ce5,0x2ce9, +0x2ced,0x2cf1,0x2cf5,0x2cf9,0x2cfd,0x2d01,0x2d05,0x2d09,0x2d0d,0x2d11,0x2d15,0x2d19,0x2d1d,0x2d21,0x2d25,0x2d29, +0x2d2d,0x2d31,0x2d35,0x2d39,0x2d3d,0x2c79,0x2d41,0x2d45,0x2d49,0x2d4d,0x2d51,0x2d55,0x2d59,0x2d5d,0x2c39,0x2d61, +0x2d65,0x2d69,0x2d6d,0x2d71,0x2d75,0x2d79,0x2d7d,0x2d81,0x2d85,0x2d89,0x2d8d,0x2d91,0x2d95,0x2d99,0x2d9d,0x2da1, +0x2da5,0x2da9,0x2dad,0x2b11,0x2db1,0x2db5,0x2db9,0x2dbd,0x2dc1,0x2dc5,0x2dc9,0x2dcd,0x2dd1,0x2dd5,0x2dd9,0x2ddd, +0x2de1,0x2de5,0x2de9,0x2ded,0x2df1,0x2df5,0x2df9,0x2dfd,0x2e01,0x2e05,0x2e09,0x2e0d,0x2e11,0x2e15,0x2e19,0x2c41, +0x2e1d,0x2e21,0x2e25,0x2e29,0x2e2d,0x2e31,0x2e35,0x2e39,0x2e3d,0x2e41,0x2e45,0x2e49,0x2e4d,0x2e51,0x2e55,0x2e59, +0x2e5d,0x2e61,0x2e65,0x2e69,0x2e6d,0x2e71,0x2e75,0x2e79,0x2e7d,0x2e81,0x2e85,0x2e89,0x2e8d,0x2e91,0x2e95,0x2e99, +0x2e9d,0x2ea1,0x2ea5,0x2ea9,0x2ead,0x2eb1,0x2eb5,0x2eb9,0x2ebd,0x2ec1,0x2ec5,0x2ec9,0x2ecd,0x2ed1,0x2ed5,0x2ed9, +0x2edd,0x2ee1,1,1,0x2ee5,1,0x2ee9,1,1,0x2eed,0x2ef1,0x2ef5,0x2ef9,0x2efd,0x2f01,0x2f05, +0x2f09,0x2f0d,0x2f11,1,0x2f15,1,0x2f19,1,1,0x2f1d,0x2f21,1,1,1,0x2f25,0x2f29, +0x2f2d,0x2f31,0x2f35,0x2f39,0x2f3d,0x2f41,0x2f45,0x2f49,0x2f4d,0x2f51,0x2f55,0x2f59,0x2f5d,0x2f61,0x2f65,0x2f69, +0x2f6d,0x2f71,0x2f75,0x2f79,0x2f7d,0x2f81,0x2f85,0x2f89,0x2f8d,0x2f91,0x2f95,0x2f99,0x2f9d,0x2fa1,0x2fa5,0x2fa9, +0x2fad,0x2fb1,0x2fb5,0x2fb9,0x2fbd,0x2fc1,0x2fc5,0x2fc9,0x2fcd,0x2fd1,0x2fd5,0x2d15,0x2fd9,0x2fdd,0x2fe1,0x2fe5, +0x2fe9,0x2fed,0x2fed,0x2ff1,0x2ff5,0x2ff9,0x2ffd,0x3001,0x3005,0x3009,0x300d,0x2f1d,0x3011,0x3015,0x3019,0x301d, +0x3021,0x3027,1,1,0x302b,0x302f,0x3033,0x3037,0x303b,0x303f,0x3043,0x3047,0x2f55,0x304b,0x304f,0x3053, +0x2ee5,0x3057,0x305b,0x305f,0x3063,0x3067,0x306b,0x306f,0x3073,0x3077,0x307b,0x307f,0x3083,0x2f79,0x3087,0x2f7d, +0x308b,0x308f,0x3093,0x3097,0x309b,0x2ee9,0x2b65,0x309f,0x30a3,0x30a7,0x2c7d,0x2dd9,0x30ab,0x30af,0x2f99,0x30b3, +0x2f9d,0x30b7,0x30bb,0x30bf,0x2ef1,0x30c3,0x30c7,0x30cb,0x30cf,0x30d3,0x2ef5,0x30d7,0x30db,0x30df,0x30e3,0x30e7, +0x30eb,0x2fd5,0x30ef,0x30f3,0x2d15,0x30f7,0x2fe5,0x30fb,0x30ff,0x3103,0x3107,0x310b,0x2ff9,0x310f,0x2f19,0x3113, +0x2ffd,0x2c31,0x3117,0x3001,0x311b,0x3009,0x311f,0x3123,0x3127,0x312b,0x312f,0x3011,0x2f09,0x3133,0x3015,0x3137, +0x3019,0x313b,0x2ae1,0x313f,0x3145,0x314b,0x3151,0x3155,0x3159,0x315d,0x3163,0x3169,0x316f,0x3173,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,0x3176,0xfe34,0x317c, +1,1,1,1,1,1,1,1,1,1,0x3182,0x3188,0x3190,0x319a,0x31a2,0x31a8, +0x31ae,0x31b4,0x31ba,0x31c0,0x31c6,0x31cc,0x31d2,1,0x31d8,0x31de,0x31e4,0x31ea,0x31f0,1,0x31f6,1, +0x31fc,0x3202,1,0x3208,0x320e,1,0x3214,0x321a,0x3220,0x3226,0x322c,0x3232,0x3238,0x323e,0x3244,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffb8,0xffb8,0xffb8,0xffb8,0xffb8,0xffb8,0xffb8,0xffcc,0xffcc, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,0xffb8,1,1, +0xffb8,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,0xffb8,1,0xffcc,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +0xffcc,0xfe02,0xffb8,1,1,1,1,0xfe12,1,1,1,1,1,0xffcc,0xffb8,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,0xffcc,0xffcc,0xffcc,0xffcc,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,0xffb8,0xffb8,0xffcc,0xffcc,0xffcc,0xffb8,0xffcc,0xffb8,0xffb8,0xffb8, +0xffb8,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,0xa94,0x2959,0xa9a, +0x2963,1,1,1,1,1,1,1,1,0xaa0,1,1,1,1,1,0x296d, +1,1,1,1,1,1,1,1,1,1,1,1,1,0xfe12,0xfc0e,1, +1,1,1,1,0xffcc,0xffcc,0xffcc,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,0xfc00,1,1,1,1,1,1,0x2977,0x2981, +1,0xaa6,0xaac,0xfe12,0xfe12,1,1,1,1,1,1,1,1,1,1,1, +0xfe12,1,1,1,1,1,1,1,1,1,0xfe0e,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,0xfe12,0xfe0e,1,1,1,1,1,1,1,1,1,1,0xfe0e,0xfe12,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,0xfe0e,0xfe0e,1,0xfc00,1, +1,1,1,1,1,1,1,0xab2,1,1,1,0x298b,0x2995,0xfe12,1,1, +1,1,1,1,1,1,1,0xfc00,1,1,1,1,1,1,1,1, +1,1,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,1,1,1,0xffcc,0xffcc,0xffcc,0xffcc, +0xffcc,1,1,1,1,1,1,1,1,1,1,1,1,1,0xfe12,1, 1,1,0xfe0e,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,0xffcc,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,0xfc00,1,1,1, +1,1,1,1,1,0xabe,0xfc00,0x299f,0x29a9,0xfc00,0x29b3,1,1,1,0xfe12,0xfe0e, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0xfc00, +1,1,1,1,1,1,1,1,0xad0,0xad6,0x29bd,0x29c7,1,1,1,0xfe12, +0xfe0e,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,0xfe12,0xfe0e,1,1,1,1,1,1,1,1,1,1,1,0xfe12, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,0xfe12,0xfe0e,1,1,1,1,1, -1,1,1,1,1,0xfe0e,0xfe12,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0xab2, -1,1,1,0x298b,0x2995,0xfe12,1,1,1,1,1,1,1,1,1,0xfc00, -1,1,1,1,1,1,1,1,1,1,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc, -0xffcc,1,1,1,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,1,1,1,1,1,1,1, -1,1,1,1,1,1,0xfe12,1,1,1,0xfe0e,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,0xfc00,1,1,1,1,1,1,1,1,0xabe,0xfc00,0x299f, -0x29a9,0xfc00,0x29b3,1,1,1,0xfe12,0xfe0e,1,1,1,1,1,1,1,1, +1,1,1,1,1,0xfe12,1,1,1,1,1,1,1,1,0xfe0e,1, +0xfe12,0xfe12,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0xfe12, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,0xfc00,1,1,1,1,1,1,1,1, -0xad0,0xad6,0x29bd,0x29c7,1,1,1,0xfe12,0xfe0e,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,0xfe12,0xfe0e,1,1,1,1, -1,1,1,1,1,1,1,0xfe12,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,0xfe12,1,1,1,1,1,1,1,1,0xfe0e,1,0xfe12,0xfe12,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,0xfe02,0xfe02,0xfe02,0xfe02,0xfe02,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,0xfe02,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,0x324a,0x3254,0x3268,0x3280,0x3298,0x32b0,0x32c8,0xffb0,0xffb0,0xfe02, -0xfe02,0xfe02,1,1,1,0xffc4,0xffb0,0xffb0,0xffb0,0xffb0,0xffb0,1,1,1,1,1, -1,1,1,0xffb8,0xffb8,0xffb8,0xffb8,0xffb8,0xffb8,0xffb8,0xffb8,1,1,0xffcc,0xffcc,0xffcc, -0xffcc,0xffcc,0xffb8,0xffb8,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,0xffcc,0xffcc,0xffcc,0xffcc,1,1, -1,1,1,1,1,1,1,1,1,1,1,0x32d6,0x32e0,0x32f4,0x330c,0x3324, -0x333c,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,0xffcc,0xffcc,0xffcc,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,1,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc, -0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,1,1,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc, -0xffcc,0xffcc,1,0xffcc,0xffcc,1,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -0xffb8,0xffb8,0xffb8,0xffb8,0xffb8,0xffb8,0xffb8,1,1,1,1,1,1,1,1,1, -0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xfe0e,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,0x334b,0x334f,0x3353,0x3357, -0x335d,0x2f3d,0x3361,0x3365,0x3369,0x336d,0x2f41,0x3371,0x3375,0x3379,0x2f45,0x337f,0x3383,0x3387,0x338b,0x3391, -0x3395,0x3399,0x339d,0x33a3,0x33a7,0x33ab,0x33af,0x302f,0x33b3,0x33b9,0x33bd,0x33c1,0x33c5,0x33c9,0x33cd,0x33d1, -0x33d5,0x3043,0x2f49,0x2f4d,0x3047,0x33d9,0x33dd,0x2c49,0x33e1,0x2f51,0x33e5,0x33e9,0x33ed,0x33f1,0x33f1,0x33f1, -0x33f5,0x33fb,0x33ff,0x3403,0x3407,0x340d,0x3411,0x3415,0x3419,0x341d,0x3421,0x3425,0x3429,0x342d,0x3431,0x3435, -0x3439,0x343d,0x343d,0x304f,0x3441,0x3445,0x3449,0x344d,0x2f59,0x3451,0x3455,0x3459,0x2ead,0x345d,0x3461,0x3465, -0x3469,0x346d,0x3471,0x3475,0x3479,0x347d,0x3483,0x3487,0x348b,0x348f,0x3493,0x3497,0x349b,0x34a1,0x34a7,0x34ab, -0x34af,0x34b3,0x34b7,0x34bb,0x34bf,0x34c3,0x34c7,0x34c7,0x34cb,0x34d1,0x34d5,0x2c39,0x34d9,0x34dd,0x34e3,0x34e7, -0x34eb,0x34ef,0x34f3,0x34f7,0x2f6d,0x34fb,0x34ff,0x3503,0x3509,0x350d,0x3513,0x3517,0x351b,0x351f,0x3523,0x3527, -0x352b,0x352f,0x3533,0x3537,0x353b,0x353f,0x3545,0x3549,0x354d,0x3551,0x2b61,0x3555,0x355b,0x355f,0x355f,0x3565, -0x3569,0x3569,0x356d,0x3571,0x3577,0x357d,0x3581,0x3585,0x3589,0x358d,0x3591,0x3595,0x3599,0x359d,0x35a1,0x2f71, -0x35a5,0x35ab,0x35af,0x35b3,0x307f,0x35b3,0x35b7,0x2f79,0x35bb,0x35bf,0x35c3,0x35c7,0x2f7d,0x2af5,0x35cb,0x35cf, -0x35d3,0x35d7,0x35db,0x35df,0x35e3,0x35e9,0x35ed,0x35f1,0x35f5,0x35f9,0x35fd,0x3603,0x3607,0x360b,0x360f,0x3613, -0x3617,0x361b,0x361f,0x3623,0x2f81,0x3627,0x362b,0x3631,0x3635,0x3639,0x363d,0x2f89,0x3641,0x3645,0x3649,0x364d, -0x3651,0x3655,0x3659,0x365d,0x2b65,0x309f,0x3661,0x3665,0x3669,0x366d,0x3673,0x3677,0x367b,0x367f,0x2f8d,0x3683, -0x3689,0x368d,0x3691,0x3151,0x3695,0x3699,0x369d,0x36a1,0x36a5,0x36ab,0x36af,0x36b3,0x36b7,0x36bd,0x36c1,0x36c5, -0x36c9,0x2c7d,0x36cd,0x36d1,0x36d7,0x36dd,0x36e3,0x36e7,0x36ed,0x36f1,0x36f5,0x36f9,0x36fd,0x2f91,0x2dd9,0x3701, -0x3705,0x3709,0x370d,0x3713,0x3717,0x371b,0x371f,0x30af,0x3723,0x3727,0x372d,0x3731,0x3735,0x373b,0x3741,0x3745, -0x30b3,0x3749,0x374d,0x3751,0x3755,0x3759,0x375d,0x3761,0x3767,0x376b,0x3771,0x3775,0x377b,0x30bb,0x377f,0x3783, -0x3789,0x378d,0x3791,0x3797,0x379d,0x37a1,0x37a5,0x37a9,0x37ad,0x37ad,0x37b1,0x37b5,0x30c3,0x37b9,0x37bd,0x37c1, -0x37c5,0x37c9,0x37cf,0x37d3,0x2c45,0x37d9,0x37df,0x37e3,0x37e9,0x37ef,0x37f5,0x37f9,0x30db,0x37fd,0x3803,0x3809, -0x380f,0x3815,0x3819,0x3819,0x30df,0x3159,0x381d,0x3821,0x3825,0x3829,0x382f,0x2bad,0x30e7,0x3833,0x3837,0x2fbd, -0x383d,0x3843,0x2f05,0x3849,0x384d,0x2fcd,0x3851,0x3855,0x3859,0x385f,0x385f,0x3865,0x3869,0x386d,0x3873,0x3877, -0x387b,0x387f,0x3885,0x3889,0x388d,0x3891,0x3895,0x3899,0x389f,0x38a3,0x38a7,0x38ab,0x38af,0x38b3,0x38b7,0x38bd, -0x38c3,0x38c7,0x38cd,0x38d1,0x38d7,0x38db,0x2fe5,0x38df,0x38e5,0x38eb,0x38ef,0x38f5,0x38f9,0x38ff,0x3903,0x3907, -0x390b,0x390f,0x3913,0x3917,0x391d,0x3923,0x3929,0x3565,0x392f,0x3933,0x3937,0x393b,0x393f,0x3943,0x3947,0x394b, -0x394f,0x3953,0x3957,0x395b,0x2c8d,0x3961,0x3965,0x3969,0x396d,0x3971,0x3975,0x2ff1,0x3979,0x397d,0x3981,0x3985, -0x3989,0x398f,0x3995,0x399b,0x399f,0x39a3,0x39a7,0x39ab,0x39b1,0x39b5,0x39bb,0x39bf,0x39c3,0x39c9,0x39cf,0x39d3, -0x2b99,0x39d7,0x39db,0x39df,0x39e3,0x39e7,0x39eb,0x3103,0x39ef,0x39f3,0x39f7,0x39fb,0x39ff,0x3a03,0x3a07,0x3a0b, -0x3a0f,0x3a13,0x3a19,0x3a1d,0x3a21,0x3a25,0x3a29,0x3a2d,0x3a33,0x3a39,0x3a3d,0x3a41,0x3117,0x311b,0x3a45,0x3a49, -0x3a4f,0x3a53,0x3a57,0x3a5b,0x3a5f,0x3a65,0x3a6b,0x3a6f,0x3a73,0x3a77,0x3a7d,0x311f,0x3a81,0x3a87,0x3a8d,0x3a91, -0x3a95,0x3a99,0x3a9f,0x3aa3,0x3aa7,0x3aab,0x3aaf,0x3ab3,0x3ab7,0x3abb,0x3ac1,0x3ac5,0x3ac9,0x3acd,0x3ad3,0x3ad7, -0x3adb,0x3adf,0x3ae3,0x3ae9,0x3aef,0x3af3,0x3af7,0x3afb,0x3b01,0x3b05,0x3137,0x3137,0x3b0b,0x3b0f,0x3b15,0x3b19, -0x3b1d,0x3b21,0x3b25,0x3b29,0x3b2d,0x3b31,0x313b,0x3b37,0x3b3b,0x3b3f,0x3b43,0x3b47,0x3b4b,0x3b51,0x3b55,0x3b5b, -0x3b61,0x3b67,0x3b6b,0x3b6f,0x3b73,0x3b77,0x3b7b,0x3b7f,0x3b83,0x3b87,1,1,2,2,2,2, -2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,0xfe00,0xfe00,0xfe00, -0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00, -0xfe00,0xfe00,1,1,1,1,1,1,1,1,1,1,0xfe00,0xfe00,0xfe00,0xfe00, -0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00, -0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,0xadc,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283, -0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283, -0xadc,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283, -0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,1,1,1,1,1,1,1,1, +0xfe02,0xfe02,0xfe02,0xfe02,0xfe02,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,0xfe02,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,0x324a,0x3254, +0x3268,0x3280,0x3298,0x32b0,0x32c8,0xffb0,0xffb0,0xfe02,0xfe02,0xfe02,1,1,1,0xffc4,0xffb0,0xffb0, +0xffb0,0xffb0,0xffb0,1,1,1,1,1,1,1,1,0xffb8,0xffb8,0xffb8,0xffb8,0xffb8, +0xffb8,0xffb8,0xffb8,1,1,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffb8,0xffb8,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,0x1283,0x1283,0x1283,0x1283,0xadc,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283, +1,1,0xffcc,0xffcc,0xffcc,0xffcc,1,1,1,1,1,1,1,1,1,1, +1,1,1,0x32d6,0x32e0,0x32f4,0x330c,0x3324,0x333c,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,0xffcc,0xffcc,0xffcc,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,1, +0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc, +0xffcc,1,1,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,1,0xffcc,0xffcc,1,0xffcc,0xffcc, +0xffcc,0xffcc,0xffcc,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,0xffb8,0xffb8,0xffb8,0xffb8,0xffb8,0xffb8,0xffb8,1, +1,1,1,1,1,1,1,1,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xffcc,0xfe0e,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,0x334b,0x334f,0x3353,0x3357,0x335d,0x2f3d,0x3361,0x3365,0x3369,0x336d,0x2f41,0x3371, +0x3375,0x3379,0x2f45,0x337f,0x3383,0x3387,0x338b,0x3391,0x3395,0x3399,0x339d,0x33a3,0x33a7,0x33ab,0x33af,0x302f, +0x33b3,0x33b9,0x33bd,0x33c1,0x33c5,0x33c9,0x33cd,0x33d1,0x33d5,0x3043,0x2f49,0x2f4d,0x3047,0x33d9,0x33dd,0x2c49, +0x33e1,0x2f51,0x33e5,0x33e9,0x33ed,0x33f1,0x33f1,0x33f1,0x33f5,0x33fb,0x33ff,0x3403,0x3407,0x340d,0x3411,0x3415, +0x3419,0x341d,0x3421,0x3425,0x3429,0x342d,0x3431,0x3435,0x3439,0x343d,0x343d,0x304f,0x3441,0x3445,0x3449,0x344d, +0x2f59,0x3451,0x3455,0x3459,0x2ead,0x345d,0x3461,0x3465,0x3469,0x346d,0x3471,0x3475,0x3479,0x347d,0x3483,0x3487, +0x348b,0x348f,0x3493,0x3497,0x349b,0x34a1,0x34a7,0x34ab,0x34af,0x34b3,0x34b7,0x34bb,0x34bf,0x34c3,0x34c7,0x34c7, +0x34cb,0x34d1,0x34d5,0x2c39,0x34d9,0x34dd,0x34e3,0x34e7,0x34eb,0x34ef,0x34f3,0x34f7,0x2f6d,0x34fb,0x34ff,0x3503, +0x3509,0x350d,0x3513,0x3517,0x351b,0x351f,0x3523,0x3527,0x352b,0x352f,0x3533,0x3537,0x353b,0x353f,0x3545,0x3549, +0x354d,0x3551,0x2b61,0x3555,0x355b,0x355f,0x355f,0x3565,0x3569,0x3569,0x356d,0x3571,0x3577,0x357d,0x3581,0x3585, +0x3589,0x358d,0x3591,0x3595,0x3599,0x359d,0x35a1,0x2f71,0x35a5,0x35ab,0x35af,0x35b3,0x307f,0x35b3,0x35b7,0x2f79, +0x35bb,0x35bf,0x35c3,0x35c7,0x2f7d,0x2af5,0x35cb,0x35cf,0x35d3,0x35d7,0x35db,0x35df,0x35e3,0x35e9,0x35ed,0x35f1, +0x35f5,0x35f9,0x35fd,0x3603,0x3607,0x360b,0x360f,0x3613,0x3617,0x361b,0x361f,0x3623,0x2f81,0x3627,0x362b,0x3631, +0x3635,0x3639,0x363d,0x2f89,0x3641,0x3645,0x3649,0x364d,0x3651,0x3655,0x3659,0x365d,0x2b65,0x309f,0x3661,0x3665, +0x3669,0x366d,0x3673,0x3677,0x367b,0x367f,0x2f8d,0x3683,0x3689,0x368d,0x3691,0x3151,0x3695,0x3699,0x369d,0x36a1, +0x36a5,0x36ab,0x36af,0x36b3,0x36b7,0x36bd,0x36c1,0x36c5,0x36c9,0x2c7d,0x36cd,0x36d1,0x36d7,0x36dd,0x36e3,0x36e7, +0x36ed,0x36f1,0x36f5,0x36f9,0x36fd,0x2f91,0x2dd9,0x3701,0x3705,0x3709,0x370d,0x3713,0x3717,0x371b,0x371f,0x30af, +0x3723,0x3727,0x372d,0x3731,0x3735,0x373b,0x3741,0x3745,0x30b3,0x3749,0x374d,0x3751,0x3755,0x3759,0x375d,0x3761, +0x3767,0x376b,0x3771,0x3775,0x377b,0x30bb,0x377f,0x3783,0x3789,0x378d,0x3791,0x3797,0x379d,0x37a1,0x37a5,0x37a9, +0x37ad,0x37ad,0x37b1,0x37b5,0x30c3,0x37b9,0x37bd,0x37c1,0x37c5,0x37c9,0x37cf,0x37d3,0x2c45,0x37d9,0x37df,0x37e3, +0x37e9,0x37ef,0x37f5,0x37f9,0x30db,0x37fd,0x3803,0x3809,0x380f,0x3815,0x3819,0x3819,0x30df,0x3159,0x381d,0x3821, +0x3825,0x3829,0x382f,0x2bad,0x30e7,0x3833,0x3837,0x2fbd,0x383d,0x3843,0x2f05,0x3849,0x384d,0x2fcd,0x3851,0x3855, +0x3859,0x385f,0x385f,0x3865,0x3869,0x386d,0x3873,0x3877,0x387b,0x387f,0x3885,0x3889,0x388d,0x3891,0x3895,0x3899, +0x389f,0x38a3,0x38a7,0x38ab,0x38af,0x38b3,0x38b7,0x38bd,0x38c3,0x38c7,0x38cd,0x38d1,0x38d7,0x38db,0x2fe5,0x38df, +0x38e5,0x38eb,0x38ef,0x38f5,0x38f9,0x38ff,0x3903,0x3907,0x390b,0x390f,0x3913,0x3917,0x391d,0x3923,0x3929,0x3565, +0x392f,0x3933,0x3937,0x393b,0x393f,0x3943,0x3947,0x394b,0x394f,0x3953,0x3957,0x395b,0x2c8d,0x3961,0x3965,0x3969, +0x396d,0x3971,0x3975,0x2ff1,0x3979,0x397d,0x3981,0x3985,0x3989,0x398f,0x3995,0x399b,0x399f,0x39a3,0x39a7,0x39ab, +0x39b1,0x39b5,0x39bb,0x39bf,0x39c3,0x39c9,0x39cf,0x39d3,0x2b99,0x39d7,0x39db,0x39df,0x39e3,0x39e7,0x39eb,0x3103, +0x39ef,0x39f3,0x39f7,0x39fb,0x39ff,0x3a03,0x3a07,0x3a0b,0x3a0f,0x3a13,0x3a19,0x3a1d,0x3a21,0x3a25,0x3a29,0x3a2d, +0x3a33,0x3a39,0x3a3d,0x3a41,0x3117,0x311b,0x3a45,0x3a49,0x3a4f,0x3a53,0x3a57,0x3a5b,0x3a5f,0x3a65,0x3a6b,0x3a6f, +0x3a73,0x3a77,0x3a7d,0x311f,0x3a81,0x3a87,0x3a8d,0x3a91,0x3a95,0x3a99,0x3a9f,0x3aa3,0x3aa7,0x3aab,0x3aaf,0x3ab3, +0x3ab7,0x3abb,0x3ac1,0x3ac5,0x3ac9,0x3acd,0x3ad3,0x3ad7,0x3adb,0x3adf,0x3ae3,0x3ae9,0x3aef,0x3af3,0x3af7,0x3afb, +0x3b01,0x3b05,0x3137,0x3137,0x3b0b,0x3b0f,0x3b15,0x3b19,0x3b1d,0x3b21,0x3b25,0x3b29,0x3b2d,0x3b31,0x313b,0x3b37, +0x3b3b,0x3b3f,0x3b43,0x3b47,0x3b4b,0x3b51,0x3b55,0x3b5b,0x3b61,0x3b67,0x3b6b,0x3b6f,0x3b73,0x3b77,0x3b7b,0x3b7f, +0x3b83,0x3b87,1,1,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00, +0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,1,1,1,1,1,1, +1,1,1,1,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00, +0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,0xfe00,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,0xadc,0x1283,0x1283,0x1283, 0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283, -0x1283,0x1283,0x1283,0x1283,0x3c54,1,0x3c54,1,0x3c54,0x3c54,0x3c54,0x3c54,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,0x3c54,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0x3c54, -1,1,1,1,0x3c54,1,1,1,0x3c54,1,0x3c54,1,1,1,1,1, +0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0xadc,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283, +0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,0x1283,0x1283,0x1283,0x1283, +0xadc,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283, +0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x1283,0x3c54,1,0x3c54,0x3c54, +0x3c54,0x3c54,0x3c54,0x3c54,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,0x3c54,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,0x3c54,1,1,1,1,0x3c54,1,1,1, +0x3c54,1,0x3c54,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,0x3b87,1,1,1,1,1 +1,1,0x3b87,1,1,1,1,1 }; static const uint16_t norm2_nfc_data_extraData[7724]={ @@ -1120,7 +1133,7 @@ static const uint16_t norm2_nfc_data_extraData[7724]={ }; static const uint8_t norm2_nfc_data_smallFCD[256]={ -0xc0,0xef,3,0x7f,0xdf,0x70,0xcf,0x87,0xc7,0x66,0x66,0x46,0x64,0x46,0x66,0x5b, +0xc0,0xef,3,0x7f,0xdf,0x70,0xcf,0x87,0xc7,0xe6,0x66,0x46,0x64,0x46,0x66,0x5b, 0x12,0,0,4,0,0,0,0x43,0x20,2,0x29,0xae,0xc2,0xc0,0xff,0xff, 0xc0,0x72,0xbf,0,0,0,0,0,0,0,0x40,0,0x80,0x88,0,0, 0xfe,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, @@ -1140,16 +1153,16 @@ static const uint8_t norm2_nfc_data_smallFCD[256]={ static const UTrie2 norm2_nfc_data_trie={ norm2_nfc_data_trieIndex, - norm2_nfc_data_trieIndex+2720, + norm2_nfc_data_trieIndex+2728, NULL, - 2720, - 7056, + 2728, + 7248, 0x188, - 0xb1c, + 0xb24, 0x1, 0x1, 0x30000, - 0x262c, + 0x26f4, NULL, 0, FALSE, FALSE, 0, NULL }; diff --git a/deps/icu-small/source/common/propname_data.h b/deps/icu-small/source/common/propname_data.h index 5876fc7073aeb5..afa876c0fa8275 100644 --- a/deps/icu-small/source/common/propname_data.h +++ b/deps/icu-small/source/common/propname_data.h @@ -13,91 +13,93 @@ U_NAMESPACE_BEGIN -const int32_t PropNameData::indexes[8]={0x20,0x13c8,0x4831,0x92bb,0x92bb,0x92bb,0x2f,0}; +const int32_t PropNameData::indexes[8]={0x20,0x1424,0x49e9,0x966c,0x966c,0x966c,0x2f,0}; -const int32_t PropNameData::valueMaps[1258]={ -6,0,0x40,0,0xdb,0x356,0xdb,0x36c,0xdb,0x381,0xdb,0x397,0xdb,0x3a2,0xdb,0x3c3, -0xdb,0x3d3,0xdb,0x3e2,0xdb,0x3f0,0xdb,0x414,0xdb,0x42b,0xdb,0x443,0xdb,0x45a,0xdb,0x469, -0xdb,0x478,0xdb,0x489,0xdb,0x497,0xdb,0x4a9,0xdb,0x4c3,0xdb,0x4de,0xdb,0x4f3,0xdb,0x510, -0xdb,0x521,0xdb,0x52c,0xdb,0x54b,0xdb,0x561,0xdb,0x572,0xdb,0x582,0xdb,0x59d,0xdb,0x5b6, -0xdb,0x5c7,0xdb,0x5e1,0xdb,0x5f4,0xdb,0x604,0xdb,0x61e,0xdb,0x637,0xdb,0x64e,0xdb,0x662, -0xdb,0x678,0xdb,0x68c,0xdb,0x6a2,0xdb,0x6bc,0xdb,0x6d4,0xdb,0x6f0,0xdb,0x6f8,0xdb,0x700, -0xdb,0x708,0xdb,0x710,0xdb,0x719,0xdb,0x726,0xdb,0x739,0xdb,0x756,0xdb,0x773,0xdb,0x790, -0xdb,0x7ae,0xdb,0x7cc,0xdb,0x7f0,0xdb,0x7fd,0xdb,0x824,0xdb,0x843,0xdb,0x86c,0xdb,0x88d, -0xdb,0x8a4,0xdb,0x1000,0x1016,0x8c6,0x155,0xae6,0x170,0x2b81,0xe1,0x2ba0,0x28d,0x2cde,0x2a3,0x2d38, -0x2ad,0x2f95,0x2cf,0x3836,0x337,0x38a6,0x341,0x3b40,0x370,0x3b7e,0x378,0x4567,0x42e,0x45e5,0x438,0x460a, -0x43e,0x4624,0x444,0x4645,0x44b,0x465f,0xe1,0x4684,0xe1,0x46aa,0x452,0x4754,0x468,0x47cd,0x47b,0x486a, -0x495,0x2000,0x2001,0x48a1,0x49c,0x3000,0x3001,0x492d,0,0x4000,0x400e,0x493f,0,0x4948,0,0x4962, -0,0x4973,0,0x4984,0,0x499a,0,0x49a3,0,0x49c0,0,0x49de,0,0x49fc,0,0x4a1a, -0,0x4a30,0,0x4a44,0,0x4a5a,0,0x7000,0x7001,0x4a73,0,0x748,0x12,0,1,0x12, -0x20,0x766,0x49,0,1,7,8,9,0xa,0xb,0xc,0xd,0xe,0xf,0x10,0x11, -0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x20,0x21, -0x22,0x23,0x24,0x54,0x5b,0x67,0x6b,0x76,0x7a,0x81,0x82,0x84,0x85,0xc8,0xca,0xd6, -0xd8,0xda,0xdc,0xde,0xe0,0xe2,0xe4,0xe6,0xe8,0xe9,0xea,0xf0,0x2e,0x40,0x4c,0x56, -0x67,0x72,0x7f,0x8c,0x99,0xa6,0xb3,0xc0,0xcd,0xda,0xe7,0xf4,0x101,0x10e,0x11b,0x128, -0x135,0x142,0x14f,0x15c,0x169,0x176,0x183,0x190,0x19d,0x1aa,0x1b7,0x1c4,0x1d1,0x1de,0x1eb,0x1fa, -0x209,0x218,0x227,0x236,0x245,0x254,0x263,0x27d,0x291,0x2a5,0x2c0,0x2cf,0x2d8,0x2e8,0x2f0,0x2f9, -0x308,0x311,0x321,0x332,0x343,0x8f8,1,0,0x17,0x8d5,0x8e6,0x8f7,0x90b,0x922,0x93a,0x94c, -0x961,0x978,0x98d,0x99d,0x9af,0x9cc,0x9e8,0x9fa,0xa17,0xa33,0xa4f,0xa64,0xa79,0xa93,0xaae,0xac9, -0xa9a,1,0,0x119,0xaf1,0xafe,0xb11,0xb39,0xb57,0xb75,0xb8d,0xbb8,0xbe2,0xbfa,0xc0d,0xc20, -0xc2f,0xc3e,0xc4d,0xc5c,0xc73,0xc84,0xc97,0xcaa,0xcb7,0xcc4,0xcd3,0xce4,0xcf9,0xd0a,0xd15,0xd1e, -0xd2f,0xd40,0xd53,0xd65,0xd78,0xd8b,0xdca,0xdd7,0xde4,0xdf1,0xe06,0xe36,0xe50,0xe71,0xe9c,0xebf, -0xf1d,0xf44,0xf5f,0xf6e,0xf95,0xfbd,0xfe0,0x1003,0x102d,0x1046,0x1065,0x1088,0x10ac,0x10bf,0x10d9,0x1103, -0x111b,0x1143,0x116c,0x117f,0x1192,0x11a5,0x11cc,0x11db,0x11fb,0x1229,0x1247,0x1275,0x1291,0x12ac,0x12c5,0x12de, -0x12ff,0x132f,0x134e,0x1370,0x13a4,0x13d1,0x1416,0x1437,0x1461,0x1482,0x14ab,0x14be,0x14f1,0x1508,0x1517,0x1528, -0x1553,0x156a,0x159b,0x15c9,0x160c,0x1617,0x1650,0x1661,0x1672,0x167f,0x1692,0x16cc,0x16f0,0x1714,0x174e,0x1786, -0x17b1,0x17c9,0x17f5,0x1821,0x182e,0x183d,0x185a,0x187c,0x18aa,0x18ca,0x18f1,0x1918,0x1937,0x194a,0x195b,0x196c, -0x1991,0x19b6,0x19dd,0x1a11,0x1a3e,0x1a5c,0x1a6f,0x1a88,0x1ac1,0x1ad0,0x1af0,0x1b12,0x1b34,0x1b4b,0x1b62,0x1b8f, -0x1ba8,0x1bc1,0x1bf2,0x1c1c,0x1c37,0x1c4a,0x1c69,0x1c72,0x1c85,0x1ca3,0x1cc1,0x1cd4,0x1ceb,0x1d00,0x1d35,0x1d59, -0x1d6e,0x1d7d,0x1d90,0x1db4,0x1dbd,0x1de1,0x1df8,0x1e0b,0x1e1a,0x1e25,0x1e46,0x1e5e,0x1e6d,0x1e7c,0x1e8b,0x1ea2, -0x1eb7,0x1ecc,0x1f05,0x1f18,0x1f34,0x1f3f,0x1f4c,0x1f7a,0x1f9e,0x1fc1,0x1fd4,0x1ff6,0x2009,0x2024,0x2047,0x206a, -0x208f,0x20a0,0x20cf,0x20fc,0x2113,0x212e,0x213d,0x2168,0x21a0,0x21da,0x2208,0x2219,0x2226,0x224a,0x2259,0x2275, -0x228f,0x22ac,0x22e4,0x22f9,0x2326,0x2345,0x2373,0x2393,0x23c7,0x23d6,0x2400,0x2423,0x244e,0x2459,0x246a,0x2485, -0x24a9,0x24b6,0x24cb,0x24f2,0x251d,0x2554,0x2567,0x2578,0x25a8,0x25b9,0x25c8,0x25dd,0x25fb,0x260e,0x2621,0x2638, -0x2655,0x2660,0x2669,0x268b,0x26a0,0x26c5,0x26dc,0x2705,0x2720,0x2735,0x274e,0x276f,0x27a4,0x27b5,0x27e6,0x280a, -0x281b,0x2834,0x283f,0x286c,0x288e,0x28bc,0x28ef,0x28fe,0x290f,0x292c,0x296e,0x2995,0x29a2,0x29b7,0x29db,0x2a01, -0x2a3a,0x2a4b,0x2a6f,0x2a7a,0x2a87,0x2a96,0x2abb,0x2ae9,0x2b05,0x2b22,0x2b2f,0x2b40,0x2b5e,0x1c30,1,0, -0x12,0x2bb7,0x2bc7,0x2bda,0x2bea,0x2bfa,0x2c09,0x2c19,0x2c2b,0x2c3e,0x2c50,0x2c60,0x2c70,0x2c7f,0x2c8e,0x2c9e, -0x2cab,0x2cba,0x2cce,0x1cee,1,0,6,0x2cf3,0x2cfe,0x2d0b,0x2d18,0x2d25,0x2d30,0x1d32,1,0, -0x1e,0x2d4d,0x2d5c,0x2d71,0x2d86,0x2d9b,0x2daf,0x2dc0,0x2dd4,0x2de7,0x2df8,0x2e11,0x2e23,0x2e34,0x2e48,0x2e5b, -0x2e73,0x2e85,0x2e90,0x2ea0,0x2eae,0x2ec3,0x2ed8,0x2eee,0x2f08,0x2f1e,0x2f2e,0x2f42,0x2f56,0x2f67,0x2f7f,0x1f5d, -1,0,0x64,0x2fa7,0x2fca,0x2fd3,0x2fe0,0x2feb,0x2ff4,0x2fff,0x3008,0x3021,0x3026,0x302f,0x304c,0x3055, -0x3062,0x306b,0x308f,0x3096,0x309f,0x30b2,0x30bd,0x30c6,0x30d1,0x30ea,0x30f3,0x3102,0x310d,0x3116,0x3121,0x312a, -0x3131,0x313a,0x3145,0x314e,0x3167,0x3170,0x317d,0x3188,0x3199,0x31a4,0x31b9,0x31d0,0x31d9,0x31e2,0x31fb,0x3206, -0x320f,0x3218,0x322f,0x324c,0x3257,0x3268,0x3273,0x327a,0x3287,0x3294,0x32c1,0x32d6,0x32df,0x32fa,0x331d,0x333e, -0x335f,0x3384,0x33ab,0x33cc,0x33ef,0x3410,0x3437,0x3458,0x347d,0x349c,0x34bb,0x34da,0x34f7,0x3518,0x3539,0x355c, -0x3581,0x35a0,0x35bf,0x35e0,0x3607,0x362c,0x364b,0x366c,0x368f,0x36aa,0x36c3,0x36de,0x36f7,0x3714,0x372f,0x374c, -0x376b,0x3788,0x37a5,0x37c4,0x37e1,0x37fc,0x3819,0x2283,1,0,6,0x3847,0x3856,0x3866,0x3876,0x3886, -0x3897,0x22e1,1,0,0x2b,0x38b5,0x38c1,0x38cf,0x38de,0x38ed,0x38fd,0x390e,0x3922,0x3937,0x394d,0x3960, -0x3974,0x3984,0x398d,0x3998,0x39a8,0x39c4,0x39d6,0x39e4,0x39f3,0x39ff,0x3a14,0x3a28,0x3a3b,0x3a49,0x3a5d,0x3a6b, -0x3a75,0x3a87,0x3a93,0x3aa1,0x3ab1,0x3ab8,0x3abf,0x3ac6,0x3acd,0x3ad4,0x3aea,0x3b0b,0x88d,0x3b1d,0x3b28,0x3b37, -0x253a,1,0,4,0x3b51,0x3b5c,0x3b68,0x3b72,0x2560,1,0,0xb2,0x3b89,0x3b96,0x3bab,0x3bb8, -0x3bc7,0x3bd5,0x3be4,0x3bf3,0x3c05,0x3c14,0x3c22,0x3c33,0x3c42,0x3c51,0x3c5e,0x3c6a,0x3c79,0x3c88,0x3c92,0x3c9f, -0x3cac,0x3cbb,0x3cc9,0x3cd8,0x3ce4,0x3cee,0x3cfa,0x3d0a,0x3d1a,0x3d28,0x3d34,0x3d45,0x3d51,0x3d5d,0x3d6b,0x3d78, -0x3d84,0x3d91,0xd0a,0x3d9e,0x3dac,0x3dc6,0x3dcf,0x3ddd,0x3deb,0x3df7,0x3e06,0x3e14,0x3e22,0x3e2e,0x3e3d,0x3e4b, -0x3e59,0x3e66,0x3e75,0x3e90,0x3e9f,0x3eb0,0x3ec1,0x3ed4,0x3ee6,0x3ef5,0x3f07,0x3f16,0x3f22,0x3f2d,0x1e1a,0x3f3a, -0x3f45,0x3f50,0x3f5b,0x3f66,0x3f81,0x3f8c,0x3f97,0x3fa2,0x3fb5,0x3fc9,0x3fd4,0x3fe3,0x3ff2,0x3ffd,0x4008,0x4015, -0x4024,0x4032,0x403d,0x4058,0x4062,0x4073,0x4084,0x4093,0x40a4,0x40af,0x40ba,0x40c5,0x40d0,0x40db,0x40e6,0x40f1, -0x40fb,0x4106,0x4116,0x4121,0x412f,0x413c,0x4147,0x4156,0x4163,0x4170,0x417f,0x418c,0x419d,0x41af,0x41bf,0x41ca, -0x41dd,0x41f4,0x4202,0x420f,0x421a,0x4227,0x4238,0x4254,0x426a,0x4275,0x4292,0x42a2,0x42b1,0x42bc,0x42c7,0x1f34, -0x42d3,0x42de,0x42f6,0x4306,0x4315,0x4323,0x4331,0x433c,0x4347,0x435b,0x4372,0x438a,0x439a,0x43aa,0x43ba,0x43cc, -0x43d7,0x43e2,0x43ec,0x43f8,0x4406,0x4419,0x4425,0x4432,0x443d,0x4459,0x4466,0x4474,0x448d,0x2834,0x449c,0x2655, -0x44a9,0x44b7,0x44c9,0x44d7,0x44e3,0x44f3,0x2a6f,0x4501,0x450d,0x4518,0x4523,0x452e,0x4542,0x4550,0x2e57,1, -0,6,0x4581,0x4594,0x45a4,0x45b2,0x45c3,0x45d3,0x2eb3,0x12,0,1,0x45fd,0x4603,0x2ec0,0x12, -0,1,0x45fd,0x4603,0x2ecd,1,0,3,0x45fd,0x4603,0x463c,0x2ee3,1,0,3,0x45fd, -0x4603,0x463c,0x2ef9,1,0,0x12,0x46c6,0x46d0,0x46dc,0x46e3,0x46ee,0x46f3,0x46fa,0x4701,0x470a,0x470f, -0x4714,0x4724,0x88d,0x3b1d,0x4730,0x3b28,0x4740,0x3b37,0x2fa2,1,0,0xf,0x46c6,0x4767,0x4771,0x477b, -0x4786,0x39f3,0x4790,0x479c,0x47a4,0x47ab,0x47b5,0x46dc,0x46e3,0x46f3,0x47bf,0x3029,1,0,0x16,0x46c6, -0x47dc,0x477b,0x47e8,0x47f5,0x4803,0x39f3,0x480e,0x46dc,0x481f,0x46f3,0x482e,0x483c,0x88d,0x3b0b,0x4848,0x4859, -0x3b1d,0x4730,0x3b28,0x4740,0x3b37,0x3139,1,0,3,0x4888,0x4890,0x4898,0x3152,0x36,1,2, -4,8,0xe,0x10,0x20,0x3e,0x40,0x80,0x100,0x1c0,0x200,0x400,0x800,0xe00,0x1000,0x2000, -0x4000,0x7000,0x8000,0x10000,0x20000,0x40000,0x78001,0x80000,0x100000,0x200000,0x400000,0x800000,0x1000000,0x2000000,0x4000000,0x8000000, -0xf000000,0x10000000,0x20000000,0x30f80000,0x2d4d,0x2d5c,0x2d71,0x2d86,0x48cf,0x2d9b,0x2daf,0x48c5,0x2dc0,0x2dd4,0x2de7,0x48e0, -0x2df8,0x2e11,0x2e23,0x48f7,0x2e34,0x2e48,0x2e5b,0x4920,0x2e73,0x2e85,0x2e90,0x2ea0,0x48bc,0x2eae,0x2ec3,0x2ed8, -0x2eee,0x2f08,0x2f1e,0x2f2e,0x2f42,0x2f56,0x4916,0x2f67,0x2f7f,0x4901 +const int32_t PropNameData::valueMaps[1281]={ +6,0,0x41,0,0xdd,0x356,0xdd,0x36c,0xdd,0x381,0xdd,0x397,0xdd,0x3a2,0xdd,0x3c3, +0xdd,0x3d3,0xdd,0x3e2,0xdd,0x3f0,0xdd,0x414,0xdd,0x42b,0xdd,0x443,0xdd,0x45a,0xdd,0x469, +0xdd,0x478,0xdd,0x489,0xdd,0x497,0xdd,0x4a9,0xdd,0x4c3,0xdd,0x4de,0xdd,0x4f3,0xdd,0x510, +0xdd,0x521,0xdd,0x52c,0xdd,0x54b,0xdd,0x561,0xdd,0x572,0xdd,0x582,0xdd,0x59d,0xdd,0x5b6, +0xdd,0x5c7,0xdd,0x5e1,0xdd,0x5f4,0xdd,0x604,0xdd,0x61e,0xdd,0x637,0xdd,0x64e,0xdd,0x662, +0xdd,0x678,0xdd,0x68c,0xdd,0x6a2,0xdd,0x6bc,0xdd,0x6d4,0xdd,0x6f0,0xdd,0x6f8,0xdd,0x700, +0xdd,0x708,0xdd,0x710,0xdd,0x719,0xdd,0x726,0xdd,0x739,0xdd,0x756,0xdd,0x773,0xdd,0x790, +0xdd,0x7ae,0xdd,0x7cc,0xdd,0x7f0,0xdd,0x7fd,0xdd,0x817,0xdd,0x82c,0xdd,0x847,0xdd,0x85e, +0xdd,0x875,0xdd,0x897,0xdd,0x1000,0x1016,0x8b6,0x157,0xad6,0x172,0x2c95,0xe3,0x2cb4,0x29a,0x2df2, +0x2b0,0x2e4c,0x2ba,0x30a9,0x2dc,0x39a4,0x346,0x3a14,0x350,0x3cae,0x37f,0x3cec,0x387,0x474b,0x444,0x47c9, +0x44e,0x47ee,0x454,0x4808,0x45a,0x4829,0x461,0x4843,0xe3,0x4868,0xe3,0x488e,0x468,0x4938,0x47e,0x49b1, +0x491,0x4a63,0x4ac,0x2000,0x2001,0x4a9a,0x4b3,0x3000,0x3001,0x4b26,0,0x4000,0x400e,0x4b38,0,0x4b41, +0,0x4b5b,0,0x4b6c,0,0x4b7d,0,0x4b93,0,0x4b9c,0,0x4bb9,0,0x4bd7,0,0x4bf5, +0,0x4c13,0,0x4c29,0,0x4c3d,0,0x4c53,0,0x7000,0x7001,0x4c6c,0,0x77c,0x12,0, +1,0x12,0x20,0x79a,0x49,0,1,7,8,9,0xa,0xb,0xc,0xd,0xe,0xf, +0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, +0x20,0x21,0x22,0x23,0x24,0x54,0x5b,0x67,0x6b,0x76,0x7a,0x81,0x82,0x84,0x85,0xc8, +0xca,0xd6,0xd8,0xda,0xdc,0xde,0xe0,0xe2,0xe4,0xe6,0xe8,0xe9,0xea,0xf0,0x2e,0x40, +0x4c,0x56,0x67,0x72,0x7f,0x8c,0x99,0xa6,0xb3,0xc0,0xcd,0xda,0xe7,0xf4,0x101,0x10e, +0x11b,0x128,0x135,0x142,0x14f,0x15c,0x169,0x176,0x183,0x190,0x19d,0x1aa,0x1b7,0x1c4,0x1d1,0x1de, +0x1eb,0x1fa,0x209,0x218,0x227,0x236,0x245,0x254,0x263,0x27d,0x291,0x2a5,0x2c0,0x2cf,0x2d8,0x2e8, +0x2f0,0x2f9,0x308,0x311,0x321,0x332,0x343,0x92c,1,0,0x17,0x8c5,0x8d6,0x8e7,0x8fb,0x912, +0x92a,0x93c,0x951,0x968,0x97d,0x98d,0x99f,0x9bc,0x9d8,0x9ea,0xa07,0xa23,0xa3f,0xa54,0xa69,0xa83, +0xa9e,0xab9,0xace,1,0,0x124,0xae1,0xaee,0xb01,0xb29,0xb47,0xb65,0xb7d,0xba8,0xbd2,0xbea, +0xbfd,0xc10,0xc1f,0xc2e,0xc3d,0xc4c,0xc63,0xc74,0xc87,0xc9a,0xca7,0xcb4,0xcc3,0xcd4,0xce9,0xcfa, +0xd05,0xd0e,0xd1f,0xd30,0xd43,0xd55,0xd68,0xd7b,0xdba,0xdc7,0xdd4,0xde1,0xdf6,0xe26,0xe40,0xe61, +0xe8c,0xeaf,0xf0d,0xf34,0xf4f,0xf5e,0xf85,0xfad,0xfd0,0xff3,0x101d,0x1036,0x1055,0x1078,0x109c,0x10af, +0x10c9,0x10f3,0x110b,0x1133,0x115c,0x116f,0x1182,0x1195,0x11bc,0x11cb,0x11eb,0x1219,0x1237,0x1265,0x1281,0x129c, +0x12b5,0x12ce,0x12ef,0x131f,0x133e,0x1360,0x1394,0x13c1,0x1406,0x1427,0x1451,0x1472,0x149b,0x14ae,0x14e1,0x14f8, +0x1507,0x1518,0x1543,0x155a,0x158b,0x15b9,0x15fc,0x1607,0x1640,0x1651,0x1662,0x166f,0x1682,0x16bc,0x16e0,0x1704, +0x173e,0x1776,0x17a1,0x17b9,0x17e5,0x1811,0x181e,0x182d,0x184a,0x186c,0x189a,0x18ba,0x18e1,0x1908,0x1927,0x193a, +0x194b,0x195c,0x1981,0x19a6,0x19cd,0x1a01,0x1a2e,0x1a4c,0x1a5f,0x1a78,0x1ab1,0x1ac0,0x1ae0,0x1b02,0x1b24,0x1b3b, +0x1b52,0x1b7f,0x1b98,0x1bb1,0x1be2,0x1c0c,0x1c27,0x1c3a,0x1c59,0x1c62,0x1c75,0x1c93,0x1cb1,0x1cc4,0x1cdb,0x1cf0, +0x1d25,0x1d49,0x1d5e,0x1d6d,0x1d80,0x1da4,0x1dad,0x1dd1,0x1de8,0x1dfb,0x1e0a,0x1e15,0x1e36,0x1e4e,0x1e5d,0x1e6c, +0x1e7b,0x1e92,0x1ea7,0x1ebc,0x1ef5,0x1f08,0x1f24,0x1f2f,0x1f3c,0x1f6a,0x1f8e,0x1fb1,0x1fc4,0x1fe6,0x1ff9,0x2014, +0x2037,0x205a,0x207f,0x2090,0x20bf,0x20ec,0x2103,0x211e,0x212d,0x2158,0x2190,0x21ca,0x21f8,0x2209,0x2216,0x223a, +0x2249,0x2265,0x227f,0x229c,0x22d4,0x22e9,0x2316,0x2335,0x2363,0x2383,0x23b7,0x23c6,0x23f0,0x2413,0x243e,0x2449, +0x245a,0x2475,0x2499,0x24a6,0x24bb,0x24e2,0x250d,0x2544,0x2557,0x2568,0x2598,0x25a9,0x25b8,0x25cd,0x25eb,0x25fe, +0x2611,0x2628,0x2645,0x2650,0x2659,0x267b,0x2690,0x26b5,0x26cc,0x26f5,0x2710,0x2725,0x273e,0x275f,0x2794,0x27a5, +0x27d6,0x27fa,0x280b,0x2824,0x282f,0x285c,0x287e,0x28ac,0x28df,0x28ee,0x28ff,0x291c,0x295e,0x2985,0x2992,0x29a7, +0x29cb,0x29f1,0x2a2a,0x2a3b,0x2a5f,0x2a6a,0x2a77,0x2a86,0x2aab,0x2ad9,0x2af5,0x2b12,0x2b1f,0x2b30,0x2b4e,0x2b71, +0x2b8e,0x2b9b,0x2bbb,0x2bd8,0x2bf9,0x2c22,0x2c33,0x2c52,0x2c6b,0x2c84,0x1cf4,1,0,0x12,0x2ccb,0x2cdb, +0x2cee,0x2cfe,0x2d0e,0x2d1d,0x2d2d,0x2d3f,0x2d52,0x2d64,0x2d74,0x2d84,0x2d93,0x2da2,0x2db2,0x2dbf,0x2dce,0x2de2, +0x1db2,1,0,6,0x2e07,0x2e12,0x2e1f,0x2e2c,0x2e39,0x2e44,0x1df6,1,0,0x1e,0x2e61,0x2e70, +0x2e85,0x2e9a,0x2eaf,0x2ec3,0x2ed4,0x2ee8,0x2efb,0x2f0c,0x2f25,0x2f37,0x2f48,0x2f5c,0x2f6f,0x2f87,0x2f99,0x2fa4, +0x2fb4,0x2fc2,0x2fd7,0x2fec,0x3002,0x301c,0x3032,0x3042,0x3056,0x306a,0x307b,0x3093,0x2021,1,0,0x66, +0x30bb,0x30de,0x30e7,0x30f4,0x30ff,0x3108,0x3113,0x311c,0x3135,0x313a,0x3143,0x3160,0x3169,0x3176,0x317f,0x31a3, +0x31aa,0x31b3,0x31c6,0x31d1,0x31da,0x31e5,0x31fe,0x3207,0x3216,0x3221,0x322a,0x3235,0x323e,0x3245,0x324e,0x3259, +0x3262,0x327b,0x3284,0x3291,0x329c,0x32ad,0x32b8,0x32cd,0x32e4,0x32ed,0x32f6,0x330f,0x331a,0x3323,0x332c,0x3343, +0x3360,0x336b,0x337c,0x3387,0x338e,0x339b,0x33a8,0x33d5,0x33ea,0x33f3,0x340e,0x3431,0x3452,0x3473,0x3498,0x34bf, +0x34e0,0x3503,0x3524,0x354b,0x356c,0x3591,0x35b0,0x35cf,0x35ee,0x360b,0x362c,0x364d,0x3670,0x3695,0x36b4,0x36d3, +0x36f4,0x371b,0x3740,0x375f,0x3780,0x37a3,0x37be,0x37d7,0x37f2,0x380b,0x3828,0x3843,0x3860,0x387f,0x389c,0x38b9, +0x38d8,0x38f5,0x3910,0x392d,0x394a,0x397d,0x2366,1,0,6,0x39b5,0x39c4,0x39d4,0x39e4,0x39f4,0x3a05, +0x23c4,1,0,0x2b,0x3a23,0x3a2f,0x3a3d,0x3a4c,0x3a5b,0x3a6b,0x3a7c,0x3a90,0x3aa5,0x3abb,0x3ace,0x3ae2, +0x3af2,0x3afb,0x3b06,0x3b16,0x3b32,0x3b44,0x3b52,0x3b61,0x3b6d,0x3b82,0x3b96,0x3ba9,0x3bb7,0x3bcb,0x3bd9,0x3be3, +0x3bf5,0x3c01,0x3c0f,0x3c1f,0x3c26,0x3c2d,0x3c34,0x3c3b,0x3c42,0x3c58,0x3c79,0x85e,0x3c8b,0x3c96,0x3ca5,0x261d, +1,0,4,0x3cbf,0x3cca,0x3cd6,0x3ce0,0x2643,1,0,0xb9,0x3cf7,0x3d04,0x3d19,0x3d26,0x3d35, +0x3d43,0x3d52,0x3d61,0x3d73,0x3d82,0x3d90,0x3da1,0x3db0,0x3dbf,0x3dcc,0x3dd8,0x3de7,0x3df6,0x3e00,0x3e0d,0x3e1a, +0x3e29,0x3e37,0x3e46,0x3e52,0x3e5c,0x3e68,0x3e78,0x3e88,0x3e96,0x3ea2,0x3eb3,0x3ebf,0x3ecb,0x3ed9,0x3ee6,0x3ef2, +0x3eff,0xcfa,0x3f0c,0x3f1a,0x3f34,0x3f3d,0x3f4b,0x3f59,0x3f65,0x3f74,0x3f82,0x3f90,0x3f9c,0x3fab,0x3fb9,0x3fc7, +0x3fd4,0x3fe3,0x3ffe,0x400d,0x401e,0x402f,0x4042,0x4054,0x4063,0x4075,0x4084,0x4090,0x409b,0x1e0a,0x40a8,0x40b3, +0x40be,0x40c9,0x40d4,0x40ef,0x40fa,0x4105,0x4110,0x4123,0x4137,0x4142,0x4151,0x4160,0x416b,0x4176,0x4183,0x4192, +0x41a0,0x41ab,0x41c6,0x41d0,0x41e1,0x41f2,0x4201,0x4212,0x421d,0x4228,0x4233,0x423e,0x4249,0x4254,0x425f,0x4269, +0x4274,0x4284,0x428f,0x429d,0x42aa,0x42b5,0x42c4,0x42d1,0x42de,0x42ed,0x42fa,0x430b,0x431d,0x432d,0x4338,0x434b, +0x4362,0x4370,0x437d,0x4388,0x4395,0x43a6,0x43c2,0x43d8,0x43e3,0x4400,0x4410,0x441f,0x442a,0x4435,0x1f24,0x4441, +0x444c,0x4464,0x4474,0x4483,0x4491,0x449f,0x44aa,0x44b5,0x44c9,0x44e0,0x44f8,0x4508,0x4518,0x4528,0x453a,0x4545, +0x4550,0x455a,0x4566,0x4574,0x4587,0x4593,0x45a0,0x45ab,0x45c7,0x45d4,0x45e2,0x45fb,0x2824,0x460a,0x2645,0x4617, +0x4625,0x4637,0x4645,0x4651,0x4661,0x2a5f,0x466f,0x467b,0x4686,0x4691,0x469c,0x46b0,0x46be,0x46d5,0x46e1,0x46f5, +0x4703,0x4715,0x472b,0x4739,0x2fa6,1,0,6,0x4765,0x4778,0x4788,0x4796,0x47a7,0x47b7,0x3002,0x12, +0,1,0x47e1,0x47e7,0x300f,0x12,0,1,0x47e1,0x47e7,0x301c,1,0,3,0x47e1,0x47e7, +0x4820,0x3032,1,0,3,0x47e1,0x47e7,0x4820,0x3048,1,0,0x12,0x48aa,0x48b4,0x48c0,0x48c7, +0x48d2,0x48d7,0x48de,0x48e5,0x48ee,0x48f3,0x48f8,0x4908,0x85e,0x3c8b,0x4914,0x3c96,0x4924,0x3ca5,0x30f1,1, +0,0xf,0x48aa,0x494b,0x4955,0x495f,0x496a,0x3b61,0x4974,0x4980,0x4988,0x498f,0x4999,0x48c0,0x48c7,0x48d7, +0x49a3,0x3178,1,0,0x17,0x48aa,0x49c0,0x495f,0x49cc,0x49d9,0x49e7,0x3b61,0x49f2,0x48c0,0x4a03,0x48d7, +0x4a12,0x4a20,0x85e,0x3c79,0x4a2c,0x4a3d,0x3c8b,0x4914,0x3c96,0x4924,0x3ca5,0x4a4e,0x3295,1,0,3, +0x4a81,0x4a89,0x4a91,0x32ae,0x36,1,2,4,8,0xe,0x10,0x20,0x3e,0x40,0x80,0x100, +0x1c0,0x200,0x400,0x800,0xe00,0x1000,0x2000,0x4000,0x7000,0x8000,0x10000,0x20000,0x40000,0x78001,0x80000,0x100000, +0x200000,0x400000,0x800000,0x1000000,0x2000000,0x4000000,0x8000000,0xf000000,0x10000000,0x20000000,0x30f80000,0x2e61,0x2e70,0x2e85,0x2e9a,0x4ac8, +0x2eaf,0x2ec3,0x4abe,0x2ed4,0x2ee8,0x2efb,0x4ad9,0x2f0c,0x2f25,0x2f37,0x4af0,0x2f48,0x2f5c,0x2f6f,0x4b19,0x2f87, +0x2f99,0x2fa4,0x2fb4,0x4ab5,0x2fc2,0x2fd7,0x2fec,0x3002,0x301c,0x3032,0x3042,0x3056,0x306a,0x4b0f,0x307b,0x3093, +0x4afa }; -const uint8_t PropNameData::bytesTries[13417]={ +const uint8_t PropNameData::bytesTries[13765]={ 0,0x15,0x6d,0xc3,0x5d,0x73,0xc1,0xf7,0x76,0x5f,0x76,0x68,0x77,0x90,0x78,1, 0x64,0x50,0x69,0x10,0x64,1,0x63,0x30,0x73,0x62,0x13,0x74,0x61,0x72,0x74,0x63, 0x60,0x16,0x6f,0x6e,0x74,0x69,0x6e,0x75,0x65,0x61,0x13,0x69,0x67,0x69,0x74,0x81, @@ -180,766 +182,788 @@ const uint8_t PropNameData::bytesTries[13417]={ 0x6e,0x64,0x37,0x12,0x61,0x73,0x65,0x35,0x11,0x78,0x74,0x37,0xc2,5,1,0x62, 0xc3,0x12,0x6d,0xd9,0x20,0,0x1c,0x6e,0x65,0x72,0x61,0x6c,0x63,0x61,0x74,0x65, 0x67,0x6f,0x72,0x79,0xc2,5,0x13,0x6d,0x61,0x73,0x6b,0xd9,0x20,0,0x61,0xa2, -0x5c,0x62,0xa2,0x8a,0x63,0xa2,0xfc,0x64,0xa4,0xc9,0x65,2,0x61,0x3a,0x6d,0x58, -0x78,0x10,0x74,0x30,0x14,0x65,0x6e,0x64,0x65,0x72,0x31,0xc2,4,0x1b,0x73,0x74, -0x61,0x73,0x69,0x61,0x6e,0x77,0x69,0x64,0x74,0x68,0xc3,4,0x12,0x6f,0x6a,0x69, -0x92,2,0x63,0x40,0x6d,0x50,0x70,0x1a,0x72,0x65,0x73,0x65,0x6e,0x74,0x61,0x74, -0x69,0x6f,0x6e,0x95,0x17,0x6f,0x6d,0x70,0x6f,0x6e,0x65,0x6e,0x74,0x9b,0x16,0x6f, -0x64,0x69,0x66,0x69,0x65,0x72,0x96,0x13,0x62,0x61,0x73,0x65,0x99,3,0x67,0x44, -0x68,0x4a,0x6c,0x4e,0x73,0x1a,0x63,0x69,0x69,0x68,0x65,0x78,0x64,0x69,0x67,0x69, -0x74,0x23,0x10,0x65,0xd9,0x40,0,0x11,0x65,0x78,0x23,1,0x6e,0x38,0x70,0x11, -0x68,0x61,0x20,0x14,0x62,0x65,0x74,0x69,0x63,0x21,0x11,0x75,0x6d,0x79,4,0x63, -0xc3,0,0x69,0x3e,0x6c,0xa2,0x57,0x6d,0xa2,0x64,0x70,1,0x62,0xd9,0x40,0xd, -0x74,0xc3,0x15,0x11,0x64,0x69,2,0x63,0x54,0x6d,0x74,0x70,0x1b,0x61,0x69,0x72, -0x65,0x64,0x62,0x72,0x61,0x63,0x6b,0x65,0x74,0xd8,0x40,0xd,0x13,0x74,0x79,0x70, -0x65,0xc3,0x15,0x24,1,0x6c,0x30,0x6f,0x14,0x6e,0x74,0x72,0x6f,0x6c,0x25,0x12, -0x61,0x73,0x73,0xc3,0,0x26,0x14,0x69,0x72,0x72,0x6f,0x72,1,0x65,0x38,0x69, -0x16,0x6e,0x67,0x67,0x6c,0x79,0x70,0x68,0xd9,0x40,1,0x10,0x64,0x27,2,0x61, -0x32,0x6b,0xc3,1,0x6f,0x11,0x63,0x6b,0xc3,1,0x11,0x6e,0x6b,0x7b,0x10,0x67, -0xd9,0x40,1,6,0x68,0x7c,0x68,0x54,0x69,0x85,0x6f,0xa2,0x6f,0x77,4,0x63, -0x30,0x6b,0x36,0x6c,0x87,0x74,0x8b,0x75,0x89,1,0x66,0x8d,0x6d,0x8f,0x11,0x63, -0x66,0x91,0x18,0x61,0x6e,0x67,0x65,0x73,0x77,0x68,0x65,0x6e,4,0x63,0x44,0x6c, -0x6c,0x6e,0x7e,0x74,0x98,0x75,0x18,0x70,0x70,0x65,0x72,0x63,0x61,0x73,0x65,0x64, -0x89,0x12,0x61,0x73,0x65,1,0x66,0x30,0x6d,0x14,0x61,0x70,0x70,0x65,0x64,0x8f, -0x14,0x6f,0x6c,0x64,0x65,0x64,0x8d,0x18,0x6f,0x77,0x65,0x72,0x63,0x61,0x73,0x65, -0x64,0x87,0x1c,0x66,0x6b,0x63,0x63,0x61,0x73,0x65,0x66,0x6f,0x6c,0x64,0x65,0x64, -0x91,0x18,0x69,0x74,0x6c,0x65,0x63,0x61,0x73,0x65,0x64,0x8b,0x13,0x6d,0x70,0x65, -0x78,0x33,0x61,0x2e,0x63,0xa2,0x48,0x66,0xd9,0x40,2,1,0x6e,0x72,0x73,0x10, -0x65,3,0x64,0x83,0x66,0x3a,0x69,0x4a,0x73,0x17,0x65,0x6e,0x73,0x69,0x74,0x69, -0x76,0x65,0x65,0x15,0x6f,0x6c,0x64,0x69,0x6e,0x67,0xd9,0x40,2,0x17,0x67,0x6e, -0x6f,0x72,0x61,0x62,0x6c,0x65,0x85,0x13,0x6f,0x6e,0x69,0x63,0x1f,0x61,0x6c,0x63, -0x6f,0x6d,0x62,0x69,0x6e,0x69,0x6e,0x67,0x63,0x6c,0x61,0x73,0x73,0xc3,2,0x10, -0x63,0xc3,2,3,0x61,0x30,0x65,0x34,0x69,0xa2,0x41,0x74,0xc3,3,0x11,0x73, -0x68,0x29,2,0x63,0x3a,0x66,0x58,0x70,0x2c,0x16,0x72,0x65,0x63,0x61,0x74,0x65, -0x64,0x2d,0x1d,0x6f,0x6d,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x74,0x79,0x70, -0x65,0xc3,3,0x15,0x61,0x75,0x6c,0x74,0x69,0x67,0x1f,0x6e,0x6f,0x72,0x61,0x62, -0x6c,0x65,0x63,0x6f,0x64,0x65,0x70,0x6f,0x69,0x6e,0x74,0x2b,0x2a,0x10,0x61,0x2e, -0x15,0x63,0x72,0x69,0x74,0x69,0x63,0x2f,3,0x66,0x34,0x6e,0x3e,0x74,0x42,0x79, -0x22,0x11,0x65,0x73,0x23,0x20,0x13,0x61,0x6c,0x73,0x65,0x21,0x20,0x10,0x6f,0x21, -0x22,0x12,0x72,0x75,0x65,0x23,0xa,0x6b,0x5b,0x6f,0x23,0x6f,0x3c,0x72,0x4c,0x76, -1,0x69,0x24,0x72,0x33,0x13,0x72,0x61,0x6d,0x61,0x33,0x10,0x76,0x22,0x14,0x65, -0x72,0x6c,0x61,0x79,0x23,0xa2,0xe2,0x13,0x69,0x67,0x68,0x74,0xa3,0xe2,0x6b,0x58, -0x6c,0x74,0x6e,3,0x6b,0x2f,0x6f,0x30,0x72,0x21,0x75,0x12,0x6b,0x74,0x61,0x2f, -0x19,0x74,0x72,0x65,0x6f,0x72,0x64,0x65,0x72,0x65,0x64,0x21,1,0x61,0x24,0x76, -0x31,0x18,0x6e,0x61,0x76,0x6f,0x69,0x63,0x69,0x6e,0x67,0x31,0xa2,0xe0,0x12,0x65, -0x66,0x74,0xa3,0xe0,0x61,0x5c,0x62,0xa2,0x77,0x63,0xa2,0x96,0x64,0xa4,0xa,0x69, -1,0x6f,0x26,0x73,0xa3,0xf0,0x1a,0x74,0x61,0x73,0x75,0x62,0x73,0x63,0x72,0x69, -0x70,0x74,0xa3,0xf0,0xa2,0xe6,3,0x62,0xa0,0x6c,0xa3,0xe4,0x72,0xa3,0xe8,0x74, -2,0x61,0x74,0x62,0x7c,0x74,0x14,0x61,0x63,0x68,0x65,0x64,1,0x61,0x3e,0x62, -0x13,0x65,0x6c,0x6f,0x77,0xa2,0xca,0x13,0x6c,0x65,0x66,0x74,0xa3,0xc8,0x13,0x62, -0x6f,0x76,0x65,0xa2,0xd6,0x14,0x72,0x69,0x67,0x68,0x74,0xa3,0xd8,0xa2,0xd6,0x10, -0x72,0xa3,0xd8,0xa2,0xca,0x10,0x6c,0xa3,0xc8,0x12,0x6f,0x76,0x65,0xa2,0xe6,1, -0x6c,0x30,0x72,0x13,0x69,0x67,0x68,0x74,0xa3,0xe8,0x12,0x65,0x66,0x74,0xa3,0xe4, -0xa2,0xdc,2,0x65,0x2c,0x6c,0xa3,0xda,0x72,0xa3,0xde,0x12,0x6c,0x6f,0x77,0xa2, -0xdc,1,0x6c,0x30,0x72,0x13,0x69,0x67,0x68,0x74,0xa3,0xde,0x12,0x65,0x66,0x74, -0xa3,0xda,0x11,0x63,0x63,4,0x31,0x3c,0x32,0xa2,0x42,0x33,0xa2,0x56,0x38,0xa2, -0x64,0x39,0x10,0x31,0xa3,0x5b,9,0x35,0xa,0x35,0x3f,0x36,0x41,0x37,0x43,0x38, -0x45,0x39,0x47,0x30,0x30,0x31,0x3c,0x32,0x42,0x33,0x4e,0x34,0x3d,0x34,1,0x33, -0xa3,0x67,0x37,0xa3,0x6b,0x36,0x10,0x38,0xa3,0x76,0x38,1,0x32,0xa3,0x7a,0x39, -0xa3,0x81,0x3a,2,0x30,0xa3,0x82,0x32,0xa3,0x84,0x33,0xa3,0x85,9,0x35,0xa, -0x35,0x53,0x36,0x55,0x37,0x57,0x38,0x59,0x39,0x5b,0x30,0x49,0x31,0x4b,0x32,0x4d, -0x33,0x4f,0x34,0x51,6,0x33,8,0x33,0x63,0x34,0x65,0x35,0x67,0x36,0x69,0x30, -0x5d,0x31,0x5f,0x32,0x61,0x10,0x34,0xa3,0x54,2,0x61,0xa3,0xea,0x62,0xa3,0xe9, -0x6f,0x13,0x75,0x62,0x6c,0x65,1,0x61,0x30,0x62,0x13,0x65,0x6c,0x6f,0x77,0xa3, -0xe9,0x13,0x62,0x6f,0x76,0x65,0xa3,0xea,0xb,0x6e,0xc0,0xca,0x72,0x5f,0x72,0x46, -0x73,0xa2,0x48,0x77,1,0x68,0x24,0x73,0x33,0x17,0x69,0x74,0x65,0x73,0x70,0x61, -0x63,0x65,0x33,0x22,1,0x69,0x30,0x6c,2,0x65,0x3d,0x69,0x4b,0x6f,0x3f,0x18, -0x67,0x68,0x74,0x74,0x6f,0x6c,0x65,0x66,0x74,0x22,2,0x65,0x38,0x69,0x48,0x6f, -0x16,0x76,0x65,0x72,0x72,0x69,0x64,0x65,0x3f,0x17,0x6d,0x62,0x65,0x64,0x64,0x69, -0x6e,0x67,0x3d,0x15,0x73,0x6f,0x6c,0x61,0x74,0x65,0x4b,0x30,0x1e,0x65,0x67,0x6d, -0x65,0x6e,0x74,0x73,0x65,0x70,0x61,0x72,0x61,0x74,0x6f,0x72,0x31,0x6e,0xa2,0x41, -0x6f,0xa2,0x53,0x70,2,0x61,0x66,0x64,0x86,0x6f,0x1b,0x70,0x64,0x69,0x72,0x65, -0x63,0x74,0x69,0x6f,0x6e,0x61,0x6c,1,0x66,0x32,0x69,0x15,0x73,0x6f,0x6c,0x61, -0x74,0x65,0x4d,0x14,0x6f,0x72,0x6d,0x61,0x74,0x41,0x1f,0x72,0x61,0x67,0x72,0x61, -0x70,0x68,0x73,0x65,0x70,0x61,0x72,0x61,0x74,0x6f,0x72,0x2f,1,0x66,0x41,0x69, -0x4d,1,0x6f,0x28,0x73,0x10,0x6d,0x43,0x1b,0x6e,0x73,0x70,0x61,0x63,0x69,0x6e, -0x67,0x6d,0x61,0x72,0x6b,0x43,1,0x6e,0x35,0x74,0x19,0x68,0x65,0x72,0x6e,0x65, -0x75,0x74,0x72,0x61,0x6c,0x35,0x65,0x88,0x65,0x98,0x66,0xa2,0x6a,0x6c,0x20,1, -0x65,0x30,0x72,2,0x65,0x37,0x69,0x49,0x6f,0x39,0x18,0x66,0x74,0x74,0x6f,0x72, -0x69,0x67,0x68,0x74,0x20,2,0x65,0x38,0x69,0x48,0x6f,0x16,0x76,0x65,0x72,0x72, -0x69,0x64,0x65,0x39,0x17,0x6d,0x62,0x65,0x64,0x64,0x69,0x6e,0x67,0x37,0x15,0x73, -0x6f,0x6c,0x61,0x74,0x65,0x49,3,0x6e,0x25,0x73,0x27,0x74,0x29,0x75,0x15,0x72, -0x6f,0x70,0x65,0x61,0x6e,2,0x6e,0x3c,0x73,0x46,0x74,0x18,0x65,0x72,0x6d,0x69, -0x6e,0x61,0x74,0x6f,0x72,0x29,0x14,0x75,0x6d,0x62,0x65,0x72,0x25,0x17,0x65,0x70, -0x61,0x72,0x61,0x74,0x6f,0x72,0x27,1,0x69,0x28,0x73,0x10,0x69,0x47,0x1f,0x72, -0x73,0x74,0x73,0x74,0x72,0x6f,0x6e,0x67,0x69,0x73,0x6f,0x6c,0x61,0x74,0x65,0x47, -0x61,0x4e,0x62,0x84,0x63,1,0x6f,0x24,0x73,0x2d,0x1c,0x6d,0x6d,0x6f,0x6e,0x73, -0x65,0x70,0x61,0x72,0x61,0x74,0x6f,0x72,0x2d,2,0x6c,0x3b,0x6e,0x2b,0x72,0x13, -0x61,0x62,0x69,0x63,1,0x6c,0x30,0x6e,0x14,0x75,0x6d,0x62,0x65,0x72,0x2b,0x14, -0x65,0x74,0x74,0x65,0x72,0x3b,0x2e,1,0x6e,0x45,0x6f,0x1c,0x75,0x6e,0x64,0x61, -0x72,0x79,0x6e,0x65,0x75,0x74,0x72,0x61,0x6c,0x45,0,0x16,0x6d,0xc7,0xc5,0x74, -0xc1,0xb8,0x77,0x57,0x77,0x48,0x79,0x5c,0x7a,0x1d,0x61,0x6e,0x61,0x62,0x61,0x7a, -0x61,0x72,0x73,0x71,0x75,0x61,0x72,0x65,0xa5,0x18,0x18,0x61,0x72,0x61,0x6e,0x67, -0x63,0x69,0x74,0x69,0xa3,0xfc,0x10,0x69,2,0x6a,0x3c,0x72,0x68,0x73,0x17,0x79, -0x6c,0x6c,0x61,0x62,0x6c,0x65,0x73,0xa3,0x48,0x12,0x69,0x6e,0x67,0xa2,0x74,0x1e, -0x68,0x65,0x78,0x61,0x67,0x72,0x61,0x6d,0x73,0x79,0x6d,0x62,0x6f,0x6c,0x73,0xa3, -0x74,0x16,0x61,0x64,0x69,0x63,0x61,0x6c,0x73,0xa3,0x49,0x74,0xa2,0x59,0x75,0xa4, -0x12,0x76,2,0x61,0x36,0x65,0x7a,0x73,0xa2,0x6c,0x12,0x73,0x75,0x70,0xa3,0x7d, -1,0x69,0xa3,0x9f,0x72,0x1e,0x69,0x61,0x74,0x69,0x6f,0x6e,0x73,0x65,0x6c,0x65, -0x63,0x74,0x6f,0x72,0x73,0xa2,0x6c,0x19,0x73,0x75,0x70,0x70,0x6c,0x65,0x6d,0x65, -0x6e,0x74,0xa3,0x7d,1,0x64,0x3c,0x72,0x19,0x74,0x69,0x63,0x61,0x6c,0x66,0x6f, -0x72,0x6d,0x73,0xa3,0x91,0x14,0x69,0x63,0x65,0x78,0x74,0xa2,0xaf,0x16,0x65,0x6e, -0x73,0x69,0x6f,0x6e,0x73,0xa3,0xaf,4,0x61,0x68,0x65,0xa2,0x8a,0x68,0xa2,0x8d, -0x69,0xa2,0x95,0x72,0x1c,0x61,0x6e,0x73,0x70,0x6f,0x72,0x74,0x61,0x6e,0x64,0x6d, -0x61,0x70,0xa2,0xcf,0x16,0x73,0x79,0x6d,0x62,0x6f,0x6c,0x73,0xa3,0xcf,4,0x67, -0x58,0x69,0x7e,0x6b,0xa2,0x58,0x6d,0xa2,0x5a,0x6e,0x12,0x67,0x75,0x74,0xa4,0x10, -0x19,0x63,0x6f,0x6d,0x70,0x6f,0x6e,0x65,0x6e,0x74,0x73,0xa5,0x11,2,0x61,0x2a, -0x62,0x32,0x73,0xa3,0x60,0x12,0x6c,0x6f,0x67,0xa3,0x62,0x13,0x61,0x6e,0x77,0x61, -0xa3,0x65,3,0x6c,0x52,0x74,0x56,0x76,0x5e,0x78,0x16,0x75,0x61,0x6e,0x6a,0x69, -0x6e,0x67,0xa2,0x7c,0x16,0x73,0x79,0x6d,0x62,0x6f,0x6c,0x73,0xa3,0x7c,0x10,0x65, -0xa3,0x70,0x12,0x68,0x61,0x6d,0xa3,0xae,0x12,0x69,0x65,0x74,0xa3,0xb7,0x11,0x72, -0x69,0xa3,0xdc,0x11,0x69,0x6c,0x49,0x13,0x6c,0x75,0x67,0x75,0x4b,0x10,0x61,1, -0x61,0x24,0x69,0x53,0x11,0x6e,0x61,0x3d,2,0x62,0x34,0x66,0x3c,0x72,0x13,0x68, -0x75,0x74,0x61,0xa3,0xfb,0x13,0x65,0x74,0x61,0x6e,0x57,0x14,0x69,0x6e,0x61,0x67, -0x68,0xa3,0x90,2,0x63,0x82,0x67,0x92,0x6e,0x1f,0x69,0x66,0x69,0x65,0x64,0x63, -0x61,0x6e,0x61,0x64,0x69,0x61,0x6e,0x61,0x62,0x6f,0x1f,0x72,0x69,0x67,0x69,0x6e, -0x61,0x6c,0x73,0x79,0x6c,0x6c,0x61,0x62,0x69,0x63,0x73,0x62,0x17,0x65,0x78,0x74, -0x65,0x6e,0x64,0x65,0x64,0xa3,0xad,0x11,0x61,0x73,0x62,0x12,0x65,0x78,0x74,0xa3, -0xad,0x15,0x61,0x72,0x69,0x74,0x69,0x63,0xa3,0x78,0x70,0xc2,0xeb,0x70,0xa6,1, -0x72,0xa6,0xbd,0x73,7,0x6f,0xc1,0x75,0x6f,0x74,0x70,0x9c,0x75,0xa2,0x5d,0x79, -1,0x6c,0x4c,0x72,0x12,0x69,0x61,0x63,0x3a,0x12,0x73,0x75,0x70,0xa4,0x17,0x16, -0x70,0x6c,0x65,0x6d,0x65,0x6e,0x74,0xa5,0x17,0x17,0x6f,0x74,0x69,0x6e,0x61,0x67, -0x72,0x69,0xa3,0x8f,1,0x72,0x30,0x79,0x13,0x6f,0x6d,0x62,0x6f,0xa5,0x16,0x17, -0x61,0x73,0x6f,0x6d,0x70,0x65,0x6e,0x67,0xa3,0xda,1,0x61,0x32,0x65,0x14,0x63, -0x69,0x61,0x6c,0x73,0xa3,0x56,0x12,0x63,0x69,0x6e,0x1f,0x67,0x6d,0x6f,0x64,0x69, -0x66,0x69,0x65,0x72,0x6c,0x65,0x74,0x74,0x65,0x72,0x73,0x2d,2,0x6e,0x48,0x70, -0x76,0x74,0x1d,0x74,0x6f,0x6e,0x73,0x69,0x67,0x6e,0x77,0x72,0x69,0x74,0x69,0x6e, -0x67,0xa5,6,0x15,0x64,0x61,0x6e,0x65,0x73,0x65,0xa2,0x9b,0x12,0x73,0x75,0x70, -0xa2,0xdb,0x16,0x70,0x6c,0x65,0x6d,0x65,0x6e,0x74,0xa3,0xdb,4,0x61,0xa2,0xa8, -0x65,0x5c,0x6d,0x9e,0x70,0xa2,0x4b,0x73,0x13,0x79,0x6d,0x62,0x6f,0x1f,0x6c,0x73, -0x61,0x6e,0x64,0x70,0x69,0x63,0x74,0x6f,0x67,0x72,0x61,0x70,0x68,0x73,0xa5,5, -0x10,0x72,1,0x61,0x4e,0x73,0x12,0x63,0x72,0x69,0x1f,0x70,0x74,0x73,0x61,0x6e, -0x64,0x73,0x75,0x62,0x73,0x63,0x72,0x69,0x70,0x74,0x73,0x73,0x14,0x6e,0x64,0x73, -0x75,0x62,0x73,0x1b,0x61,0x74,0x68,0x6f,0x70,0x65,0x72,0x61,0x74,0x6f,0x72,0x73, -0xa3,0x6a,1,0x6c,0x40,0x75,1,0x61,0x6e,0x6e,0x17,0x63,0x74,0x75,0x61,0x74, -0x69,0x6f,0x6e,0xa3,0x8e,0x15,0x65,0x6d,0x65,0x6e,0x74,0x61,1,0x6c,0x50,0x72, -0x1e,0x79,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x75,0x73,0x65,0x61,0x72,0x65,0x61, -1,0x61,0xa3,0x6d,0x62,0xa3,0x6e,3,0x61,0x5c,0x6d,0x78,0x70,0xa2,0x41,0x73, -0x13,0x79,0x6d,0x62,0x6f,0x1f,0x6c,0x73,0x61,0x6e,0x64,0x70,0x69,0x63,0x74,0x6f, -0x67,0x72,0x61,0x70,0x68,0x73,0xa5,5,0x14,0x72,0x72,0x6f,0x77,0x73,2,0x61, -0xa3,0x67,0x62,0xa3,0x68,0x63,0xa3,0xfa,0x13,0x61,0x74,0x68,0x65,0x1f,0x6d,0x61, -0x74,0x69,0x63,0x61,0x6c,0x6f,0x70,0x65,0x72,0x61,0x74,0x6f,0x72,0x73,0xa3,0x6a, -0x19,0x75,0x6e,0x63,0x74,0x75,0x61,0x74,0x69,0x6f,0x6e,0xa3,0x8e,0x61,0x5a,0x68, -0x84,0x69,0xa2,0x5b,0x6d,0x16,0x61,0x6c,0x6c,0x66,0x6f,0x72,0x6d,1,0x73,0xa3, -0x54,0x76,0x16,0x61,0x72,0x69,0x61,0x6e,0x74,0x73,0xa3,0x54,1,0x6d,0x36,0x75, -0x16,0x72,0x61,0x73,0x68,0x74,0x72,0x61,0xa3,0xa1,0x15,0x61,0x72,0x69,0x74,0x61, -0x6e,0xa3,0xac,1,0x61,0x52,0x6f,0x13,0x72,0x74,0x68,0x61,0x1f,0x6e,0x64,0x66, -0x6f,0x72,0x6d,0x61,0x74,0x63,0x6f,0x6e,0x74,0x72,0x6f,0x6c,0x73,0xa3,0xf7,1, -0x72,0x2e,0x76,0x12,0x69,0x61,0x6e,0xa3,0x79,0x12,0x61,0x64,0x61,0xa3,0xd9,1, -0x64,0x50,0x6e,0x13,0x68,0x61,0x6c,0x61,0x50,0x1d,0x61,0x72,0x63,0x68,0x61,0x69, -0x63,0x6e,0x75,0x6d,0x62,0x65,0x72,0x73,0xa3,0xf9,0x13,0x64,0x68,0x61,0x6d,0xa3, -0xf8,5,0x72,0x35,0x72,0x44,0x73,0x64,0x75,1,0x61,0xa3,0x4e,0x6e,0x17,0x63, -0x74,0x75,0x61,0x74,0x69,0x6f,0x6e,0x71,0x17,0x69,0x76,0x61,0x74,0x65,0x75,0x73, -0x65,0xa2,0x4e,0x13,0x61,0x72,0x65,0x61,0xa3,0x4e,0x1b,0x61,0x6c,0x74,0x65,0x72, -0x70,0x61,0x68,0x6c,0x61,0x76,0x69,0xa3,0xf6,0x61,0x40,0x68,0x82,0x6c,0x19,0x61, -0x79,0x69,0x6e,0x67,0x63,0x61,0x72,0x64,0x73,0xa3,0xcc,2,0x68,0x38,0x6c,0x4a, -0x75,0x15,0x63,0x69,0x6e,0x68,0x61,0x75,0xa3,0xf5,0x17,0x61,0x77,0x68,0x68,0x6d, -0x6f,0x6e,0x67,0xa3,0xf3,0x15,0x6d,0x79,0x72,0x65,0x6e,0x65,0xa3,0xf4,1,0x61, -0x8e,0x6f,1,0x65,0x74,0x6e,0x16,0x65,0x74,0x69,0x63,0x65,0x78,0x74,0xa2,0x72, -1,0x65,0x2c,0x73,0x11,0x75,0x70,0xa3,0x8d,0x15,0x6e,0x73,0x69,0x6f,0x6e,0x73, -0xa2,0x72,0x19,0x73,0x75,0x70,0x70,0x6c,0x65,0x6d,0x65,0x6e,0x74,0xa3,0x8d,0x15, -0x6e,0x69,0x63,0x69,0x61,0x6e,0xa3,0x97,1,0x67,0x3e,0x69,0x13,0x73,0x74,0x6f, -0x73,0xa2,0xa6,0x13,0x64,0x69,0x73,0x63,0xa3,0xa6,0x12,0x73,0x70,0x61,0xa3,0x96, -1,0x65,0x5c,0x75,1,0x6d,0x2a,0x6e,0x11,0x69,0x63,0x67,0x10,0x69,0xa2,0xc0, -0x1d,0x6e,0x75,0x6d,0x65,0x72,0x61,0x6c,0x73,0x79,0x6d,0x62,0x6f,0x6c,0x73,0xa3, -0xc0,0x13,0x6a,0x61,0x6e,0x67,0xa3,0xa3,0x6d,0xa2,0xc3,0x6e,0xa6,0xd2,0x6f,5, -0x70,0x4b,0x70,0x46,0x72,0x7a,0x73,1,0x61,0x30,0x6d,0x13,0x61,0x6e,0x79,0x61, -0xa3,0x7a,0x11,0x67,0x65,0xa5,0xf,0x18,0x74,0x69,0x63,0x61,0x6c,0x63,0x68,0x61, -0x72,0x1f,0x61,0x63,0x74,0x65,0x72,0x72,0x65,0x63,0x6f,0x67,0x6e,0x69,0x74,0x69, -0x6f,0x6e,0x85,1,0x69,0x46,0x6e,0x1e,0x61,0x6d,0x65,0x6e,0x74,0x61,0x6c,0x64, -0x69,0x6e,0x67,0x62,0x61,0x74,0x73,0xa3,0xf2,0x11,0x79,0x61,0x47,0x63,0xa2,0x66, -0x67,0xa2,0x66,0x6c,1,0x63,0xa2,0x57,0x64,5,0x70,0x2d,0x70,0x36,0x73,0x56, -0x74,0x14,0x75,0x72,0x6b,0x69,0x63,0xa3,0xbf,0x11,0x65,0x72,1,0x6d,0x2e,0x73, -0x12,0x69,0x61,0x6e,0xa3,0x8c,0x11,0x69,0x63,0xa3,0xf1,0x1a,0x6f,0x75,0x74,0x68, -0x61,0x72,0x61,0x62,0x69,0x61,0x6e,0xa3,0xbb,0x68,0x42,0x69,0x54,0x6e,0x1a,0x6f, -0x72,0x74,0x68,0x61,0x72,0x61,0x62,0x69,0x61,0x6e,0xa3,0xf0,0x17,0x75,0x6e,0x67, -0x61,0x72,0x69,0x61,0x6e,0xa5,4,0x14,0x74,0x61,0x6c,0x69,0x63,0xa3,0x58,0x13, -0x68,0x69,0x6b,0x69,0xa3,0x9d,0x10,0x72,0x85,0x12,0x68,0x61,0x6d,0x65,6,0x6f, -0x86,0x6f,0x6c,0x72,0xa2,0x61,0x75,0xa2,0x62,0x79,0x14,0x61,0x6e,0x6d,0x61,0x72, -0x58,0x12,0x65,0x78,0x74,2,0x61,0xa3,0xb6,0x62,0xa3,0xee,0x65,0x13,0x6e,0x64, -0x65,0x64,1,0x61,0xa3,0xb6,0x62,0xa3,0xee,1,0x64,0x52,0x6e,0x15,0x67,0x6f, -0x6c,0x69,0x61,0x6e,0x6a,0x12,0x73,0x75,0x70,0xa4,0xd,0x16,0x70,0x6c,0x65,0x6d, -0x65,0x6e,0x74,0xa5,0xd,0x10,0x69,0xa2,0xec,0x13,0x66,0x69,0x65,0x72,1,0x6c, -0x3c,0x74,0x19,0x6f,0x6e,0x65,0x6c,0x65,0x74,0x74,0x65,0x72,0x73,0xa3,0x8a,0x15, -0x65,0x74,0x74,0x65,0x72,0x73,0x2d,0x10,0x6f,0xa3,0xed,1,0x6c,0x44,0x73,0x11, -0x69,0x63,0xa2,0x5c,0x18,0x61,0x6c,0x73,0x79,0x6d,0x62,0x6f,0x6c,0x73,0xa3,0x5c, -0x13,0x74,0x61,0x6e,0x69,0xa5,3,0x61,0xa2,0x9b,0x65,0xa4,0x36,0x69,1,0x61, -0xa2,0x8f,0x73,0x10,0x63,5,0x70,0x18,0x70,0xa2,0x71,0x73,0x36,0x74,0x17,0x65, -0x63,0x68,0x6e,0x69,0x63,0x61,0x6c,0x81,0x15,0x79,0x6d,0x62,0x6f,0x6c,0x73,0x8f, -0x61,0xa2,0x66,0x65,0x46,0x6d,0x19,0x61,0x74,0x68,0x73,0x79,0x6d,0x62,0x6f,0x6c, -0x73,1,0x61,0xa3,0x66,0x62,0xa3,0x69,0x17,0x6c,0x6c,0x61,0x6e,0x65,0x6f,0x75, -0x73,2,0x6d,0x3a,0x73,0x6c,0x74,0x17,0x65,0x63,0x68,0x6e,0x69,0x63,0x61,0x6c, -0x81,0x11,0x61,0x74,0x1f,0x68,0x65,0x6d,0x61,0x74,0x69,0x63,0x61,0x6c,0x73,0x79, -0x6d,0x62,0x6f,0x6c,0x73,1,0x61,0xa3,0x66,0x62,0xa3,0x69,0x15,0x79,0x6d,0x62, -0x6f,0x6c,0x73,0x8e,0x12,0x61,0x6e,0x64,1,0x61,0x3c,0x70,0x19,0x69,0x63,0x74, -0x6f,0x67,0x72,0x61,0x70,0x68,0x73,0xa3,0xcd,0x14,0x72,0x72,0x6f,0x77,0x73,0xa3, -0x73,0x10,0x6f,0xa3,0xd8,5,0x72,0x62,0x72,0xa2,0x4c,0x73,0xa2,0x50,0x74,0x10, -0x68,2,0x61,0x3a,0x65,0x4a,0x6f,0x17,0x70,0x65,0x72,0x61,0x74,0x6f,0x72,0x73, -0x7f,0x16,0x6c,0x70,0x68,0x61,0x6e,0x75,0x6d,0xa3,0x5d,0x16,0x6d,0x61,0x74,0x69, -0x63,0x61,0x6c,1,0x61,0x36,0x6f,0x17,0x70,0x65,0x72,0x61,0x74,0x6f,0x72,0x73, -0x7f,0x11,0x6c,0x70,0x1f,0x68,0x61,0x6e,0x75,0x6d,0x65,0x72,0x69,0x63,0x73,0x79, -0x6d,0x62,0x6f,0x6c,0x73,0xa3,0x5d,0x13,0x63,0x68,0x65,0x6e,0xa5,0xc,0x18,0x61, -0x72,0x61,0x6d,0x67,0x6f,0x6e,0x64,0x69,0xa5,0x14,0x68,0x4c,0x6c,0x7a,0x6e,1, -0x64,0x34,0x69,0x15,0x63,0x68,0x61,0x65,0x61,0x6e,0xa3,0xea,0x12,0x61,0x69,0x63, -0xa3,0xc6,1,0x61,0x3e,0x6a,0x12,0x6f,0x6e,0x67,0xa2,0xaa,0x14,0x74,0x69,0x6c, -0x65,0x73,0xa3,0xaa,0x13,0x6a,0x61,0x6e,0x69,0xa3,0xe9,0x15,0x61,0x79,0x61,0x6c, -0x61,0x6d,0x4f,2,0x65,0x66,0x6e,0x98,0x72,0x14,0x6f,0x69,0x74,0x69,0x63,1, -0x63,0x3c,0x68,0x19,0x69,0x65,0x72,0x6f,0x67,0x6c,0x79,0x70,0x68,0x73,0xa3,0xd7, -0x15,0x75,0x72,0x73,0x69,0x76,0x65,0xa3,0xd6,0x17,0x74,0x65,0x69,0x6d,0x61,0x79, -0x65,0x6b,0xa2,0xb8,0x12,0x65,0x78,0x74,0xa2,0xd5,0x16,0x65,0x6e,0x73,0x69,0x6f, -0x6e,0x73,0xa3,0xd5,0x18,0x64,0x65,0x6b,0x69,0x6b,0x61,0x6b,0x75,0x69,0xa3,0xeb, -5,0x6b,0x23,0x6b,0x4c,0x6f,0x50,0x75,1,0x6d,0x2c,0x73,0x11,0x68,0x75,0xa5, -0x15,0x17,0x62,0x65,0x72,0x66,0x6f,0x72,0x6d,0x73,0x7b,0x10,0x6f,0xa3,0x92,0x14, -0x62,0x6c,0x6f,0x63,0x6b,0x21,0x61,0x44,0x62,0x21,0x65,0x10,0x77,1,0x61,0xa5, -0xe,0x74,0x14,0x61,0x69,0x6c,0x75,0x65,0xa3,0x8b,0x16,0x62,0x61,0x74,0x61,0x65, -0x61,0x6e,0xa3,0xef,0x67,0xc3,0xcd,0x6a,0xc1,0x95,0x6a,0xa2,0xc5,0x6b,0xa2,0xde, -0x6c,4,0x61,0x54,0x65,0xa2,0x61,0x69,0xa2,0x78,0x6f,0xa2,0xa7,0x79,1,0x63, -0x2e,0x64,0x12,0x69,0x61,0x6e,0xa3,0xa9,0x12,0x69,0x61,0x6e,0xa3,0xa7,1,0x6f, -0x55,0x74,0x11,0x69,0x6e,1,0x31,0x82,0x65,0x11,0x78,0x74,4,0x61,0x5c,0x62, -0x29,0x63,0xa3,0x94,0x64,0xa3,0x95,0x65,0xa2,0xe7,0x13,0x6e,0x64,0x65,0x64,4, -0x61,0x36,0x62,0x29,0x63,0xa3,0x94,0x64,0xa3,0x95,0x65,0xa3,0xe7,0x26,0x18,0x64, -0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x6d,0x24,0x12,0x73,0x75,0x70,0x24,0x16, -0x70,0x6c,0x65,0x6d,0x65,0x6e,0x74,0x25,1,0x70,0x42,0x74,0x1d,0x74,0x65,0x72, -0x6c,0x69,0x6b,0x65,0x73,0x79,0x6d,0x62,0x6f,0x6c,0x73,0x79,0x12,0x63,0x68,0x61, -0xa3,0x9c,2,0x6d,0x2e,0x6e,0x34,0x73,0x10,0x75,0xa3,0xb0,0x11,0x62,0x75,0xa3, -0x6f,0x12,0x65,0x61,0x72,1,0x61,0xa3,0xe8,0x62,1,0x69,0x38,0x73,0x17,0x79, -0x6c,0x6c,0x61,0x62,0x61,0x72,0x79,0xa3,0x75,0x17,0x64,0x65,0x6f,0x67,0x72,0x61, -0x6d,0x73,0xa3,0x76,0x1a,0x77,0x73,0x75,0x72,0x72,0x6f,0x67,0x61,0x74,0x65,0x73, -0xa3,0x4d,0x10,0x61,1,0x6d,0x32,0x76,0x14,0x61,0x6e,0x65,0x73,0x65,0xa3,0xb5, -0x10,0x6f,0x5c,0x12,0x65,0x78,0x74,1,0x61,0xa3,0xb4,0x62,0xa3,0xb9,1,0x61, -0x80,0x68,3,0x61,0x3c,0x6d,0x4c,0x6f,0x64,0x75,0x15,0x64,0x61,0x77,0x61,0x64, -0x69,0xa3,0xe6,0x16,0x72,0x6f,0x73,0x68,0x74,0x68,0x69,0xa3,0x89,0x11,0x65,0x72, -0x68,0x16,0x73,0x79,0x6d,0x62,0x6f,0x6c,0x73,0xa3,0x71,0x12,0x6a,0x6b,0x69,0xa3, -0xe5,3,0x69,0x3a,0x6e,0x42,0x74,0xa2,0x51,0x79,0x13,0x61,0x68,0x6c,0x69,0xa3, -0xa2,0x12,0x74,0x68,0x69,0xa3,0xc1,3,0x61,0x34,0x62,0x76,0x67,0x7c,0x6e,0x12, -0x61,0x64,0x61,0x4d,1,0x65,0x40,0x73,0x11,0x75,0x70,0xa2,0xcb,0x16,0x70,0x6c, -0x65,0x6d,0x65,0x6e,0x74,0xa3,0xcb,0x11,0x78,0x74,1,0x61,0xa5,0x13,0x65,0x14, -0x6e,0x64,0x65,0x64,0x61,0xa5,0x13,0x11,0x75,0x6e,0xa3,0x42,0x11,0x78,0x69,0x96, -0x17,0x72,0x61,0x64,0x69,0x63,0x61,0x6c,0x73,0x97,0x14,0x61,0x6b,0x61,0x6e,0x61, -0x9e,1,0x65,0x4c,0x70,0x10,0x68,0x1f,0x6f,0x6e,0x65,0x74,0x69,0x63,0x65,0x78, -0x74,0x65,0x6e,0x73,0x69,0x6f,0x6e,0x73,0xa3,0x6b,0x11,0x78,0x74,0xa3,0x6b,0x67, -0xa2,0xa3,0x68,0xa4,0x53,0x69,3,0x64,0x4c,0x6d,0xa2,0x55,0x6e,0xa2,0x62,0x70, -0x13,0x61,0x65,0x78,0x74,0x2a,0x16,0x65,0x6e,0x73,0x69,0x6f,0x6e,0x73,0x2b,1, -0x63,0x99,0x65,0x17,0x6f,0x67,0x72,0x61,0x70,0x68,0x69,0x63,1,0x64,0x56,0x73, -0x15,0x79,0x6d,0x62,0x6f,0x6c,0x73,0xa4,0xb,0x1d,0x61,0x6e,0x64,0x70,0x75,0x6e, -0x63,0x74,0x75,0x61,0x74,0x69,0x6f,0x6e,0xa5,0xb,0x13,0x65,0x73,0x63,0x72,0x1f, -0x69,0x70,0x74,0x69,0x6f,0x6e,0x63,0x68,0x61,0x72,0x61,0x63,0x74,0x65,0x72,0x73, -0x99,0x1c,0x70,0x65,0x72,0x69,0x61,0x6c,0x61,0x72,0x61,0x6d,0x61,0x69,0x63,0xa3, -0xba,1,0x64,0x62,0x73,0x1b,0x63,0x72,0x69,0x70,0x74,0x69,0x6f,0x6e,0x61,0x6c, -0x70,0x61,1,0x68,0x32,0x72,0x14,0x74,0x68,0x69,0x61,0x6e,0xa3,0xbd,0x13,0x6c, -0x61,0x76,0x69,0xa3,0xbe,0x1c,0x69,0x63,0x6e,0x75,0x6d,0x62,0x65,0x72,0x66,0x6f, -0x72,0x6d,0x73,0xa3,0xb2,4,0x65,0x58,0x6c,0xa2,0x63,0x6f,0xa2,0x7b,0x72,0xa2, -0x7f,0x75,1,0x6a,0x30,0x72,0x14,0x6d,0x75,0x6b,0x68,0x69,0x43,0x14,0x61,0x72, -0x61,0x74,0x69,0x45,1,0x6e,0x8c,0x6f,1,0x6d,0x4e,0x72,0x13,0x67,0x69,0x61, -0x6e,0x5a,0x12,0x73,0x75,0x70,0xa2,0x87,0x16,0x70,0x6c,0x65,0x6d,0x65,0x6e,0x74, -0xa3,0x87,0x1a,0x65,0x74,0x72,0x69,0x63,0x73,0x68,0x61,0x70,0x65,0x73,0x8c,0x12, -0x65,0x78,0x74,0xa2,0xe3,0x14,0x65,0x6e,0x64,0x65,0x64,0xa3,0xe3,0x1e,0x65,0x72, -0x61,0x6c,0x70,0x75,0x6e,0x63,0x74,0x75,0x61,0x74,0x69,0x6f,0x6e,0x71,0x17,0x61, -0x67,0x6f,0x6c,0x69,0x74,0x69,0x63,0xa2,0x88,0x12,0x73,0x75,0x70,0xa4,0xa,0x16, -0x70,0x6c,0x65,0x6d,0x65,0x6e,0x74,0xa5,0xa,0x13,0x74,0x68,0x69,0x63,0xa3,0x59, -1,0x61,0x5c,0x65,0x11,0x65,0x6b,0x30,1,0x61,0x38,0x65,0x11,0x78,0x74,0x6e, -0x14,0x65,0x6e,0x64,0x65,0x64,0x6f,0x17,0x6e,0x64,0x63,0x6f,0x70,0x74,0x69,0x63, -0x31,0x13,0x6e,0x74,0x68,0x61,0xa3,0xe4,2,0x61,0xa2,0x48,0x65,0xa2,0xcf,0x69, -1,0x67,0x30,0x72,0x14,0x61,0x67,0x61,0x6e,0x61,0x9d,0x10,0x68,1,0x70,0x3a, -0x73,0x18,0x75,0x72,0x72,0x6f,0x67,0x61,0x74,0x65,0x73,0xa3,0x4b,1,0x72,0x3c, -0x75,0x19,0x73,0x75,0x72,0x72,0x6f,0x67,0x61,0x74,0x65,0x73,0xa3,0x4c,0x11,0x69, -0x76,0x1f,0x61,0x74,0x65,0x75,0x73,0x65,0x73,0x75,0x72,0x72,0x6f,0x67,0x61,0x74, -0x65,0x73,0xa3,0x4c,2,0x6c,0x32,0x6e,0x9a,0x74,0x12,0x72,0x61,0x6e,0xa5,2, -0x10,0x66,2,0x61,0x58,0x6d,0x70,0x77,0x14,0x69,0x64,0x74,0x68,0x61,0x1f,0x6e, -0x64,0x66,0x75,0x6c,0x6c,0x77,0x69,0x64,0x74,0x68,0x66,0x6f,0x72,0x6d,0x73,0xa3, -0x57,0x1a,0x6e,0x64,0x66,0x75,0x6c,0x6c,0x66,0x6f,0x72,0x6d,0x73,0xa3,0x57,0x13, -0x61,0x72,0x6b,0x73,0xa3,0x52,1,0x67,0x2e,0x75,0x12,0x6e,0x6f,0x6f,0xa3,0x63, -0x11,0x75,0x6c,0xa2,0x4a,2,0x63,0x3c,0x6a,0x5e,0x73,0x17,0x79,0x6c,0x6c,0x61, -0x62,0x6c,0x65,0x73,0xa3,0x4a,0x1f,0x6f,0x6d,0x70,0x61,0x74,0x69,0x62,0x69,0x6c, -0x69,0x74,0x79,0x6a,0x61,0x6d,0x6f,0xa3,0x41,0x12,0x61,0x6d,0x6f,0x5c,0x17,0x65, -0x78,0x74,0x65,0x6e,0x64,0x65,0x64,1,0x61,0xa3,0xb4,0x62,0xa3,0xb9,0x13,0x62, -0x72,0x65,0x77,0x37,0x61,0xa2,0xe9,0x62,0xa6,0x29,0x63,0xa6,0xfe,0x64,0xac,0x7c, -0x65,5,0x6d,0xa2,0x6d,0x86,0x6e,0x96,0x74,0x15,0x68,0x69,0x6f,0x70,0x69,0x63, -0x5e,1,0x65,0x40,0x73,0x11,0x75,0x70,0xa2,0x86,0x16,0x70,0x6c,0x65,0x6d,0x65, -0x6e,0x74,0xa3,0x86,0x11,0x78,0x74,0xa2,0x85,1,0x61,0xa3,0xc8,0x65,0x13,0x6e, -0x64,0x65,0x64,0xa2,0x85,0x10,0x61,0xa3,0xc8,0x16,0x6f,0x74,0x69,0x63,0x6f,0x6e, -0x73,0xa3,0xce,0x15,0x63,0x6c,0x6f,0x73,0x65,0x64,2,0x61,0x5a,0x63,0x9e,0x69, -0x1c,0x64,0x65,0x6f,0x67,0x72,0x61,0x70,0x68,0x69,0x63,0x73,0x75,0x70,0xa2,0xc4, -0x16,0x70,0x6c,0x65,0x6d,0x65,0x6e,0x74,0xa3,0xc4,0x16,0x6c,0x70,0x68,0x61,0x6e, -0x75,0x6d,0x86,1,0x65,0x2c,0x73,0x11,0x75,0x70,0xa3,0xc3,0x13,0x72,0x69,0x63, -0x73,0x86,0x18,0x75,0x70,0x70,0x6c,0x65,0x6d,0x65,0x6e,0x74,0xa3,0xc3,0x11,0x6a, -0x6b,0xa2,0x44,0x1f,0x6c,0x65,0x74,0x74,0x65,0x72,0x73,0x61,0x6e,0x64,0x6d,0x6f, -0x6e,0x74,0x68,0x73,0xa3,0x44,0x61,0x36,0x67,0x62,0x6c,0x14,0x62,0x61,0x73,0x61, -0x6e,0xa3,0xe2,0x13,0x72,0x6c,0x79,0x64,0x1f,0x79,0x6e,0x61,0x73,0x74,0x69,0x63, -0x63,0x75,0x6e,0x65,0x69,0x66,0x6f,0x72,0x6d,0xa5,1,0x10,0x79,0x1f,0x70,0x74, -0x69,0x61,0x6e,0x68,0x69,0x65,0x72,0x6f,0x67,0x6c,0x79,0x70,0x68,0x73,0xa3,0xc2, -7,0x6e,0xc0,0xe5,0x6e,0x3e,0x72,0xa2,0x5d,0x73,0xa2,0xd8,0x76,0x14,0x65,0x73, -0x74,0x61,0x6e,0xa3,0xbc,1,0x61,0x92,0x63,0x13,0x69,0x65,0x6e,0x74,1,0x67, -0x34,0x73,0x15,0x79,0x6d,0x62,0x6f,0x6c,0x73,0xa3,0xa5,0x13,0x72,0x65,0x65,0x6b, -1,0x6d,0x34,0x6e,0x15,0x75,0x6d,0x62,0x65,0x72,0x73,0xa3,0x7f,0x13,0x75,0x73, -0x69,0x63,0xa2,0x7e,0x19,0x61,0x6c,0x6e,0x6f,0x74,0x61,0x74,0x69,0x6f,0x6e,0xa3, -0x7e,0x10,0x74,0x1f,0x6f,0x6c,0x69,0x61,0x6e,0x68,0x69,0x65,0x72,0x6f,0x67,0x6c, -0x79,0x70,0x68,0x73,0xa3,0xfe,2,0x61,0x32,0x6d,0xa2,0x71,0x72,0x12,0x6f,0x77, -0x73,0x7d,0x12,0x62,0x69,0x63,0x38,3,0x65,0x4a,0x6d,0x66,0x70,0xa2,0x43,0x73, -0x11,0x75,0x70,0xa2,0x80,0x16,0x70,0x6c,0x65,0x6d,0x65,0x6e,0x74,0xa3,0x80,0x11, -0x78,0x74,1,0x61,0xa3,0xd2,0x65,0x14,0x6e,0x64,0x65,0x64,0x61,0xa3,0xd2,0x12, -0x61,0x74,0x68,0xa2,0xd3,0x18,0x65,0x6d,0x61,0x74,0x69,0x63,0x61,0x6c,0x61,0x1f, -0x6c,0x70,0x68,0x61,0x62,0x65,0x74,0x69,0x63,0x73,0x79,0x6d,0x62,0x6f,0x6c,0x73, -0xa3,0xd3,1,0x66,0x42,0x72,0x1e,0x65,0x73,0x65,0x6e,0x74,0x61,0x74,0x69,0x6f, -0x6e,0x66,0x6f,0x72,0x6d,0x73,1,0x61,0xa3,0x51,0x62,0xa3,0x55,0x14,0x65,0x6e, -0x69,0x61,0x6e,0x35,0x12,0x63,0x69,0x69,0x23,0x64,0x9e,0x65,0xa2,0x42,0x68,0xa2, -0x4d,0x6c,1,0x63,0x62,0x70,0x17,0x68,0x61,0x62,0x65,0x74,0x69,0x63,0x70,1, -0x66,0xa3,0x50,0x72,0x1e,0x65,0x73,0x65,0x6e,0x74,0x61,0x74,0x69,0x6f,0x6e,0x66, -0x6f,0x72,0x6d,0x73,0xa3,0x50,0x16,0x68,0x65,0x6d,0x69,0x63,0x61,0x6c,0xa2,0xd0, -0x16,0x73,0x79,0x6d,0x62,0x6f,0x6c,0x73,0xa3,0xd0,0x12,0x6c,0x61,0x6d,0xa5,7, -0x1a,0x67,0x65,0x61,0x6e,0x6e,0x75,0x6d,0x62,0x65,0x72,0x73,0xa3,0x77,0x11,0x6f, -0x6d,0xa3,0xfd,7,0x6f,0x71,0x6f,0x64,0x72,0xa2,0x41,0x75,0xa2,0x58,0x79,0x1b, -0x7a,0x61,0x6e,0x74,0x69,0x6e,0x65,0x6d,0x75,0x73,0x69,0x63,0xa2,0x5b,0x18,0x61, -0x6c,0x73,0x79,0x6d,0x62,0x6f,0x6c,0x73,0xa3,0x5b,1,0x70,0x34,0x78,0x16,0x64, -0x72,0x61,0x77,0x69,0x6e,0x67,0x89,0x14,0x6f,0x6d,0x6f,0x66,0x6f,0xa0,0x12,0x65, -0x78,0x74,0xa2,0x43,0x14,0x65,0x6e,0x64,0x65,0x64,0xa3,0x43,0x10,0x61,1,0x68, -0x40,0x69,0x12,0x6c,0x6c,0x65,0x92,0x17,0x70,0x61,0x74,0x74,0x65,0x72,0x6e,0x73, -0x93,0x11,0x6d,0x69,0xa3,0xc9,1,0x67,0x2c,0x68,0x11,0x69,0x64,0xa3,0x64,0x14, -0x69,0x6e,0x65,0x73,0x65,0xa3,0x81,0x61,0x48,0x65,0xa2,0x4e,0x68,0xa2,0x52,0x6c, -0x1a,0x6f,0x63,0x6b,0x65,0x6c,0x65,0x6d,0x65,0x6e,0x74,0x73,0x8b,3,0x6c,0x34, -0x6d,0x40,0x73,0x66,0x74,0x11,0x61,0x6b,0xa3,0xc7,0x14,0x69,0x6e,0x65,0x73,0x65, -0xa3,0x93,0x11,0x75,0x6d,0xa2,0xb1,0x12,0x73,0x75,0x70,0xa2,0xca,0x16,0x70,0x6c, -0x65,0x6d,0x65,0x6e,0x74,0xa3,0xca,1,0x69,0x30,0x73,0x13,0x61,0x76,0x61,0x68, -0xa3,0xdd,0x15,0x63,0x6c,0x61,0x74,0x69,0x6e,0x23,0x14,0x6e,0x67,0x61,0x6c,0x69, -0x41,0x16,0x61,0x69,0x6b,0x73,0x75,0x6b,0x69,0xa5,8,5,0x6f,0xc1,0x4c,0x6f, -0xa2,0x55,0x75,0xa4,0x10,0x79,1,0x70,0x9c,0x72,0x14,0x69,0x6c,0x6c,0x69,0x63, -0x32,1,0x65,0x4c,0x73,0x11,0x75,0x70,0xa2,0x61,0x16,0x70,0x6c,0x65,0x6d,0x65, -0x6e,0x74,0xa2,0x61,0x12,0x61,0x72,0x79,0xa3,0x61,0x11,0x78,0x74,3,0x61,0xa3, -0x9e,0x62,0xa3,0xa0,0x63,0xa5,9,0x65,0x13,0x6e,0x64,0x65,0x64,2,0x61,0xa3, -0x9e,0x62,0xa3,0xa0,0x63,0xa5,9,0x1c,0x72,0x69,0x6f,0x74,0x73,0x79,0x6c,0x6c, -0x61,0x62,0x61,0x72,0x79,0xa3,0x7b,3,0x6d,0x5a,0x6e,0xa2,0x95,0x70,0xa2,0xa0, -0x75,0x17,0x6e,0x74,0x69,0x6e,0x67,0x72,0x6f,0x64,0xa2,0x9a,0x17,0x6e,0x75,0x6d, -0x65,0x72,0x61,0x6c,0x73,0xa3,0x9a,2,0x62,0x3a,0x6d,0xa2,0x5f,0x70,0x15,0x61, -0x74,0x6a,0x61,0x6d,0x6f,0xa3,0x41,0x14,0x69,0x6e,0x69,0x6e,0x67,2,0x64,0x46, -0x68,0x9e,0x6d,0x1d,0x61,0x72,0x6b,0x73,0x66,0x6f,0x72,0x73,0x79,0x6d,0x62,0x6f, -0x6c,0x73,0x77,0x1e,0x69,0x61,0x63,0x72,0x69,0x74,0x69,0x63,0x61,0x6c,0x6d,0x61, -0x72,0x6b,0x73,0x2e,2,0x65,0x40,0x66,0xa6,0x1c,0x73,0x18,0x75,0x70,0x70,0x6c, -0x65,0x6d,0x65,0x6e,0x74,0xa3,0x83,0x16,0x78,0x74,0x65,0x6e,0x64,0x65,0x64,0xa3, -0xe0,0x17,0x61,0x6c,0x66,0x6d,0x61,0x72,0x6b,0x73,0xa3,0x52,0x11,0x6f,0x6e,0x1f, -0x69,0x6e,0x64,0x69,0x63,0x6e,0x75,0x6d,0x62,0x65,0x72,0x66,0x6f,0x72,0x6d,0x73, -0xa3,0xb2,0x1b,0x74,0x72,0x6f,0x6c,0x70,0x69,0x63,0x74,0x75,0x72,0x65,0x73,0x83, -0x12,0x74,0x69,0x63,0xa2,0x84,0x1b,0x65,0x70,0x61,0x63,0x74,0x6e,0x75,0x6d,0x62, -0x65,0x72,0x73,0xa3,0xdf,1,0x6e,0x3e,0x72,0x1b,0x72,0x65,0x6e,0x63,0x79,0x73, -0x79,0x6d,0x62,0x6f,0x6c,0x73,0x75,0x15,0x65,0x69,0x66,0x6f,0x72,0x6d,0xa2,0x98, -0x16,0x6e,0x75,0x6d,0x62,0x65,0x72,0x73,0xa2,0x99,0x1d,0x61,0x6e,0x64,0x70,0x75, -0x6e,0x63,0x74,0x75,0x61,0x74,0x69,0x6f,0x6e,0xa3,0x99,0x61,0xa2,0xdb,0x68,0xa4, -5,0x6a,0x10,0x6b,0xa2,0x47,4,0x63,0x86,0x65,0xa2,0x7d,0x72,0xa2,0x92,0x73, -0xa2,0xa4,0x75,0x1f,0x6e,0x69,0x66,0x69,0x65,0x64,0x69,0x64,0x65,0x6f,0x67,0x72, -0x61,0x70,0x68,0x73,0xa2,0x47,0x18,0x65,0x78,0x74,0x65,0x6e,0x73,0x69,0x6f,0x6e, -5,0x64,0x65,0x64,0xa3,0xd1,0x65,0xa5,0,0x66,0xa5,0x12,0x14,0x6f,0x6d,0x70, -0x61,0x74,0xa2,0x45,1,0x66,0x96,0x69,1,0x62,0x44,0x64,0x17,0x65,0x6f,0x67, -0x72,0x61,0x70,0x68,0x73,0xa2,0x4f,0x12,0x73,0x75,0x70,0xa3,0x5f,0x14,0x69,0x6c, -0x69,0x74,0x79,0xa2,0x45,1,0x66,0x54,0x69,0x18,0x64,0x65,0x6f,0x67,0x72,0x61, -0x70,0x68,0x73,0xa2,0x4f,0x19,0x73,0x75,0x70,0x70,0x6c,0x65,0x6d,0x65,0x6e,0x74, -0xa3,0x5f,0x13,0x6f,0x72,0x6d,0x73,0xa3,0x53,0x11,0x78,0x74,5,0x64,9,0x64, -0xa3,0xd1,0x65,0xa5,0,0x66,0xa5,0x12,0x61,0xa3,0x46,0x62,0xa3,0x5e,0x63,0xa3, -0xc5,0x19,0x61,0x64,0x69,0x63,0x61,0x6c,0x73,0x73,0x75,0x70,0x94,0x16,0x70,0x6c, -0x65,0x6d,0x65,0x6e,0x74,0x95,1,0x74,0x50,0x79,0x14,0x6d,0x62,0x6f,0x6c,0x73, -0x9a,0x1d,0x61,0x6e,0x64,0x70,0x75,0x6e,0x63,0x74,0x75,0x61,0x74,0x69,0x6f,0x6e, -0x9b,0x14,0x72,0x6f,0x6b,0x65,0x73,0xa3,0x82,2,0x6e,0x48,0x72,0x64,0x75,0x1d, -0x63,0x61,0x73,0x69,0x61,0x6e,0x61,0x6c,0x62,0x61,0x6e,0x69,0x61,0x6e,0xa3,0xde, -0x1d,0x61,0x64,0x69,0x61,0x6e,0x73,0x79,0x6c,0x6c,0x61,0x62,0x69,0x63,0x73,0x63, -0x12,0x69,0x61,0x6e,0xa3,0xa8,1,0x61,0x50,0x65,0x14,0x72,0x6f,0x6b,0x65,0x65, -0x60,0x12,0x73,0x75,0x70,0xa2,0xff,0x16,0x70,0x6c,0x65,0x6d,0x65,0x6e,0x74,0xa3, -0xff,1,0x6b,0x26,0x6d,0xa3,0xa4,0x11,0x6d,0x61,0xa3,0xd4,3,0x65,0x3e,0x69, -0x7e,0x6f,0xa2,0x5d,0x75,0x15,0x70,0x6c,0x6f,0x79,0x61,0x6e,0xa3,0xe1,1,0x73, -0x50,0x76,0x16,0x61,0x6e,0x61,0x67,0x61,0x72,0x69,0x3e,0x12,0x65,0x78,0x74,0xa2, -0xb3,0x14,0x65,0x6e,0x64,0x65,0x64,0xa3,0xb3,0x13,0x65,0x72,0x65,0x74,0xa3,0x5a, -1,0x61,0x30,0x6e,0x14,0x67,0x62,0x61,0x74,0x73,0x91,0x18,0x63,0x72,0x69,0x74, -0x69,0x63,0x61,0x6c,0x73,0x2e,2,0x65,0x30,0x66,0x36,0x73,0x11,0x75,0x70,0xa3, -0x83,0x11,0x78,0x74,0xa3,0xe0,0x18,0x6f,0x72,0x73,0x79,0x6d,0x62,0x6f,0x6c,0x73, -0x77,0x13,0x6d,0x69,0x6e,0x6f,0xa2,0xab,0x14,0x74,0x69,0x6c,0x65,0x73,0xa3,0xab, -8,0x6d,0x5f,0x6d,0x3a,0x6e,0x48,0x73,0x7a,0x76,0xa2,0x4b,0x77,0x12,0x69,0x64, -0x65,0x43,0x11,0x65,0x64,0x32,0x12,0x69,0x61,0x6c,0x33,2,0x61,0x40,0x62,0x37, -0x6f,1,0x62,0x28,0x6e,0x10,0x65,0x21,0x13,0x72,0x65,0x61,0x6b,0x37,0x10,0x72, -0x34,0x12,0x72,0x6f,0x77,0x35,2,0x6d,0x38,0x71,0x46,0x75,1,0x62,0x3d,0x70, -0x3e,0x11,0x65,0x72,0x3f,1,0x61,0x24,0x6c,0x39,0x11,0x6c,0x6c,0x39,1,0x72, -0x3b,0x75,0x12,0x61,0x72,0x65,0x3b,0x12,0x65,0x72,0x74,0x40,0x13,0x69,0x63,0x61, -0x6c,0x41,0x63,0x58,0x65,0x92,0x66,0x96,0x69,1,0x6e,0x36,0x73,0x10,0x6f,0x30, -0x14,0x6c,0x61,0x74,0x65,0x64,0x31,0x11,0x69,0x74,0x2e,0x12,0x69,0x61,0x6c,0x2f, -2,0x61,0x36,0x69,0x48,0x6f,0x10,0x6d,0x24,0x12,0x70,0x61,0x74,0x25,0x10,0x6e, -0x22,0x15,0x6f,0x6e,0x69,0x63,0x61,0x6c,0x23,0x13,0x72,0x63,0x6c,0x65,0x27,0x11, -0x6e,0x63,0x27,2,0x69,0x3a,0x6f,0x44,0x72,0x10,0x61,0x2c,0x14,0x63,0x74,0x69, -0x6f,0x6e,0x2d,0x10,0x6e,0x28,0x11,0x61,0x6c,0x29,0x11,0x6e,0x74,0x2b,4,0x61, -0x3a,0x66,0x4c,0x68,0x5e,0x6e,0x70,0x77,0x2a,0x12,0x69,0x64,0x65,0x2b,0x22,0x17, -0x6d,0x62,0x69,0x67,0x75,0x6f,0x75,0x73,0x23,0x26,0x17,0x75,0x6c,0x6c,0x77,0x69, -0x64,0x74,0x68,0x27,0x24,0x17,0x61,0x6c,0x66,0x77,0x69,0x64,0x74,0x68,0x25,0x20, -1,0x61,0x30,0x65,0x14,0x75,0x74,0x72,0x61,0x6c,0x21,0x28,0x13,0x72,0x72,0x6f, -0x77,0x29,0xd,0x6e,0xc0,0xfb,0x73,0x6d,0x73,0x3a,0x74,0x98,0x75,0xa2,0x49,0x7a, -2,0x6c,0x3b,0x70,0x3d,0x73,0x39,5,0x6f,0x28,0x6f,0x57,0x70,0x34,0x75,0x16, -0x72,0x72,0x6f,0x67,0x61,0x74,0x65,0x45,0x11,0x61,0x63,1,0x65,0x32,0x69,0x15, -0x6e,0x67,0x6d,0x61,0x72,0x6b,0x31,0x18,0x73,0x65,0x70,0x61,0x72,0x61,0x74,0x6f, -0x72,0x39,0x63,0x53,0x6b,0x55,0x6d,0x51,0x1d,0x69,0x74,0x6c,0x65,0x63,0x61,0x73, -0x65,0x6c,0x65,0x74,0x74,0x65,0x72,0x27,1,0x6e,0x40,0x70,0x1c,0x70,0x65,0x72, -0x63,0x61,0x73,0x65,0x6c,0x65,0x74,0x74,0x65,0x72,0x23,0x17,0x61,0x73,0x73,0x69, -0x67,0x6e,0x65,0x64,0x21,0x6e,0x8a,0x6f,0xa2,0x47,0x70,8,0x66,0x14,0x66,0x5b, -0x69,0x59,0x6f,0x4f,0x72,0x24,0x73,0x49,0x17,0x69,0x76,0x61,0x74,0x65,0x75,0x73, -0x65,0x43,0x61,0x2c,0x63,0x4d,0x64,0x47,0x65,0x4b,0x1f,0x72,0x61,0x67,0x72,0x61, -0x70,0x68,0x73,0x65,0x70,0x61,0x72,0x61,0x74,0x6f,0x72,0x3d,2,0x64,0x33,0x6c, -0x35,0x6f,0x36,0x1b,0x6e,0x73,0x70,0x61,0x63,0x69,0x6e,0x67,0x6d,0x61,0x72,0x6b, -0x2d,1,0x70,0x7c,0x74,0x12,0x68,0x65,0x72,3,0x6c,0x38,0x6e,0x42,0x70,0x4c, -0x73,0x14,0x79,0x6d,0x62,0x6f,0x6c,0x57,0x14,0x65,0x74,0x74,0x65,0x72,0x2b,0x14, -0x75,0x6d,0x62,0x65,0x72,0x37,0x19,0x75,0x6e,0x63,0x74,0x75,0x61,0x74,0x69,0x6f, -0x6e,0x4f,0x1c,0x65,0x6e,0x70,0x75,0x6e,0x63,0x74,0x75,0x61,0x74,0x69,0x6f,0x6e, -0x49,0x66,0x9e,0x66,0x88,0x69,0xa2,0x4b,0x6c,0xa2,0x5c,0x6d,4,0x61,0x60,0x63, -0x31,0x65,0x2f,0x6e,0x2d,0x6f,0x15,0x64,0x69,0x66,0x69,0x65,0x72,1,0x6c,0x30, -0x73,0x14,0x79,0x6d,0x62,0x6f,0x6c,0x55,0x14,0x65,0x74,0x74,0x65,0x72,0x29,0x17, -0x74,0x68,0x73,0x79,0x6d,0x62,0x6f,0x6c,0x51,1,0x69,0x2e,0x6f,0x13,0x72,0x6d, -0x61,0x74,0x41,0x1d,0x6e,0x61,0x6c,0x70,0x75,0x6e,0x63,0x74,0x75,0x61,0x74,0x69, -0x6f,0x6e,0x5b,0x10,0x6e,0x1f,0x69,0x74,0x69,0x61,0x6c,0x70,0x75,0x6e,0x63,0x74, -0x75,0x61,0x74,0x69,0x6f,0x6e,0x59,6,0x6d,0x18,0x6d,0x29,0x6f,0x28,0x74,0x27, -0x75,0x23,0x2a,0x1c,0x77,0x65,0x72,0x63,0x61,0x73,0x65,0x6c,0x65,0x74,0x74,0x65, -0x72,0x25,0x65,0x28,0x69,0x3c,0x6c,0x25,0x19,0x74,0x74,0x65,0x72,0x6e,0x75,0x6d, -0x62,0x65,0x72,0x35,0x1a,0x6e,0x65,0x73,0x65,0x70,0x61,0x72,0x61,0x74,0x6f,0x72, -0x3b,0x63,0x44,0x64,0xa2,0x60,0x65,0x1b,0x6e,0x63,0x6c,0x6f,0x73,0x69,0x6e,0x67, -0x6d,0x61,0x72,0x6b,0x2f,6,0x6e,0x39,0x6e,0x46,0x6f,0x4e,0x73,0x45,0x75,0x1b, -0x72,0x72,0x65,0x6e,0x63,0x79,0x73,0x79,0x6d,0x62,0x6f,0x6c,0x53,0x20,0x12,0x74, -0x72,0x6c,0x3f,0x42,0x10,0x6e,1,0x6e,0x2c,0x74,0x12,0x72,0x6f,0x6c,0x3f,0x1f, -0x65,0x63,0x74,0x6f,0x72,0x70,0x75,0x6e,0x63,0x74,0x75,0x61,0x74,0x69,0x6f,0x6e, -0x4d,0x63,0x3f,0x66,0x41,0x6c,0x1d,0x6f,0x73,0x65,0x70,0x75,0x6e,0x63,0x74,0x75, -0x61,0x74,0x69,0x6f,0x6e,0x4b,2,0x61,0x30,0x65,0x4a,0x69,0x12,0x67,0x69,0x74, -0x33,0x1c,0x73,0x68,0x70,0x75,0x6e,0x63,0x74,0x75,0x61,0x74,0x69,0x6f,0x6e,0x47, -0x1a,0x63,0x69,0x6d,0x61,0x6c,0x6e,0x75,0x6d,0x62,0x65,0x72,0x33,0,0x12,0x6d, -0xc2,0x3f,0x73,0xa1,0x73,0x4e,0x74,0xa2,0x56,0x77,0xa2,0x72,0x79,0xa2,0x73,0x7a, -1,0x61,0x2c,0x68,0x12,0x61,0x69,0x6e,0x8b,0x11,0x69,0x6e,0x85,5,0x74,0x22, -0x74,0x38,0x77,0x4c,0x79,0x16,0x72,0x69,0x61,0x63,0x77,0x61,0x77,0x6f,0x18,0x72, -0x61,0x69,0x67,0x68,0x74,0x77,0x61,0x77,0xa3,0x55,0x15,0x61,0x73,0x68,0x6b,0x61, -0x66,0x6d,0x61,0x2e,0x65,0x38,0x68,0x11,0x69,0x6e,0x6b,0x10,0x64,0x62,0x11,0x68, -0x65,0x65,1,0x65,0x2e,0x6d,0x13,0x6b,0x61,0x74,0x68,0x69,0x10,0x6e,0x67,1, -0x61,0x4e,0x65,1,0x68,0x28,0x74,0x10,0x68,0x77,0x16,0x6d,0x61,0x72,0x62,0x75, -0x74,0x61,0x74,0x13,0x67,0x6f,0x61,0x6c,0x3d,1,0x68,0x71,0x77,0x73,0x11,0x61, -0x77,0x79,1,0x65,0x32,0x75,0x11,0x64,0x68,0x80,0x11,0x68,0x65,0x83,0x10,0x68, -0x7a,1,0x62,0x34,0x77,0x16,0x69,0x74,0x68,0x74,0x61,0x69,0x6c,0x7f,0x14,0x61, -0x72,0x72,0x65,0x65,0x7d,0x6d,0x6c,0x6e,0xa4,0x6b,0x70,0xa4,0x88,0x71,0xa4,0x88, -0x72,1,0x65,0x38,0x6f,0x18,0x68,0x69,0x6e,0x67,0x79,0x61,0x79,0x65,0x68,0x93, -1,0x68,0x5f,0x76,0x16,0x65,0x72,0x73,0x65,0x64,0x70,0x65,0x61,2,0x61,0x2e, -0x65,0xa4,0x3e,0x69,0x10,0x6d,0x53,1,0x6c,0xa2,0xe7,0x6e,0x16,0x69,0x63,0x68, -0x61,0x65,0x61,0x6e,0,0x12,0x6e,0x76,0x73,0x51,0x73,0x3e,0x74,0x5c,0x77,0xa0, -0x79,0xa2,0x42,0x7a,0x13,0x61,0x79,0x69,0x6e,0xa3,0x54,0x10,0x61,1,0x64,0x2e, -0x6d,0x12,0x65,0x6b,0x68,0xa3,0x4c,0x11,0x68,0x65,0xa3,0x4b,3,0x61,0x38,0x65, -0x3c,0x68,0x4a,0x77,0x13,0x65,0x6e,0x74,0x79,0xa3,0x51,0x10,0x77,0xa3,0x4d,1, -0x6e,0xa3,0x4e,0x74,0x10,0x68,0xa3,0x4f,0x14,0x61,0x6d,0x65,0x64,0x68,0xa3,0x50, -0x11,0x61,0x77,0xa3,0x52,0x12,0x6f,0x64,0x68,0xa3,0x53,0x6e,0x3a,0x6f,0x40,0x70, -0x46,0x71,0x4a,0x72,0x12,0x65,0x73,0x68,0xa3,0x4a,0x11,0x75,0x6e,0xa3,0x46,0x11, -0x6e,0x65,0xa3,0x47,0x10,0x65,0xa3,0x48,0x12,0x6f,0x70,0x68,0xa3,0x49,0x67,0x33, -0x67,0x38,0x68,0x40,0x6b,0x5e,0x6c,0x66,0x6d,0x11,0x65,0x6d,0xa3,0x45,0x13,0x69, -0x6d,0x65,0x6c,0xa1,1,0x65,0x32,0x75,0x14,0x6e,0x64,0x72,0x65,0x64,0xa3,0x42, -0x11,0x74,0x68,0xa3,0x41,0x12,0x61,0x70,0x68,0xa3,0x43,0x14,0x61,0x6d,0x65,0x64, -0x68,0xa3,0x44,0x61,0x34,0x62,0x4a,0x64,0x50,0x66,0x12,0x69,0x76,0x65,0x9f,1, -0x6c,0x2a,0x79,0x11,0x69,0x6e,0x97,0x12,0x65,0x70,0x68,0x95,0x12,0x65,0x74,0x68, -0x99,1,0x61,0x30,0x68,0x14,0x61,0x6d,0x65,0x64,0x68,0x9d,0x13,0x6c,0x65,0x74, -0x68,0x9b,0x15,0x61,0x79,0x61,0x6c,0x61,0x6d,6,0x6e,0x2c,0x6e,0x34,0x72,0x5e, -0x73,0x62,0x74,0x11,0x74,0x61,0xa3,0x63,2,0x67,0x2e,0x6e,0x32,0x79,0x10,0x61, -0xa3,0x60,0x10,0x61,0xa3,0x5d,1,0x61,0xa3,0x5e,0x6e,0x10,0x61,0xa3,0x5f,0x10, -0x61,0xa3,0x61,0x11,0x73,0x61,0xa3,0x62,0x62,0x3c,0x6a,0x42,0x6c,0x10,0x6c,1, -0x61,0xa3,0x5b,0x6c,0x10,0x61,0xa3,0x5c,0x11,0x68,0x61,0xa3,0x59,0x10,0x61,0xa3, -0x5a,0x11,0x65,0x6d,0x51,2,0x6f,0x2c,0x75,0x50,0x79,0x10,0x61,0x91,1,0x6a, -0x28,0x6f,0x10,0x6e,0x55,0x1a,0x6f,0x69,0x6e,0x69,0x6e,0x67,0x67,0x72,0x6f,0x75, -0x70,0x21,0x10,0x6e,0x57,0x10,0x65,0x59,0x10,0x61,1,0x66,0x5b,0x70,0x10,0x68, -0x5d,0x66,0x7b,0x66,0x42,0x67,0x7a,0x68,0x8a,0x6b,0xa2,0x56,0x6c,0x11,0x61,0x6d, -0x4c,0x12,0x61,0x64,0x68,0x4f,2,0x61,0x3e,0x65,0x4a,0x69,0x19,0x6e,0x61,0x6c, -0x73,0x65,0x6d,0x6b,0x61,0x74,0x68,0x35,0x15,0x72,0x73,0x69,0x79,0x65,0x68,0x8f, -0x86,0x10,0x68,0x33,0x10,0x61,1,0x66,0x37,0x6d,0x11,0x61,0x6c,0x39,1,0x61, -0x40,0x65,0x3e,1,0x68,0x28,0x74,0x10,0x68,0x45,0x40,0x13,0x67,0x6f,0x61,0x6c, -0x43,1,0x68,0x3b,0x6d,0x1a,0x7a,0x61,0x6f,0x6e,0x68,0x65,0x68,0x67,0x6f,0x61, -0x6c,0x3d,2,0x61,0x3a,0x68,0x44,0x6e,0x17,0x6f,0x74,0x74,0x65,0x64,0x68,0x65, -0x68,0x4b,1,0x66,0x47,0x70,0x10,0x68,0x49,0x12,0x61,0x70,0x68,0x89,0x61,0x2e, -0x62,0x8a,0x64,0xa2,0x51,0x65,0x31,2,0x66,0x3c,0x69,0x70,0x6c,1,0x61,0x28, -0x65,0x10,0x66,0x27,0x11,0x70,0x68,0x25,0x14,0x72,0x69,0x63,0x61,0x6e,2,0x66, -0x30,0x6e,0x36,0x71,0x11,0x61,0x66,0xa3,0x58,0x11,0x65,0x68,0xa3,0x56,0x12,0x6f, -0x6f,0x6e,0xa3,0x57,0x10,0x6e,0x23,1,0x65,0x4a,0x75,0x10,0x72,0x1f,0x75,0x73, -0x68,0x61,0x73,0x6b,0x69,0x79,0x65,0x68,0x62,0x61,0x72,0x72,0x65,0x65,0x8d,1, -0x68,0x29,0x74,0x10,0x68,0x2b,0x11,0x61,0x6c,0x2c,0x16,0x61,0x74,0x68,0x72,0x69, -0x73,0x68,0x2f,7,0x6e,0x2e,0x6e,0x2c,0x72,0x3e,0x74,0x56,0x75,0x21,0x18,0x6f, -0x6e,0x6a,0x6f,0x69,0x6e,0x69,0x6e,0x67,0x21,0x28,0x1a,0x69,0x67,0x68,0x74,0x6a, -0x6f,0x69,0x6e,0x69,0x6e,0x67,0x29,0x2a,0x19,0x72,0x61,0x6e,0x73,0x70,0x61,0x72, -0x65,0x6e,0x74,0x2b,0x63,0x23,0x64,0x40,0x6a,0x56,0x6c,0x26,0x19,0x65,0x66,0x74, -0x6a,0x6f,0x69,0x6e,0x69,0x6e,0x67,0x27,0x24,0x19,0x75,0x61,0x6c,0x6a,0x6f,0x69, -0x6e,0x69,0x6e,0x67,0x25,0x19,0x6f,0x69,0x6e,0x63,0x61,0x75,0x73,0x69,0x6e,0x67, -0x23,0,0x13,0x6e,0xc0,0xd0,0x73,0x49,0x73,0x48,0x75,0x78,0x77,0x84,0x78,0x9c, -0x7a,0x10,0x77,0x58,1,0x6a,0x75,0x73,0x13,0x70,0x61,0x63,0x65,0x59,4,0x61, -0x51,0x67,0x53,0x70,0x28,0x75,0x30,0x79,0x57,0x54,0x12,0x61,0x63,0x65,0x55,0x16, -0x72,0x72,0x6f,0x67,0x61,0x74,0x65,0x53,0x15,0x6e,0x6b,0x6e,0x6f,0x77,0x6e,0x21, -1,0x6a,0x5d,0x6f,0x17,0x72,0x64,0x6a,0x6f,0x69,0x6e,0x65,0x72,0x5d,0x10,0x78, -0x21,0x6e,0x60,0x6f,0xa2,0x41,0x70,0xa2,0x50,0x71,0xa2,0x6e,0x72,1,0x65,0x24, -0x69,0x6f,0x1e,0x67,0x69,0x6f,0x6e,0x61,0x6c,0x69,0x6e,0x64,0x69,0x63,0x61,0x74, -0x6f,0x72,0x6f,4,0x65,0x3e,0x6c,0x5b,0x6f,0x46,0x73,0x45,0x75,0x46,0x14,0x6d, -0x65,0x72,0x69,0x63,0x47,0x15,0x78,0x74,0x6c,0x69,0x6e,0x65,0x5b,0x17,0x6e,0x73, -0x74,0x61,0x72,0x74,0x65,0x72,0x45,0x10,0x70,0x48,0x1c,0x65,0x6e,0x70,0x75,0x6e, -0x63,0x74,0x75,0x61,0x74,0x69,0x6f,0x6e,0x49,1,0x6f,0x3e,0x72,0x4c,0x1a,0x65, -0x66,0x69,0x78,0x6e,0x75,0x6d,0x65,0x72,0x69,0x63,0x4d,0x4a,0x1b,0x73,0x74,0x66, -0x69,0x78,0x6e,0x75,0x6d,0x65,0x72,0x69,0x63,0x4b,0x10,0x75,0x4e,0x16,0x6f,0x74, -0x61,0x74,0x69,0x6f,0x6e,0x4f,0x68,0x7b,0x68,0x50,0x69,0x86,0x6a,0xa2,0x61,0x6c, -0xa2,0x65,0x6d,0x1c,0x61,0x6e,0x64,0x61,0x74,0x6f,0x72,0x79,0x62,0x72,0x65,0x61, -0x6b,0x2d,4,0x32,0x5f,0x33,0x61,0x65,0x34,0x6c,0x6d,0x79,0x3a,0x13,0x70,0x68, -0x65,0x6e,0x3b,0x19,0x62,0x72,0x65,0x77,0x6c,0x65,0x74,0x74,0x65,0x72,0x6d,2, -0x64,0x28,0x6e,0x3c,0x73,0x41,0x3c,0x18,0x65,0x6f,0x67,0x72,0x61,0x70,0x68,0x69, -0x63,0x3d,0x3e,1,0x66,0x3e,0x73,0x11,0x65,0x70,1,0x61,0x22,0x65,0x14,0x72, -0x61,0x62,0x6c,0x65,0x3f,0x18,0x69,0x78,0x6e,0x75,0x6d,0x65,0x72,0x69,0x63,0x41, -2,0x6c,0x63,0x74,0x65,0x76,0x67,1,0x66,0x43,0x69,0x15,0x6e,0x65,0x66,0x65, -0x65,0x64,0x43,0x61,0x40,0x62,0x70,0x63,0xa2,0x55,0x65,0xa2,0xdb,0x67,0x10,0x6c, -0x38,0x11,0x75,0x65,0x39,2,0x69,0x23,0x6c,0x34,0x6d,0x16,0x62,0x69,0x67,0x75, -0x6f,0x75,0x73,0x23,0x24,0x17,0x70,0x68,0x61,0x62,0x65,0x74,0x69,0x63,0x25,4, -0x32,0x27,0x61,0x29,0x62,0x2b,0x6b,0x2d,0x72,0x12,0x65,0x61,0x6b,2,0x61,0x36, -0x62,0x3e,0x73,0x15,0x79,0x6d,0x62,0x6f,0x6c,0x73,0x57,0x13,0x66,0x74,0x65,0x72, -0x29,1,0x65,0x2a,0x6f,0x11,0x74,0x68,0x27,0x13,0x66,0x6f,0x72,0x65,0x2b,7, -0x6d,0x51,0x6d,0x33,0x6f,0x28,0x70,0x69,0x72,0x35,1,0x6d,0x76,0x6e,1,0x64, -0x3c,0x74,0x1a,0x69,0x6e,0x67,0x65,0x6e,0x74,0x62,0x72,0x65,0x61,0x6b,0x2f,0x15, -0x69,0x74,0x69,0x6f,0x6e,0x61,0x1f,0x6c,0x6a,0x61,0x70,0x61,0x6e,0x65,0x73,0x65, -0x73,0x74,0x61,0x72,0x74,0x65,0x72,0x6b,1,0x62,0x3a,0x70,0x19,0x6c,0x65,0x78, -0x63,0x6f,0x6e,0x74,0x65,0x78,0x74,0x51,0x18,0x69,0x6e,0x69,0x6e,0x67,0x6d,0x61, -0x72,0x6b,0x33,0x61,0x6a,0x62,0x2f,0x6a,0x6b,0x6c,0x30,0x13,0x6f,0x73,0x65,0x70, -1,0x61,0x38,0x75,0x18,0x6e,0x63,0x74,0x75,0x61,0x74,0x69,0x6f,0x6e,0x31,0x18, -0x72,0x65,0x6e,0x74,0x68,0x65,0x73,0x69,0x73,0x69,0x1b,0x72,0x72,0x69,0x61,0x67, -0x65,0x72,0x65,0x74,0x75,0x72,0x6e,0x35,2,0x62,0x3e,0x6d,0x46,0x78,0x36,0x18, -0x63,0x6c,0x61,0x6d,0x61,0x74,0x69,0x6f,0x6e,0x37,0x70,0x12,0x61,0x73,0x65,0x71, -0x72,0x16,0x6f,0x64,0x69,0x66,0x69,0x65,0x72,0x73,1,0x64,0x42,0x6e,1,0x6f, -0x32,0x75,0x26,0x14,0x6d,0x65,0x72,0x69,0x63,0x27,0x11,0x6e,0x65,0x21,1,0x65, -0x2e,0x69,0x24,0x12,0x67,0x69,0x74,0x25,0x22,0x14,0x63,0x69,0x6d,0x61,0x6c,0x23, -0,0x18,0x6e,0xc3,0xc4,0x74,0xc1,0x51,0x77,0x7a,0x77,0xa2,0x4c,0x78,0xa2,0x60, -0x79,0xa2,0x6a,0x7a,6,0x73,0x1e,0x73,0x34,0x78,0x42,0x79,0x48,0x7a,0x11,0x7a, -0x7a,0xa3,0x67,0x10,0x79,1,0x65,0xa3,0xae,0x6d,0xa3,0x81,0x11,0x78,0x78,0xa3, -0x66,0x11,0x79,0x79,0x21,0x61,0x30,0x69,0x58,0x6d,0x11,0x74,0x68,0xa3,0x80,0x10, -0x6e,1,0x61,0x26,0x62,0xa3,0xb1,0x1a,0x62,0x61,0x7a,0x61,0x72,0x73,0x71,0x75, -0x61,0x72,0x65,0xa3,0xb1,0x11,0x6e,0x68,0x23,1,0x61,0x2c,0x6f,0x11,0x6c,0x65, -0xa3,0x9b,0x11,0x72,0x61,0xa2,0x92,0x15,0x6e,0x67,0x63,0x69,0x74,0x69,0xa3,0x92, -1,0x70,0x2c,0x73,0x11,0x75,0x78,0xa3,0x65,0x11,0x65,0x6f,0x9b,0x10,0x69,0x72, -0x11,0x69,0x69,0x73,0x74,0x4a,0x75,0xa2,0xba,0x76,1,0x61,0x2c,0x69,0x11,0x73, -0x70,0xa3,0x64,0x10,0x69,0xa2,0x63,0x10,0x69,0xa3,0x63,5,0x67,0x36,0x67,0x68, -0x68,0x6c,0x69,2,0x62,0x3a,0x66,0x4a,0x72,0x10,0x68,0xa2,0x9e,0x12,0x75,0x74, -0x61,0xa3,0x9e,1,0x65,0x24,0x74,0x6f,0x12,0x74,0x61,0x6e,0x6f,0x14,0x69,0x6e, -0x61,0x67,0x68,0x99,0x11,0x6c,0x67,0x75,0x10,0x61,1,0x61,0x24,0x69,0x6d,0x6a, -0x11,0x6e,0x61,0x6b,0x61,0x30,0x65,0xa2,0x5b,0x66,0x11,0x6e,0x67,0x99,6,0x6c, -0x21,0x6c,0x32,0x6d,0x38,0x6e,0x44,0x76,0x10,0x74,0xa3,0x7f,1,0x65,0x89,0x75, -0x97,1,0x69,0x24,0x6c,0x67,0x10,0x6c,0x67,0x10,0x67,0xa2,0x9a,0x11,0x75,0x74, -0xa3,0x9a,0x67,0x36,0x69,0x52,0x6b,0x10,0x72,0xa2,0x99,0x10,0x69,0xa3,0x99,1, -0x61,0x30,0x62,0x7a,0x13,0x61,0x6e,0x77,0x61,0x7b,0x12,0x6c,0x6f,0x67,0x75,2, -0x6c,0x32,0x74,0x34,0x76,0x12,0x69,0x65,0x74,0xa3,0x7f,0x10,0x65,0x89,0x12,0x68, -0x61,0x6d,0xa3,0x6a,1,0x6c,0x2a,0x6e,0x10,0x67,0xa3,0x62,0x10,0x75,0x68,0x11, -0x67,0x75,0x69,1,0x67,0x32,0x6e,0x14,0x6b,0x6e,0x6f,0x77,0x6e,0xa3,0x67,0x11, -0x61,0x72,0x8a,0x13,0x69,0x74,0x69,0x63,0x8b,0x71,0xc0,0xfc,0x71,0xa2,0xce,0x72, -0xa2,0xd3,0x73,6,0x69,0x7a,0x69,0x72,0x6f,0xa2,0x4c,0x75,0xa2,0x65,0x79,1, -0x6c,0x46,0x72,4,0x63,0x65,0x65,0xa3,0x5f,0x69,0x2c,0x6a,0xa3,0x60,0x6e,0xa3, -0x61,0x11,0x61,0x63,0x65,0x10,0x6f,0x94,0x16,0x74,0x69,0x6e,0x61,0x67,0x72,0x69, -0x95,2,0x64,0x3c,0x67,0x4c,0x6e,1,0x64,0xa3,0x91,0x68,0x62,0x12,0x61,0x6c, -0x61,0x63,0x10,0x64,0xa2,0xa6,0x12,0x68,0x61,0x6d,0xa3,0xa6,0x17,0x6e,0x77,0x72, -0x69,0x74,0x69,0x6e,0x67,0xa3,0x70,1,0x72,0x36,0x79,0x10,0x6f,0xa2,0xb0,0x12, -0x6d,0x62,0x6f,0xa3,0xb0,0x10,0x61,0xa2,0x98,0x16,0x73,0x6f,0x6d,0x70,0x65,0x6e, -0x67,0xa3,0x98,0x11,0x6e,0x64,0xa2,0x71,0x14,0x61,0x6e,0x65,0x73,0x65,0xa3,0x71, -0x61,0x5c,0x67,0xa2,0x43,0x68,1,0x61,0x2a,0x72,0x10,0x64,0xa3,0x97,2,0x72, -0x28,0x76,0x30,0x77,0x87,0x12,0x61,0x64,0x61,0xa3,0x97,0x12,0x69,0x61,0x6e,0x87, -2,0x6d,0x40,0x72,0x58,0x75,0x10,0x72,0xa2,0x6f,0x15,0x61,0x73,0x68,0x74,0x72, -0x61,0xa3,0x6f,1,0x61,0x26,0x72,0xa3,0x7e,0x14,0x72,0x69,0x74,0x61,0x6e,0xa3, -0x7e,1,0x61,0xa3,0x5e,0x62,0xa3,0x85,0x11,0x6e,0x77,0xa3,0x70,0x11,0x61,0x61, -1,0x63,0x2f,0x69,0x23,3,0x65,0x3e,0x6a,0x48,0x6f,0x4e,0x75,0x10,0x6e,1, -0x69,0x24,0x72,0x61,0x10,0x63,0x61,0x13,0x6a,0x61,0x6e,0x67,0xa3,0x6e,0x11,0x6e, -0x67,0xa3,0x6e,0x11,0x72,0x6f,0xa3,0x5d,0x6e,0xa2,0x83,0x6f,0xa2,0xca,0x70,5, -0x6c,0x1e,0x6c,0x44,0x72,0x4a,0x73,0x1b,0x61,0x6c,0x74,0x65,0x72,0x70,0x61,0x68, -0x6c,0x61,0x76,0x69,0xa3,0x7b,0x11,0x72,0x64,0xa3,0x5c,0x11,0x74,0x69,0xa3,0x7d, -0x61,0x7c,0x65,0xa2,0x54,0x68,3,0x61,0x3e,0x6c,0x4e,0x6e,0x5e,0x6f,0x16,0x65, -0x6e,0x69,0x63,0x69,0x61,0x6e,0xa3,0x5b,0x10,0x67,0xa2,0x5a,0x12,0x73,0x70,0x61, -0xa3,0x5a,2,0x69,0xa3,0x7a,0x70,0xa3,0x7b,0x76,0xa3,0x7c,0x10,0x78,0xa3,0x5b, -2,0x68,0x3e,0x6c,0x50,0x75,0x10,0x63,0xa2,0xa5,0x14,0x69,0x6e,0x68,0x61,0x75, -0xa3,0xa5,0x17,0x61,0x77,0x68,0x68,0x6d,0x6f,0x6e,0x67,0xa3,0x4b,0x10,0x6d,0xa2, -0x90,0x14,0x79,0x72,0x65,0x6e,0x65,0xa3,0x90,0x11,0x72,0x6d,0xa3,0x59,5,0x6b, -0x1e,0x6b,0x32,0x73,0x4a,0x75,0x12,0x73,0x68,0x75,0xa3,0x96,1,0x67,0x2e,0x6f, -0xa2,0x57,0x10,0x6f,0xa3,0x57,0x10,0x62,0xa3,0x84,0x11,0x68,0x75,0xa3,0x96,0x61, -0x42,0x62,0x60,0x65,0x10,0x77,1,0x61,0xa3,0xaa,0x74,0x14,0x61,0x69,0x6c,0x75, -0x65,0x97,1,0x62,0x2a,0x72,0x10,0x62,0xa3,0x8e,0x15,0x61,0x74,0x61,0x65,0x61, -0x6e,0xa3,0x8f,0x11,0x61,0x74,0xa3,0x8f,3,0x67,0x5a,0x6c,0x6c,0x72,0xa2,0x88, -0x73,2,0x61,0x36,0x67,0x3c,0x6d,0x10,0x61,0x84,0x12,0x6e,0x79,0x61,0x85,0x11, -0x67,0x65,0xa3,0xab,0x10,0x65,0xa3,0xab,1,0x61,0x2a,0x68,0x11,0x61,0x6d,0x5b, -0x10,0x6d,0x5b,1,0x63,0xa2,0x55,0x64,5,0x70,0x2c,0x70,0x36,0x73,0x54,0x74, -0x14,0x75,0x72,0x6b,0x69,0x63,0xa3,0x58,0x11,0x65,0x72,1,0x6d,0x2c,0x73,0x12, -0x69,0x61,0x6e,0x9b,0x11,0x69,0x63,0xa3,0x59,0x1a,0x6f,0x75,0x74,0x68,0x61,0x72, -0x61,0x62,0x69,0x61,0x6e,0xa3,0x85,0x68,0x42,0x69,0x54,0x6e,0x1a,0x6f,0x72,0x74, -0x68,0x61,0x72,0x61,0x62,0x69,0x61,0x6e,0xa3,0x8e,0x17,0x75,0x6e,0x67,0x61,0x72, -0x69,0x61,0x6e,0xa3,0x4c,0x14,0x74,0x61,0x6c,0x69,0x63,0x5d,1,0x68,0x26,0x6b, -0xa3,0x6d,0x12,0x69,0x6b,0x69,0xa3,0x6d,2,0x69,0x2c,0x6b,0x30,0x79,0x10,0x61, -0x5f,0x11,0x79,0x61,0x5f,0x10,0x68,0xa3,0x58,0x68,0xc2,0xc2,0x6b,0xc1,0xeb,0x6b, -0xa2,0xf8,0x6c,0xa4,0x79,0x6d,8,0x6f,0x46,0x6f,0x48,0x72,0x74,0x74,0x80,0x75, -0x86,0x79,1,0x61,0x28,0x6d,0x10,0x72,0x59,0x13,0x6e,0x6d,0x61,0x72,0x59,2, -0x64,0x2e,0x6e,0x32,0x6f,0x10,0x6e,0xa3,0x72,0x10,0x69,0xa3,0xa3,0x10,0x67,0x56, -0x14,0x6f,0x6c,0x69,0x61,0x6e,0x57,0x10,0x6f,0xa2,0x95,0x10,0x6f,0xa3,0x95,0x11, -0x65,0x69,0xa3,0x73,0x11,0x6c,0x74,0xa2,0xa4,0x12,0x61,0x6e,0x69,0xa3,0xa4,0x61, -0x36,0x65,0xa2,0x5b,0x69,0xa2,0x9e,0x6c,0x11,0x79,0x6d,0x55,5,0x72,0x1f,0x72, -0x2e,0x73,0x3e,0x79,0x10,0x61,0xa3,0x55,0x10,0x63,0xa2,0xa9,0x12,0x68,0x65,0x6e, -0xa3,0xa9,0x18,0x61,0x72,0x61,0x6d,0x67,0x6f,0x6e,0x64,0x69,0xa3,0xaf,0x68,0x54, -0x6c,0x6a,0x6e,1,0x64,0x38,0x69,0xa2,0x79,0x15,0x63,0x68,0x61,0x65,0x61,0x6e, -0xa3,0x79,0xa2,0x54,0x12,0x61,0x69,0x63,0xa3,0x54,1,0x61,0x26,0x6a,0xa3,0xa0, -0x13,0x6a,0x61,0x6e,0x69,0xa3,0xa0,0x15,0x61,0x79,0x61,0x6c,0x61,0x6d,0x55,2, -0x65,0x72,0x6e,0x84,0x72,1,0x63,0xa3,0x8d,0x6f,0xa2,0x56,0x13,0x69,0x74,0x69, +0x90,0x62,0xa2,0xbe,0x63,0xa4,0x30,0x64,0xa4,0xfd,0x65,5,0x6d,0x63,0x6d,0x6e, +0x70,0xa2,0x59,0x78,0x10,0x74,0x30,1,0x65,0x2c,0x70,0x12,0x69,0x63,0x74,0xa1, +0x12,0x6e,0x64,0x65,1,0x64,0x24,0x72,0x31,0x1b,0x70,0x69,0x63,0x74,0x6f,0x67, +0x72,0x61,0x70,0x68,0x69,0x63,0xa1,0x10,0x6f,1,0x64,0x97,0x6a,0x10,0x69,0x92, +2,0x63,0x40,0x6d,0x50,0x70,0x1a,0x72,0x65,0x73,0x65,0x6e,0x74,0x61,0x74,0x69, +0x6f,0x6e,0x95,0x17,0x6f,0x6d,0x70,0x6f,0x6e,0x65,0x6e,0x74,0x9b,0x16,0x6f,0x64, +0x69,0x66,0x69,0x65,0x72,0x96,0x13,0x62,0x61,0x73,0x65,0x99,0x12,0x72,0x65,0x73, +0x95,0x61,0x30,0x62,0x4e,0x63,0x12,0x6f,0x6d,0x70,0x9b,0xc2,4,0x1b,0x73,0x74, +0x61,0x73,0x69,0x61,0x6e,0x77,0x69,0x64,0x74,0x68,0xc3,4,0x12,0x61,0x73,0x65, +0x99,3,0x67,0x44,0x68,0x4a,0x6c,0x4e,0x73,0x1a,0x63,0x69,0x69,0x68,0x65,0x78, +0x64,0x69,0x67,0x69,0x74,0x23,0x10,0x65,0xd9,0x40,0,0x11,0x65,0x78,0x23,1, +0x6e,0x38,0x70,0x11,0x68,0x61,0x20,0x14,0x62,0x65,0x74,0x69,0x63,0x21,0x11,0x75, +0x6d,0x79,4,0x63,0xc3,0,0x69,0x3e,0x6c,0xa2,0x57,0x6d,0xa2,0x64,0x70,1, +0x62,0xd9,0x40,0xd,0x74,0xc3,0x15,0x11,0x64,0x69,2,0x63,0x54,0x6d,0x74,0x70, +0x1b,0x61,0x69,0x72,0x65,0x64,0x62,0x72,0x61,0x63,0x6b,0x65,0x74,0xd8,0x40,0xd, +0x13,0x74,0x79,0x70,0x65,0xc3,0x15,0x24,1,0x6c,0x30,0x6f,0x14,0x6e,0x74,0x72, +0x6f,0x6c,0x25,0x12,0x61,0x73,0x73,0xc3,0,0x26,0x14,0x69,0x72,0x72,0x6f,0x72, +1,0x65,0x38,0x69,0x16,0x6e,0x67,0x67,0x6c,0x79,0x70,0x68,0xd9,0x40,1,0x10, +0x64,0x27,2,0x61,0x32,0x6b,0xc3,1,0x6f,0x11,0x63,0x6b,0xc3,1,0x11,0x6e, +0x6b,0x7b,0x10,0x67,0xd9,0x40,1,6,0x68,0x7c,0x68,0x54,0x69,0x85,0x6f,0xa2, +0x6f,0x77,4,0x63,0x30,0x6b,0x36,0x6c,0x87,0x74,0x8b,0x75,0x89,1,0x66,0x8d, +0x6d,0x8f,0x11,0x63,0x66,0x91,0x18,0x61,0x6e,0x67,0x65,0x73,0x77,0x68,0x65,0x6e, +4,0x63,0x44,0x6c,0x6c,0x6e,0x7e,0x74,0x98,0x75,0x18,0x70,0x70,0x65,0x72,0x63, +0x61,0x73,0x65,0x64,0x89,0x12,0x61,0x73,0x65,1,0x66,0x30,0x6d,0x14,0x61,0x70, +0x70,0x65,0x64,0x8f,0x14,0x6f,0x6c,0x64,0x65,0x64,0x8d,0x18,0x6f,0x77,0x65,0x72, +0x63,0x61,0x73,0x65,0x64,0x87,0x1c,0x66,0x6b,0x63,0x63,0x61,0x73,0x65,0x66,0x6f, +0x6c,0x64,0x65,0x64,0x91,0x18,0x69,0x74,0x6c,0x65,0x63,0x61,0x73,0x65,0x64,0x8b, +0x13,0x6d,0x70,0x65,0x78,0x33,0x61,0x2e,0x63,0xa2,0x48,0x66,0xd9,0x40,2,1, +0x6e,0x72,0x73,0x10,0x65,3,0x64,0x83,0x66,0x3a,0x69,0x4a,0x73,0x17,0x65,0x6e, +0x73,0x69,0x74,0x69,0x76,0x65,0x65,0x15,0x6f,0x6c,0x64,0x69,0x6e,0x67,0xd9,0x40, +2,0x17,0x67,0x6e,0x6f,0x72,0x61,0x62,0x6c,0x65,0x85,0x13,0x6f,0x6e,0x69,0x63, +0x1f,0x61,0x6c,0x63,0x6f,0x6d,0x62,0x69,0x6e,0x69,0x6e,0x67,0x63,0x6c,0x61,0x73, +0x73,0xc3,2,0x10,0x63,0xc3,2,3,0x61,0x30,0x65,0x34,0x69,0xa2,0x41,0x74, +0xc3,3,0x11,0x73,0x68,0x29,2,0x63,0x3a,0x66,0x58,0x70,0x2c,0x16,0x72,0x65, +0x63,0x61,0x74,0x65,0x64,0x2d,0x1d,0x6f,0x6d,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f, +0x6e,0x74,0x79,0x70,0x65,0xc3,3,0x15,0x61,0x75,0x6c,0x74,0x69,0x67,0x1f,0x6e, +0x6f,0x72,0x61,0x62,0x6c,0x65,0x63,0x6f,0x64,0x65,0x70,0x6f,0x69,0x6e,0x74,0x2b, +0x2a,0x10,0x61,0x2e,0x15,0x63,0x72,0x69,0x74,0x69,0x63,0x2f,3,0x66,0x34,0x6e, +0x3e,0x74,0x42,0x79,0x22,0x11,0x65,0x73,0x23,0x20,0x13,0x61,0x6c,0x73,0x65,0x21, +0x20,0x10,0x6f,0x21,0x22,0x12,0x72,0x75,0x65,0x23,0xa,0x6b,0x5b,0x6f,0x23,0x6f, +0x3c,0x72,0x4c,0x76,1,0x69,0x24,0x72,0x33,0x13,0x72,0x61,0x6d,0x61,0x33,0x10, +0x76,0x22,0x14,0x65,0x72,0x6c,0x61,0x79,0x23,0xa2,0xe2,0x13,0x69,0x67,0x68,0x74, +0xa3,0xe2,0x6b,0x58,0x6c,0x74,0x6e,3,0x6b,0x2f,0x6f,0x30,0x72,0x21,0x75,0x12, +0x6b,0x74,0x61,0x2f,0x19,0x74,0x72,0x65,0x6f,0x72,0x64,0x65,0x72,0x65,0x64,0x21, +1,0x61,0x24,0x76,0x31,0x18,0x6e,0x61,0x76,0x6f,0x69,0x63,0x69,0x6e,0x67,0x31, +0xa2,0xe0,0x12,0x65,0x66,0x74,0xa3,0xe0,0x61,0x5c,0x62,0xa2,0x77,0x63,0xa2,0x96, +0x64,0xa4,0xa,0x69,1,0x6f,0x26,0x73,0xa3,0xf0,0x1a,0x74,0x61,0x73,0x75,0x62, +0x73,0x63,0x72,0x69,0x70,0x74,0xa3,0xf0,0xa2,0xe6,3,0x62,0xa0,0x6c,0xa3,0xe4, +0x72,0xa3,0xe8,0x74,2,0x61,0x74,0x62,0x7c,0x74,0x14,0x61,0x63,0x68,0x65,0x64, +1,0x61,0x3e,0x62,0x13,0x65,0x6c,0x6f,0x77,0xa2,0xca,0x13,0x6c,0x65,0x66,0x74, +0xa3,0xc8,0x13,0x62,0x6f,0x76,0x65,0xa2,0xd6,0x14,0x72,0x69,0x67,0x68,0x74,0xa3, +0xd8,0xa2,0xd6,0x10,0x72,0xa3,0xd8,0xa2,0xca,0x10,0x6c,0xa3,0xc8,0x12,0x6f,0x76, +0x65,0xa2,0xe6,1,0x6c,0x30,0x72,0x13,0x69,0x67,0x68,0x74,0xa3,0xe8,0x12,0x65, +0x66,0x74,0xa3,0xe4,0xa2,0xdc,2,0x65,0x2c,0x6c,0xa3,0xda,0x72,0xa3,0xde,0x12, +0x6c,0x6f,0x77,0xa2,0xdc,1,0x6c,0x30,0x72,0x13,0x69,0x67,0x68,0x74,0xa3,0xde, +0x12,0x65,0x66,0x74,0xa3,0xda,0x11,0x63,0x63,4,0x31,0x3c,0x32,0xa2,0x42,0x33, +0xa2,0x56,0x38,0xa2,0x64,0x39,0x10,0x31,0xa3,0x5b,9,0x35,0xa,0x35,0x3f,0x36, +0x41,0x37,0x43,0x38,0x45,0x39,0x47,0x30,0x30,0x31,0x3c,0x32,0x42,0x33,0x4e,0x34, +0x3d,0x34,1,0x33,0xa3,0x67,0x37,0xa3,0x6b,0x36,0x10,0x38,0xa3,0x76,0x38,1, +0x32,0xa3,0x7a,0x39,0xa3,0x81,0x3a,2,0x30,0xa3,0x82,0x32,0xa3,0x84,0x33,0xa3, +0x85,9,0x35,0xa,0x35,0x53,0x36,0x55,0x37,0x57,0x38,0x59,0x39,0x5b,0x30,0x49, +0x31,0x4b,0x32,0x4d,0x33,0x4f,0x34,0x51,6,0x33,8,0x33,0x63,0x34,0x65,0x35, +0x67,0x36,0x69,0x30,0x5d,0x31,0x5f,0x32,0x61,0x10,0x34,0xa3,0x54,2,0x61,0xa3, +0xea,0x62,0xa3,0xe9,0x6f,0x13,0x75,0x62,0x6c,0x65,1,0x61,0x30,0x62,0x13,0x65, +0x6c,0x6f,0x77,0xa3,0xe9,0x13,0x62,0x6f,0x76,0x65,0xa3,0xea,0xb,0x6e,0xc0,0xca, +0x72,0x5f,0x72,0x46,0x73,0xa2,0x48,0x77,1,0x68,0x24,0x73,0x33,0x17,0x69,0x74, +0x65,0x73,0x70,0x61,0x63,0x65,0x33,0x22,1,0x69,0x30,0x6c,2,0x65,0x3d,0x69, +0x4b,0x6f,0x3f,0x18,0x67,0x68,0x74,0x74,0x6f,0x6c,0x65,0x66,0x74,0x22,2,0x65, +0x38,0x69,0x48,0x6f,0x16,0x76,0x65,0x72,0x72,0x69,0x64,0x65,0x3f,0x17,0x6d,0x62, +0x65,0x64,0x64,0x69,0x6e,0x67,0x3d,0x15,0x73,0x6f,0x6c,0x61,0x74,0x65,0x4b,0x30, +0x1e,0x65,0x67,0x6d,0x65,0x6e,0x74,0x73,0x65,0x70,0x61,0x72,0x61,0x74,0x6f,0x72, +0x31,0x6e,0xa2,0x41,0x6f,0xa2,0x53,0x70,2,0x61,0x66,0x64,0x86,0x6f,0x1b,0x70, +0x64,0x69,0x72,0x65,0x63,0x74,0x69,0x6f,0x6e,0x61,0x6c,1,0x66,0x32,0x69,0x15, +0x73,0x6f,0x6c,0x61,0x74,0x65,0x4d,0x14,0x6f,0x72,0x6d,0x61,0x74,0x41,0x1f,0x72, +0x61,0x67,0x72,0x61,0x70,0x68,0x73,0x65,0x70,0x61,0x72,0x61,0x74,0x6f,0x72,0x2f, +1,0x66,0x41,0x69,0x4d,1,0x6f,0x28,0x73,0x10,0x6d,0x43,0x1b,0x6e,0x73,0x70, +0x61,0x63,0x69,0x6e,0x67,0x6d,0x61,0x72,0x6b,0x43,1,0x6e,0x35,0x74,0x19,0x68, +0x65,0x72,0x6e,0x65,0x75,0x74,0x72,0x61,0x6c,0x35,0x65,0x88,0x65,0x98,0x66,0xa2, +0x6a,0x6c,0x20,1,0x65,0x30,0x72,2,0x65,0x37,0x69,0x49,0x6f,0x39,0x18,0x66, +0x74,0x74,0x6f,0x72,0x69,0x67,0x68,0x74,0x20,2,0x65,0x38,0x69,0x48,0x6f,0x16, +0x76,0x65,0x72,0x72,0x69,0x64,0x65,0x39,0x17,0x6d,0x62,0x65,0x64,0x64,0x69,0x6e, +0x67,0x37,0x15,0x73,0x6f,0x6c,0x61,0x74,0x65,0x49,3,0x6e,0x25,0x73,0x27,0x74, +0x29,0x75,0x15,0x72,0x6f,0x70,0x65,0x61,0x6e,2,0x6e,0x3c,0x73,0x46,0x74,0x18, +0x65,0x72,0x6d,0x69,0x6e,0x61,0x74,0x6f,0x72,0x29,0x14,0x75,0x6d,0x62,0x65,0x72, +0x25,0x17,0x65,0x70,0x61,0x72,0x61,0x74,0x6f,0x72,0x27,1,0x69,0x28,0x73,0x10, +0x69,0x47,0x1f,0x72,0x73,0x74,0x73,0x74,0x72,0x6f,0x6e,0x67,0x69,0x73,0x6f,0x6c, +0x61,0x74,0x65,0x47,0x61,0x4e,0x62,0x84,0x63,1,0x6f,0x24,0x73,0x2d,0x1c,0x6d, +0x6d,0x6f,0x6e,0x73,0x65,0x70,0x61,0x72,0x61,0x74,0x6f,0x72,0x2d,2,0x6c,0x3b, +0x6e,0x2b,0x72,0x13,0x61,0x62,0x69,0x63,1,0x6c,0x30,0x6e,0x14,0x75,0x6d,0x62, +0x65,0x72,0x2b,0x14,0x65,0x74,0x74,0x65,0x72,0x3b,0x2e,1,0x6e,0x45,0x6f,0x1c, +0x75,0x6e,0x64,0x61,0x72,0x79,0x6e,0x65,0x75,0x74,0x72,0x61,0x6c,0x45,0,0x16, +0x6d,0xc7,0xfe,0x74,0xc1,0xb8,0x77,0x57,0x77,0x48,0x79,0x5c,0x7a,0x1d,0x61,0x6e, +0x61,0x62,0x61,0x7a,0x61,0x72,0x73,0x71,0x75,0x61,0x72,0x65,0xa5,0x18,0x18,0x61, +0x72,0x61,0x6e,0x67,0x63,0x69,0x74,0x69,0xa3,0xfc,0x10,0x69,2,0x6a,0x3c,0x72, +0x68,0x73,0x17,0x79,0x6c,0x6c,0x61,0x62,0x6c,0x65,0x73,0xa3,0x48,0x12,0x69,0x6e, +0x67,0xa2,0x74,0x1e,0x68,0x65,0x78,0x61,0x67,0x72,0x61,0x6d,0x73,0x79,0x6d,0x62, +0x6f,0x6c,0x73,0xa3,0x74,0x16,0x61,0x64,0x69,0x63,0x61,0x6c,0x73,0xa3,0x49,0x74, +0xa2,0x59,0x75,0xa4,0x12,0x76,2,0x61,0x36,0x65,0x7a,0x73,0xa2,0x6c,0x12,0x73, +0x75,0x70,0xa3,0x7d,1,0x69,0xa3,0x9f,0x72,0x1e,0x69,0x61,0x74,0x69,0x6f,0x6e, +0x73,0x65,0x6c,0x65,0x63,0x74,0x6f,0x72,0x73,0xa2,0x6c,0x19,0x73,0x75,0x70,0x70, +0x6c,0x65,0x6d,0x65,0x6e,0x74,0xa3,0x7d,1,0x64,0x3c,0x72,0x19,0x74,0x69,0x63, +0x61,0x6c,0x66,0x6f,0x72,0x6d,0x73,0xa3,0x91,0x14,0x69,0x63,0x65,0x78,0x74,0xa2, +0xaf,0x16,0x65,0x6e,0x73,0x69,0x6f,0x6e,0x73,0xa3,0xaf,4,0x61,0x68,0x65,0xa2, +0x8a,0x68,0xa2,0x8d,0x69,0xa2,0x95,0x72,0x1c,0x61,0x6e,0x73,0x70,0x6f,0x72,0x74, +0x61,0x6e,0x64,0x6d,0x61,0x70,0xa2,0xcf,0x16,0x73,0x79,0x6d,0x62,0x6f,0x6c,0x73, +0xa3,0xcf,4,0x67,0x58,0x69,0x7e,0x6b,0xa2,0x58,0x6d,0xa2,0x5a,0x6e,0x12,0x67, +0x75,0x74,0xa4,0x10,0x19,0x63,0x6f,0x6d,0x70,0x6f,0x6e,0x65,0x6e,0x74,0x73,0xa5, +0x11,2,0x61,0x2a,0x62,0x32,0x73,0xa3,0x60,0x12,0x6c,0x6f,0x67,0xa3,0x62,0x13, +0x61,0x6e,0x77,0x61,0xa3,0x65,3,0x6c,0x52,0x74,0x56,0x76,0x5e,0x78,0x16,0x75, +0x61,0x6e,0x6a,0x69,0x6e,0x67,0xa2,0x7c,0x16,0x73,0x79,0x6d,0x62,0x6f,0x6c,0x73, +0xa3,0x7c,0x10,0x65,0xa3,0x70,0x12,0x68,0x61,0x6d,0xa3,0xae,0x12,0x69,0x65,0x74, +0xa3,0xb7,0x11,0x72,0x69,0xa3,0xdc,0x11,0x69,0x6c,0x49,0x13,0x6c,0x75,0x67,0x75, +0x4b,0x10,0x61,1,0x61,0x24,0x69,0x53,0x11,0x6e,0x61,0x3d,2,0x62,0x34,0x66, +0x3c,0x72,0x13,0x68,0x75,0x74,0x61,0xa3,0xfb,0x13,0x65,0x74,0x61,0x6e,0x57,0x14, +0x69,0x6e,0x61,0x67,0x68,0xa3,0x90,2,0x63,0x82,0x67,0x92,0x6e,0x1f,0x69,0x66, +0x69,0x65,0x64,0x63,0x61,0x6e,0x61,0x64,0x69,0x61,0x6e,0x61,0x62,0x6f,0x1f,0x72, +0x69,0x67,0x69,0x6e,0x61,0x6c,0x73,0x79,0x6c,0x6c,0x61,0x62,0x69,0x63,0x73,0x62, +0x17,0x65,0x78,0x74,0x65,0x6e,0x64,0x65,0x64,0xa3,0xad,0x11,0x61,0x73,0x62,0x12, +0x65,0x78,0x74,0xa3,0xad,0x15,0x61,0x72,0x69,0x74,0x69,0x63,0xa3,0x78,0x70,0xc2, +0xf5,0x70,0xa6,0xb,0x72,0xa6,0xc7,0x73,7,0x6f,0xc1,0x7f,0x6f,0x76,0x70,0xa2, +0x47,0x75,0xa2,0x66,0x79,1,0x6c,0x4c,0x72,0x12,0x69,0x61,0x63,0x3a,0x12,0x73, +0x75,0x70,0xa4,0x17,0x16,0x70,0x6c,0x65,0x6d,0x65,0x6e,0x74,0xa5,0x17,0x17,0x6f, +0x74,0x69,0x6e,0x61,0x67,0x72,0x69,0xa3,0x8f,2,0x67,0x34,0x72,0x3e,0x79,0x13, +0x6f,0x6d,0x62,0x6f,0xa5,0x16,0x13,0x64,0x69,0x61,0x6e,0xa5,0x23,0x17,0x61,0x73, +0x6f,0x6d,0x70,0x65,0x6e,0x67,0xa3,0xda,1,0x61,0x32,0x65,0x14,0x63,0x69,0x61, +0x6c,0x73,0xa3,0x56,0x12,0x63,0x69,0x6e,0x1f,0x67,0x6d,0x6f,0x64,0x69,0x66,0x69, +0x65,0x72,0x6c,0x65,0x74,0x74,0x65,0x72,0x73,0x2d,2,0x6e,0x48,0x70,0x76,0x74, +0x1d,0x74,0x6f,0x6e,0x73,0x69,0x67,0x6e,0x77,0x72,0x69,0x74,0x69,0x6e,0x67,0xa5, +6,0x15,0x64,0x61,0x6e,0x65,0x73,0x65,0xa2,0x9b,0x12,0x73,0x75,0x70,0xa2,0xdb, +0x16,0x70,0x6c,0x65,0x6d,0x65,0x6e,0x74,0xa3,0xdb,4,0x61,0xa2,0xa8,0x65,0x5c, +0x6d,0x9e,0x70,0xa2,0x4b,0x73,0x13,0x79,0x6d,0x62,0x6f,0x1f,0x6c,0x73,0x61,0x6e, +0x64,0x70,0x69,0x63,0x74,0x6f,0x67,0x72,0x61,0x70,0x68,0x73,0xa5,5,0x10,0x72, +1,0x61,0x4e,0x73,0x12,0x63,0x72,0x69,0x1f,0x70,0x74,0x73,0x61,0x6e,0x64,0x73, +0x75,0x62,0x73,0x63,0x72,0x69,0x70,0x74,0x73,0x73,0x14,0x6e,0x64,0x73,0x75,0x62, +0x73,0x1b,0x61,0x74,0x68,0x6f,0x70,0x65,0x72,0x61,0x74,0x6f,0x72,0x73,0xa3,0x6a, +1,0x6c,0x40,0x75,1,0x61,0x6e,0x6e,0x17,0x63,0x74,0x75,0x61,0x74,0x69,0x6f, +0x6e,0xa3,0x8e,0x15,0x65,0x6d,0x65,0x6e,0x74,0x61,1,0x6c,0x50,0x72,0x1e,0x79, +0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x75,0x73,0x65,0x61,0x72,0x65,0x61,1,0x61, +0xa3,0x6d,0x62,0xa3,0x6e,3,0x61,0x5c,0x6d,0x78,0x70,0xa2,0x41,0x73,0x13,0x79, +0x6d,0x62,0x6f,0x1f,0x6c,0x73,0x61,0x6e,0x64,0x70,0x69,0x63,0x74,0x6f,0x67,0x72, +0x61,0x70,0x68,0x73,0xa5,5,0x14,0x72,0x72,0x6f,0x77,0x73,2,0x61,0xa3,0x67, +0x62,0xa3,0x68,0x63,0xa3,0xfa,0x13,0x61,0x74,0x68,0x65,0x1f,0x6d,0x61,0x74,0x69, +0x63,0x61,0x6c,0x6f,0x70,0x65,0x72,0x61,0x74,0x6f,0x72,0x73,0xa3,0x6a,0x19,0x75, +0x6e,0x63,0x74,0x75,0x61,0x74,0x69,0x6f,0x6e,0xa3,0x8e,0x61,0x5a,0x68,0x84,0x69, +0xa2,0x5b,0x6d,0x16,0x61,0x6c,0x6c,0x66,0x6f,0x72,0x6d,1,0x73,0xa3,0x54,0x76, +0x16,0x61,0x72,0x69,0x61,0x6e,0x74,0x73,0xa3,0x54,1,0x6d,0x36,0x75,0x16,0x72, +0x61,0x73,0x68,0x74,0x72,0x61,0xa3,0xa1,0x15,0x61,0x72,0x69,0x74,0x61,0x6e,0xa3, +0xac,1,0x61,0x52,0x6f,0x13,0x72,0x74,0x68,0x61,0x1f,0x6e,0x64,0x66,0x6f,0x72, +0x6d,0x61,0x74,0x63,0x6f,0x6e,0x74,0x72,0x6f,0x6c,0x73,0xa3,0xf7,1,0x72,0x2e, +0x76,0x12,0x69,0x61,0x6e,0xa3,0x79,0x12,0x61,0x64,0x61,0xa3,0xd9,1,0x64,0x50, +0x6e,0x13,0x68,0x61,0x6c,0x61,0x50,0x1d,0x61,0x72,0x63,0x68,0x61,0x69,0x63,0x6e, +0x75,0x6d,0x62,0x65,0x72,0x73,0xa3,0xf9,0x13,0x64,0x68,0x61,0x6d,0xa3,0xf8,5, +0x72,0x35,0x72,0x44,0x73,0x64,0x75,1,0x61,0xa3,0x4e,0x6e,0x17,0x63,0x74,0x75, +0x61,0x74,0x69,0x6f,0x6e,0x71,0x17,0x69,0x76,0x61,0x74,0x65,0x75,0x73,0x65,0xa2, +0x4e,0x13,0x61,0x72,0x65,0x61,0xa3,0x4e,0x1b,0x61,0x6c,0x74,0x65,0x72,0x70,0x61, +0x68,0x6c,0x61,0x76,0x69,0xa3,0xf6,0x61,0x40,0x68,0x82,0x6c,0x19,0x61,0x79,0x69, +0x6e,0x67,0x63,0x61,0x72,0x64,0x73,0xa3,0xcc,2,0x68,0x38,0x6c,0x4a,0x75,0x15, +0x63,0x69,0x6e,0x68,0x61,0x75,0xa3,0xf5,0x17,0x61,0x77,0x68,0x68,0x6d,0x6f,0x6e, +0x67,0xa3,0xf3,0x15,0x6d,0x79,0x72,0x65,0x6e,0x65,0xa3,0xf4,1,0x61,0x8e,0x6f, +1,0x65,0x74,0x6e,0x16,0x65,0x74,0x69,0x63,0x65,0x78,0x74,0xa2,0x72,1,0x65, +0x2c,0x73,0x11,0x75,0x70,0xa3,0x8d,0x15,0x6e,0x73,0x69,0x6f,0x6e,0x73,0xa2,0x72, +0x19,0x73,0x75,0x70,0x70,0x6c,0x65,0x6d,0x65,0x6e,0x74,0xa3,0x8d,0x15,0x6e,0x69, +0x63,0x69,0x61,0x6e,0xa3,0x97,1,0x67,0x3e,0x69,0x13,0x73,0x74,0x6f,0x73,0xa2, +0xa6,0x13,0x64,0x69,0x73,0x63,0xa3,0xa6,0x12,0x73,0x70,0x61,0xa3,0x96,1,0x65, +0x5c,0x75,1,0x6d,0x2a,0x6e,0x11,0x69,0x63,0x67,0x10,0x69,0xa2,0xc0,0x1d,0x6e, +0x75,0x6d,0x65,0x72,0x61,0x6c,0x73,0x79,0x6d,0x62,0x6f,0x6c,0x73,0xa3,0xc0,0x13, +0x6a,0x61,0x6e,0x67,0xa3,0xa3,0x6d,0xa2,0xce,0x6e,0xa8,1,0x6f,5,0x70,0x4b, +0x70,0x46,0x72,0x7a,0x73,1,0x61,0x30,0x6d,0x13,0x61,0x6e,0x79,0x61,0xa3,0x7a, +0x11,0x67,0x65,0xa5,0xf,0x18,0x74,0x69,0x63,0x61,0x6c,0x63,0x68,0x61,0x72,0x1f, +0x61,0x63,0x74,0x65,0x72,0x72,0x65,0x63,0x6f,0x67,0x6e,0x69,0x74,0x69,0x6f,0x6e, +0x85,1,0x69,0x46,0x6e,0x1e,0x61,0x6d,0x65,0x6e,0x74,0x61,0x6c,0x64,0x69,0x6e, +0x67,0x62,0x61,0x74,0x73,0xa3,0xf2,0x11,0x79,0x61,0x47,0x63,0xa2,0x71,0x67,0xa2, +0x71,0x6c,1,0x63,0xa2,0x62,0x64,5,0x70,0x38,0x70,0x36,0x73,0x56,0x74,0x14, +0x75,0x72,0x6b,0x69,0x63,0xa3,0xbf,0x11,0x65,0x72,1,0x6d,0x2e,0x73,0x12,0x69, +0x61,0x6e,0xa3,0x8c,0x11,0x69,0x63,0xa3,0xf1,0x10,0x6f,1,0x67,0x3a,0x75,0x18, +0x74,0x68,0x61,0x72,0x61,0x62,0x69,0x61,0x6e,0xa3,0xbb,0x13,0x64,0x69,0x61,0x6e, +0xa5,0x22,0x68,0x42,0x69,0x54,0x6e,0x1a,0x6f,0x72,0x74,0x68,0x61,0x72,0x61,0x62, +0x69,0x61,0x6e,0xa3,0xf0,0x17,0x75,0x6e,0x67,0x61,0x72,0x69,0x61,0x6e,0xa5,4, +0x14,0x74,0x61,0x6c,0x69,0x63,0xa3,0x58,0x13,0x68,0x69,0x6b,0x69,0xa3,0x9d,0x10, +0x72,0x85,0x12,0x68,0x61,0x6d,0x65,6,0x6f,0x86,0x6f,0x6c,0x72,0xa2,0x61,0x75, +0xa2,0x62,0x79,0x14,0x61,0x6e,0x6d,0x61,0x72,0x58,0x12,0x65,0x78,0x74,2,0x61, +0xa3,0xb6,0x62,0xa3,0xee,0x65,0x13,0x6e,0x64,0x65,0x64,1,0x61,0xa3,0xb6,0x62, +0xa3,0xee,1,0x64,0x52,0x6e,0x15,0x67,0x6f,0x6c,0x69,0x61,0x6e,0x6a,0x12,0x73, +0x75,0x70,0xa4,0xd,0x16,0x70,0x6c,0x65,0x6d,0x65,0x6e,0x74,0xa5,0xd,0x10,0x69, +0xa2,0xec,0x13,0x66,0x69,0x65,0x72,1,0x6c,0x3c,0x74,0x19,0x6f,0x6e,0x65,0x6c, +0x65,0x74,0x74,0x65,0x72,0x73,0xa3,0x8a,0x15,0x65,0x74,0x74,0x65,0x72,0x73,0x2d, +0x10,0x6f,0xa3,0xed,1,0x6c,0x44,0x73,0x11,0x69,0x63,0xa2,0x5c,0x18,0x61,0x6c, +0x73,0x79,0x6d,0x62,0x6f,0x6c,0x73,0xa3,0x5c,0x13,0x74,0x61,0x6e,0x69,0xa5,3, +0x61,0xa2,0x9b,0x65,0xa4,0x4c,0x69,1,0x61,0xa2,0x8f,0x73,0x10,0x63,5,0x70, +0x18,0x70,0xa2,0x71,0x73,0x36,0x74,0x17,0x65,0x63,0x68,0x6e,0x69,0x63,0x61,0x6c, +0x81,0x15,0x79,0x6d,0x62,0x6f,0x6c,0x73,0x8f,0x61,0xa2,0x66,0x65,0x46,0x6d,0x19, +0x61,0x74,0x68,0x73,0x79,0x6d,0x62,0x6f,0x6c,0x73,1,0x61,0xa3,0x66,0x62,0xa3, +0x69,0x17,0x6c,0x6c,0x61,0x6e,0x65,0x6f,0x75,0x73,2,0x6d,0x3a,0x73,0x6c,0x74, +0x17,0x65,0x63,0x68,0x6e,0x69,0x63,0x61,0x6c,0x81,0x11,0x61,0x74,0x1f,0x68,0x65, +0x6d,0x61,0x74,0x69,0x63,0x61,0x6c,0x73,0x79,0x6d,0x62,0x6f,0x6c,0x73,1,0x61, +0xa3,0x66,0x62,0xa3,0x69,0x15,0x79,0x6d,0x62,0x6f,0x6c,0x73,0x8e,0x12,0x61,0x6e, +0x64,1,0x61,0x3c,0x70,0x19,0x69,0x63,0x74,0x6f,0x67,0x72,0x61,0x70,0x68,0x73, +0xa3,0xcd,0x14,0x72,0x72,0x6f,0x77,0x73,0xa3,0x73,0x10,0x6f,0xa3,0xd8,7,0x72, +0x6f,0x72,0x44,0x73,0x4e,0x74,0x62,0x79,0x19,0x61,0x6e,0x6e,0x75,0x6d,0x65,0x72, +0x61,0x6c,0x73,0xa5,0x20,0x13,0x63,0x68,0x65,0x6e,0xa5,0xc,0x18,0x61,0x72,0x61, +0x6d,0x67,0x6f,0x6e,0x64,0x69,0xa5,0x14,0x10,0x68,2,0x61,0x3a,0x65,0x4a,0x6f, +0x17,0x70,0x65,0x72,0x61,0x74,0x6f,0x72,0x73,0x7f,0x16,0x6c,0x70,0x68,0x61,0x6e, +0x75,0x6d,0xa3,0x5d,0x16,0x6d,0x61,0x74,0x69,0x63,0x61,0x6c,1,0x61,0x36,0x6f, +0x17,0x70,0x65,0x72,0x61,0x74,0x6f,0x72,0x73,0x7f,0x11,0x6c,0x70,0x1f,0x68,0x61, +0x6e,0x75,0x6d,0x65,0x72,0x69,0x63,0x73,0x79,0x6d,0x62,0x6f,0x6c,0x73,0xa3,0x5d, +0x68,0x50,0x6b,0x7e,0x6c,0x88,0x6e,1,0x64,0x34,0x69,0x15,0x63,0x68,0x61,0x65, +0x61,0x6e,0xa3,0xea,0x12,0x61,0x69,0x63,0xa3,0xc6,1,0x61,0x3e,0x6a,0x12,0x6f, +0x6e,0x67,0xa2,0xaa,0x14,0x74,0x69,0x6c,0x65,0x73,0xa3,0xaa,0x13,0x6a,0x61,0x6e, +0x69,0xa3,0xe9,0x13,0x61,0x73,0x61,0x72,0xa5,0x1f,0x15,0x61,0x79,0x61,0x6c,0x61, +0x6d,0x4f,3,0x64,0x6c,0x65,0x7e,0x6e,0xa2,0x47,0x72,0x14,0x6f,0x69,0x74,0x69, 0x63,1,0x63,0x3c,0x68,0x19,0x69,0x65,0x72,0x6f,0x67,0x6c,0x79,0x70,0x68,0x73, -0xa3,0x56,0x15,0x75,0x72,0x73,0x69,0x76,0x65,0xa3,0x8d,0x17,0x74,0x65,0x69,0x6d, -0x61,0x79,0x65,0x6b,0xa3,0x73,0x10,0x64,0xa2,0x8c,0x17,0x65,0x6b,0x69,0x6b,0x61, -0x6b,0x75,0x69,0xa3,0x8c,0x11,0x61,0x6f,0xa3,0x5c,5,0x6f,0x14,0x6f,0x30,0x70, -0x36,0x74,0x11,0x68,0x69,0xa3,0x78,0x11,0x72,0x65,0xa3,0x77,0x11,0x65,0x6c,0xa3, -0x8a,0x61,0x2e,0x68,0x98,0x6e,0x11,0x64,0x61,0x4b,4,0x69,0x3c,0x6c,0x44,0x6e, -0x48,0x74,0x56,0x79,0x13,0x61,0x68,0x6c,0x69,0xa3,0x4f,0x12,0x74,0x68,0x69,0xa3, -0x78,0x10,0x69,0xa3,0x4f,1,0x61,0x4d,0x6e,0x12,0x61,0x64,0x61,0x4b,0x14,0x61, -0x6b,0x61,0x6e,0x61,0x4c,0x19,0x6f,0x72,0x68,0x69,0x72,0x61,0x67,0x61,0x6e,0x61, -0x8d,3,0x61,0x3c,0x6d,0x4e,0x6f,0x5a,0x75,0x15,0x64,0x61,0x77,0x61,0x64,0x69, -0xa3,0x91,0x10,0x72,0x92,0x15,0x6f,0x73,0x68,0x74,0x68,0x69,0x93,1,0x65,0x24, -0x72,0x4f,0x10,0x72,0x4f,0x10,0x6a,0xa2,0x9d,0x11,0x6b,0x69,0xa3,0x9d,4,0x61, -0x5c,0x65,0x90,0x69,0xa0,0x6f,0xa2,0x5d,0x79,1,0x63,0x34,0x64,0x10,0x69,0xa2, -0x6c,0x11,0x61,0x6e,0xa3,0x6c,0x10,0x69,0xa2,0x6b,0x11,0x61,0x6e,0xa3,0x6b,2, -0x6e,0x42,0x6f,0x46,0x74,3,0x66,0xa3,0x50,0x67,0xa3,0x51,0x69,0x24,0x6e,0x53, -0x10,0x6e,0x53,0x10,0x61,0xa3,0x6a,0x50,0x10,0x6f,0x51,0x11,0x70,0x63,0xa2,0x52, -0x11,0x68,0x61,0xa3,0x52,2,0x6d,0x2e,0x6e,0x36,0x73,0x10,0x75,0xa3,0x83,0x10, -0x62,0x80,0x10,0x75,0x81,2,0x61,0xa3,0x53,0x62,0x83,0x65,0x11,0x61,0x72,1, -0x61,0xa3,0x53,0x62,0x83,0x11,0x6d,0x61,0xa3,0x8b,0x68,0x6e,0x69,0xa2,0x83,0x6a, -2,0x61,0x30,0x70,0x52,0x75,0x11,0x72,0x63,0xa3,0x94,1,0x6d,0x38,0x76,0x10, -0x61,0xa2,0x4e,0x13,0x6e,0x65,0x73,0x65,0xa3,0x4e,0x10,0x6f,0xa3,0xad,0x11,0x61, -0x6e,0xa3,0x69,6,0x6c,0x1a,0x6c,0x34,0x6d,0x3a,0x72,0x40,0x75,0x11,0x6e,0x67, -0xa3,0x4c,0x11,0x75,0x77,0xa3,0x9c,0x11,0x6e,0x67,0xa3,0x4b,0x11,0x6b,0x74,0x8d, -0x61,0x3a,0x65,0x8c,0x69,0x11,0x72,0x61,0x48,0x13,0x67,0x61,0x6e,0x61,0x49,1, -0x6e,0x34,0x74,0x10,0x72,0xa2,0xa2,0x11,0x61,0x6e,0xa3,0xa2,0x42,6,0x6f,0xe, -0x6f,0x77,0x73,0xa3,0x49,0x74,0xa3,0x4a,0x75,0x12,0x6e,0x6f,0x6f,0x77,0x62,0xa3, -0xac,0x67,0x24,0x69,0x43,0x44,0x11,0x75,0x6c,0x45,0x11,0x62,0x72,0x46,0x11,0x65, -0x77,0x47,2,0x6d,0x2e,0x6e,0x4a,0x74,0x11,0x61,0x6c,0x5d,0x1c,0x70,0x65,0x72, -0x69,0x61,0x6c,0x61,0x72,0x61,0x6d,0x61,0x69,0x63,0xa3,0x74,2,0x64,0x66,0x68, -0x6a,0x73,0x1b,0x63,0x72,0x69,0x70,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x70,0x61,1, -0x68,0x32,0x72,0x14,0x74,0x68,0x69,0x61,0x6e,0xa3,0x7d,0x13,0x6c,0x61,0x76,0x69, -0xa3,0x7a,0x10,0x73,0xa3,0x4d,0x15,0x65,0x72,0x69,0x74,0x65,0x64,0x23,0x64,0xc0, -0xcf,0x64,0xa2,0x68,0x65,0xa2,0x90,0x67,4,0x65,0x64,0x6c,0x7c,0x6f,0x90,0x72, -0xa2,0x44,0x75,1,0x6a,0x38,0x72,1,0x6d,0x24,0x75,0x41,0x13,0x75,0x6b,0x68, -0x69,0x41,1,0x61,0x24,0x72,0x3f,0x13,0x72,0x61,0x74,0x69,0x3f,0x10,0x6f,1, -0x6b,0xa3,0x48,0x72,0x38,0x13,0x67,0x69,0x61,0x6e,0x39,0x11,0x61,0x67,0x90,0x15, -0x6f,0x6c,0x69,0x74,0x69,0x63,0x91,1,0x6e,0x30,0x74,0x10,0x68,0x3a,0x11,0x69, -0x63,0x3b,0x10,0x6d,0xa3,0xaf,1,0x61,0x32,0x65,1,0x65,0x24,0x6b,0x3d,0x10, -0x6b,0x3d,0x10,0x6e,0xa2,0x89,0x12,0x74,0x68,0x61,0xa3,0x89,2,0x65,0x3e,0x73, -0x64,0x75,0x11,0x70,0x6c,0xa2,0x87,0x13,0x6f,0x79,0x61,0x6e,0xa3,0x87,1,0x73, -0x38,0x76,0x10,0x61,0x34,0x15,0x6e,0x61,0x67,0x61,0x72,0x69,0x35,0x13,0x65,0x72, -0x65,0x74,0x33,0x11,0x72,0x74,0x33,2,0x67,0x3a,0x6c,0x72,0x74,0x11,0x68,0x69, -0x36,0x13,0x6f,0x70,0x69,0x63,0x37,0x10,0x79,2,0x64,0xa3,0x45,0x68,0xa3,0x46, -0x70,0xa2,0x47,0x1e,0x74,0x69,0x61,0x6e,0x68,0x69,0x65,0x72,0x6f,0x67,0x6c,0x79, -0x70,0x68,0x73,0xa3,0x47,0x11,0x62,0x61,0xa2,0x88,0x12,0x73,0x61,0x6e,0xa3,0x88, -0x61,0xa2,0xa2,0x62,0xa4,7,0x63,6,0x6f,0x3d,0x6f,0x5a,0x70,0x76,0x75,0x7a, -0x79,1,0x70,0x3e,0x72,2,0x69,0x2a,0x6c,0x31,0x73,0xa3,0x44,0x13,0x6c,0x6c, -0x69,0x63,0x31,0x13,0x72,0x69,0x6f,0x74,0x7f,1,0x6d,0x30,0x70,0x10,0x74,0x2e, -0x11,0x69,0x63,0x2f,0x12,0x6d,0x6f,0x6e,0x21,0x11,0x72,0x74,0x7f,0x16,0x6e,0x65, -0x69,0x66,0x6f,0x72,0x6d,0xa3,0x65,0x61,0x32,0x68,0xa2,0x41,0x69,0x11,0x72,0x74, -0xa3,0x43,3,0x6b,0x4c,0x6e,0x50,0x72,0x76,0x75,0x1d,0x63,0x61,0x73,0x69,0x61, -0x6e,0x61,0x6c,0x62,0x61,0x6e,0x69,0x61,0x6e,0xa3,0x9f,0x10,0x6d,0xa3,0x76,1, -0x61,0x24,0x73,0x71,0x1d,0x64,0x69,0x61,0x6e,0x61,0x62,0x6f,0x72,0x69,0x67,0x69, -0x6e,0x61,0x6c,0x71,0x10,0x69,0xa2,0x68,0x11,0x61,0x6e,0xa3,0x68,1,0x61,0x34, -0x65,0x10,0x72,0x2c,0x13,0x6f,0x6b,0x65,0x65,0x2d,1,0x6b,0x26,0x6d,0xa3,0x42, -0x11,0x6d,0x61,0xa3,0x76,6,0x68,0x4a,0x68,0x48,0x6e,0x4e,0x72,0x76,0x76,1, -0x65,0x2a,0x73,0x10,0x74,0xa3,0x75,0x13,0x73,0x74,0x61,0x6e,0xa3,0x75,0x11,0x6f, -0x6d,0xa3,0xa1,0x11,0x61,0x74,0x1f,0x6f,0x6c,0x69,0x61,0x6e,0x68,0x69,0x65,0x72, -0x6f,0x67,0x6c,0x79,0x70,0x68,0x73,0xa3,0x9c,1,0x61,0x3e,0x6d,2,0x65,0x2a, -0x69,0xa3,0x74,0x6e,0x27,0x13,0x6e,0x69,0x61,0x6e,0x27,0x10,0x62,0x24,0x11,0x69, -0x63,0x25,0x64,0x30,0x66,0x44,0x67,0x11,0x68,0x62,0xa3,0x9f,0x10,0x6c,1,0x61, -0x26,0x6d,0xa3,0xa7,0x10,0x6d,0xa3,0xa7,0x11,0x61,0x6b,0xa3,0x93,6,0x6c,0x3c, -0x6c,0x52,0x6f,0x56,0x72,0x66,0x75,1,0x67,0x30,0x68,1,0x64,0x79,0x69,0x10, -0x64,0x79,0x10,0x69,0x8e,0x13,0x6e,0x65,0x73,0x65,0x8f,0x11,0x69,0x73,0xa1,0x11, -0x70,0x6f,0x2a,0x13,0x6d,0x6f,0x66,0x6f,0x2b,0x10,0x61,1,0x68,0x2e,0x69,0x7c, -0x12,0x6c,0x6c,0x65,0x7d,0xa2,0x41,0x11,0x6d,0x69,0xa3,0x41,0x61,0x48,0x65,0x9c, -0x68,1,0x61,0x2a,0x6b,0x10,0x73,0xa3,0xa8,0x15,0x69,0x6b,0x73,0x75,0x6b,0x69, -0xa3,0xa8,3,0x6c,0x3a,0x6d,0x48,0x73,0x54,0x74,1,0x61,0x24,0x6b,0x9f,0x10, -0x6b,0x9f,0x10,0x69,0x9c,0x13,0x6e,0x65,0x73,0x65,0x9d,0x10,0x75,0xa2,0x82,0x10, -0x6d,0xa3,0x82,0x10,0x73,0xa2,0x86,0x13,0x61,0x76,0x61,0x68,0xa3,0x86,0x11,0x6e, -0x67,0x28,0x12,0x61,0x6c,0x69,0x29,3,0x6c,0x42,0x6e,0x90,0x74,0xa2,0x46,0x76, -0x24,0x17,0x6f,0x77,0x65,0x6c,0x6a,0x61,0x6d,0x6f,0x25,0x22,1,0x65,0x54,0x76, -0x28,1,0x73,0x38,0x74,0x2a,0x17,0x73,0x79,0x6c,0x6c,0x61,0x62,0x6c,0x65,0x2b, -0x16,0x79,0x6c,0x6c,0x61,0x62,0x6c,0x65,0x29,0x18,0x61,0x64,0x69,0x6e,0x67,0x6a, -0x61,0x6d,0x6f,0x23,1,0x61,0x21,0x6f,0x1a,0x74,0x61,0x70,0x70,0x6c,0x69,0x63, -0x61,0x62,0x6c,0x65,0x21,0x26,0x1a,0x72,0x61,0x69,0x6c,0x69,0x6e,0x67,0x6a,0x61, -0x6d,0x6f,0x27,1,0x6e,0x2c,0x79,0x22,0x11,0x65,0x73,0x23,0x20,0x10,0x6f,0x21, -1,0x6e,0x2c,0x79,0x22,0x11,0x65,0x73,0x23,0x20,0x10,0x6f,0x21,2,0x6d,0x30, -0x6e,0x3a,0x79,0x22,0x11,0x65,0x73,0x23,0x24,0x13,0x61,0x79,0x62,0x65,0x25,0x20, -0x10,0x6f,0x21,2,0x6d,0x30,0x6e,0x3a,0x79,0x22,0x11,0x65,0x73,0x23,0x24,0x13, -0x61,0x79,0x62,0x65,0x25,0x20,0x10,0x6f,0x21,0xb,0x72,0x39,0x76,0xc,0x76,0x33, -0x78,0x2a,0x7a,0x11,0x77,0x6a,0x43,0x10,0x78,0x21,0x72,0x28,0x73,0x50,0x74,0x31, -1,0x65,0x24,0x69,0x39,0x1e,0x67,0x69,0x6f,0x6e,0x61,0x6c,0x69,0x6e,0x64,0x69, -0x63,0x61,0x74,0x6f,0x72,0x39,1,0x6d,0x35,0x70,0x18,0x61,0x63,0x69,0x6e,0x67, -0x6d,0x61,0x72,0x6b,0x35,0x6c,0x1f,0x6c,0x3c,0x6f,0x4a,0x70,1,0x70,0x37,0x72, -0x14,0x65,0x70,0x65,0x6e,0x64,0x37,0x28,1,0x66,0x2b,0x76,0x2c,0x10,0x74,0x2f, -0x13,0x74,0x68,0x65,0x72,0x21,0x63,0x4c,0x65,0x64,0x67,1,0x61,0x3a,0x6c,0x19, -0x75,0x65,0x61,0x66,0x74,0x65,0x72,0x7a,0x77,0x6a,0x41,0x10,0x7a,0x41,2,0x6e, -0x23,0x6f,0x24,0x72,0x25,0x14,0x6e,0x74,0x72,0x6f,0x6c,0x23,2,0x62,0x34,0x6d, -0x4e,0x78,0x26,0x13,0x74,0x65,0x6e,0x64,0x27,0x3a,1,0x61,0x24,0x67,0x3d,0x11, -0x73,0x65,0x3a,0x12,0x67,0x61,0x7a,0x3d,0x3e,0x16,0x6f,0x64,0x69,0x66,0x69,0x65, -0x72,0x3f,9,0x6e,0x4a,0x6e,0x34,0x6f,0x44,0x73,0x60,0x75,0x94,0x78,0x10,0x78, -0x21,0x10,0x75,0x2a,0x14,0x6d,0x65,0x72,0x69,0x63,0x2b,1,0x6c,0x2c,0x74,0x12, -0x68,0x65,0x72,0x21,0x14,0x65,0x74,0x74,0x65,0x72,0x2d,3,0x63,0x36,0x65,0x46, -0x70,0x31,0x74,0x32,0x12,0x65,0x72,0x6d,0x33,0x3c,0x16,0x6f,0x6e,0x74,0x69,0x6e, -0x75,0x65,0x3d,0x2e,0x10,0x70,0x2f,0x10,0x70,0x34,0x12,0x70,0x65,0x72,0x35,0x61, -0x46,0x63,0x52,0x65,0x64,0x66,0x72,0x6c,2,0x65,0x2d,0x66,0x3b,0x6f,0x28,0x12, -0x77,0x65,0x72,0x29,0x10,0x74,0x22,0x12,0x65,0x72,0x6d,0x23,1,0x6c,0x24,0x72, -0x37,0x24,0x12,0x6f,0x73,0x65,0x25,0x10,0x78,0x38,0x13,0x74,0x65,0x6e,0x64,0x39, -0x10,0x6f,0x26,0x13,0x72,0x6d,0x61,0x74,0x27,0xf,0x6c,0x7c,0x72,0x34,0x72,0x32, -0x73,0x5a,0x78,0x76,0x7a,0x11,0x77,0x6a,0x4b,1,0x65,0x24,0x69,0x3b,0x1e,0x67, -0x69,0x6f,0x6e,0x61,0x6c,0x69,0x6e,0x64,0x69,0x63,0x61,0x74,0x6f,0x72,0x3b,1, -0x69,0x24,0x71,0x3f,0x18,0x6e,0x67,0x6c,0x65,0x71,0x75,0x6f,0x74,0x65,0x3f,0x10, -0x78,0x21,0x6c,0x36,0x6d,0x3c,0x6e,0x76,0x6f,0x13,0x74,0x68,0x65,0x72,0x21,1, -0x65,0x23,0x66,0x35,3,0x62,0x37,0x69,0x28,0x6c,0x29,0x6e,0x2b,0x10,0x64,1, -0x6c,0x34,0x6e,0x11,0x75,0x6d,0x2a,0x12,0x6c,0x65,0x74,0x37,0x14,0x65,0x74,0x74, -0x65,0x72,0x29,2,0x65,0x36,0x6c,0x39,0x75,0x2c,0x14,0x6d,0x65,0x72,0x69,0x63, -0x2d,0x14,0x77,0x6c,0x69,0x6e,0x65,0x39,0x66,0x3f,0x66,0x40,0x67,0x4e,0x68,0x70, -0x6b,0x10,0x61,0x26,0x15,0x74,0x61,0x6b,0x61,0x6e,0x61,0x27,0x10,0x6f,0x24,0x13, -0x72,0x6d,0x61,0x74,0x25,1,0x61,0x3a,0x6c,0x19,0x75,0x65,0x61,0x66,0x74,0x65, -0x72,0x7a,0x77,0x6a,0x49,0x10,0x7a,0x49,1,0x65,0x24,0x6c,0x3d,0x19,0x62,0x72, -0x65,0x77,0x6c,0x65,0x74,0x74,0x65,0x72,0x3d,0x61,0x86,0x63,0x92,0x64,0x94,0x65, -2,0x62,0x44,0x6d,0x5e,0x78,0x2e,0x13,0x74,0x65,0x6e,0x64,0x32,0x15,0x6e,0x75, -0x6d,0x6c,0x65,0x74,0x2f,0x42,1,0x61,0x24,0x67,0x45,0x11,0x73,0x65,0x42,0x12, -0x67,0x61,0x7a,0x45,0x46,0x16,0x6f,0x64,0x69,0x66,0x69,0x65,0x72,0x47,0x15,0x6c, -0x65,0x74,0x74,0x65,0x72,0x23,0x10,0x72,0x31,1,0x6f,0x24,0x71,0x41,0x18,0x75, -0x62,0x6c,0x65,0x71,0x75,0x6f,0x74,0x65,0x41,2,0x63,0x32,0x6e,0x3c,0x6f,0x22, -0x12,0x70,0x65,0x6e,0x23,0x24,0x13,0x6c,0x6f,0x73,0x65,0x25,0x20,0x12,0x6f,0x6e, -0x65,0x21,0xd,0x6e,0xc1,0x86,0x73,0xa8,0x73,0x4c,0x74,0xa2,0x76,0x75,0xa2,0x83, -0x7a,0xd8,0x70,0,2,0x6c,0xd9,0x20,0,0x70,0xd9,0x40,0,0x73,0xc3,0, -0xfe,0xf,0,0,0,7,0x6f,0x3c,0x6f,0xff,8,0,0,0,0x70,0x3a, -0x75,0x6e,0x79,0x13,0x6d,0x62,0x6f,0x6c,0xff,0xf,0,0,0,0x11,0x61,0x63, -1,0x65,0x34,0x69,0x15,0x6e,0x67,0x6d,0x61,0x72,0x6b,0xa5,0,0x18,0x73,0x65, -0x70,0x61,0x72,0x61,0x74,0x6f,0x72,0xc3,0,0x16,0x72,0x72,0x6f,0x67,0x61,0x74, -0x65,0xe1,0,0,0x63,0xff,2,0,0,0,0x65,0x38,0x6b,0xff,4,0, -0,0,0x6d,0xff,1,0,0,0,0x16,0x70,0x61,0x72,0x61,0x74,0x6f,0x72, -0xd9,0x70,0,0x1d,0x69,0x74,0x6c,0x65,0x63,0x61,0x73,0x65,0x6c,0x65,0x74,0x74, -0x65,0x72,0x31,1,0x6e,0x40,0x70,0x1c,0x70,0x65,0x72,0x63,0x61,0x73,0x65,0x6c, -0x65,0x74,0x74,0x65,0x72,0x25,0x17,0x61,0x73,0x73,0x69,0x67,0x6e,0x65,0x64,0x23, -0x6e,0xa2,0x69,0x6f,0xa2,0x89,0x70,0xfe,0x30,0xf8,0,0,9,0x69,0x33,0x69, -0xff,0x10,0,0,0,0x6f,0xfd,0x80,0,0,0x72,0x54,0x73,0xf9,0,0, -0x75,0x12,0x6e,0x63,0x74,0xfe,0x30,0xf8,0,0,0x15,0x75,0x61,0x74,0x69,0x6f, -0x6e,0xff,0x30,0xf8,0,0,0x17,0x69,0x76,0x61,0x74,0x65,0x75,0x73,0x65,0xdd, -0,0,0x61,0x48,0x63,0xfd,0x40,0,0,0x64,0xe9,0,0,0x65,0xfd,0x20, -0,0,0x66,0xff,0x20,0,0,0,0x1f,0x72,0x61,0x67,0x72,0x61,0x70,0x68, -0x73,0x65,0x70,0x61,0x72,0x61,0x74,0x6f,0x72,0xd9,0x40,0,0xbe,0,3,0x64, -0xa7,0,0x6c,0xab,0,0x6f,0x30,0x75,0x13,0x6d,0x62,0x65,0x72,0xbf,0,0xb2, -0,0x1b,0x6e,0x73,0x70,0x61,0x63,0x69,0x6e,0x67,0x6d,0x61,0x72,0x6b,0xa1,1, -0x70,0x92,0x74,0x12,0x68,0x65,0x72,0xe6,0x80,1,3,0x6c,0x40,0x6e,0x4a,0x70, -0x56,0x73,0x14,0x79,0x6d,0x62,0x6f,0x6c,0xff,8,0,0,0,0x14,0x65,0x74, -0x74,0x65,0x72,0x61,0x14,0x75,0x6d,0x62,0x65,0x72,0xb3,0,0x19,0x75,0x6e,0x63, -0x74,0x75,0x61,0x74,0x69,0x6f,0x6e,0xfd,0x80,0,0,0x1c,0x65,0x6e,0x70,0x75, -0x6e,0x63,0x74,0x75,0x61,0x74,0x69,0x6f,0x6e,0xf9,0,0,0x66,0xc0,0xc4,0x66, -0xa2,0x47,0x69,0xa2,0x64,0x6c,0xa2,0x79,0x6d,0xa4,0xc0,4,0x61,0x6c,0x63,0xa5, -0,0x65,0xa3,0x80,0x6e,0xa1,0x6f,0x15,0x64,0x69,0x66,0x69,0x65,0x72,1,0x6c, -0x38,0x73,0x14,0x79,0x6d,0x62,0x6f,0x6c,0xff,4,0,0,0,0x14,0x65,0x74, -0x74,0x65,0x72,0x41,1,0x72,0x3c,0x74,0x16,0x68,0x73,0x79,0x6d,0x62,0x6f,0x6c, -0xff,1,0,0,0,0x10,0x6b,0xa5,0xc0,1,0x69,0x32,0x6f,0x13,0x72,0x6d, -0x61,0x74,0xdb,0,0,0x1d,0x6e,0x61,0x6c,0x70,0x75,0x6e,0x63,0x74,0x75,0x61, -0x74,0x69,0x6f,0x6e,0xff,0x20,0,0,0,0x10,0x6e,0x1f,0x69,0x74,0x69,0x61, -0x6c,0x70,0x75,0x6e,0x63,0x74,0x75,0x61,0x74,0x69,0x6f,0x6e,0xff,0x10,0,0, -0,0x9c,7,0x6d,0x18,0x6d,0x41,0x6f,0x28,0x74,0x31,0x75,0x25,0x60,0x1c,0x77, -0x65,0x72,0x63,0x61,0x73,0x65,0x6c,0x65,0x74,0x74,0x65,0x72,0x29,0x63,0x3d,0x65, -0x28,0x69,0x42,0x6c,0x29,0x13,0x74,0x74,0x65,0x72,0x9c,0x15,0x6e,0x75,0x6d,0x62, -0x65,0x72,0xab,0,0x1a,0x6e,0x65,0x73,0x65,0x70,0x61,0x72,0x61,0x74,0x6f,0x72, -0xd9,0x20,0,0x63,0x46,0x64,0xa2,0x96,0x65,0x1b,0x6e,0x63,0x6c,0x6f,0x73,0x69, -0x6e,0x67,0x6d,0x61,0x72,0x6b,0xa3,0x80,0xe6,0x80,1,7,0x6e,0x57,0x6e,0x52, -0x6f,0x5e,0x73,0xe1,0,0,0x75,0x1b,0x72,0x72,0x65,0x6e,0x63,0x79,0x73,0x79, -0x6d,0x62,0x6f,0x6c,0xff,2,0,0,0,0x22,0x12,0x74,0x72,0x6c,0xd9,0x80, -0,0xdc,0,0,1,0x6d,0x62,0x6e,1,0x6e,0x30,0x74,0x12,0x72,0x6f,0x6c, -0xd9,0x80,0,0x1f,0x65,0x63,0x74,0x6f,0x72,0x70,0x75,0x6e,0x63,0x74,0x75,0x61, -0x74,0x69,0x6f,0x6e,0xfd,0x40,0,0,0x19,0x62,0x69,0x6e,0x69,0x6e,0x67,0x6d, -0x61,0x72,0x6b,0xa5,0xc0,0x61,0x58,0x63,0xd9,0x80,0,0x66,0xdb,0,0,0x6c, -0x1d,0x6f,0x73,0x65,0x70,0x75,0x6e,0x63,0x74,0x75,0x61,0x74,0x69,0x6f,0x6e,0xfd, -0x20,0,0,0x18,0x73,0x65,0x64,0x6c,0x65,0x74,0x74,0x65,0x72,0x3d,2,0x61, -0x32,0x65,0x50,0x69,0x12,0x67,0x69,0x74,0xa7,0,0x1c,0x73,0x68,0x70,0x75,0x6e, -0x63,0x74,0x75,0x61,0x74,0x69,0x6f,0x6e,0xe9,0,0,0x1a,0x63,0x69,0x6d,0x61, -0x6c,0x6e,0x75,0x6d,0x62,0x65,0x72,0xa7,0 +0xa3,0xd7,0x15,0x75,0x72,0x73,0x69,0x76,0x65,0xa3,0xd6,0x17,0x65,0x66,0x61,0x69, +0x64,0x72,0x69,0x6e,0xa5,0x21,0x17,0x74,0x65,0x69,0x6d,0x61,0x79,0x65,0x6b,0xa2, +0xb8,0x12,0x65,0x78,0x74,0xa2,0xd5,0x16,0x65,0x6e,0x73,0x69,0x6f,0x6e,0x73,0xa3, +0xd5,0x18,0x64,0x65,0x6b,0x69,0x6b,0x61,0x6b,0x75,0x69,0xa3,0xeb,5,0x6b,0x23, +0x6b,0x4c,0x6f,0x50,0x75,1,0x6d,0x2c,0x73,0x11,0x68,0x75,0xa5,0x15,0x17,0x62, +0x65,0x72,0x66,0x6f,0x72,0x6d,0x73,0x7b,0x10,0x6f,0xa3,0x92,0x14,0x62,0x6c,0x6f, +0x63,0x6b,0x21,0x61,0x44,0x62,0x21,0x65,0x10,0x77,1,0x61,0xa5,0xe,0x74,0x14, +0x61,0x69,0x6c,0x75,0x65,0xa3,0x8b,0x16,0x62,0x61,0x74,0x61,0x65,0x61,0x6e,0xa3, +0xef,0x67,0xc4,0xe,0x6a,0xc1,0x95,0x6a,0xa2,0xc5,0x6b,0xa2,0xde,0x6c,4,0x61, +0x54,0x65,0xa2,0x61,0x69,0xa2,0x78,0x6f,0xa2,0xa7,0x79,1,0x63,0x2e,0x64,0x12, +0x69,0x61,0x6e,0xa3,0xa9,0x12,0x69,0x61,0x6e,0xa3,0xa7,1,0x6f,0x55,0x74,0x11, +0x69,0x6e,1,0x31,0x82,0x65,0x11,0x78,0x74,4,0x61,0x5c,0x62,0x29,0x63,0xa3, +0x94,0x64,0xa3,0x95,0x65,0xa2,0xe7,0x13,0x6e,0x64,0x65,0x64,4,0x61,0x36,0x62, +0x29,0x63,0xa3,0x94,0x64,0xa3,0x95,0x65,0xa3,0xe7,0x26,0x18,0x64,0x64,0x69,0x74, +0x69,0x6f,0x6e,0x61,0x6c,0x6d,0x24,0x12,0x73,0x75,0x70,0x24,0x16,0x70,0x6c,0x65, +0x6d,0x65,0x6e,0x74,0x25,1,0x70,0x42,0x74,0x1d,0x74,0x65,0x72,0x6c,0x69,0x6b, +0x65,0x73,0x79,0x6d,0x62,0x6f,0x6c,0x73,0x79,0x12,0x63,0x68,0x61,0xa3,0x9c,2, +0x6d,0x2e,0x6e,0x34,0x73,0x10,0x75,0xa3,0xb0,0x11,0x62,0x75,0xa3,0x6f,0x12,0x65, +0x61,0x72,1,0x61,0xa3,0xe8,0x62,1,0x69,0x38,0x73,0x17,0x79,0x6c,0x6c,0x61, +0x62,0x61,0x72,0x79,0xa3,0x75,0x17,0x64,0x65,0x6f,0x67,0x72,0x61,0x6d,0x73,0xa3, +0x76,0x1a,0x77,0x73,0x75,0x72,0x72,0x6f,0x67,0x61,0x74,0x65,0x73,0xa3,0x4d,0x10, +0x61,1,0x6d,0x32,0x76,0x14,0x61,0x6e,0x65,0x73,0x65,0xa3,0xb5,0x10,0x6f,0x5c, +0x12,0x65,0x78,0x74,1,0x61,0xa3,0xb4,0x62,0xa3,0xb9,1,0x61,0x80,0x68,3, +0x61,0x3c,0x6d,0x4c,0x6f,0x64,0x75,0x15,0x64,0x61,0x77,0x61,0x64,0x69,0xa3,0xe6, +0x16,0x72,0x6f,0x73,0x68,0x74,0x68,0x69,0xa3,0x89,0x11,0x65,0x72,0x68,0x16,0x73, +0x79,0x6d,0x62,0x6f,0x6c,0x73,0xa3,0x71,0x12,0x6a,0x6b,0x69,0xa3,0xe5,3,0x69, +0x3a,0x6e,0x42,0x74,0xa2,0x51,0x79,0x13,0x61,0x68,0x6c,0x69,0xa3,0xa2,0x12,0x74, +0x68,0x69,0xa3,0xc1,3,0x61,0x34,0x62,0x76,0x67,0x7c,0x6e,0x12,0x61,0x64,0x61, +0x4d,1,0x65,0x40,0x73,0x11,0x75,0x70,0xa2,0xcb,0x16,0x70,0x6c,0x65,0x6d,0x65, +0x6e,0x74,0xa3,0xcb,0x11,0x78,0x74,1,0x61,0xa5,0x13,0x65,0x14,0x6e,0x64,0x65, +0x64,0x61,0xa5,0x13,0x11,0x75,0x6e,0xa3,0x42,0x11,0x78,0x69,0x96,0x17,0x72,0x61, +0x64,0x69,0x63,0x61,0x6c,0x73,0x97,0x14,0x61,0x6b,0x61,0x6e,0x61,0x9e,1,0x65, +0x4c,0x70,0x10,0x68,0x1f,0x6f,0x6e,0x65,0x74,0x69,0x63,0x65,0x78,0x74,0x65,0x6e, +0x73,0x69,0x6f,0x6e,0x73,0xa3,0x6b,0x11,0x78,0x74,0xa3,0x6b,0x67,0xa2,0xb5,0x68, +0xa4,0x84,0x69,3,0x64,0x4c,0x6d,0xa2,0x55,0x6e,0xa2,0x62,0x70,0x13,0x61,0x65, +0x78,0x74,0x2a,0x16,0x65,0x6e,0x73,0x69,0x6f,0x6e,0x73,0x2b,1,0x63,0x99,0x65, +0x17,0x6f,0x67,0x72,0x61,0x70,0x68,0x69,0x63,1,0x64,0x56,0x73,0x15,0x79,0x6d, +0x62,0x6f,0x6c,0x73,0xa4,0xb,0x1d,0x61,0x6e,0x64,0x70,0x75,0x6e,0x63,0x74,0x75, +0x61,0x74,0x69,0x6f,0x6e,0xa5,0xb,0x13,0x65,0x73,0x63,0x72,0x1f,0x69,0x70,0x74, +0x69,0x6f,0x6e,0x63,0x68,0x61,0x72,0x61,0x63,0x74,0x65,0x72,0x73,0x99,0x1c,0x70, +0x65,0x72,0x69,0x61,0x6c,0x61,0x72,0x61,0x6d,0x61,0x69,0x63,0xa3,0xba,1,0x64, +0x62,0x73,0x1b,0x63,0x72,0x69,0x70,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x70,0x61,1, +0x68,0x32,0x72,0x14,0x74,0x68,0x69,0x61,0x6e,0xa3,0xbd,0x13,0x6c,0x61,0x76,0x69, +0xa3,0xbe,0x11,0x69,0x63,1,0x6e,0x3e,0x73,0x1a,0x69,0x79,0x61,0x71,0x6e,0x75, +0x6d,0x62,0x65,0x72,0x73,0xa5,0x1e,0x19,0x75,0x6d,0x62,0x65,0x72,0x66,0x6f,0x72, +0x6d,0x73,0xa3,0xb2,4,0x65,0x74,0x6c,0xa2,0x82,0x6f,0xa2,0x9a,0x72,0xa2,0x9e, +0x75,2,0x6a,0x34,0x6e,0x3e,0x72,0x14,0x6d,0x75,0x6b,0x68,0x69,0x43,0x14,0x61, +0x72,0x61,0x74,0x69,0x45,0x18,0x6a,0x61,0x6c,0x61,0x67,0x6f,0x6e,0x64,0x69,0xa5, +0x1c,1,0x6e,0xa2,0x46,0x6f,1,0x6d,0x6e,0x72,0x13,0x67,0x69,0x61,0x6e,0x5a, +1,0x65,0x40,0x73,0x11,0x75,0x70,0xa2,0x87,0x16,0x70,0x6c,0x65,0x6d,0x65,0x6e, +0x74,0xa3,0x87,0x11,0x78,0x74,0xa4,0x1b,0x14,0x65,0x6e,0x64,0x65,0x64,0xa5,0x1b, +0x1a,0x65,0x74,0x72,0x69,0x63,0x73,0x68,0x61,0x70,0x65,0x73,0x8c,0x12,0x65,0x78, +0x74,0xa2,0xe3,0x14,0x65,0x6e,0x64,0x65,0x64,0xa3,0xe3,0x1e,0x65,0x72,0x61,0x6c, +0x70,0x75,0x6e,0x63,0x74,0x75,0x61,0x74,0x69,0x6f,0x6e,0x71,0x17,0x61,0x67,0x6f, +0x6c,0x69,0x74,0x69,0x63,0xa2,0x88,0x12,0x73,0x75,0x70,0xa4,0xa,0x16,0x70,0x6c, +0x65,0x6d,0x65,0x6e,0x74,0xa5,0xa,0x13,0x74,0x68,0x69,0x63,0xa3,0x59,1,0x61, +0x5c,0x65,0x11,0x65,0x6b,0x30,1,0x61,0x38,0x65,0x11,0x78,0x74,0x6e,0x14,0x65, +0x6e,0x64,0x65,0x64,0x6f,0x17,0x6e,0x64,0x63,0x6f,0x70,0x74,0x69,0x63,0x31,0x13, +0x6e,0x74,0x68,0x61,0xa3,0xe4,2,0x61,0xa2,0x48,0x65,0xa2,0xdf,0x69,1,0x67, +0x30,0x72,0x14,0x61,0x67,0x61,0x6e,0x61,0x9d,0x10,0x68,1,0x70,0x3a,0x73,0x18, +0x75,0x72,0x72,0x6f,0x67,0x61,0x74,0x65,0x73,0xa3,0x4b,1,0x72,0x3c,0x75,0x19, +0x73,0x75,0x72,0x72,0x6f,0x67,0x61,0x74,0x65,0x73,0xa3,0x4c,0x11,0x69,0x76,0x1f, +0x61,0x74,0x65,0x75,0x73,0x65,0x73,0x75,0x72,0x72,0x6f,0x67,0x61,0x74,0x65,0x73, +0xa3,0x4c,2,0x6c,0x32,0x6e,0x9a,0x74,0x12,0x72,0x61,0x6e,0xa5,2,0x10,0x66, +2,0x61,0x58,0x6d,0x70,0x77,0x14,0x69,0x64,0x74,0x68,0x61,0x1f,0x6e,0x64,0x66, +0x75,0x6c,0x6c,0x77,0x69,0x64,0x74,0x68,0x66,0x6f,0x72,0x6d,0x73,0xa3,0x57,0x1a, +0x6e,0x64,0x66,0x75,0x6c,0x6c,0x66,0x6f,0x72,0x6d,0x73,0xa3,0x57,0x13,0x61,0x72, +0x6b,0x73,0xa3,0x52,2,0x67,0x34,0x69,0xa2,0x45,0x75,0x12,0x6e,0x6f,0x6f,0xa3, +0x63,0x11,0x75,0x6c,0xa2,0x4a,2,0x63,0x3c,0x6a,0x5e,0x73,0x17,0x79,0x6c,0x6c, +0x61,0x62,0x6c,0x65,0x73,0xa3,0x4a,0x1f,0x6f,0x6d,0x70,0x61,0x74,0x69,0x62,0x69, +0x6c,0x69,0x74,0x79,0x6a,0x61,0x6d,0x6f,0xa3,0x41,0x12,0x61,0x6d,0x6f,0x5c,0x17, +0x65,0x78,0x74,0x65,0x6e,0x64,0x65,0x64,1,0x61,0xa3,0xb4,0x62,0xa3,0xb9,0x19, +0x66,0x69,0x72,0x6f,0x68,0x69,0x6e,0x67,0x79,0x61,0xa5,0x1d,0x13,0x62,0x72,0x65, +0x77,0x37,0x61,0xa2,0xe9,0x62,0xa6,0x29,0x63,0xa6,0xfe,0x64,0xac,0x8a,0x65,5, +0x6d,0xa2,0x6d,0x86,0x6e,0x96,0x74,0x15,0x68,0x69,0x6f,0x70,0x69,0x63,0x5e,1, +0x65,0x40,0x73,0x11,0x75,0x70,0xa2,0x86,0x16,0x70,0x6c,0x65,0x6d,0x65,0x6e,0x74, +0xa3,0x86,0x11,0x78,0x74,0xa2,0x85,1,0x61,0xa3,0xc8,0x65,0x13,0x6e,0x64,0x65, +0x64,0xa2,0x85,0x10,0x61,0xa3,0xc8,0x16,0x6f,0x74,0x69,0x63,0x6f,0x6e,0x73,0xa3, +0xce,0x15,0x63,0x6c,0x6f,0x73,0x65,0x64,2,0x61,0x5a,0x63,0x9e,0x69,0x1c,0x64, +0x65,0x6f,0x67,0x72,0x61,0x70,0x68,0x69,0x63,0x73,0x75,0x70,0xa2,0xc4,0x16,0x70, +0x6c,0x65,0x6d,0x65,0x6e,0x74,0xa3,0xc4,0x16,0x6c,0x70,0x68,0x61,0x6e,0x75,0x6d, +0x86,1,0x65,0x2c,0x73,0x11,0x75,0x70,0xa3,0xc3,0x13,0x72,0x69,0x63,0x73,0x86, +0x18,0x75,0x70,0x70,0x6c,0x65,0x6d,0x65,0x6e,0x74,0xa3,0xc3,0x11,0x6a,0x6b,0xa2, +0x44,0x1f,0x6c,0x65,0x74,0x74,0x65,0x72,0x73,0x61,0x6e,0x64,0x6d,0x6f,0x6e,0x74, +0x68,0x73,0xa3,0x44,0x61,0x36,0x67,0x62,0x6c,0x14,0x62,0x61,0x73,0x61,0x6e,0xa3, +0xe2,0x13,0x72,0x6c,0x79,0x64,0x1f,0x79,0x6e,0x61,0x73,0x74,0x69,0x63,0x63,0x75, +0x6e,0x65,0x69,0x66,0x6f,0x72,0x6d,0xa5,1,0x10,0x79,0x1f,0x70,0x74,0x69,0x61, +0x6e,0x68,0x69,0x65,0x72,0x6f,0x67,0x6c,0x79,0x70,0x68,0x73,0xa3,0xc2,7,0x6e, +0xc0,0xe5,0x6e,0x3e,0x72,0xa2,0x5d,0x73,0xa2,0xd8,0x76,0x14,0x65,0x73,0x74,0x61, +0x6e,0xa3,0xbc,1,0x61,0x92,0x63,0x13,0x69,0x65,0x6e,0x74,1,0x67,0x34,0x73, +0x15,0x79,0x6d,0x62,0x6f,0x6c,0x73,0xa3,0xa5,0x13,0x72,0x65,0x65,0x6b,1,0x6d, +0x34,0x6e,0x15,0x75,0x6d,0x62,0x65,0x72,0x73,0xa3,0x7f,0x13,0x75,0x73,0x69,0x63, +0xa2,0x7e,0x19,0x61,0x6c,0x6e,0x6f,0x74,0x61,0x74,0x69,0x6f,0x6e,0xa3,0x7e,0x10, +0x74,0x1f,0x6f,0x6c,0x69,0x61,0x6e,0x68,0x69,0x65,0x72,0x6f,0x67,0x6c,0x79,0x70, +0x68,0x73,0xa3,0xfe,2,0x61,0x32,0x6d,0xa2,0x71,0x72,0x12,0x6f,0x77,0x73,0x7d, +0x12,0x62,0x69,0x63,0x38,3,0x65,0x4a,0x6d,0x66,0x70,0xa2,0x43,0x73,0x11,0x75, +0x70,0xa2,0x80,0x16,0x70,0x6c,0x65,0x6d,0x65,0x6e,0x74,0xa3,0x80,0x11,0x78,0x74, +1,0x61,0xa3,0xd2,0x65,0x14,0x6e,0x64,0x65,0x64,0x61,0xa3,0xd2,0x12,0x61,0x74, +0x68,0xa2,0xd3,0x18,0x65,0x6d,0x61,0x74,0x69,0x63,0x61,0x6c,0x61,0x1f,0x6c,0x70, +0x68,0x61,0x62,0x65,0x74,0x69,0x63,0x73,0x79,0x6d,0x62,0x6f,0x6c,0x73,0xa3,0xd3, +1,0x66,0x42,0x72,0x1e,0x65,0x73,0x65,0x6e,0x74,0x61,0x74,0x69,0x6f,0x6e,0x66, +0x6f,0x72,0x6d,0x73,1,0x61,0xa3,0x51,0x62,0xa3,0x55,0x14,0x65,0x6e,0x69,0x61, +0x6e,0x35,0x12,0x63,0x69,0x69,0x23,0x64,0x9e,0x65,0xa2,0x42,0x68,0xa2,0x4d,0x6c, +1,0x63,0x62,0x70,0x17,0x68,0x61,0x62,0x65,0x74,0x69,0x63,0x70,1,0x66,0xa3, +0x50,0x72,0x1e,0x65,0x73,0x65,0x6e,0x74,0x61,0x74,0x69,0x6f,0x6e,0x66,0x6f,0x72, +0x6d,0x73,0xa3,0x50,0x16,0x68,0x65,0x6d,0x69,0x63,0x61,0x6c,0xa2,0xd0,0x16,0x73, +0x79,0x6d,0x62,0x6f,0x6c,0x73,0xa3,0xd0,0x12,0x6c,0x61,0x6d,0xa5,7,0x1a,0x67, +0x65,0x61,0x6e,0x6e,0x75,0x6d,0x62,0x65,0x72,0x73,0xa3,0x77,0x11,0x6f,0x6d,0xa3, +0xfd,7,0x6f,0x71,0x6f,0x64,0x72,0xa2,0x41,0x75,0xa2,0x58,0x79,0x1b,0x7a,0x61, +0x6e,0x74,0x69,0x6e,0x65,0x6d,0x75,0x73,0x69,0x63,0xa2,0x5b,0x18,0x61,0x6c,0x73, +0x79,0x6d,0x62,0x6f,0x6c,0x73,0xa3,0x5b,1,0x70,0x34,0x78,0x16,0x64,0x72,0x61, +0x77,0x69,0x6e,0x67,0x89,0x14,0x6f,0x6d,0x6f,0x66,0x6f,0xa0,0x12,0x65,0x78,0x74, +0xa2,0x43,0x14,0x65,0x6e,0x64,0x65,0x64,0xa3,0x43,0x10,0x61,1,0x68,0x40,0x69, +0x12,0x6c,0x6c,0x65,0x92,0x17,0x70,0x61,0x74,0x74,0x65,0x72,0x6e,0x73,0x93,0x11, +0x6d,0x69,0xa3,0xc9,1,0x67,0x2c,0x68,0x11,0x69,0x64,0xa3,0x64,0x14,0x69,0x6e, +0x65,0x73,0x65,0xa3,0x81,0x61,0x48,0x65,0xa2,0x4e,0x68,0xa2,0x52,0x6c,0x1a,0x6f, +0x63,0x6b,0x65,0x6c,0x65,0x6d,0x65,0x6e,0x74,0x73,0x8b,3,0x6c,0x34,0x6d,0x40, +0x73,0x66,0x74,0x11,0x61,0x6b,0xa3,0xc7,0x14,0x69,0x6e,0x65,0x73,0x65,0xa3,0x93, +0x11,0x75,0x6d,0xa2,0xb1,0x12,0x73,0x75,0x70,0xa2,0xca,0x16,0x70,0x6c,0x65,0x6d, +0x65,0x6e,0x74,0xa3,0xca,1,0x69,0x30,0x73,0x13,0x61,0x76,0x61,0x68,0xa3,0xdd, +0x15,0x63,0x6c,0x61,0x74,0x69,0x6e,0x23,0x14,0x6e,0x67,0x61,0x6c,0x69,0x41,0x16, +0x61,0x69,0x6b,0x73,0x75,0x6b,0x69,0xa5,8,5,0x6f,0xc1,0x4c,0x6f,0xa2,0x55, +0x75,0xa4,0x10,0x79,1,0x70,0x9c,0x72,0x14,0x69,0x6c,0x6c,0x69,0x63,0x32,1, +0x65,0x4c,0x73,0x11,0x75,0x70,0xa2,0x61,0x16,0x70,0x6c,0x65,0x6d,0x65,0x6e,0x74, +0xa2,0x61,0x12,0x61,0x72,0x79,0xa3,0x61,0x11,0x78,0x74,3,0x61,0xa3,0x9e,0x62, +0xa3,0xa0,0x63,0xa5,9,0x65,0x13,0x6e,0x64,0x65,0x64,2,0x61,0xa3,0x9e,0x62, +0xa3,0xa0,0x63,0xa5,9,0x1c,0x72,0x69,0x6f,0x74,0x73,0x79,0x6c,0x6c,0x61,0x62, +0x61,0x72,0x79,0xa3,0x7b,3,0x6d,0x5a,0x6e,0xa2,0x95,0x70,0xa2,0xa0,0x75,0x17, +0x6e,0x74,0x69,0x6e,0x67,0x72,0x6f,0x64,0xa2,0x9a,0x17,0x6e,0x75,0x6d,0x65,0x72, +0x61,0x6c,0x73,0xa3,0x9a,2,0x62,0x3a,0x6d,0xa2,0x5f,0x70,0x15,0x61,0x74,0x6a, +0x61,0x6d,0x6f,0xa3,0x41,0x14,0x69,0x6e,0x69,0x6e,0x67,2,0x64,0x46,0x68,0x9e, +0x6d,0x1d,0x61,0x72,0x6b,0x73,0x66,0x6f,0x72,0x73,0x79,0x6d,0x62,0x6f,0x6c,0x73, +0x77,0x1e,0x69,0x61,0x63,0x72,0x69,0x74,0x69,0x63,0x61,0x6c,0x6d,0x61,0x72,0x6b, +0x73,0x2e,2,0x65,0x40,0x66,0xa6,0x2a,0x73,0x18,0x75,0x70,0x70,0x6c,0x65,0x6d, +0x65,0x6e,0x74,0xa3,0x83,0x16,0x78,0x74,0x65,0x6e,0x64,0x65,0x64,0xa3,0xe0,0x17, +0x61,0x6c,0x66,0x6d,0x61,0x72,0x6b,0x73,0xa3,0x52,0x11,0x6f,0x6e,0x1f,0x69,0x6e, +0x64,0x69,0x63,0x6e,0x75,0x6d,0x62,0x65,0x72,0x66,0x6f,0x72,0x6d,0x73,0xa3,0xb2, +0x1b,0x74,0x72,0x6f,0x6c,0x70,0x69,0x63,0x74,0x75,0x72,0x65,0x73,0x83,0x12,0x74, +0x69,0x63,0xa2,0x84,0x1b,0x65,0x70,0x61,0x63,0x74,0x6e,0x75,0x6d,0x62,0x65,0x72, +0x73,0xa3,0xdf,1,0x6e,0x3e,0x72,0x1b,0x72,0x65,0x6e,0x63,0x79,0x73,0x79,0x6d, +0x62,0x6f,0x6c,0x73,0x75,0x15,0x65,0x69,0x66,0x6f,0x72,0x6d,0xa2,0x98,0x16,0x6e, +0x75,0x6d,0x62,0x65,0x72,0x73,0xa2,0x99,0x1d,0x61,0x6e,0x64,0x70,0x75,0x6e,0x63, +0x74,0x75,0x61,0x74,0x69,0x6f,0x6e,0xa3,0x99,0x61,0xa2,0xdb,0x68,0xa4,5,0x6a, +0x10,0x6b,0xa2,0x47,4,0x63,0x86,0x65,0xa2,0x7d,0x72,0xa2,0x92,0x73,0xa2,0xa4, +0x75,0x1f,0x6e,0x69,0x66,0x69,0x65,0x64,0x69,0x64,0x65,0x6f,0x67,0x72,0x61,0x70, +0x68,0x73,0xa2,0x47,0x18,0x65,0x78,0x74,0x65,0x6e,0x73,0x69,0x6f,0x6e,5,0x64, +0x65,0x64,0xa3,0xd1,0x65,0xa5,0,0x66,0xa5,0x12,0x14,0x6f,0x6d,0x70,0x61,0x74, +0xa2,0x45,1,0x66,0x96,0x69,1,0x62,0x44,0x64,0x17,0x65,0x6f,0x67,0x72,0x61, +0x70,0x68,0x73,0xa2,0x4f,0x12,0x73,0x75,0x70,0xa3,0x5f,0x14,0x69,0x6c,0x69,0x74, +0x79,0xa2,0x45,1,0x66,0x54,0x69,0x18,0x64,0x65,0x6f,0x67,0x72,0x61,0x70,0x68, +0x73,0xa2,0x4f,0x19,0x73,0x75,0x70,0x70,0x6c,0x65,0x6d,0x65,0x6e,0x74,0xa3,0x5f, +0x13,0x6f,0x72,0x6d,0x73,0xa3,0x53,0x11,0x78,0x74,5,0x64,9,0x64,0xa3,0xd1, +0x65,0xa5,0,0x66,0xa5,0x12,0x61,0xa3,0x46,0x62,0xa3,0x5e,0x63,0xa3,0xc5,0x19, +0x61,0x64,0x69,0x63,0x61,0x6c,0x73,0x73,0x75,0x70,0x94,0x16,0x70,0x6c,0x65,0x6d, +0x65,0x6e,0x74,0x95,1,0x74,0x50,0x79,0x14,0x6d,0x62,0x6f,0x6c,0x73,0x9a,0x1d, +0x61,0x6e,0x64,0x70,0x75,0x6e,0x63,0x74,0x75,0x61,0x74,0x69,0x6f,0x6e,0x9b,0x14, +0x72,0x6f,0x6b,0x65,0x73,0xa3,0x82,2,0x6e,0x48,0x72,0x64,0x75,0x1d,0x63,0x61, +0x73,0x69,0x61,0x6e,0x61,0x6c,0x62,0x61,0x6e,0x69,0x61,0x6e,0xa3,0xde,0x1d,0x61, +0x64,0x69,0x61,0x6e,0x73,0x79,0x6c,0x6c,0x61,0x62,0x69,0x63,0x73,0x63,0x12,0x69, +0x61,0x6e,0xa3,0xa8,1,0x61,0x6c,0x65,1,0x72,0x38,0x73,0x17,0x73,0x73,0x79, +0x6d,0x62,0x6f,0x6c,0x73,0xa5,0x19,0x13,0x6f,0x6b,0x65,0x65,0x60,0x12,0x73,0x75, +0x70,0xa2,0xff,0x16,0x70,0x6c,0x65,0x6d,0x65,0x6e,0x74,0xa3,0xff,1,0x6b,0x26, +0x6d,0xa3,0xa4,0x11,0x6d,0x61,0xa3,0xd4,3,0x65,0x3e,0x69,0x7e,0x6f,0xa2,0x5d, +0x75,0x15,0x70,0x6c,0x6f,0x79,0x61,0x6e,0xa3,0xe1,1,0x73,0x50,0x76,0x16,0x61, +0x6e,0x61,0x67,0x61,0x72,0x69,0x3e,0x12,0x65,0x78,0x74,0xa2,0xb3,0x14,0x65,0x6e, +0x64,0x65,0x64,0xa3,0xb3,0x13,0x65,0x72,0x65,0x74,0xa3,0x5a,1,0x61,0x30,0x6e, +0x14,0x67,0x62,0x61,0x74,0x73,0x91,0x18,0x63,0x72,0x69,0x74,0x69,0x63,0x61,0x6c, +0x73,0x2e,2,0x65,0x30,0x66,0x36,0x73,0x11,0x75,0x70,0xa3,0x83,0x11,0x78,0x74, +0xa3,0xe0,0x18,0x6f,0x72,0x73,0x79,0x6d,0x62,0x6f,0x6c,0x73,0x77,1,0x67,0x3e, +0x6d,0x12,0x69,0x6e,0x6f,0xa2,0xab,0x14,0x74,0x69,0x6c,0x65,0x73,0xa3,0xab,0x11, +0x72,0x61,0xa5,0x1a,8,0x6d,0x5f,0x6d,0x3a,0x6e,0x48,0x73,0x7a,0x76,0xa2,0x4b, +0x77,0x12,0x69,0x64,0x65,0x43,0x11,0x65,0x64,0x32,0x12,0x69,0x61,0x6c,0x33,2, +0x61,0x40,0x62,0x37,0x6f,1,0x62,0x28,0x6e,0x10,0x65,0x21,0x13,0x72,0x65,0x61, +0x6b,0x37,0x10,0x72,0x34,0x12,0x72,0x6f,0x77,0x35,2,0x6d,0x38,0x71,0x46,0x75, +1,0x62,0x3d,0x70,0x3e,0x11,0x65,0x72,0x3f,1,0x61,0x24,0x6c,0x39,0x11,0x6c, +0x6c,0x39,1,0x72,0x3b,0x75,0x12,0x61,0x72,0x65,0x3b,0x12,0x65,0x72,0x74,0x40, +0x13,0x69,0x63,0x61,0x6c,0x41,0x63,0x58,0x65,0x92,0x66,0x96,0x69,1,0x6e,0x36, +0x73,0x10,0x6f,0x30,0x14,0x6c,0x61,0x74,0x65,0x64,0x31,0x11,0x69,0x74,0x2e,0x12, +0x69,0x61,0x6c,0x2f,2,0x61,0x36,0x69,0x48,0x6f,0x10,0x6d,0x24,0x12,0x70,0x61, +0x74,0x25,0x10,0x6e,0x22,0x15,0x6f,0x6e,0x69,0x63,0x61,0x6c,0x23,0x13,0x72,0x63, +0x6c,0x65,0x27,0x11,0x6e,0x63,0x27,2,0x69,0x3a,0x6f,0x44,0x72,0x10,0x61,0x2c, +0x14,0x63,0x74,0x69,0x6f,0x6e,0x2d,0x10,0x6e,0x28,0x11,0x61,0x6c,0x29,0x11,0x6e, +0x74,0x2b,4,0x61,0x3a,0x66,0x4c,0x68,0x5e,0x6e,0x70,0x77,0x2a,0x12,0x69,0x64, +0x65,0x2b,0x22,0x17,0x6d,0x62,0x69,0x67,0x75,0x6f,0x75,0x73,0x23,0x26,0x17,0x75, +0x6c,0x6c,0x77,0x69,0x64,0x74,0x68,0x27,0x24,0x17,0x61,0x6c,0x66,0x77,0x69,0x64, +0x74,0x68,0x25,0x20,1,0x61,0x30,0x65,0x14,0x75,0x74,0x72,0x61,0x6c,0x21,0x28, +0x13,0x72,0x72,0x6f,0x77,0x29,0xd,0x6e,0xc0,0xfb,0x73,0x6d,0x73,0x3a,0x74,0x98, +0x75,0xa2,0x49,0x7a,2,0x6c,0x3b,0x70,0x3d,0x73,0x39,5,0x6f,0x28,0x6f,0x57, +0x70,0x34,0x75,0x16,0x72,0x72,0x6f,0x67,0x61,0x74,0x65,0x45,0x11,0x61,0x63,1, +0x65,0x32,0x69,0x15,0x6e,0x67,0x6d,0x61,0x72,0x6b,0x31,0x18,0x73,0x65,0x70,0x61, +0x72,0x61,0x74,0x6f,0x72,0x39,0x63,0x53,0x6b,0x55,0x6d,0x51,0x1d,0x69,0x74,0x6c, +0x65,0x63,0x61,0x73,0x65,0x6c,0x65,0x74,0x74,0x65,0x72,0x27,1,0x6e,0x40,0x70, +0x1c,0x70,0x65,0x72,0x63,0x61,0x73,0x65,0x6c,0x65,0x74,0x74,0x65,0x72,0x23,0x17, +0x61,0x73,0x73,0x69,0x67,0x6e,0x65,0x64,0x21,0x6e,0x8a,0x6f,0xa2,0x47,0x70,8, +0x66,0x14,0x66,0x5b,0x69,0x59,0x6f,0x4f,0x72,0x24,0x73,0x49,0x17,0x69,0x76,0x61, +0x74,0x65,0x75,0x73,0x65,0x43,0x61,0x2c,0x63,0x4d,0x64,0x47,0x65,0x4b,0x1f,0x72, +0x61,0x67,0x72,0x61,0x70,0x68,0x73,0x65,0x70,0x61,0x72,0x61,0x74,0x6f,0x72,0x3d, +2,0x64,0x33,0x6c,0x35,0x6f,0x36,0x1b,0x6e,0x73,0x70,0x61,0x63,0x69,0x6e,0x67, +0x6d,0x61,0x72,0x6b,0x2d,1,0x70,0x7c,0x74,0x12,0x68,0x65,0x72,3,0x6c,0x38, +0x6e,0x42,0x70,0x4c,0x73,0x14,0x79,0x6d,0x62,0x6f,0x6c,0x57,0x14,0x65,0x74,0x74, +0x65,0x72,0x2b,0x14,0x75,0x6d,0x62,0x65,0x72,0x37,0x19,0x75,0x6e,0x63,0x74,0x75, +0x61,0x74,0x69,0x6f,0x6e,0x4f,0x1c,0x65,0x6e,0x70,0x75,0x6e,0x63,0x74,0x75,0x61, +0x74,0x69,0x6f,0x6e,0x49,0x66,0x9e,0x66,0x88,0x69,0xa2,0x4b,0x6c,0xa2,0x5c,0x6d, +4,0x61,0x60,0x63,0x31,0x65,0x2f,0x6e,0x2d,0x6f,0x15,0x64,0x69,0x66,0x69,0x65, +0x72,1,0x6c,0x30,0x73,0x14,0x79,0x6d,0x62,0x6f,0x6c,0x55,0x14,0x65,0x74,0x74, +0x65,0x72,0x29,0x17,0x74,0x68,0x73,0x79,0x6d,0x62,0x6f,0x6c,0x51,1,0x69,0x2e, +0x6f,0x13,0x72,0x6d,0x61,0x74,0x41,0x1d,0x6e,0x61,0x6c,0x70,0x75,0x6e,0x63,0x74, +0x75,0x61,0x74,0x69,0x6f,0x6e,0x5b,0x10,0x6e,0x1f,0x69,0x74,0x69,0x61,0x6c,0x70, +0x75,0x6e,0x63,0x74,0x75,0x61,0x74,0x69,0x6f,0x6e,0x59,6,0x6d,0x18,0x6d,0x29, +0x6f,0x28,0x74,0x27,0x75,0x23,0x2a,0x1c,0x77,0x65,0x72,0x63,0x61,0x73,0x65,0x6c, +0x65,0x74,0x74,0x65,0x72,0x25,0x65,0x28,0x69,0x3c,0x6c,0x25,0x19,0x74,0x74,0x65, +0x72,0x6e,0x75,0x6d,0x62,0x65,0x72,0x35,0x1a,0x6e,0x65,0x73,0x65,0x70,0x61,0x72, +0x61,0x74,0x6f,0x72,0x3b,0x63,0x44,0x64,0xa2,0x60,0x65,0x1b,0x6e,0x63,0x6c,0x6f, +0x73,0x69,0x6e,0x67,0x6d,0x61,0x72,0x6b,0x2f,6,0x6e,0x39,0x6e,0x46,0x6f,0x4e, +0x73,0x45,0x75,0x1b,0x72,0x72,0x65,0x6e,0x63,0x79,0x73,0x79,0x6d,0x62,0x6f,0x6c, +0x53,0x20,0x12,0x74,0x72,0x6c,0x3f,0x42,0x10,0x6e,1,0x6e,0x2c,0x74,0x12,0x72, +0x6f,0x6c,0x3f,0x1f,0x65,0x63,0x74,0x6f,0x72,0x70,0x75,0x6e,0x63,0x74,0x75,0x61, +0x74,0x69,0x6f,0x6e,0x4d,0x63,0x3f,0x66,0x41,0x6c,0x1d,0x6f,0x73,0x65,0x70,0x75, +0x6e,0x63,0x74,0x75,0x61,0x74,0x69,0x6f,0x6e,0x4b,2,0x61,0x30,0x65,0x4a,0x69, +0x12,0x67,0x69,0x74,0x33,0x1c,0x73,0x68,0x70,0x75,0x6e,0x63,0x74,0x75,0x61,0x74, +0x69,0x6f,0x6e,0x47,0x1a,0x63,0x69,0x6d,0x61,0x6c,0x6e,0x75,0x6d,0x62,0x65,0x72, +0x33,0,0x12,0x6d,0xc2,0x3f,0x73,0xa1,0x73,0x4e,0x74,0xa2,0x56,0x77,0xa2,0x72, +0x79,0xa2,0x73,0x7a,1,0x61,0x2c,0x68,0x12,0x61,0x69,0x6e,0x8b,0x11,0x69,0x6e, +0x85,5,0x74,0x22,0x74,0x38,0x77,0x4c,0x79,0x16,0x72,0x69,0x61,0x63,0x77,0x61, +0x77,0x6f,0x18,0x72,0x61,0x69,0x67,0x68,0x74,0x77,0x61,0x77,0xa3,0x55,0x15,0x61, +0x73,0x68,0x6b,0x61,0x66,0x6d,0x61,0x2e,0x65,0x38,0x68,0x11,0x69,0x6e,0x6b,0x10, +0x64,0x62,0x11,0x68,0x65,0x65,1,0x65,0x2e,0x6d,0x13,0x6b,0x61,0x74,0x68,0x69, +0x10,0x6e,0x67,1,0x61,0x4e,0x65,1,0x68,0x28,0x74,0x10,0x68,0x77,0x16,0x6d, +0x61,0x72,0x62,0x75,0x74,0x61,0x74,0x13,0x67,0x6f,0x61,0x6c,0x3d,1,0x68,0x71, +0x77,0x73,0x11,0x61,0x77,0x79,1,0x65,0x32,0x75,0x11,0x64,0x68,0x80,0x11,0x68, +0x65,0x83,0x10,0x68,0x7a,1,0x62,0x34,0x77,0x16,0x69,0x74,0x68,0x74,0x61,0x69, +0x6c,0x7f,0x14,0x61,0x72,0x72,0x65,0x65,0x7d,0x6d,0x6c,0x6e,0xa4,0x6b,0x70,0xa4, +0x88,0x71,0xa4,0x88,0x72,1,0x65,0x38,0x6f,0x18,0x68,0x69,0x6e,0x67,0x79,0x61, +0x79,0x65,0x68,0x93,1,0x68,0x5f,0x76,0x16,0x65,0x72,0x73,0x65,0x64,0x70,0x65, +0x61,2,0x61,0x2e,0x65,0xa4,0x3e,0x69,0x10,0x6d,0x53,1,0x6c,0xa2,0xe7,0x6e, +0x16,0x69,0x63,0x68,0x61,0x65,0x61,0x6e,0,0x12,0x6e,0x76,0x73,0x51,0x73,0x3e, +0x74,0x5c,0x77,0xa0,0x79,0xa2,0x42,0x7a,0x13,0x61,0x79,0x69,0x6e,0xa3,0x54,0x10, +0x61,1,0x64,0x2e,0x6d,0x12,0x65,0x6b,0x68,0xa3,0x4c,0x11,0x68,0x65,0xa3,0x4b, +3,0x61,0x38,0x65,0x3c,0x68,0x4a,0x77,0x13,0x65,0x6e,0x74,0x79,0xa3,0x51,0x10, +0x77,0xa3,0x4d,1,0x6e,0xa3,0x4e,0x74,0x10,0x68,0xa3,0x4f,0x14,0x61,0x6d,0x65, +0x64,0x68,0xa3,0x50,0x11,0x61,0x77,0xa3,0x52,0x12,0x6f,0x64,0x68,0xa3,0x53,0x6e, +0x3a,0x6f,0x40,0x70,0x46,0x71,0x4a,0x72,0x12,0x65,0x73,0x68,0xa3,0x4a,0x11,0x75, +0x6e,0xa3,0x46,0x11,0x6e,0x65,0xa3,0x47,0x10,0x65,0xa3,0x48,0x12,0x6f,0x70,0x68, +0xa3,0x49,0x67,0x33,0x67,0x38,0x68,0x40,0x6b,0x5e,0x6c,0x66,0x6d,0x11,0x65,0x6d, +0xa3,0x45,0x13,0x69,0x6d,0x65,0x6c,0xa1,1,0x65,0x32,0x75,0x14,0x6e,0x64,0x72, +0x65,0x64,0xa3,0x42,0x11,0x74,0x68,0xa3,0x41,0x12,0x61,0x70,0x68,0xa3,0x43,0x14, +0x61,0x6d,0x65,0x64,0x68,0xa3,0x44,0x61,0x34,0x62,0x4a,0x64,0x50,0x66,0x12,0x69, +0x76,0x65,0x9f,1,0x6c,0x2a,0x79,0x11,0x69,0x6e,0x97,0x12,0x65,0x70,0x68,0x95, +0x12,0x65,0x74,0x68,0x99,1,0x61,0x30,0x68,0x14,0x61,0x6d,0x65,0x64,0x68,0x9d, +0x13,0x6c,0x65,0x74,0x68,0x9b,0x15,0x61,0x79,0x61,0x6c,0x61,0x6d,6,0x6e,0x2c, +0x6e,0x34,0x72,0x5e,0x73,0x62,0x74,0x11,0x74,0x61,0xa3,0x63,2,0x67,0x2e,0x6e, +0x32,0x79,0x10,0x61,0xa3,0x60,0x10,0x61,0xa3,0x5d,1,0x61,0xa3,0x5e,0x6e,0x10, +0x61,0xa3,0x5f,0x10,0x61,0xa3,0x61,0x11,0x73,0x61,0xa3,0x62,0x62,0x3c,0x6a,0x42, +0x6c,0x10,0x6c,1,0x61,0xa3,0x5b,0x6c,0x10,0x61,0xa3,0x5c,0x11,0x68,0x61,0xa3, +0x59,0x10,0x61,0xa3,0x5a,0x11,0x65,0x6d,0x51,2,0x6f,0x2c,0x75,0x50,0x79,0x10, +0x61,0x91,1,0x6a,0x28,0x6f,0x10,0x6e,0x55,0x1a,0x6f,0x69,0x6e,0x69,0x6e,0x67, +0x67,0x72,0x6f,0x75,0x70,0x21,0x10,0x6e,0x57,0x10,0x65,0x59,0x10,0x61,1,0x66, +0x5b,0x70,0x10,0x68,0x5d,0x66,0x9a,0x66,0x42,0x67,0x7a,0x68,0x8a,0x6b,0xa2,0x75, +0x6c,0x11,0x61,0x6d,0x4c,0x12,0x61,0x64,0x68,0x4f,2,0x61,0x3e,0x65,0x4a,0x69, +0x19,0x6e,0x61,0x6c,0x73,0x65,0x6d,0x6b,0x61,0x74,0x68,0x35,0x15,0x72,0x73,0x69, +0x79,0x65,0x68,0x8f,0x86,0x10,0x68,0x33,0x10,0x61,1,0x66,0x37,0x6d,0x11,0x61, +0x6c,0x39,1,0x61,0x40,0x65,0x3e,1,0x68,0x28,0x74,0x10,0x68,0x45,0x40,0x13, +0x67,0x6f,0x61,0x6c,0x43,2,0x68,0x3b,0x6d,0x5c,0x6e,0x1a,0x69,0x66,0x69,0x72, +0x6f,0x68,0x69,0x6e,0x67,0x79,0x61,1,0x6b,0x2a,0x70,0x10,0x61,0xa3,0x65,0x15, +0x69,0x6e,0x6e,0x61,0x79,0x61,0xa3,0x64,0x1a,0x7a,0x61,0x6f,0x6e,0x68,0x65,0x68, +0x67,0x6f,0x61,0x6c,0x3d,2,0x61,0x3a,0x68,0x44,0x6e,0x17,0x6f,0x74,0x74,0x65, +0x64,0x68,0x65,0x68,0x4b,1,0x66,0x47,0x70,0x10,0x68,0x49,0x12,0x61,0x70,0x68, +0x89,0x61,0x2e,0x62,0x8a,0x64,0xa2,0x51,0x65,0x31,2,0x66,0x3c,0x69,0x70,0x6c, +1,0x61,0x28,0x65,0x10,0x66,0x27,0x11,0x70,0x68,0x25,0x14,0x72,0x69,0x63,0x61, +0x6e,2,0x66,0x30,0x6e,0x36,0x71,0x11,0x61,0x66,0xa3,0x58,0x11,0x65,0x68,0xa3, +0x56,0x12,0x6f,0x6f,0x6e,0xa3,0x57,0x10,0x6e,0x23,1,0x65,0x4a,0x75,0x10,0x72, +0x1f,0x75,0x73,0x68,0x61,0x73,0x6b,0x69,0x79,0x65,0x68,0x62,0x61,0x72,0x72,0x65, +0x65,0x8d,1,0x68,0x29,0x74,0x10,0x68,0x2b,0x11,0x61,0x6c,0x2c,0x16,0x61,0x74, +0x68,0x72,0x69,0x73,0x68,0x2f,7,0x6e,0x2e,0x6e,0x2c,0x72,0x3e,0x74,0x56,0x75, +0x21,0x18,0x6f,0x6e,0x6a,0x6f,0x69,0x6e,0x69,0x6e,0x67,0x21,0x28,0x1a,0x69,0x67, +0x68,0x74,0x6a,0x6f,0x69,0x6e,0x69,0x6e,0x67,0x29,0x2a,0x19,0x72,0x61,0x6e,0x73, +0x70,0x61,0x72,0x65,0x6e,0x74,0x2b,0x63,0x23,0x64,0x40,0x6a,0x56,0x6c,0x26,0x19, +0x65,0x66,0x74,0x6a,0x6f,0x69,0x6e,0x69,0x6e,0x67,0x27,0x24,0x19,0x75,0x61,0x6c, +0x6a,0x6f,0x69,0x6e,0x69,0x6e,0x67,0x25,0x19,0x6f,0x69,0x6e,0x63,0x61,0x75,0x73, +0x69,0x6e,0x67,0x23,0,0x13,0x6e,0xc0,0xd0,0x73,0x49,0x73,0x48,0x75,0x78,0x77, +0x84,0x78,0x9c,0x7a,0x10,0x77,0x58,1,0x6a,0x75,0x73,0x13,0x70,0x61,0x63,0x65, +0x59,4,0x61,0x51,0x67,0x53,0x70,0x28,0x75,0x30,0x79,0x57,0x54,0x12,0x61,0x63, +0x65,0x55,0x16,0x72,0x72,0x6f,0x67,0x61,0x74,0x65,0x53,0x15,0x6e,0x6b,0x6e,0x6f, +0x77,0x6e,0x21,1,0x6a,0x5d,0x6f,0x17,0x72,0x64,0x6a,0x6f,0x69,0x6e,0x65,0x72, +0x5d,0x10,0x78,0x21,0x6e,0x60,0x6f,0xa2,0x41,0x70,0xa2,0x50,0x71,0xa2,0x6e,0x72, +1,0x65,0x24,0x69,0x6f,0x1e,0x67,0x69,0x6f,0x6e,0x61,0x6c,0x69,0x6e,0x64,0x69, +0x63,0x61,0x74,0x6f,0x72,0x6f,4,0x65,0x3e,0x6c,0x5b,0x6f,0x46,0x73,0x45,0x75, +0x46,0x14,0x6d,0x65,0x72,0x69,0x63,0x47,0x15,0x78,0x74,0x6c,0x69,0x6e,0x65,0x5b, +0x17,0x6e,0x73,0x74,0x61,0x72,0x74,0x65,0x72,0x45,0x10,0x70,0x48,0x1c,0x65,0x6e, +0x70,0x75,0x6e,0x63,0x74,0x75,0x61,0x74,0x69,0x6f,0x6e,0x49,1,0x6f,0x3e,0x72, +0x4c,0x1a,0x65,0x66,0x69,0x78,0x6e,0x75,0x6d,0x65,0x72,0x69,0x63,0x4d,0x4a,0x1b, +0x73,0x74,0x66,0x69,0x78,0x6e,0x75,0x6d,0x65,0x72,0x69,0x63,0x4b,0x10,0x75,0x4e, +0x16,0x6f,0x74,0x61,0x74,0x69,0x6f,0x6e,0x4f,0x68,0x7b,0x68,0x50,0x69,0x86,0x6a, +0xa2,0x61,0x6c,0xa2,0x65,0x6d,0x1c,0x61,0x6e,0x64,0x61,0x74,0x6f,0x72,0x79,0x62, +0x72,0x65,0x61,0x6b,0x2d,4,0x32,0x5f,0x33,0x61,0x65,0x34,0x6c,0x6d,0x79,0x3a, +0x13,0x70,0x68,0x65,0x6e,0x3b,0x19,0x62,0x72,0x65,0x77,0x6c,0x65,0x74,0x74,0x65, +0x72,0x6d,2,0x64,0x28,0x6e,0x3c,0x73,0x41,0x3c,0x18,0x65,0x6f,0x67,0x72,0x61, +0x70,0x68,0x69,0x63,0x3d,0x3e,1,0x66,0x3e,0x73,0x11,0x65,0x70,1,0x61,0x22, +0x65,0x14,0x72,0x61,0x62,0x6c,0x65,0x3f,0x18,0x69,0x78,0x6e,0x75,0x6d,0x65,0x72, +0x69,0x63,0x41,2,0x6c,0x63,0x74,0x65,0x76,0x67,1,0x66,0x43,0x69,0x15,0x6e, +0x65,0x66,0x65,0x65,0x64,0x43,0x61,0x40,0x62,0x70,0x63,0xa2,0x55,0x65,0xa2,0xdb, +0x67,0x10,0x6c,0x38,0x11,0x75,0x65,0x39,2,0x69,0x23,0x6c,0x34,0x6d,0x16,0x62, +0x69,0x67,0x75,0x6f,0x75,0x73,0x23,0x24,0x17,0x70,0x68,0x61,0x62,0x65,0x74,0x69, +0x63,0x25,4,0x32,0x27,0x61,0x29,0x62,0x2b,0x6b,0x2d,0x72,0x12,0x65,0x61,0x6b, +2,0x61,0x36,0x62,0x3e,0x73,0x15,0x79,0x6d,0x62,0x6f,0x6c,0x73,0x57,0x13,0x66, +0x74,0x65,0x72,0x29,1,0x65,0x2a,0x6f,0x11,0x74,0x68,0x27,0x13,0x66,0x6f,0x72, +0x65,0x2b,7,0x6d,0x51,0x6d,0x33,0x6f,0x28,0x70,0x69,0x72,0x35,1,0x6d,0x76, +0x6e,1,0x64,0x3c,0x74,0x1a,0x69,0x6e,0x67,0x65,0x6e,0x74,0x62,0x72,0x65,0x61, +0x6b,0x2f,0x15,0x69,0x74,0x69,0x6f,0x6e,0x61,0x1f,0x6c,0x6a,0x61,0x70,0x61,0x6e, +0x65,0x73,0x65,0x73,0x74,0x61,0x72,0x74,0x65,0x72,0x6b,1,0x62,0x3a,0x70,0x19, +0x6c,0x65,0x78,0x63,0x6f,0x6e,0x74,0x65,0x78,0x74,0x51,0x18,0x69,0x6e,0x69,0x6e, +0x67,0x6d,0x61,0x72,0x6b,0x33,0x61,0x6a,0x62,0x2f,0x6a,0x6b,0x6c,0x30,0x13,0x6f, +0x73,0x65,0x70,1,0x61,0x38,0x75,0x18,0x6e,0x63,0x74,0x75,0x61,0x74,0x69,0x6f, +0x6e,0x31,0x18,0x72,0x65,0x6e,0x74,0x68,0x65,0x73,0x69,0x73,0x69,0x1b,0x72,0x72, +0x69,0x61,0x67,0x65,0x72,0x65,0x74,0x75,0x72,0x6e,0x35,2,0x62,0x3e,0x6d,0x46, +0x78,0x36,0x18,0x63,0x6c,0x61,0x6d,0x61,0x74,0x69,0x6f,0x6e,0x37,0x70,0x12,0x61, +0x73,0x65,0x71,0x72,0x16,0x6f,0x64,0x69,0x66,0x69,0x65,0x72,0x73,1,0x64,0x42, +0x6e,1,0x6f,0x32,0x75,0x26,0x14,0x6d,0x65,0x72,0x69,0x63,0x27,0x11,0x6e,0x65, +0x21,1,0x65,0x2e,0x69,0x24,0x12,0x67,0x69,0x74,0x25,0x22,0x14,0x63,0x69,0x6d, +0x61,0x6c,0x23,0,0x18,0x6e,0xc3,0xe6,0x74,0xc1,0x51,0x77,0x7a,0x77,0xa2,0x4c, +0x78,0xa2,0x60,0x79,0xa2,0x6a,0x7a,6,0x73,0x1e,0x73,0x34,0x78,0x42,0x79,0x48, +0x7a,0x11,0x7a,0x7a,0xa3,0x67,0x10,0x79,1,0x65,0xa3,0xae,0x6d,0xa3,0x81,0x11, +0x78,0x78,0xa3,0x66,0x11,0x79,0x79,0x21,0x61,0x30,0x69,0x58,0x6d,0x11,0x74,0x68, +0xa3,0x80,0x10,0x6e,1,0x61,0x26,0x62,0xa3,0xb1,0x1a,0x62,0x61,0x7a,0x61,0x72, +0x73,0x71,0x75,0x61,0x72,0x65,0xa3,0xb1,0x11,0x6e,0x68,0x23,1,0x61,0x2c,0x6f, +0x11,0x6c,0x65,0xa3,0x9b,0x11,0x72,0x61,0xa2,0x92,0x15,0x6e,0x67,0x63,0x69,0x74, +0x69,0xa3,0x92,1,0x70,0x2c,0x73,0x11,0x75,0x78,0xa3,0x65,0x11,0x65,0x6f,0x9b, +0x10,0x69,0x72,0x11,0x69,0x69,0x73,0x74,0x4a,0x75,0xa2,0xba,0x76,1,0x61,0x2c, +0x69,0x11,0x73,0x70,0xa3,0x64,0x10,0x69,0xa2,0x63,0x10,0x69,0xa3,0x63,5,0x67, +0x36,0x67,0x68,0x68,0x6c,0x69,2,0x62,0x3a,0x66,0x4a,0x72,0x10,0x68,0xa2,0x9e, +0x12,0x75,0x74,0x61,0xa3,0x9e,1,0x65,0x24,0x74,0x6f,0x12,0x74,0x61,0x6e,0x6f, +0x14,0x69,0x6e,0x61,0x67,0x68,0x99,0x11,0x6c,0x67,0x75,0x10,0x61,1,0x61,0x24, +0x69,0x6d,0x6a,0x11,0x6e,0x61,0x6b,0x61,0x30,0x65,0xa2,0x5b,0x66,0x11,0x6e,0x67, +0x99,6,0x6c,0x21,0x6c,0x32,0x6d,0x38,0x6e,0x44,0x76,0x10,0x74,0xa3,0x7f,1, +0x65,0x89,0x75,0x97,1,0x69,0x24,0x6c,0x67,0x10,0x6c,0x67,0x10,0x67,0xa2,0x9a, +0x11,0x75,0x74,0xa3,0x9a,0x67,0x36,0x69,0x52,0x6b,0x10,0x72,0xa2,0x99,0x10,0x69, +0xa3,0x99,1,0x61,0x30,0x62,0x7a,0x13,0x61,0x6e,0x77,0x61,0x7b,0x12,0x6c,0x6f, +0x67,0x75,2,0x6c,0x32,0x74,0x34,0x76,0x12,0x69,0x65,0x74,0xa3,0x7f,0x10,0x65, +0x89,0x12,0x68,0x61,0x6d,0xa3,0x6a,1,0x6c,0x2a,0x6e,0x10,0x67,0xa3,0x62,0x10, +0x75,0x68,0x11,0x67,0x75,0x69,1,0x67,0x32,0x6e,0x14,0x6b,0x6e,0x6f,0x77,0x6e, +0xa3,0x67,0x11,0x61,0x72,0x8a,0x13,0x69,0x74,0x69,0x63,0x8b,0x71,0xc1,0x13,0x71, +0xa2,0xde,0x72,0xa2,0xe3,0x73,6,0x69,0x8a,0x69,0x72,0x6f,0xa2,0x4c,0x75,0xa2, +0x75,0x79,1,0x6c,0x46,0x72,4,0x63,0x65,0x65,0xa3,0x5f,0x69,0x2c,0x6a,0xa3, +0x60,0x6e,0xa3,0x61,0x11,0x61,0x63,0x65,0x10,0x6f,0x94,0x16,0x74,0x69,0x6e,0x61, +0x67,0x72,0x69,0x95,2,0x64,0x3c,0x67,0x4c,0x6e,1,0x64,0xa3,0x91,0x68,0x62, +0x12,0x61,0x6c,0x61,0x63,0x10,0x64,0xa2,0xa6,0x12,0x68,0x61,0x6d,0xa3,0xa6,0x17, +0x6e,0x77,0x72,0x69,0x74,0x69,0x6e,0x67,0xa3,0x70,2,0x67,0x3a,0x72,0x52,0x79, +0x10,0x6f,0xa2,0xb0,0x12,0x6d,0x62,0x6f,0xa3,0xb0,1,0x64,0x26,0x6f,0xa3,0xb8, +0xa2,0xb7,0x12,0x69,0x61,0x6e,0xa3,0xb7,0x10,0x61,0xa2,0x98,0x16,0x73,0x6f,0x6d, +0x70,0x65,0x6e,0x67,0xa3,0x98,0x11,0x6e,0x64,0xa2,0x71,0x14,0x61,0x6e,0x65,0x73, +0x65,0xa3,0x71,0x61,0x5c,0x67,0xa2,0x43,0x68,1,0x61,0x2a,0x72,0x10,0x64,0xa3, +0x97,2,0x72,0x28,0x76,0x30,0x77,0x87,0x12,0x61,0x64,0x61,0xa3,0x97,0x12,0x69, +0x61,0x6e,0x87,2,0x6d,0x40,0x72,0x58,0x75,0x10,0x72,0xa2,0x6f,0x15,0x61,0x73, +0x68,0x74,0x72,0x61,0xa3,0x6f,1,0x61,0x26,0x72,0xa3,0x7e,0x14,0x72,0x69,0x74, +0x61,0x6e,0xa3,0x7e,1,0x61,0xa3,0x5e,0x62,0xa3,0x85,0x11,0x6e,0x77,0xa3,0x70, +0x11,0x61,0x61,1,0x63,0x2f,0x69,0x23,3,0x65,0x3e,0x6a,0x48,0x6f,0x4e,0x75, +0x10,0x6e,1,0x69,0x24,0x72,0x61,0x10,0x63,0x61,0x13,0x6a,0x61,0x6e,0x67,0xa3, +0x6e,0x11,0x6e,0x67,0xa3,0x6e,1,0x68,0x2a,0x72,0x10,0x6f,0xa3,0x5d,0x10,0x67, +0xa3,0xb6,0x6e,0xa2,0x83,0x6f,0xa2,0xca,0x70,5,0x6c,0x1e,0x6c,0x44,0x72,0x4a, +0x73,0x1b,0x61,0x6c,0x74,0x65,0x72,0x70,0x61,0x68,0x6c,0x61,0x76,0x69,0xa3,0x7b, +0x11,0x72,0x64,0xa3,0x5c,0x11,0x74,0x69,0xa3,0x7d,0x61,0x7c,0x65,0xa2,0x54,0x68, +3,0x61,0x3e,0x6c,0x4e,0x6e,0x5e,0x6f,0x16,0x65,0x6e,0x69,0x63,0x69,0x61,0x6e, +0xa3,0x5b,0x10,0x67,0xa2,0x5a,0x12,0x73,0x70,0x61,0xa3,0x5a,2,0x69,0xa3,0x7a, +0x70,0xa3,0x7b,0x76,0xa3,0x7c,0x10,0x78,0xa3,0x5b,2,0x68,0x3e,0x6c,0x50,0x75, +0x10,0x63,0xa2,0xa5,0x14,0x69,0x6e,0x68,0x61,0x75,0xa3,0xa5,0x17,0x61,0x77,0x68, +0x68,0x6d,0x6f,0x6e,0x67,0xa3,0x4b,0x10,0x6d,0xa2,0x90,0x14,0x79,0x72,0x65,0x6e, +0x65,0xa3,0x90,0x11,0x72,0x6d,0xa3,0x59,5,0x6b,0x1e,0x6b,0x32,0x73,0x4a,0x75, +0x12,0x73,0x68,0x75,0xa3,0x96,1,0x67,0x2e,0x6f,0xa2,0x57,0x10,0x6f,0xa3,0x57, +0x10,0x62,0xa3,0x84,0x11,0x68,0x75,0xa3,0x96,0x61,0x42,0x62,0x60,0x65,0x10,0x77, +1,0x61,0xa3,0xaa,0x74,0x14,0x61,0x69,0x6c,0x75,0x65,0x97,1,0x62,0x2a,0x72, +0x10,0x62,0xa3,0x8e,0x15,0x61,0x74,0x61,0x65,0x61,0x6e,0xa3,0x8f,0x11,0x61,0x74, +0xa3,0x8f,3,0x67,0x5a,0x6c,0x6c,0x72,0xa2,0x93,0x73,2,0x61,0x36,0x67,0x3c, +0x6d,0x10,0x61,0x84,0x12,0x6e,0x79,0x61,0x85,0x11,0x67,0x65,0xa3,0xab,0x10,0x65, +0xa3,0xab,1,0x61,0x2a,0x68,0x11,0x61,0x6d,0x5b,0x10,0x6d,0x5b,1,0x63,0xa2, +0x60,0x64,5,0x70,0x37,0x70,0x36,0x73,0x54,0x74,0x14,0x75,0x72,0x6b,0x69,0x63, +0xa3,0x58,0x11,0x65,0x72,1,0x6d,0x2c,0x73,0x12,0x69,0x61,0x6e,0x9b,0x11,0x69, +0x63,0xa3,0x59,0x10,0x6f,1,0x67,0x3a,0x75,0x18,0x74,0x68,0x61,0x72,0x61,0x62, +0x69,0x61,0x6e,0xa3,0x85,0x13,0x64,0x69,0x61,0x6e,0xa3,0xb8,0x68,0x42,0x69,0x54, +0x6e,0x1a,0x6f,0x72,0x74,0x68,0x61,0x72,0x61,0x62,0x69,0x61,0x6e,0xa3,0x8e,0x17, +0x75,0x6e,0x67,0x61,0x72,0x69,0x61,0x6e,0xa3,0x4c,0x14,0x74,0x61,0x6c,0x69,0x63, +0x5d,1,0x68,0x26,0x6b,0xa3,0x6d,0x12,0x69,0x6b,0x69,0xa3,0x6d,2,0x69,0x2c, +0x6b,0x30,0x79,0x10,0x61,0x5f,0x11,0x79,0x61,0x5f,0x10,0x68,0xa3,0x58,0x68,0xc2, +0xef,0x6b,0xc2,0xa,0x6b,0xa4,0x17,0x6c,0xa4,0x98,0x6d,8,0x6f,0x46,0x6f,0x48, +0x72,0x74,0x74,0x80,0x75,0x86,0x79,1,0x61,0x28,0x6d,0x10,0x72,0x59,0x13,0x6e, +0x6d,0x61,0x72,0x59,2,0x64,0x2e,0x6e,0x32,0x6f,0x10,0x6e,0xa3,0x72,0x10,0x69, +0xa3,0xa3,0x10,0x67,0x56,0x14,0x6f,0x6c,0x69,0x61,0x6e,0x57,0x10,0x6f,0xa2,0x95, +0x10,0x6f,0xa3,0x95,0x11,0x65,0x69,0xa3,0x73,0x11,0x6c,0x74,0xa2,0xa4,0x12,0x61, +0x6e,0x69,0xa3,0xa4,0x61,0x36,0x65,0xa2,0x67,0x69,0xa2,0xbd,0x6c,0x11,0x79,0x6d, +0x55,6,0x6e,0x38,0x6e,0x32,0x72,0x5c,0x73,0x6c,0x79,0x10,0x61,0xa3,0x55,1, +0x64,0x38,0x69,0xa2,0x79,0x15,0x63,0x68,0x61,0x65,0x61,0x6e,0xa3,0x79,0xa2,0x54, +0x12,0x61,0x69,0x63,0xa3,0x54,0x10,0x63,0xa2,0xa9,0x12,0x68,0x65,0x6e,0xa3,0xa9, +0x18,0x61,0x72,0x61,0x6d,0x67,0x6f,0x6e,0x64,0x69,0xa3,0xaf,0x68,0x36,0x6b,0x4c, +0x6c,0x15,0x61,0x79,0x61,0x6c,0x61,0x6d,0x55,1,0x61,0x26,0x6a,0xa3,0xa0,0x13, +0x6a,0x61,0x6e,0x69,0xa3,0xa0,0x10,0x61,0xa2,0xb4,0x12,0x73,0x61,0x72,0xa3,0xb4, +3,0x64,0x78,0x65,0x94,0x6e,0xa2,0x42,0x72,1,0x63,0xa3,0x8d,0x6f,0xa2,0x56, +0x13,0x69,0x74,0x69,0x63,1,0x63,0x3c,0x68,0x19,0x69,0x65,0x72,0x6f,0x67,0x6c, +0x79,0x70,0x68,0x73,0xa3,0x56,0x15,0x75,0x72,0x73,0x69,0x76,0x65,0xa3,0x8d,1, +0x65,0x26,0x66,0xa3,0xb5,0x16,0x66,0x61,0x69,0x64,0x72,0x69,0x6e,0xa3,0xb5,0x17, +0x74,0x65,0x69,0x6d,0x61,0x79,0x65,0x6b,0xa3,0x73,0x10,0x64,0xa2,0x8c,0x17,0x65, +0x6b,0x69,0x6b,0x61,0x6b,0x75,0x69,0xa3,0x8c,0x11,0x61,0x6f,0xa3,0x5c,5,0x6f, +0x14,0x6f,0x30,0x70,0x36,0x74,0x11,0x68,0x69,0xa3,0x78,0x11,0x72,0x65,0xa3,0x77, +0x11,0x65,0x6c,0xa3,0x8a,0x61,0x2e,0x68,0x98,0x6e,0x11,0x64,0x61,0x4b,4,0x69, +0x3c,0x6c,0x44,0x6e,0x48,0x74,0x56,0x79,0x13,0x61,0x68,0x6c,0x69,0xa3,0x4f,0x12, +0x74,0x68,0x69,0xa3,0x78,0x10,0x69,0xa3,0x4f,1,0x61,0x4d,0x6e,0x12,0x61,0x64, +0x61,0x4b,0x14,0x61,0x6b,0x61,0x6e,0x61,0x4c,0x19,0x6f,0x72,0x68,0x69,0x72,0x61, +0x67,0x61,0x6e,0x61,0x8d,3,0x61,0x3c,0x6d,0x4e,0x6f,0x5a,0x75,0x15,0x64,0x61, +0x77,0x61,0x64,0x69,0xa3,0x91,0x10,0x72,0x92,0x15,0x6f,0x73,0x68,0x74,0x68,0x69, +0x93,1,0x65,0x24,0x72,0x4f,0x10,0x72,0x4f,0x10,0x6a,0xa2,0x9d,0x11,0x6b,0x69, +0xa3,0x9d,4,0x61,0x5c,0x65,0x90,0x69,0xa0,0x6f,0xa2,0x5d,0x79,1,0x63,0x34, +0x64,0x10,0x69,0xa2,0x6c,0x11,0x61,0x6e,0xa3,0x6c,0x10,0x69,0xa2,0x6b,0x11,0x61, +0x6e,0xa3,0x6b,2,0x6e,0x42,0x6f,0x46,0x74,3,0x66,0xa3,0x50,0x67,0xa3,0x51, +0x69,0x24,0x6e,0x53,0x10,0x6e,0x53,0x10,0x61,0xa3,0x6a,0x50,0x10,0x6f,0x51,0x11, +0x70,0x63,0xa2,0x52,0x11,0x68,0x61,0xa3,0x52,2,0x6d,0x2e,0x6e,0x36,0x73,0x10, +0x75,0xa3,0x83,0x10,0x62,0x80,0x10,0x75,0x81,2,0x61,0xa3,0x53,0x62,0x83,0x65, +0x11,0x61,0x72,1,0x61,0xa3,0x53,0x62,0x83,0x11,0x6d,0x61,0xa3,0x8b,0x68,0x6e, +0x69,0xa2,0x91,0x6a,2,0x61,0x30,0x70,0x52,0x75,0x11,0x72,0x63,0xa3,0x94,1, +0x6d,0x38,0x76,0x10,0x61,0xa2,0x4e,0x13,0x6e,0x65,0x73,0x65,0xa3,0x4e,0x10,0x6f, +0xa3,0xad,0x11,0x61,0x6e,0xa3,0x69,6,0x6c,0x1a,0x6c,0x34,0x6d,0x3a,0x72,0x40, +0x75,0x11,0x6e,0x67,0xa3,0x4c,0x11,0x75,0x77,0xa3,0x9c,0x11,0x6e,0x67,0xa3,0x4b, +0x11,0x6b,0x74,0x8d,0x61,0x3c,0x65,0xa2,0x43,0x69,0x11,0x72,0x61,0x48,0x13,0x67, +0x61,0x6e,0x61,0x49,1,0x6e,0x34,0x74,0x10,0x72,0xa2,0xa2,0x11,0x61,0x6e,0xa3, +0xa2,0x42,6,0x6f,0xe,0x6f,0x77,0x73,0xa3,0x49,0x74,0xa3,0x4a,0x75,0x12,0x6e, +0x6f,0x6f,0x77,0x62,0xa3,0xac,0x67,0x3e,0x69,0x42,0x19,0x66,0x69,0x72,0x6f,0x68, +0x69,0x6e,0x67,0x79,0x61,0xa3,0xb6,0x44,0x11,0x75,0x6c,0x45,0x11,0x62,0x72,0x46, +0x11,0x65,0x77,0x47,2,0x6d,0x2e,0x6e,0x4a,0x74,0x11,0x61,0x6c,0x5d,0x1c,0x70, +0x65,0x72,0x69,0x61,0x6c,0x61,0x72,0x61,0x6d,0x61,0x69,0x63,0xa3,0x74,2,0x64, +0x66,0x68,0x6a,0x73,0x1b,0x63,0x72,0x69,0x70,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x70, +0x61,1,0x68,0x32,0x72,0x14,0x74,0x68,0x69,0x61,0x6e,0xa3,0x7d,0x13,0x6c,0x61, +0x76,0x69,0xa3,0x7a,0x10,0x73,0xa3,0x4d,0x15,0x65,0x72,0x69,0x74,0x65,0x64,0x23, +0x64,0xc0,0xec,0x64,0xa2,0x7a,0x65,0xa2,0xad,0x67,4,0x65,0x82,0x6c,0x9a,0x6f, +0xa2,0x46,0x72,0xa2,0x55,0x75,2,0x6a,0x3c,0x6e,0x4e,0x72,1,0x6d,0x24,0x75, +0x41,0x13,0x75,0x6b,0x68,0x69,0x41,1,0x61,0x24,0x72,0x3f,0x13,0x72,0x61,0x74, +0x69,0x3f,0x18,0x6a,0x61,0x6c,0x61,0x67,0x6f,0x6e,0x64,0x69,0xa3,0xb3,0x10,0x6f, +1,0x6b,0xa3,0x48,0x72,0x38,0x13,0x67,0x69,0x61,0x6e,0x39,0x11,0x61,0x67,0x90, +0x15,0x6f,0x6c,0x69,0x74,0x69,0x63,0x91,1,0x6e,0x30,0x74,0x10,0x68,0x3a,0x11, +0x69,0x63,0x3b,1,0x67,0xa3,0xb3,0x6d,0xa3,0xaf,1,0x61,0x32,0x65,1,0x65, +0x24,0x6b,0x3d,0x10,0x6b,0x3d,0x10,0x6e,0xa2,0x89,0x12,0x74,0x68,0x61,0xa3,0x89, +3,0x65,0x42,0x6f,0x68,0x73,0x76,0x75,0x11,0x70,0x6c,0xa2,0x87,0x13,0x6f,0x79, +0x61,0x6e,0xa3,0x87,1,0x73,0x38,0x76,0x10,0x61,0x34,0x15,0x6e,0x61,0x67,0x61, +0x72,0x69,0x35,0x13,0x65,0x72,0x65,0x74,0x33,0x11,0x67,0x72,0xa2,0xb2,0x10,0x61, +0xa3,0xb2,0x11,0x72,0x74,0x33,2,0x67,0x3a,0x6c,0x72,0x74,0x11,0x68,0x69,0x36, +0x13,0x6f,0x70,0x69,0x63,0x37,0x10,0x79,2,0x64,0xa3,0x45,0x68,0xa3,0x46,0x70, +0xa2,0x47,0x1e,0x74,0x69,0x61,0x6e,0x68,0x69,0x65,0x72,0x6f,0x67,0x6c,0x79,0x70, +0x68,0x73,0xa3,0x47,0x11,0x62,0x61,0xa2,0x88,0x12,0x73,0x61,0x6e,0xa3,0x88,0x61, +0xa2,0xa2,0x62,0xa4,7,0x63,6,0x6f,0x3d,0x6f,0x5a,0x70,0x76,0x75,0x7a,0x79, +1,0x70,0x3e,0x72,2,0x69,0x2a,0x6c,0x31,0x73,0xa3,0x44,0x13,0x6c,0x6c,0x69, +0x63,0x31,0x13,0x72,0x69,0x6f,0x74,0x7f,1,0x6d,0x30,0x70,0x10,0x74,0x2e,0x11, +0x69,0x63,0x2f,0x12,0x6d,0x6f,0x6e,0x21,0x11,0x72,0x74,0x7f,0x16,0x6e,0x65,0x69, +0x66,0x6f,0x72,0x6d,0xa3,0x65,0x61,0x32,0x68,0xa2,0x41,0x69,0x11,0x72,0x74,0xa3, +0x43,3,0x6b,0x4c,0x6e,0x50,0x72,0x76,0x75,0x1d,0x63,0x61,0x73,0x69,0x61,0x6e, +0x61,0x6c,0x62,0x61,0x6e,0x69,0x61,0x6e,0xa3,0x9f,0x10,0x6d,0xa3,0x76,1,0x61, +0x24,0x73,0x71,0x1d,0x64,0x69,0x61,0x6e,0x61,0x62,0x6f,0x72,0x69,0x67,0x69,0x6e, +0x61,0x6c,0x71,0x10,0x69,0xa2,0x68,0x11,0x61,0x6e,0xa3,0x68,1,0x61,0x34,0x65, +0x10,0x72,0x2c,0x13,0x6f,0x6b,0x65,0x65,0x2d,1,0x6b,0x26,0x6d,0xa3,0x42,0x11, +0x6d,0x61,0xa3,0x76,6,0x68,0x4a,0x68,0x48,0x6e,0x4e,0x72,0x76,0x76,1,0x65, +0x2a,0x73,0x10,0x74,0xa3,0x75,0x13,0x73,0x74,0x61,0x6e,0xa3,0x75,0x11,0x6f,0x6d, +0xa3,0xa1,0x11,0x61,0x74,0x1f,0x6f,0x6c,0x69,0x61,0x6e,0x68,0x69,0x65,0x72,0x6f, +0x67,0x6c,0x79,0x70,0x68,0x73,0xa3,0x9c,1,0x61,0x3e,0x6d,2,0x65,0x2a,0x69, +0xa3,0x74,0x6e,0x27,0x13,0x6e,0x69,0x61,0x6e,0x27,0x10,0x62,0x24,0x11,0x69,0x63, +0x25,0x64,0x30,0x66,0x44,0x67,0x11,0x68,0x62,0xa3,0x9f,0x10,0x6c,1,0x61,0x26, +0x6d,0xa3,0xa7,0x10,0x6d,0xa3,0xa7,0x11,0x61,0x6b,0xa3,0x93,6,0x6c,0x3c,0x6c, +0x52,0x6f,0x56,0x72,0x66,0x75,1,0x67,0x30,0x68,1,0x64,0x79,0x69,0x10,0x64, +0x79,0x10,0x69,0x8e,0x13,0x6e,0x65,0x73,0x65,0x8f,0x11,0x69,0x73,0xa1,0x11,0x70, +0x6f,0x2a,0x13,0x6d,0x6f,0x66,0x6f,0x2b,0x10,0x61,1,0x68,0x2e,0x69,0x7c,0x12, +0x6c,0x6c,0x65,0x7d,0xa2,0x41,0x11,0x6d,0x69,0xa3,0x41,0x61,0x48,0x65,0x9c,0x68, +1,0x61,0x2a,0x6b,0x10,0x73,0xa3,0xa8,0x15,0x69,0x6b,0x73,0x75,0x6b,0x69,0xa3, +0xa8,3,0x6c,0x3a,0x6d,0x48,0x73,0x54,0x74,1,0x61,0x24,0x6b,0x9f,0x10,0x6b, +0x9f,0x10,0x69,0x9c,0x13,0x6e,0x65,0x73,0x65,0x9d,0x10,0x75,0xa2,0x82,0x10,0x6d, +0xa3,0x82,0x10,0x73,0xa2,0x86,0x13,0x61,0x76,0x61,0x68,0xa3,0x86,0x11,0x6e,0x67, +0x28,0x12,0x61,0x6c,0x69,0x29,3,0x6c,0x42,0x6e,0x90,0x74,0xa2,0x46,0x76,0x24, +0x17,0x6f,0x77,0x65,0x6c,0x6a,0x61,0x6d,0x6f,0x25,0x22,1,0x65,0x54,0x76,0x28, +1,0x73,0x38,0x74,0x2a,0x17,0x73,0x79,0x6c,0x6c,0x61,0x62,0x6c,0x65,0x2b,0x16, +0x79,0x6c,0x6c,0x61,0x62,0x6c,0x65,0x29,0x18,0x61,0x64,0x69,0x6e,0x67,0x6a,0x61, +0x6d,0x6f,0x23,1,0x61,0x21,0x6f,0x1a,0x74,0x61,0x70,0x70,0x6c,0x69,0x63,0x61, +0x62,0x6c,0x65,0x21,0x26,0x1a,0x72,0x61,0x69,0x6c,0x69,0x6e,0x67,0x6a,0x61,0x6d, +0x6f,0x27,1,0x6e,0x2c,0x79,0x22,0x11,0x65,0x73,0x23,0x20,0x10,0x6f,0x21,1, +0x6e,0x2c,0x79,0x22,0x11,0x65,0x73,0x23,0x20,0x10,0x6f,0x21,2,0x6d,0x30,0x6e, +0x3a,0x79,0x22,0x11,0x65,0x73,0x23,0x24,0x13,0x61,0x79,0x62,0x65,0x25,0x20,0x10, +0x6f,0x21,2,0x6d,0x30,0x6e,0x3a,0x79,0x22,0x11,0x65,0x73,0x23,0x24,0x13,0x61, +0x79,0x62,0x65,0x25,0x20,0x10,0x6f,0x21,0xb,0x72,0x39,0x76,0xc,0x76,0x33,0x78, +0x2a,0x7a,0x11,0x77,0x6a,0x43,0x10,0x78,0x21,0x72,0x28,0x73,0x50,0x74,0x31,1, +0x65,0x24,0x69,0x39,0x1e,0x67,0x69,0x6f,0x6e,0x61,0x6c,0x69,0x6e,0x64,0x69,0x63, +0x61,0x74,0x6f,0x72,0x39,1,0x6d,0x35,0x70,0x18,0x61,0x63,0x69,0x6e,0x67,0x6d, +0x61,0x72,0x6b,0x35,0x6c,0x1f,0x6c,0x3c,0x6f,0x4a,0x70,1,0x70,0x37,0x72,0x14, +0x65,0x70,0x65,0x6e,0x64,0x37,0x28,1,0x66,0x2b,0x76,0x2c,0x10,0x74,0x2f,0x13, +0x74,0x68,0x65,0x72,0x21,0x63,0x4c,0x65,0x64,0x67,1,0x61,0x3a,0x6c,0x19,0x75, +0x65,0x61,0x66,0x74,0x65,0x72,0x7a,0x77,0x6a,0x41,0x10,0x7a,0x41,2,0x6e,0x23, +0x6f,0x24,0x72,0x25,0x14,0x6e,0x74,0x72,0x6f,0x6c,0x23,2,0x62,0x34,0x6d,0x4e, +0x78,0x26,0x13,0x74,0x65,0x6e,0x64,0x27,0x3a,1,0x61,0x24,0x67,0x3d,0x11,0x73, +0x65,0x3a,0x12,0x67,0x61,0x7a,0x3d,0x3e,0x16,0x6f,0x64,0x69,0x66,0x69,0x65,0x72, +0x3f,9,0x6e,0x4a,0x6e,0x34,0x6f,0x44,0x73,0x60,0x75,0x94,0x78,0x10,0x78,0x21, +0x10,0x75,0x2a,0x14,0x6d,0x65,0x72,0x69,0x63,0x2b,1,0x6c,0x2c,0x74,0x12,0x68, +0x65,0x72,0x21,0x14,0x65,0x74,0x74,0x65,0x72,0x2d,3,0x63,0x36,0x65,0x46,0x70, +0x31,0x74,0x32,0x12,0x65,0x72,0x6d,0x33,0x3c,0x16,0x6f,0x6e,0x74,0x69,0x6e,0x75, +0x65,0x3d,0x2e,0x10,0x70,0x2f,0x10,0x70,0x34,0x12,0x70,0x65,0x72,0x35,0x61,0x46, +0x63,0x52,0x65,0x64,0x66,0x72,0x6c,2,0x65,0x2d,0x66,0x3b,0x6f,0x28,0x12,0x77, +0x65,0x72,0x29,0x10,0x74,0x22,0x12,0x65,0x72,0x6d,0x23,1,0x6c,0x24,0x72,0x37, +0x24,0x12,0x6f,0x73,0x65,0x25,0x10,0x78,0x38,0x13,0x74,0x65,0x6e,0x64,0x39,0x10, +0x6f,0x26,0x13,0x72,0x6d,0x61,0x74,0x27,0,0x10,0x6c,0x88,0x72,0x40,0x72,0x36, +0x73,0x5e,0x77,0x7a,0x78,0x8a,0x7a,0x11,0x77,0x6a,0x4b,1,0x65,0x24,0x69,0x3b, +0x1e,0x67,0x69,0x6f,0x6e,0x61,0x6c,0x69,0x6e,0x64,0x69,0x63,0x61,0x74,0x6f,0x72, +0x3b,1,0x69,0x24,0x71,0x3f,0x18,0x6e,0x67,0x6c,0x65,0x71,0x75,0x6f,0x74,0x65, +0x3f,0x17,0x73,0x65,0x67,0x73,0x70,0x61,0x63,0x65,0x4d,0x10,0x78,0x21,0x6c,0x36, +0x6d,0x3c,0x6e,0x76,0x6f,0x13,0x74,0x68,0x65,0x72,0x21,1,0x65,0x23,0x66,0x35, +3,0x62,0x37,0x69,0x28,0x6c,0x29,0x6e,0x2b,0x10,0x64,1,0x6c,0x34,0x6e,0x11, +0x75,0x6d,0x2a,0x12,0x6c,0x65,0x74,0x37,0x14,0x65,0x74,0x74,0x65,0x72,0x29,2, +0x65,0x36,0x6c,0x39,0x75,0x2c,0x14,0x6d,0x65,0x72,0x69,0x63,0x2d,0x14,0x77,0x6c, +0x69,0x6e,0x65,0x39,0x66,0x3f,0x66,0x40,0x67,0x4e,0x68,0x70,0x6b,0x10,0x61,0x26, +0x15,0x74,0x61,0x6b,0x61,0x6e,0x61,0x27,0x10,0x6f,0x24,0x13,0x72,0x6d,0x61,0x74, +0x25,1,0x61,0x3a,0x6c,0x19,0x75,0x65,0x61,0x66,0x74,0x65,0x72,0x7a,0x77,0x6a, +0x49,0x10,0x7a,0x49,1,0x65,0x24,0x6c,0x3d,0x19,0x62,0x72,0x65,0x77,0x6c,0x65, +0x74,0x74,0x65,0x72,0x3d,0x61,0x86,0x63,0x92,0x64,0x94,0x65,2,0x62,0x44,0x6d, +0x5e,0x78,0x2e,0x13,0x74,0x65,0x6e,0x64,0x32,0x15,0x6e,0x75,0x6d,0x6c,0x65,0x74, +0x2f,0x42,1,0x61,0x24,0x67,0x45,0x11,0x73,0x65,0x42,0x12,0x67,0x61,0x7a,0x45, +0x46,0x16,0x6f,0x64,0x69,0x66,0x69,0x65,0x72,0x47,0x15,0x6c,0x65,0x74,0x74,0x65, +0x72,0x23,0x10,0x72,0x31,1,0x6f,0x24,0x71,0x41,0x18,0x75,0x62,0x6c,0x65,0x71, +0x75,0x6f,0x74,0x65,0x41,2,0x63,0x32,0x6e,0x3c,0x6f,0x22,0x12,0x70,0x65,0x6e, +0x23,0x24,0x13,0x6c,0x6f,0x73,0x65,0x25,0x20,0x12,0x6f,0x6e,0x65,0x21,0xd,0x6e, +0xc1,0x86,0x73,0xa8,0x73,0x4c,0x74,0xa2,0x76,0x75,0xa2,0x83,0x7a,0xd8,0x70,0, +2,0x6c,0xd9,0x20,0,0x70,0xd9,0x40,0,0x73,0xc3,0,0xfe,0xf,0,0, +0,7,0x6f,0x3c,0x6f,0xff,8,0,0,0,0x70,0x3a,0x75,0x6e,0x79,0x13, +0x6d,0x62,0x6f,0x6c,0xff,0xf,0,0,0,0x11,0x61,0x63,1,0x65,0x34,0x69, +0x15,0x6e,0x67,0x6d,0x61,0x72,0x6b,0xa5,0,0x18,0x73,0x65,0x70,0x61,0x72,0x61, +0x74,0x6f,0x72,0xc3,0,0x16,0x72,0x72,0x6f,0x67,0x61,0x74,0x65,0xe1,0,0, +0x63,0xff,2,0,0,0,0x65,0x38,0x6b,0xff,4,0,0,0,0x6d,0xff, +1,0,0,0,0x16,0x70,0x61,0x72,0x61,0x74,0x6f,0x72,0xd9,0x70,0,0x1d, +0x69,0x74,0x6c,0x65,0x63,0x61,0x73,0x65,0x6c,0x65,0x74,0x74,0x65,0x72,0x31,1, +0x6e,0x40,0x70,0x1c,0x70,0x65,0x72,0x63,0x61,0x73,0x65,0x6c,0x65,0x74,0x74,0x65, +0x72,0x25,0x17,0x61,0x73,0x73,0x69,0x67,0x6e,0x65,0x64,0x23,0x6e,0xa2,0x69,0x6f, +0xa2,0x89,0x70,0xfe,0x30,0xf8,0,0,9,0x69,0x33,0x69,0xff,0x10,0,0, +0,0x6f,0xfd,0x80,0,0,0x72,0x54,0x73,0xf9,0,0,0x75,0x12,0x6e,0x63, +0x74,0xfe,0x30,0xf8,0,0,0x15,0x75,0x61,0x74,0x69,0x6f,0x6e,0xff,0x30,0xf8, +0,0,0x17,0x69,0x76,0x61,0x74,0x65,0x75,0x73,0x65,0xdd,0,0,0x61,0x48, +0x63,0xfd,0x40,0,0,0x64,0xe9,0,0,0x65,0xfd,0x20,0,0,0x66,0xff, +0x20,0,0,0,0x1f,0x72,0x61,0x67,0x72,0x61,0x70,0x68,0x73,0x65,0x70,0x61, +0x72,0x61,0x74,0x6f,0x72,0xd9,0x40,0,0xbe,0,3,0x64,0xa7,0,0x6c,0xab, +0,0x6f,0x30,0x75,0x13,0x6d,0x62,0x65,0x72,0xbf,0,0xb2,0,0x1b,0x6e,0x73, +0x70,0x61,0x63,0x69,0x6e,0x67,0x6d,0x61,0x72,0x6b,0xa1,1,0x70,0x92,0x74,0x12, +0x68,0x65,0x72,0xe6,0x80,1,3,0x6c,0x40,0x6e,0x4a,0x70,0x56,0x73,0x14,0x79, +0x6d,0x62,0x6f,0x6c,0xff,8,0,0,0,0x14,0x65,0x74,0x74,0x65,0x72,0x61, +0x14,0x75,0x6d,0x62,0x65,0x72,0xb3,0,0x19,0x75,0x6e,0x63,0x74,0x75,0x61,0x74, +0x69,0x6f,0x6e,0xfd,0x80,0,0,0x1c,0x65,0x6e,0x70,0x75,0x6e,0x63,0x74,0x75, +0x61,0x74,0x69,0x6f,0x6e,0xf9,0,0,0x66,0xc0,0xc4,0x66,0xa2,0x47,0x69,0xa2, +0x64,0x6c,0xa2,0x79,0x6d,0xa4,0xc0,4,0x61,0x6c,0x63,0xa5,0,0x65,0xa3,0x80, +0x6e,0xa1,0x6f,0x15,0x64,0x69,0x66,0x69,0x65,0x72,1,0x6c,0x38,0x73,0x14,0x79, +0x6d,0x62,0x6f,0x6c,0xff,4,0,0,0,0x14,0x65,0x74,0x74,0x65,0x72,0x41, +1,0x72,0x3c,0x74,0x16,0x68,0x73,0x79,0x6d,0x62,0x6f,0x6c,0xff,1,0,0, +0,0x10,0x6b,0xa5,0xc0,1,0x69,0x32,0x6f,0x13,0x72,0x6d,0x61,0x74,0xdb,0, +0,0x1d,0x6e,0x61,0x6c,0x70,0x75,0x6e,0x63,0x74,0x75,0x61,0x74,0x69,0x6f,0x6e, +0xff,0x20,0,0,0,0x10,0x6e,0x1f,0x69,0x74,0x69,0x61,0x6c,0x70,0x75,0x6e, +0x63,0x74,0x75,0x61,0x74,0x69,0x6f,0x6e,0xff,0x10,0,0,0,0x9c,7,0x6d, +0x18,0x6d,0x41,0x6f,0x28,0x74,0x31,0x75,0x25,0x60,0x1c,0x77,0x65,0x72,0x63,0x61, +0x73,0x65,0x6c,0x65,0x74,0x74,0x65,0x72,0x29,0x63,0x3d,0x65,0x28,0x69,0x42,0x6c, +0x29,0x13,0x74,0x74,0x65,0x72,0x9c,0x15,0x6e,0x75,0x6d,0x62,0x65,0x72,0xab,0, +0x1a,0x6e,0x65,0x73,0x65,0x70,0x61,0x72,0x61,0x74,0x6f,0x72,0xd9,0x20,0,0x63, +0x46,0x64,0xa2,0x96,0x65,0x1b,0x6e,0x63,0x6c,0x6f,0x73,0x69,0x6e,0x67,0x6d,0x61, +0x72,0x6b,0xa3,0x80,0xe6,0x80,1,7,0x6e,0x57,0x6e,0x52,0x6f,0x5e,0x73,0xe1, +0,0,0x75,0x1b,0x72,0x72,0x65,0x6e,0x63,0x79,0x73,0x79,0x6d,0x62,0x6f,0x6c, +0xff,2,0,0,0,0x22,0x12,0x74,0x72,0x6c,0xd9,0x80,0,0xdc,0,0, +1,0x6d,0x62,0x6e,1,0x6e,0x30,0x74,0x12,0x72,0x6f,0x6c,0xd9,0x80,0,0x1f, +0x65,0x63,0x74,0x6f,0x72,0x70,0x75,0x6e,0x63,0x74,0x75,0x61,0x74,0x69,0x6f,0x6e, +0xfd,0x40,0,0,0x19,0x62,0x69,0x6e,0x69,0x6e,0x67,0x6d,0x61,0x72,0x6b,0xa5, +0xc0,0x61,0x58,0x63,0xd9,0x80,0,0x66,0xdb,0,0,0x6c,0x1d,0x6f,0x73,0x65, +0x70,0x75,0x6e,0x63,0x74,0x75,0x61,0x74,0x69,0x6f,0x6e,0xfd,0x20,0,0,0x18, +0x73,0x65,0x64,0x6c,0x65,0x74,0x74,0x65,0x72,0x3d,2,0x61,0x32,0x65,0x50,0x69, +0x12,0x67,0x69,0x74,0xa7,0,0x1c,0x73,0x68,0x70,0x75,0x6e,0x63,0x74,0x75,0x61, +0x74,0x69,0x6f,0x6e,0xe9,0,0,0x1a,0x63,0x69,0x6d,0x61,0x6c,0x6e,0x75,0x6d, +0x62,0x65,0x72,0xa7,0 }; -const char PropNameData::nameGroups[19082]={ +const char PropNameData::nameGroups[19587]={ 2,'A','l','p','h','a',0,'A','l','p','h','a','b','e','t','i','c',0, 4,'N',0,'N','o',0,'F',0,'F','a','l','s','e',0,4,'Y',0,'Y','e','s',0,'T',0,'T','r','u','e',0, 2,'N','R',0,'N','o','t','_','R','e','o','r','d','e','r','e','d',0, @@ -1025,14 +1049,14 @@ const char PropNameData::nameGroups[19082]={ 2,'C','W','C','M',0,'C','h','a','n','g','e','s','_','W','h','e','n','_','C','a','s','e','m','a','p','p','e','d',0, 2,'C','W','K','C','F',0,'C','h','a','n','g','e','s','_','W','h','e','n','_','N','F','K','C','_','C','a','s','e','f','o','l', 'd','e','d',0,2,'E','m','o','j','i',0,'E','m','o','j','i',0, -2,'E','m','o','j','i','_','P','r','e','s','e','n','t','a','t','i','o','n',0,'E','m','o','j','i','_','P','r','e','s','e','n', -'t','a','t','i','o','n',0,2,'E','m','o','j','i','_','M','o','d','i','f','i','e','r',0,'E','m','o','j','i','_','M','o','d', -'i','f','i','e','r',0,2,'E','m','o','j','i','_','M','o','d','i','f','i','e','r','_','B','a','s','e',0, -'E','m','o','j','i','_','M','o','d','i','f','i','e','r','_','B','a','s','e',0, -2,'E','m','o','j','i','_','C','o','m','p','o','n','e','n','t',0,'E','m','o','j','i','_','C','o','m','p','o','n','e','n','t', -0,2,'R','I',0,'R','e','g','i','o','n','a','l','_','I','n','d','i','c','a','t','o','r',0, +2,'E','P','r','e','s',0,'E','m','o','j','i','_','P','r','e','s','e','n','t','a','t','i','o','n',0, +2,'E','M','o','d',0,'E','m','o','j','i','_','M','o','d','i','f','i','e','r',0, +2,'E','B','a','s','e',0,'E','m','o','j','i','_','M','o','d','i','f','i','e','r','_','B','a','s','e',0, +2,'E','C','o','m','p',0,'E','m','o','j','i','_','C','o','m','p','o','n','e','n','t',0, +2,'R','I',0,'R','e','g','i','o','n','a','l','_','I','n','d','i','c','a','t','o','r',0, 2,'P','C','M',0,'P','r','e','p','e','n','d','e','d','_','C','o','n','c','a','t','e','n','a','t','i','o','n','_','M','a','r', -'k',0,2,'b','c',0,'B','i','d','i','_','C','l','a','s','s',0, +'k',0,2,'E','x','t','P','i','c','t',0,'E','x','t','e','n','d','e','d','_','P','i','c','t','o','g','r','a','p','h','i','c', +0,2,'b','c',0,'B','i','d','i','_','C','l','a','s','s',0, 2,'L',0,'L','e','f','t','_','T','o','_','R','i','g','h','t',0, 2,'R',0,'R','i','g','h','t','_','T','o','_','L','e','f','t',0, 2,'E','N',0,'E','u','r','o','p','e','a','n','_','N','u','m','b','e','r',0, @@ -1371,8 +1395,19 @@ const char PropNameData::nameGroups[19082]={ 2,'N','u','s','h','u',0,'N','u','s','h','u',0,2,'S','o','y','o','m','b','o',0,'S','o','y','o','m','b','o',0, 2,'S','y','r','i','a','c','_','S','u','p',0,'S','y','r','i','a','c','_','S','u','p','p','l','e','m','e','n','t',0, 2,'Z','a','n','a','b','a','z','a','r','_','S','q','u','a','r','e',0,'Z','a','n','a','b','a','z','a','r','_','S','q','u','a', -'r','e',0,2,'c','c','c',0,'C','a','n','o','n','i','c','a','l','_','C','o','m','b','i','n','i','n','g','_','C','l','a','s', -'s',0,2,'d','t',0,'D','e','c','o','m','p','o','s','i','t','i','o','n','_','T','y','p','e',0, +'r','e',0,2,'C','h','e','s','s','_','S','y','m','b','o','l','s',0,'C','h','e','s','s','_','S','y','m','b','o','l','s',0, +2,'D','o','g','r','a',0,'D','o','g','r','a',0,2,'G','e','o','r','g','i','a','n','_','E','x','t',0, +'G','e','o','r','g','i','a','n','_','E','x','t','e','n','d','e','d',0, +2,'G','u','n','j','a','l','a','_','G','o','n','d','i',0,'G','u','n','j','a','l','a','_','G','o','n','d','i',0, +2,'H','a','n','i','f','i','_','R','o','h','i','n','g','y','a',0,'H','a','n','i','f','i','_','R','o','h','i','n','g','y','a', +0,2,'I','n','d','i','c','_','S','i','y','a','q','_','N','u','m','b','e','r','s',0,'I','n','d','i','c','_','S','i','y','a', +'q','_','N','u','m','b','e','r','s',0,2,'M','a','k','a','s','a','r',0,'M','a','k','a','s','a','r',0, +2,'M','a','y','a','n','_','N','u','m','e','r','a','l','s',0,'M','a','y','a','n','_','N','u','m','e','r','a','l','s',0, +2,'M','e','d','e','f','a','i','d','r','i','n',0,'M','e','d','e','f','a','i','d','r','i','n',0, +2,'O','l','d','_','S','o','g','d','i','a','n',0,'O','l','d','_','S','o','g','d','i','a','n',0, +2,'S','o','g','d','i','a','n',0,'S','o','g','d','i','a','n',0, +2,'c','c','c',0,'C','a','n','o','n','i','c','a','l','_','C','o','m','b','i','n','i','n','g','_','C','l','a','s','s',0, +2,'d','t',0,'D','e','c','o','m','p','o','s','i','t','i','o','n','_','T','y','p','e',0, 3,'N','o','n','e',0,'N','o','n','e',0,'n','o','n','e',0, 3,'C','a','n',0,'C','a','n','o','n','i','c','a','l',0,'c','a','n',0, 3,'C','o','m',0,'C','o','m','p','a','t',0,'c','o','m',0, @@ -1503,7 +1538,10 @@ const char PropNameData::nameGroups[19082]={ 2,'M','a','l','a','y','a','l','a','m','_','R','a',0,'M','a','l','a','y','a','l','a','m','_','R','a',0, 2,'M','a','l','a','y','a','l','a','m','_','S','s','a',0,'M','a','l','a','y','a','l','a','m','_','S','s','a',0, 2,'M','a','l','a','y','a','l','a','m','_','T','t','a',0,'M','a','l','a','y','a','l','a','m','_','T','t','a',0, -2,'j','t',0,'J','o','i','n','i','n','g','_','T','y','p','e',0, +2,'H','a','n','i','f','i','_','R','o','h','i','n','g','y','a','_','K','i','n','n','a','_','Y','a',0, +'H','a','n','i','f','i','_','R','o','h','i','n','g','y','a','_','K','i','n','n','a','_','Y','a',0, +2,'H','a','n','i','f','i','_','R','o','h','i','n','g','y','a','_','P','a',0,'H','a','n','i','f','i','_','R','o','h','i','n', +'g','y','a','_','P','a',0,2,'j','t',0,'J','o','i','n','i','n','g','_','T','y','p','e',0, 2,'U',0,'N','o','n','_','J','o','i','n','i','n','g',0,2,'C',0,'J','o','i','n','_','C','a','u','s','i','n','g',0, 2,'D',0,'D','u','a','l','_','J','o','i','n','i','n','g',0, 2,'L',0,'L','e','f','t','_','J','o','i','n','i','n','g',0, @@ -1644,7 +1682,12 @@ const char PropNameData::nameGroups[19082]={ 2,'H','a','n','b',0,'H','a','n','b',0,2,'J','a','m','o',0,'J','a','m','o',0, 2,'Z','s','y','e',0,'Z','s','y','e',0,2,'G','o','n','m',0,'M','a','s','a','r','a','m','_','G','o','n','d','i',0, 2,'S','o','y','o',0,'S','o','y','o','m','b','o',0,2,'Z','a','n','b',0,'Z','a','n','a','b','a','z','a','r','_','S','q', -'u','a','r','e',0,2,'h','s','t',0,'H','a','n','g','u','l','_','S','y','l','l','a','b','l','e','_','T','y','p','e',0, +'u','a','r','e',0,2,'D','o','g','r',0,'D','o','g','r','a',0, +2,'G','o','n','g',0,'G','u','n','j','a','l','a','_','G','o','n','d','i',0, +2,'M','a','k','a',0,'M','a','k','a','s','a','r',0,2,'M','e','d','f',0,'M','e','d','e','f','a','i','d','r','i','n',0, +2,'R','o','h','g',0,'H','a','n','i','f','i','_','R','o','h','i','n','g','y','a',0, +2,'S','o','g','d',0,'S','o','g','d','i','a','n',0,2,'S','o','g','o',0,'O','l','d','_','S','o','g','d','i','a','n',0, +2,'h','s','t',0,'H','a','n','g','u','l','_','S','y','l','l','a','b','l','e','_','T','y','p','e',0, 2,'N','A',0,'N','o','t','_','A','p','p','l','i','c','a','b','l','e',0, 2,'L',0,'L','e','a','d','i','n','g','_','J','a','m','o',0, 2,'V',0,'V','o','w','e','l','_','J','a','m','o',0,2,'T',0,'T','r','a','i','l','i','n','g','_','J','a','m','o',0, @@ -1676,6 +1719,7 @@ const char PropNameData::nameGroups[19082]={ 2,'E','x','t','e','n','d',0,'E','x','t','e','n','d',0,2,'M','B',0,'M','i','d','N','u','m','L','e','t',0, 2,'N','L',0,'N','e','w','l','i','n','e',0,2,'S','Q',0,'S','i','n','g','l','e','_','Q','u','o','t','e',0, 2,'D','Q',0,'D','o','u','b','l','e','_','Q','u','o','t','e',0, +2,'W','S','e','g','S','p','a','c','e',0,'W','S','e','g','S','p','a','c','e',0, 2,'b','p','t',0,'B','i','d','i','_','P','a','i','r','e','d','_','B','r','a','c','k','e','t','_','T','y','p','e',0, 2,'n',0,'N','o','n','e',0,2,'o',0,'O','p','e','n',0, 2,'c',0,'C','l','o','s','e',0,2,'g','c','m',0,'G','e','n','e','r','a','l','_','C','a','t','e','g','o','r','y','_','M', diff --git a/deps/icu-small/source/common/putil.cpp b/deps/icu-small/source/common/putil.cpp index e367fa6d30e866..a1e16a9cd94387 100644 --- a/deps/icu-small/source/common/putil.cpp +++ b/deps/icu-small/source/common/putil.cpp @@ -533,6 +533,28 @@ uprv_fmin(double x, double y) return (x > y ? y : x); } +U_CAPI UBool U_EXPORT2 +uprv_add32_overflow(int32_t a, int32_t b, int32_t* res) { + // NOTE: Some compilers (GCC, Clang) have primitives available, like __builtin_add_overflow. + // This function could be optimized by calling one of those primitives. + auto a64 = static_cast(a); + auto b64 = static_cast(b); + int64_t res64 = a64 + b64; + *res = static_cast(res64); + return res64 != *res; +} + +U_CAPI UBool U_EXPORT2 +uprv_mul32_overflow(int32_t a, int32_t b, int32_t* res) { + // NOTE: Some compilers (GCC, Clang) have primitives available, like __builtin_mul_overflow. + // This function could be optimized by calling one of those primitives. + auto a64 = static_cast(a); + auto b64 = static_cast(b); + int64_t res64 = a64 * b64; + *res = static_cast(res64); + return res64 != *res; +} + /** * Truncates the given double. * trunc(3.3) = 3.0, trunc (-3.3) = -3.0 diff --git a/deps/icu-small/source/common/putilimp.h b/deps/icu-small/source/common/putilimp.h index 56ea8df00959d1..023e06879a0655 100644 --- a/deps/icu-small/source/common/putilimp.h +++ b/deps/icu-small/source/common/putilimp.h @@ -391,6 +391,32 @@ U_INTERNAL double U_EXPORT2 uprv_log(double d); */ U_INTERNAL double U_EXPORT2 uprv_round(double x); +/** + * Adds the signed integers a and b, storing the result in res. + * Checks for signed integer overflow. + * Similar to the GCC/Clang extension __builtin_add_overflow + * + * @param a The first operand. + * @param b The second operand. + * @param res a + b + * @return true if overflow occurred; false if no overflow occurred. + * @internal + */ +U_INTERNAL UBool U_EXPORT2 uprv_add32_overflow(int32_t a, int32_t b, int32_t* res); + +/** + * Multiplies the signed integers a and b, storing the result in res. + * Checks for signed integer overflow. + * Similar to the GCC/Clang extension __builtin_mul_overflow + * + * @param a The first multiplicand. + * @param b The second multiplicand. + * @param res a * b + * @return true if overflow occurred; false if no overflow occurred. + * @internal + */ +U_INTERNAL UBool U_EXPORT2 uprv_mul32_overflow(int32_t a, int32_t b, int32_t* res); + #if 0 /** * Returns the number of digits after the decimal point in a double number x. diff --git a/deps/icu-small/source/common/rbbi.cpp b/deps/icu-small/source/common/rbbi.cpp index 69f92d94c602c3..c5ea2770ba9854 100644 --- a/deps/icu-small/source/common/rbbi.cpp +++ b/deps/icu-small/source/common/rbbi.cpp @@ -651,7 +651,7 @@ UBool RuleBasedBreakIterator::isBoundary(int32_t offset) { } // Adjust offset to be on a code point boundary and not beyond the end of the text. - // Note that isBoundary() is always be false for offsets that are not on code point boundaries. + // Note that isBoundary() is always false for offsets that are not on code point boundaries. // But we still need the side effect of leaving iteration at the following boundary. utext_setNativeIndex(&fText, offset); @@ -937,26 +937,23 @@ int32_t RuleBasedBreakIterator::handleNext() { } - //----------------------------------------------------------------------------------- // -// handlePrevious() +// handleSafePrevious() // // Iterate backwards using the safe reverse rules. -// The logic of this function is very similar to handleNext(), above. +// The logic of this function is similar to handleNext(), but simpler +// because the safe table does not require as many options. // //----------------------------------------------------------------------------------- -int32_t RuleBasedBreakIterator::handlePrevious(int32_t fromPosition) { +int32_t RuleBasedBreakIterator::handleSafePrevious(int32_t fromPosition) { int32_t state; uint16_t category = 0; - RBBIRunMode mode; RBBIStateTableRow *row; UChar32 c; - LookAheadResults lookAheadMatches; int32_t result = 0; - int32_t initialPosition = 0; - const RBBIStateTable *stateTable = fData->fSafeRevTable; + const RBBIStateTable *stateTable = fData->fReverseTable; UTEXT_SETNATIVEINDEX(&fText, fromPosition); #ifdef RBBI_DEBUG if (gTrace) { @@ -969,54 +966,24 @@ int32_t RuleBasedBreakIterator::handlePrevious(int32_t fromPosition) { return BreakIterator::DONE; } - // Set up the starting char. - initialPosition = (int32_t)UTEXT_GETNATIVEINDEX(&fText); - result = initialPosition; - c = UTEXT_PREVIOUS32(&fText); - // Set the initial state for the state machine + c = UTEXT_PREVIOUS32(&fText); state = START_STATE; row = (RBBIStateTableRow *) (stateTable->fTableData + (stateTable->fRowLen * state)); - category = 3; - mode = RBBI_RUN; - if (stateTable->fFlags & RBBI_BOF_REQUIRED) { - category = 2; - mode = RBBI_START; - } - // loop until we reach the start of the text or transition to state 0 // - for (;;) { - if (c == U_SENTINEL) { - // Reached end of input string. - if (mode == RBBI_END) { - // We have already run the loop one last time with the - // character set to the psueudo {eof} value. Now it is time - // to unconditionally bail out. - break; - } - // Run the loop one last time with the fake end-of-input character category. - mode = RBBI_END; - category = 1; - } + for (; c != U_SENTINEL; c = UTEXT_PREVIOUS32(&fText)) { + // look up the current character's character category, which tells us + // which column in the state table to look at. + // Note: the 16 in UTRIE_GET16 refers to the size of the data being returned, + // not the size of the character going in, which is a UChar32. // - // Get the char category. An incoming category of 1 or 2 means that - // we are preset for doing the beginning or end of input, and - // that we shouldn't get a category from an actual text input character. - // - if (mode == RBBI_RUN) { - // look up the current character's character category, which tells us - // which column in the state table to look at. - // Note: the 16 in UTRIE_GET16 refers to the size of the data being returned, - // not the size of the character going in, which is a UChar32. - // - // And off the dictionary flag bit. For reverse iteration it is not used. - category = UTRIE2_GET16(fData->fTrie, c); - category &= ~0x4000; - } + // And off the dictionary flag bit. For reverse iteration it is not used. + category = UTRIE2_GET16(fData->fTrie, c); + category &= ~0x4000; #ifdef RBBI_DEBUG if (gTrace) { @@ -1032,65 +999,21 @@ int32_t RuleBasedBreakIterator::handlePrevious(int32_t fromPosition) { // State Transition - move machine to its next state // - // fNextState is a variable-length array. U_ASSERT(categoryfHeader->fCatCount); state = row->fNextState[category]; /*Not accessing beyond memory*/ row = (RBBIStateTableRow *) (stateTable->fTableData + (stateTable->fRowLen * state)); - if (row->fAccepting == -1) { - // Match found, common case. - result = (int32_t)UTEXT_GETNATIVEINDEX(&fText); - } - - int16_t completedRule = row->fAccepting; - if (completedRule > 0) { - // Lookahead match is completed. - int32_t lookaheadResult = lookAheadMatches.getPosition(completedRule); - if (lookaheadResult >= 0) { - UTEXT_SETNATIVEINDEX(&fText, lookaheadResult); - return lookaheadResult; - } - } - int16_t rule = row->fLookAhead; - if (rule != 0) { - // At the position of a '/' in a look-ahead match. Record it. - int32_t pos = (int32_t)UTEXT_GETNATIVEINDEX(&fText); - lookAheadMatches.setPosition(rule, pos); - } - if (state == STOP_STATE) { // This is the normal exit from the lookup state machine. - // We have advanced through the string until it is certain that no - // longer match is possible, no matter what characters follow. + // Transistion to state zero means we have found a safe point. break; } - - // Move (backwards) to the next character to process. - // If this is a beginning-of-input loop iteration, don't advance - // the input position. The next iteration will be processing the - // first real input character. - if (mode == RBBI_RUN) { - c = UTEXT_PREVIOUS32(&fText); - } else { - if (mode == RBBI_START) { - mode = RBBI_RUN; - } - } } // The state machine is done. Check whether it found a match... - - // If the iterator failed to advance in the match engine, force it ahead by one. - // (This really indicates a defect in the break rules. They should always match - // at least one character.) - if (result == initialPosition) { - UTEXT_SETNATIVEINDEX(&fText, initialPosition); - UTEXT_PREVIOUS32(&fText); - result = (int32_t)UTEXT_GETNATIVEINDEX(&fText); - } - + result = (int32_t)UTEXT_GETNATIVEINDEX(&fText); #ifdef RBBI_DEBUG if (gTrace) { RBBIDebugPrintf("result = %d\n\n", result); @@ -1099,7 +1022,6 @@ int32_t RuleBasedBreakIterator::handlePrevious(int32_t fromPosition) { return result; } - //------------------------------------------------------------------------------- // // getRuleStatus() Return the break rule tag associated with the current diff --git a/deps/icu-small/source/common/rbbi_cache.cpp b/deps/icu-small/source/common/rbbi_cache.cpp index ba9329d4771321..60316ce6420dc5 100644 --- a/deps/icu-small/source/common/rbbi_cache.cpp +++ b/deps/icu-small/source/common/rbbi_cache.cpp @@ -354,14 +354,31 @@ UBool RuleBasedBreakIterator::BreakCache::populateNear(int32_t position, UErrorC if ((position < fBoundaries[fStartBufIdx] - 15) || position > (fBoundaries[fEndBufIdx] + 15)) { int32_t aBoundary = 0; int32_t ruleStatusIndex = 0; - // TODO: check for position == length of text. Although may still need to back up to get rule status. if (position > 20) { - int32_t backupPos = fBI->handlePrevious(position); - fBI->fPosition = backupPos; - aBoundary = fBI->handleNext(); // Ignore dictionary, just finding a rule based boundary. - ruleStatusIndex = fBI->fRuleStatusIndex; + int32_t backupPos = fBI->handleSafePrevious(position); + + if (backupPos > 0) { + // Advance to the boundary following the backup position. + // There is a complication: the safe reverse rules identify pairs of code points + // that are safe. If advancing from the safe point moves forwards by less than + // two code points, we need to advance one more time to ensure that the boundary + // is good, including a correct rules status value. + // + fBI->fPosition = backupPos; + aBoundary = fBI->handleNext(); + if (aBoundary <= backupPos + 4) { + // +4 is a quick test for possibly having advanced only one codepoint. + // Four being the length of the longest potential code point, a supplementary in UTF-8 + utext_setNativeIndex(&fBI->fText, aBoundary); + if (backupPos == utext_getPreviousNativeIndex(&fBI->fText)) { + // The initial handleNext() only advanced by a single code point. Go again. + aBoundary = fBI->handleNext(); // Safe rules identify safe pairs. + } + } + ruleStatusIndex = fBI->fRuleStatusIndex; + } } - reset(aBoundary, ruleStatusIndex); // Reset cache to hold aBoundary as a single starting point. + reset(aBoundary, ruleStatusIndex); // Reset cache to hold aBoundary as a single starting point. } // Fill in boundaries between existing cache content and the new requested position. @@ -485,16 +502,30 @@ UBool RuleBasedBreakIterator::BreakCache::populatePreceding(UErrorCode &status) if (backupPosition <= 0) { backupPosition = 0; } else { - backupPosition = fBI->handlePrevious(backupPosition); + backupPosition = fBI->handleSafePrevious(backupPosition); } if (backupPosition == UBRK_DONE || backupPosition == 0) { position = 0; positionStatusIdx = 0; } else { - fBI->fPosition = backupPosition; // TODO: pass starting position in a clearer way. + // Advance to the boundary following the backup position. + // There is a complication: the safe reverse rules identify pairs of code points + // that are safe. If advancing from the safe point moves forwards by less than + // two code points, we need to advance one more time to ensure that the boundary + // is good, including a correct rules status value. + // + fBI->fPosition = backupPosition; position = fBI->handleNext(); + if (position <= backupPosition + 4) { + // +4 is a quick test for possibly having advanced only one codepoint. + // Four being the length of the longest potential code point, a supplementary in UTF-8 + utext_setNativeIndex(&fBI->fText, position); + if (backupPosition == utext_getPreviousNativeIndex(&fBI->fText)) { + // The initial handleNext() only advanced by a single code point. Go again. + position = fBI->handleNext(); // Safe rules identify safe pairs. + } + }; positionStatusIdx = fBI->fRuleStatusIndex; - } } while (position >= fromPosition); diff --git a/deps/icu-small/source/common/rbbidata.cpp b/deps/icu-small/source/common/rbbidata.cpp index 18912a6a7b3dcf..fdcb564961362e 100644 --- a/deps/icu-small/source/common/rbbidata.cpp +++ b/deps/icu-small/source/common/rbbidata.cpp @@ -81,8 +81,6 @@ void RBBIDataWrapper::init0() { fHeader = NULL; fForwardTable = NULL; fReverseTable = NULL; - fSafeFwdTable = NULL; - fSafeRevTable = NULL; fRuleSource = NULL; fRuleStatusTable = NULL; fTrie = NULL; @@ -111,21 +109,6 @@ void RBBIDataWrapper::init(const RBBIDataHeader *data, UErrorCode &status) { if (data->fRTableLen != 0) { fReverseTable = (RBBIStateTable *)((char *)data + fHeader->fRTable); } - if (data->fSFTableLen != 0) { - fSafeFwdTable = (RBBIStateTable *)((char *)data + fHeader->fSFTable); - } - if (data->fSRTableLen != 0) { - fSafeRevTable = (RBBIStateTable *)((char *)data + fHeader->fSRTable); - } - - // Rule Compatibility Hacks - // If a rule set includes reverse rules but does not explicitly include safe reverse rules, - // the reverse rules are to be treated as safe reverse rules. - - if (fSafeRevTable == NULL && fReverseTable != NULL) { - fSafeRevTable = fReverseTable; - fReverseTable = NULL; - } fTrie = utrie2_openFromSerialized(UTRIE2_16_VALUE_BITS, (uint8_t *)data + fHeader->fTrie, @@ -277,8 +260,6 @@ void RBBIDataWrapper::printData() { printTable("Forward State Transition Table", fForwardTable); printTable("Reverse State Transition Table", fReverseTable); - printTable("Safe Forward State Transition Table", fSafeFwdTable); - printTable("Safe Reverse State Transition Table", fSafeRevTable); RBBIDebugPrintf("\nOrignal Rules source:\n"); for (int32_t c=0; fRuleSource[c] != 0; c++) { @@ -418,28 +399,6 @@ ubrk_swap(const UDataSwapper *ds, const void *inData, int32_t length, void *outD outBytes+tableStartOffset+topSize, status); } - // Safe Forward state table. Same layout as forward table, above. - tableStartOffset = ds->readUInt32(rbbiDH->fSFTable); - tableLength = ds->readUInt32(rbbiDH->fSFTableLen); - - if (tableLength > 0) { - ds->swapArray32(ds, inBytes+tableStartOffset, topSize, - outBytes+tableStartOffset, status); - ds->swapArray16(ds, inBytes+tableStartOffset+topSize, tableLength-topSize, - outBytes+tableStartOffset+topSize, status); - } - - // Safe Reverse state table. Same layout as forward table, above. - tableStartOffset = ds->readUInt32(rbbiDH->fSRTable); - tableLength = ds->readUInt32(rbbiDH->fSRTableLen); - - if (tableLength > 0) { - ds->swapArray32(ds, inBytes+tableStartOffset, topSize, - outBytes+tableStartOffset, status); - ds->swapArray16(ds, inBytes+tableStartOffset+topSize, tableLength-topSize, - outBytes+tableStartOffset+topSize, status); - } - // Trie table for character categories utrie2_swap(ds, inBytes+ds->readUInt32(rbbiDH->fTrie), ds->readUInt32(rbbiDH->fTrieLen), outBytes+ds->readUInt32(rbbiDH->fTrie), status); diff --git a/deps/icu-small/source/common/rbbidata.h b/deps/icu-small/source/common/rbbidata.h index 8b21acca304d5f..3e573039d0f491 100644 --- a/deps/icu-small/source/common/rbbidata.h +++ b/deps/icu-small/source/common/rbbidata.h @@ -58,7 +58,7 @@ ubrk_swap(const UDataSwapper *ds, U_NAMESPACE_BEGIN // The current RBBI data format version. -static const uint8_t RBBI_DATA_FORMAT_VERSION[] = {4, 0, 0, 0}; +static const uint8_t RBBI_DATA_FORMAT_VERSION[] = {5, 0, 0, 0}; /* * The following structs map exactly onto the raw data from ICU common data file. @@ -81,10 +81,6 @@ struct RBBIDataHeader { uint32_t fFTableLen; uint32_t fRTable; /* Offset to the reverse state transition table. */ uint32_t fRTableLen; - uint32_t fSFTable; /* safe point forward transition table */ - uint32_t fSFTableLen; - uint32_t fSRTable; /* safe point reverse transition table */ - uint32_t fSRTableLen; uint32_t fTrie; /* Offset to Trie data for character categories */ uint32_t fTrieLen; uint32_t fRuleSource; /* Offset to the source for for the break */ @@ -174,8 +170,6 @@ class RBBIDataWrapper : public UMemory { const RBBIDataHeader *fHeader; const RBBIStateTable *fForwardTable; const RBBIStateTable *fReverseTable; - const RBBIStateTable *fSafeFwdTable; - const RBBIStateTable *fSafeRevTable; const UChar *fRuleSource; const int32_t *fRuleStatusTable; diff --git a/deps/icu-small/source/common/rbbirb.cpp b/deps/icu-small/source/common/rbbirb.cpp index 9fc8f8e814200a..a46f483d23334a 100644 --- a/deps/icu-small/source/common/rbbirb.cpp +++ b/deps/icu-small/source/common/rbbirb.cpp @@ -62,10 +62,7 @@ RBBIRuleBuilder::RBBIRuleBuilder(const UnicodeString &rules, fSafeFwdTree = NULL; fSafeRevTree = NULL; fDefaultTree = &fForwardTree; - fForwardTables = NULL; - fReverseTables = NULL; - fSafeFwdTables = NULL; - fSafeRevTables = NULL; + fForwardTable = NULL; fRuleStatusVals = NULL; fChainRules = FALSE; fLBCMNoChain = FALSE; @@ -114,11 +111,7 @@ RBBIRuleBuilder::~RBBIRuleBuilder() { delete fUSetNodes; delete fSetBuilder; - delete fForwardTables; - delete fReverseTables; - delete fSafeFwdTables; - delete fSafeRevTables; - + delete fForwardTable; delete fForwardTree; delete fReverseTree; delete fSafeFwdTree; @@ -157,21 +150,15 @@ RBBIDataHeader *RBBIRuleBuilder::flattenData() { // without the padding. // int32_t headerSize = align8(sizeof(RBBIDataHeader)); - int32_t forwardTableSize = align8(fForwardTables->getTableSize()); - int32_t reverseTableSize = align8(fReverseTables->getTableSize()); - int32_t safeFwdTableSize = align8(fSafeFwdTables->getTableSize()); - int32_t safeRevTableSize = align8(fSafeRevTables->getTableSize()); + int32_t forwardTableSize = align8(fForwardTable->getTableSize()); + int32_t reverseTableSize = align8(fForwardTable->getSafeTableSize()); int32_t trieSize = align8(fSetBuilder->getTrieSize()); int32_t statusTableSize = align8(fRuleStatusVals->size() * sizeof(int32_t)); int32_t rulesSize = align8((fStrippedRules.length()+1) * sizeof(UChar)); - (void)safeFwdTableSize; - int32_t totalSize = headerSize + forwardTableSize - + /* reverseTableSize */ 0 - + /* safeFwdTableSize */ 0 - + (safeRevTableSize ? safeRevTableSize : reverseTableSize) + + reverseTableSize + statusTableSize + trieSize + rulesSize; RBBIDataHeader *data = (RBBIDataHeader *)uprv_malloc(totalSize); @@ -190,38 +177,13 @@ RBBIDataHeader *RBBIRuleBuilder::flattenData() { data->fLength = totalSize; data->fCatCount = fSetBuilder->getNumCharCategories(); - // Only save the forward table and the safe reverse table, - // because these are the only ones used at run-time. - // - // For the moment, we still build the other tables if they are present in the rule source files, - // for backwards compatibility. Old rule files need to work, and this is the simplest approach. - // - // Additional backwards compatibility consideration: if no safe rules are provided, consider the - // reverse rules to actually be the safe reverse rules. - data->fFTable = headerSize; data->fFTableLen = forwardTableSize; - // Do not save Reverse Table. - data->fRTable = data->fFTable + forwardTableSize; - data->fRTableLen = 0; - - // Do not save the Safe Forward table. - data->fSFTable = data->fRTable + 0; - data->fSFTableLen = 0; - - data->fSRTable = data->fSFTable + 0; - if (safeRevTableSize > 0) { - data->fSRTableLen = safeRevTableSize; - } else if (reverseTableSize > 0) { - data->fSRTableLen = reverseTableSize; - } else { - U_ASSERT(FALSE); // Rule build should have failed for lack of a reverse table - // before reaching this point. - } - + data->fRTable = data->fFTable + data->fFTableLen; + data->fRTableLen = reverseTableSize; - data->fTrie = data->fSRTable + data->fSRTableLen; + data->fTrie = data->fRTable + data->fRTableLen; data->fTrieLen = fSetBuilder->getTrieSize(); data->fStatusTable = data->fTrie + trieSize; data->fStatusTableLen= statusTableSize; @@ -230,15 +192,8 @@ RBBIDataHeader *RBBIRuleBuilder::flattenData() { uprv_memset(data->fReserved, 0, sizeof(data->fReserved)); - fForwardTables->exportTable((uint8_t *)data + data->fFTable); - // fReverseTables->exportTable((uint8_t *)data + data->fRTable); - // fSafeFwdTables->exportTable((uint8_t *)data + data->fSFTable); - if (safeRevTableSize > 0) { - fSafeRevTables->exportTable((uint8_t *)data + data->fSRTable); - } else { - fReverseTables->exportTable((uint8_t *)data + data->fSRTable); - } - + fForwardTable->exportTable((uint8_t *)data + data->fFTable); + fForwardTable->exportSafeTable((uint8_t *)data + data->fRTable); fSetBuilder->serializeTrie ((uint8_t *)data + data->fTrie); int32_t *ruleStatusTable = (int32_t *)((uint8_t *)data + data->fStatusTable); @@ -252,10 +207,6 @@ RBBIDataHeader *RBBIRuleBuilder::flattenData() { } - - - - //---------------------------------------------------------------------------------------- // // createRuleBasedBreakIterator construct from source rules that are passed in @@ -267,8 +218,6 @@ RBBIRuleBuilder::createRuleBasedBreakIterator( const UnicodeString &rules, UParseError *parseError, UErrorCode &status) { - // status checked below - // // Read the input rules, generate a parse tree, symbol table, // and list of all Unicode Sets referenced by the rules. @@ -277,7 +226,38 @@ RBBIRuleBuilder::createRuleBasedBreakIterator( const UnicodeString &rules, if (U_FAILURE(status)) { // status checked here bcos build below doesn't return NULL; } - builder.fScanner->parse(); + + RBBIDataHeader *data = builder.build(status); + + if (U_FAILURE(status)) { + return nullptr; + } + + // + // Create a break iterator from the compiled rules. + // (Identical to creation from stored pre-compiled rules) + // + // status is checked after init in construction. + RuleBasedBreakIterator *This = new RuleBasedBreakIterator(data, status); + if (U_FAILURE(status)) { + delete This; + This = NULL; + } + else if(This == NULL) { // test for NULL + status = U_MEMORY_ALLOCATION_ERROR; + } + return This; +} + +RBBIDataHeader *RBBIRuleBuilder::build(UErrorCode &status) { + if (U_FAILURE(status)) { + return nullptr; + } + + fScanner->parse(); + if (U_FAILURE(status)) { + return nullptr; + } // // UnicodeSet processing. @@ -285,95 +265,55 @@ RBBIRuleBuilder::createRuleBasedBreakIterator( const UnicodeString &rules, // Generate the mapping tables (TRIE) from input code points to // the character categories. // - builder.fSetBuilder->buildRanges(); - + fSetBuilder->buildRanges(); // // Generate the DFA state transition table. // - builder.fForwardTables = new RBBITableBuilder(&builder, &builder.fForwardTree); - builder.fReverseTables = new RBBITableBuilder(&builder, &builder.fReverseTree); - builder.fSafeFwdTables = new RBBITableBuilder(&builder, &builder.fSafeFwdTree); - builder.fSafeRevTables = new RBBITableBuilder(&builder, &builder.fSafeRevTree); - if (builder.fForwardTables == NULL || builder.fReverseTables == NULL || - builder.fSafeFwdTables == NULL || builder.fSafeRevTables == NULL) - { + fForwardTable = new RBBITableBuilder(this, &fForwardTree, status); + if (fForwardTable == nullptr) { status = U_MEMORY_ALLOCATION_ERROR; - delete builder.fForwardTables; builder.fForwardTables = NULL; - delete builder.fReverseTables; builder.fReverseTables = NULL; - delete builder.fSafeFwdTables; builder.fSafeFwdTables = NULL; - delete builder.fSafeRevTables; builder.fSafeRevTables = NULL; - return NULL; + return nullptr; } - builder.fForwardTables->build(); - builder.fReverseTables->build(); - builder.fSafeFwdTables->build(); - builder.fSafeRevTables->build(); + fForwardTable->buildForwardTable(); + optimizeTables(); + fForwardTable->buildSafeReverseTable(status); + #ifdef RBBI_DEBUG - if (builder.fDebugEnv && uprv_strstr(builder.fDebugEnv, "states")) { - builder.fForwardTables->printRuleStatusTable(); + if (fDebugEnv && uprv_strstr(fDebugEnv, "states")) { + fForwardTable->printStates(); + fForwardTable->printRuleStatusTable(); + fForwardTable->printReverseTable(); } #endif - builder.optimizeTables(); - builder.fSetBuilder->buildTrie(); - - + fSetBuilder->buildTrie(); // // Package up the compiled data into a memory image // in the run-time format. // - RBBIDataHeader *data = builder.flattenData(); // returns NULL if error - if (U_FAILURE(*builder.fStatus)) { - return NULL; - } - - - // - // Clean up the compiler related stuff - // - - - // - // Create a break iterator from the compiled rules. - // (Identical to creation from stored pre-compiled rules) - // - // status is checked after init in construction. - RuleBasedBreakIterator *This = new RuleBasedBreakIterator(data, status); + RBBIDataHeader *data = flattenData(); // returns NULL if error if (U_FAILURE(status)) { - delete This; - This = NULL; + return nullptr; } - else if(This == NULL) { // test for NULL - status = U_MEMORY_ALLOCATION_ERROR; - } - return This; + return data; } void RBBIRuleBuilder::optimizeTables() { - int32_t leftClass; - int32_t rightClass; - - leftClass = 3; - rightClass = 0; - while (fForwardTables->findDuplCharClassFrom(leftClass, rightClass)) { - fSetBuilder->mergeCategories(leftClass, rightClass); - fForwardTables->removeColumn(rightClass); - fReverseTables->removeColumn(rightClass); - fSafeFwdTables->removeColumn(rightClass); - fSafeRevTables->removeColumn(rightClass); - } - - fForwardTables->removeDuplicateStates(); - fReverseTables->removeDuplicateStates(); - fSafeFwdTables->removeDuplicateStates(); - fSafeRevTables->removeDuplicateStates(); - + // Begin looking for duplicates with char class 3. + // Classes 0, 1 and 2 are special; they are unused, {bof} and {eof} respectively, + // and should not have other categories merged into them. + IntPair duplPair = {3, 0}; + while (fForwardTable->findDuplCharClassFrom(&duplPair)) { + fSetBuilder->mergeCategories(duplPair); + fForwardTable->removeColumn(duplPair.second); + } + fForwardTable->removeDuplicateStates(); } U_NAMESPACE_END diff --git a/deps/icu-small/source/common/rbbirb.h b/deps/icu-small/source/common/rbbirb.h index 511f394b458e93..37992daabb0b14 100644 --- a/deps/icu-small/source/common/rbbirb.h +++ b/deps/icu-small/source/common/rbbirb.h @@ -18,6 +18,8 @@ #if !UCONFIG_NO_BREAK_ITERATION +#include + #include "unicode/uobject.h" #include "unicode/rbbi.h" #include "unicode/uniset.h" @@ -25,8 +27,7 @@ #include "uhash.h" #include "uvector.h" #include "unicode/symtable.h"// For UnicodeSet parsing, is the interface that - // looks up references to $variables within a set. - + // looks up references to $variables within a set. U_NAMESPACE_BEGIN @@ -123,10 +124,16 @@ class RBBIRuleBuilder : public UMemory { RBBIRuleBuilder(const UnicodeString &rules, UParseError *parseErr, UErrorCode &status - ); + ); virtual ~RBBIRuleBuilder(); + /** + * Build the state tables and char class Trie from the source rules. + */ + RBBIDataHeader *build(UErrorCode &status); + + /** * Fold together redundant character classes (table columns) and * redundant states (table rows). Done after initial table generation, @@ -162,10 +169,7 @@ class RBBIRuleBuilder : public UMemory { RBBISetBuilder *fSetBuilder; // Set and Character Category builder. UVector *fUSetNodes; // Vector of all uset nodes. - RBBITableBuilder *fForwardTables; // State transition tables - RBBITableBuilder *fReverseTables; - RBBITableBuilder *fSafeFwdTables; - RBBITableBuilder *fSafeRevTables; + RBBITableBuilder *fForwardTable; // State transition table, build time form. UVector *fRuleStatusVals; // The values that can be returned // from getRuleStatus(). @@ -200,6 +204,11 @@ struct RBBISetTableEl { RBBINode *val; }; +/** + * A pair of ints, used to bundle pairs of states or pairs of character classes. + */ +typedef std::pair IntPair; + //---------------------------------------------------------------------------- // diff --git a/deps/icu-small/source/common/rbbiscan.cpp b/deps/icu-small/source/common/rbbiscan.cpp index e3472ed599e15e..ecc1663d8f84f7 100644 --- a/deps/icu-small/source/common/rbbiscan.cpp +++ b/deps/icu-small/source/common/rbbiscan.cpp @@ -372,7 +372,7 @@ UBool RBBIRuleScanner::doParseActions(int32_t action) // (forward, reverse, safe_forward, safe_reverse) // OR this rule into the appropriate group of them. // - RBBINode **destRules = (fReverseRule? &fRB->fReverseTree : fRB->fDefaultTree); + RBBINode **destRules = (fReverseRule? &fRB->fSafeRevTree : fRB->fDefaultTree); if (*destRules != NULL) { // This is not the first rule encounted. @@ -1122,22 +1122,6 @@ void RBBIRuleScanner::parse() { return; } - // - // If there were NO user specified reverse rules, set up the equivalent of ".*;" - // - if (fRB->fReverseTree == NULL) { - fRB->fReverseTree = pushNewNode(RBBINode::opStar); - RBBINode *operand = pushNewNode(RBBINode::setRef); - if (U_FAILURE(*fRB->fStatus)) { - return; - } - findSetFor(UnicodeString(TRUE, kAny, 3), operand); - fRB->fReverseTree->fLeftChild = operand; - operand->fParent = fRB->fReverseTree; - fNodeStackPtr -= 2; - } - - // // Parsing of the input RBBI rules is complete. // We now have a parse tree for the rule expressions diff --git a/deps/icu-small/source/common/rbbisetb.cpp b/deps/icu-small/source/common/rbbisetb.cpp index 4e7389b4af039b..e6b98cf3e22b98 100644 --- a/deps/icu-small/source/common/rbbisetb.cpp +++ b/deps/icu-small/source/common/rbbisetb.cpp @@ -270,15 +270,15 @@ void RBBISetBuilder::buildTrie() { } -void RBBISetBuilder::mergeCategories(int32_t left, int32_t right) { - U_ASSERT(left >= 1); - U_ASSERT(right > left); +void RBBISetBuilder::mergeCategories(IntPair categories) { + U_ASSERT(categories.first >= 1); + U_ASSERT(categories.second > categories.first); for (RangeDescriptor *rd = fRangeList; rd != nullptr; rd = rd->fNext) { int32_t rangeNum = rd->fNum & ~DICT_BIT; int32_t rangeDict = rd->fNum & DICT_BIT; - if (rangeNum == right) { - rd->fNum = left | rangeDict; - } else if (rangeNum > right) { + if (rangeNum == categories.second) { + rd->fNum = categories.first | rangeDict; + } else if (rangeNum > categories.second) { rd->fNum--; } } diff --git a/deps/icu-small/source/common/rbbisetb.h b/deps/icu-small/source/common/rbbisetb.h index a7a91b3b375b75..ed6a76b1214621 100644 --- a/deps/icu-small/source/common/rbbisetb.h +++ b/deps/icu-small/source/common/rbbisetb.h @@ -94,10 +94,12 @@ class RBBISetBuilder : public UMemory { UChar32 getFirstChar(int32_t val) const; UBool sawBOF() const; // Indicate whether any references to the {bof} pseudo // character were encountered. - /** merge two character categories that have been identified as having equivalent behavior. - * The ranges belonging to the right category (table column) will be added to the left. + /** + * Merge two character categories that have been identified as having equivalent behavior. + * The ranges belonging to the second category (table column) will be added to the first. + * @param categories the pair of categories to be merged. */ - void mergeCategories(int32_t left, int32_t right); + void mergeCategories(IntPair categories); static constexpr int32_t DICT_BIT = 0x4000; diff --git a/deps/icu-small/source/common/rbbitblb.cpp b/deps/icu-small/source/common/rbbitblb.cpp index 61661a544249e2..42116b0f95575f 100644 --- a/deps/icu-small/source/common/rbbitblb.cpp +++ b/deps/icu-small/source/common/rbbitblb.cpp @@ -27,21 +27,19 @@ U_NAMESPACE_BEGIN -RBBITableBuilder::RBBITableBuilder(RBBIRuleBuilder *rb, RBBINode **rootNode) : - fTree(*rootNode) { - fRB = rb; - fStatus = fRB->fStatus; - UErrorCode status = U_ZERO_ERROR; - fDStates = new UVector(status); - if (U_FAILURE(*fStatus)) { - return; - } +RBBITableBuilder::RBBITableBuilder(RBBIRuleBuilder *rb, RBBINode **rootNode, UErrorCode &status) : + fRB(rb), + fTree(*rootNode), + fStatus(&status), + fDStates(nullptr), + fSafeTable(nullptr) { if (U_FAILURE(status)) { - *fStatus = status; return; } - if (fDStates == NULL) { - *fStatus = U_MEMORY_ALLOCATION_ERROR;; + // fDStates is UVector + fDStates = new UVector(status); + if (U_SUCCESS(status) && fDStates == nullptr ) { + status = U_MEMORY_ALLOCATION_ERROR; } } @@ -52,17 +50,18 @@ RBBITableBuilder::~RBBITableBuilder() { for (i=0; isize(); i++) { delete (RBBIStateDescriptor *)fDStates->elementAt(i); } - delete fDStates; + delete fDStates; + delete fSafeTable; } //----------------------------------------------------------------------------- // -// RBBITableBuilder::build - This is the main function for building the DFA state transtion -// table from the RBBI rules parse tree. +// RBBITableBuilder::buildForwardTable - This is the main function for building +// the DFA state transition table from the RBBI rules parse tree. // //----------------------------------------------------------------------------- -void RBBITableBuilder::build() { +void RBBITableBuilder::buildForwardTable() { if (U_FAILURE(*fStatus)) { return; @@ -189,8 +188,6 @@ void RBBITableBuilder::build() { // for all tables. Merge the ones from this table into the global set. // mergeRuleStatusVals(); - - if (fRB->fDebugEnv && uprv_strstr(fRB->fDebugEnv, "states")) {printStates();}; } @@ -1081,18 +1078,18 @@ void RBBITableBuilder::printPosSets(RBBINode *n) { // // findDuplCharClassFrom() // -bool RBBITableBuilder::findDuplCharClassFrom(int32_t &baseCategory, int32_t &duplCategory) { +bool RBBITableBuilder::findDuplCharClassFrom(IntPair *categories) { int32_t numStates = fDStates->size(); int32_t numCols = fRB->fSetBuilder->getNumCharCategories(); uint16_t table_base; uint16_t table_dupl; - for (; baseCategory < numCols-1; ++baseCategory) { - for (duplCategory=baseCategory+1; duplCategory < numCols; ++duplCategory) { + for (; categories->first < numCols-1; categories->first++) { + for (categories->second=categories->first+1; categories->second < numCols; categories->second++) { for (int32_t state=0; stateelementAt(state); - table_base = (uint16_t)sd->fDtran->elementAti(baseCategory); - table_dupl = (uint16_t)sd->fDtran->elementAti(duplCategory); + table_base = (uint16_t)sd->fDtran->elementAti(categories->first); + table_dupl = (uint16_t)sd->fDtran->elementAti(categories->second); if (table_base != table_dupl) { break; } @@ -1121,14 +1118,14 @@ void RBBITableBuilder::removeColumn(int32_t column) { /* * findDuplicateState */ -bool RBBITableBuilder::findDuplicateState(int32_t &firstState, int32_t &duplState) { +bool RBBITableBuilder::findDuplicateState(IntPair *states) { int32_t numStates = fDStates->size(); int32_t numCols = fRB->fSetBuilder->getNumCharCategories(); - for (; firstStateelementAt(firstState); - for (duplState=firstState+1; duplStateelementAt(duplState); + for (; states->firstfirst++) { + RBBIStateDescriptor *firstSD = (RBBIStateDescriptor *)fDStates->elementAt(states->first); + for (states->second=states->first+1; states->secondsecond++) { + RBBIStateDescriptor *duplSD = (RBBIStateDescriptor *)fDStates->elementAt(states->second); if (firstSD->fAccepting != duplSD->fAccepting || firstSD->fLookAhead != duplSD->fLookAhead || firstSD->fTagsIdx != duplSD->fTagsIdx) { @@ -1139,8 +1136,36 @@ bool RBBITableBuilder::findDuplicateState(int32_t &firstState, int32_t &duplStat int32_t firstVal = firstSD->fDtran->elementAti(col); int32_t duplVal = duplSD->fDtran->elementAti(col); if (!((firstVal == duplVal) || - ((firstVal == firstState || firstVal == duplState) && - (duplVal == firstState || duplVal == duplState)))) { + ((firstVal == states->first || firstVal == states->second) && + (duplVal == states->first || duplVal == states->second)))) { + rowsMatch = false; + break; + } + } + if (rowsMatch) { + return true; + } + } + } + return false; +} + + +bool RBBITableBuilder::findDuplicateSafeState(IntPair *states) { + int32_t numStates = fSafeTable->size(); + + for (; states->firstfirst++) { + UnicodeString *firstRow = static_cast(fSafeTable->elementAt(states->first)); + for (states->second=states->first+1; states->secondsecond++) { + UnicodeString *duplRow = static_cast(fSafeTable->elementAt(states->second)); + bool rowsMatch = true; + int32_t numCols = firstRow->length(); + for (int32_t col=0; col < numCols; ++col) { + int32_t firstVal = firstRow->charAt(col); + int32_t duplVal = duplRow->charAt(col); + if (!((firstVal == duplVal) || + ((firstVal == states->first || firstVal == states->second) && + (duplVal == states->first || duplVal == states->second)))) { rowsMatch = false; break; } @@ -1153,7 +1178,10 @@ bool RBBITableBuilder::findDuplicateState(int32_t &firstState, int32_t &duplStat return false; } -void RBBITableBuilder::removeState(int32_t keepState, int32_t duplState) { + +void RBBITableBuilder::removeState(IntPair duplStates) { + const int32_t keepState = duplStates.first; + const int32_t duplState = duplStates.second; U_ASSERT(keepState < duplState); U_ASSERT(duplState < fDStates->size()); @@ -1188,19 +1216,44 @@ void RBBITableBuilder::removeState(int32_t keepState, int32_t duplState) { } } +void RBBITableBuilder::removeSafeState(IntPair duplStates) { + const int32_t keepState = duplStates.first; + const int32_t duplState = duplStates.second; + U_ASSERT(keepState < duplState); + U_ASSERT(duplState < fSafeTable->size()); + + fSafeTable->removeElementAt(duplState); // Note that fSafeTable has a deleter function + // and will auto-delete the removed element. + int32_t numStates = fSafeTable->size(); + for (int32_t state=0; stateelementAt(state); + int32_t numCols = sd->length(); + for (int32_t col=0; colcharAt(col); + int32_t newVal = existingVal; + if (existingVal == duplState) { + newVal = keepState; + } else if (existingVal > duplState) { + newVal = existingVal - 1; + } + sd->setCharAt(col, newVal); + } + } +} + /* * RemoveDuplicateStates */ void RBBITableBuilder::removeDuplicateStates() { - int32_t firstState = 3; - int32_t duplicateState = 0; - while (findDuplicateState(firstState, duplicateState)) { - // printf("Removing duplicate states (%d, %d)\n", firstState, duplicateState); - removeState(firstState, duplicateState); + IntPair dupls = {3, 0}; + while (findDuplicateState(&dupls)) { + // printf("Removing duplicate states (%d, %d)\n", dupls.first, dupls.second); + removeState(dupls); } } + //----------------------------------------------------------------------------- // // getTableSize() Calculate the size of the runtime form of this @@ -1277,6 +1330,185 @@ void RBBITableBuilder::exportTable(void *where) { } +/** + * Synthesize a safe state table from the main state table. + */ +void RBBITableBuilder::buildSafeReverseTable(UErrorCode &status) { + // The safe table creation has three steps: + + // 1. Identifiy pairs of character classes that are "safe." Safe means that boundaries + // following the pair do not depend on context or state before the pair. To test + // whether a pair is safe, run it through the main forward state table, starting + // from each state. If the the final state is the same, no matter what the starting state, + // the pair is safe. + // + // 2. Build a state table that recognizes the safe pairs. It's similar to their + // forward table, with a column for each input character [class], and a row for + // each state. Row 1 is the start state, and row 0 is the stop state. Initially + // create an additional state for each input character category; being in + // one of these states means that the character has been seen, and is potentially + // the first of a pair. In each of these rows, the entry for the second character + // of a safe pair is set to the stop state (0), indicating that a match was found. + // All other table entries are set to the state corresponding the current input + // character, allowing that charcter to be the of a start following pair. + // + // Because the safe rules are to be run in reverse, moving backwards in the text, + // the first and second pair categories are swapped when building the table. + // + // 3. Compress the table. There are typically many rows (states) that are + // equivalent - that have zeroes (match completed) in the same columns - + // and can be folded together. + + // Each safe pair is stored as two UChars in the safePair string. + UnicodeString safePairs; + + int32_t numCharClasses = fRB->fSetBuilder->getNumCharCategories(); + int32_t numStates = fDStates->size(); + + for (int32_t c1=0; c1(fDStates->elementAt(startState)); + int32_t s2 = startStateD->fDtran->elementAti(c1); + RBBIStateDescriptor *s2StateD = static_cast(fDStates->elementAt(s2)); + endState = s2StateD->fDtran->elementAti(c2); + if (wantedEndState < 0) { + wantedEndState = endState; + } else { + if (wantedEndState != endState) { + break; + } + } + } + if (wantedEndState == endState) { + safePairs.append((char16_t)c1); + safePairs.append((char16_t)c2); + // printf("(%d, %d) ", c1, c2); + } + } + // printf("\n"); + } + + // Populate the initial safe table. + // The table as a whole is UVector + // Each row is represented by a UnicodeString, being used as a Vector. + // Row 0 is the stop state. + // Row 1 is the start sate. + // Row 2 and beyond are other states, initially one per char class, but + // after initial construction, many of the states will be combined, compacting the table. + // The String holds the nextState data only. The four leading fields of a row, fAccepting, + // fLookAhead, etc. are not needed for the safe table, and are omitted at this stage of building. + + U_ASSERT(fSafeTable == nullptr); + fSafeTable = new UVector(uprv_deleteUObject, uhash_compareUnicodeString, numCharClasses + 2, status); + for (int32_t row=0; rowaddElement(new UnicodeString(numCharClasses, 0, numCharClasses+4), status); + } + + // From the start state, each input char class transitions to the state for that input. + UnicodeString &startState = *static_cast(fSafeTable->elementAt(1)); + for (int32_t charClass=0; charClass < numCharClasses; ++charClass) { + // Note: +2 for the start & stop state. + startState.setCharAt(charClass, charClass+2); + } + + // Initially make every other state table row look like the start state row, + for (int32_t row=2; row(fSafeTable->elementAt(row)); + rowState = startState; // UnicodeString assignment, copies contents. + } + + // Run through the safe pairs, set the next state to zero when pair has been seen. + // Zero being the stop state, meaning we found a safe point. + for (int32_t pairIdx=0; pairIdx(fSafeTable->elementAt(c2 + 2)); + rowState.setCharAt(c1, 0); + } + + // Remove duplicate or redundant rows from the table. + IntPair states = {1, 0}; + while (findDuplicateSafeState(&states)) { + // printf("Removing duplicate safe states (%d, %d)\n", states.first, states.second); + removeSafeState(states); + } +} + + +//----------------------------------------------------------------------------- +// +// getSafeTableSize() Calculate the size of the runtime form of this +// safe state table. +// +//----------------------------------------------------------------------------- +int32_t RBBITableBuilder::getSafeTableSize() const { + int32_t size = 0; + int32_t numRows; + int32_t numCols; + int32_t rowSize; + + if (fSafeTable == nullptr) { + return 0; + } + + size = offsetof(RBBIStateTable, fTableData); // The header, with no rows to the table. + + numRows = fSafeTable->size(); + numCols = fRB->fSetBuilder->getNumCharCategories(); + + rowSize = offsetof(RBBIStateTableRow, fNextState) + sizeof(uint16_t)*numCols; + size += numRows * rowSize; + return size; +} + + +//----------------------------------------------------------------------------- +// +// exportSafeTable() export the state transition table in the format required +// by the runtime engine. getTableSize() bytes of memory +// must be available at the output address "where". +// +//----------------------------------------------------------------------------- +void RBBITableBuilder::exportSafeTable(void *where) { + RBBIStateTable *table = (RBBIStateTable *)where; + uint32_t state; + int col; + + if (U_FAILURE(*fStatus) || fSafeTable == nullptr) { + return; + } + + int32_t catCount = fRB->fSetBuilder->getNumCharCategories(); + if (catCount > 0x7fff || + fSafeTable->size() > 0x7fff) { + *fStatus = U_BRK_INTERNAL_ERROR; + return; + } + + table->fRowLen = offsetof(RBBIStateTableRow, fNextState) + sizeof(uint16_t) * catCount; + table->fNumStates = fSafeTable->size(); + table->fFlags = 0; + table->fReserved = 0; + + for (state=0; statefNumStates; state++) { + UnicodeString *rowString = (UnicodeString *)fSafeTable->elementAt(state); + RBBIStateTableRow *row = (RBBIStateTableRow *)(table->fTableData + state*table->fRowLen); + row->fAccepting = 0; + row->fLookAhead = 0; + row->fTagIdx = 0; + row->fReserved = 0; + for (col=0; colfNextState[col] = rowString->charAt(col); + } + } +} + + + //----------------------------------------------------------------------------- // @@ -1331,6 +1563,47 @@ void RBBITableBuilder::printStates() { #endif +//----------------------------------------------------------------------------- +// +// printSafeTable Debug Function. Dump the fully constructed safe table. +// +//----------------------------------------------------------------------------- +#ifdef RBBI_DEBUG +void RBBITableBuilder::printReverseTable() { + int c; // input "character" + int n; // state number + + RBBIDebugPrintf(" Safe Reverse Table \n"); + if (fSafeTable == nullptr) { + RBBIDebugPrintf(" --- nullptr ---\n"); + return; + } + RBBIDebugPrintf("state | i n p u t s y m b o l s \n"); + RBBIDebugPrintf(" | Acc LA Tag"); + for (c=0; cfSetBuilder->getNumCharCategories(); c++) { + RBBIDebugPrintf(" %2d", c); + } + RBBIDebugPrintf("\n"); + RBBIDebugPrintf(" |---------------"); + for (c=0; cfSetBuilder->getNumCharCategories(); c++) { + RBBIDebugPrintf("---"); + } + RBBIDebugPrintf("\n"); + + for (n=0; nsize(); n++) { + UnicodeString *rowString = (UnicodeString *)fSafeTable->elementAt(n); + RBBIDebugPrintf(" %3d | " , n); + RBBIDebugPrintf("%3d %3d %5d ", 0, 0, 0); // Accepting, LookAhead, Tags + for (c=0; cfSetBuilder->getNumCharCategories(); c++) { + RBBIDebugPrintf(" %2d", rowString->charAt(c)); + } + RBBIDebugPrintf("\n"); + } + RBBIDebugPrintf("\n\n"); +} +#endif + + //----------------------------------------------------------------------------- // diff --git a/deps/icu-small/source/common/rbbitblb.h b/deps/icu-small/source/common/rbbitblb.h index 09b57b5cf0f4c3..eea243e4cdd6c3 100644 --- a/deps/icu-small/source/common/rbbitblb.h +++ b/deps/icu-small/source/common/rbbitblb.h @@ -17,6 +17,7 @@ #include "unicode/utypes.h" #include "unicode/uobject.h" #include "unicode/rbbi.h" +#include "rbbirb.h" #include "rbbinode.h" @@ -37,22 +38,28 @@ class UVector32; class RBBITableBuilder : public UMemory { public: - RBBITableBuilder(RBBIRuleBuilder *rb, RBBINode **rootNode); + RBBITableBuilder(RBBIRuleBuilder *rb, RBBINode **rootNode, UErrorCode &status); ~RBBITableBuilder(); - void build(); - int32_t getTableSize() const; // Return the runtime size in bytes of - // the built state table + void buildForwardTable(); + + /** Return the runtime size in bytes of the built state table. */ + int32_t getTableSize() const; /** Fill in the runtime state table. Sufficient memory must exist at the specified location. */ void exportTable(void *where); - /** Find duplicate (redundant) character classes, beginning after the specifed - * pair, within this state table. This is an iterator-like function, used to - * identify char classes (state table columns) that can be eliminated. + /** + * Find duplicate (redundant) character classes. Begin looking with categories.first. + * Duplicate, if found are returned in the categories parameter. + * This is an iterator-like function, used to identify character classes + * (state table columns) that can be eliminated. + * @param categories in/out parameter, specifies where to start looking for duplicates, + * and returns the first pair of duplicates found, if any. + * @return true if duplicate char classes were found, false otherwise. */ - bool findDuplCharClassFrom(int &baseClass, int &duplClass); + bool findDuplCharClassFrom(IntPair *categories); /** Remove a column from the state table. Used when two character categories * have been found equivalent, and merged together, to eliminate the uneeded table column. @@ -62,6 +69,16 @@ class RBBITableBuilder : public UMemory { /** Check for, and remove dupicate states (table rows). */ void removeDuplicateStates(); + /** Build the safe reverse table from the already-constructed forward table. */ + void buildSafeReverseTable(UErrorCode &status); + + /** Return the runtime size in bytes of the built safe reverse state table. */ + int32_t getSafeTableSize() const; + + /** Fill in the runtime safe state table. Sufficient memory must exist at the specified location. + */ + void exportSafeTable(void *where); + private: void calcNullable(RBBINode *n); @@ -84,20 +101,36 @@ class RBBITableBuilder : public UMemory { void addRuleRootNodes(UVector *dest, RBBINode *node); - /** Find the next duplicate state. An iterator function. - * @param firstState (in/out) begin looking at this state, return the first of the - * pair of duplicates. - * @param duplicateState returns the duplicate state of fistState - * @return true if a duplicate pair of states was found. + /** + * Find duplicate (redundant) states, beginning at the specified pair, + * within this state table. This is an iterator-like function, used to + * identify states (state table rows) that can be eliminated. + * @param states in/out parameter, specifies where to start looking for duplicates, + * and returns the first pair of duplicates found, if any. + * @return true if duplicate states were found, false otherwise. + */ + bool findDuplicateState(IntPair *states); + + /** Remove a duplicate state. + * @param duplStates The duplicate states. The first is kept, the second is removed. + * All references to the second in the state table are retargeted + * to the first. */ - bool findDuplicateState(int32_t &firstState, int32_t &duplicateState); + void removeState(IntPair duplStates); - /** Remove a duplicate state/ - * @param keepState First of the duplicate pair. Keep it. - * @param duplState Duplicate state. Remove it. Redirect all references to the duplicate state - * to refer to keepState instead. + /** Find the next duplicate state in the safe reverse table. An iterator function. + * @param states in/out parameter, specifies where to start looking for duplicates, + * and returns the first pair of duplicates found, if any. + * @return true if a duplicate pair of states was found. */ - void removeState(int32_t keepState, int32_t duplState); + bool findDuplicateSafeState(IntPair *states); + + /** Remove a duplicate state from the safe table. + * @param duplStates The duplicate states. The first is kept, the second is removed. + * All references to the second in the state table are retargeted + * to the first. + */ + void removeSafeState(IntPair duplStates); // Set functions for UVector. // TODO: make a USet subclass of UVector @@ -113,11 +146,13 @@ class RBBITableBuilder : public UMemory { void printPosSets(RBBINode *n /* = NULL*/); void printStates(); void printRuleStatusTable(); + void printReverseTable(); #else #define printSet(s) #define printPosSets(n) #define printStates() #define printRuleStatusTable() + #define printReverseTable() #endif private: @@ -126,10 +161,14 @@ class RBBITableBuilder : public UMemory { // table for. UErrorCode *fStatus; + /** State Descriptors, UVector */ UVector *fDStates; // D states (Aho's terminology) // Index is state number // Contents are RBBIStateDescriptor pointers. + /** Synthesized safe table, UVector of UnicodeString, one string per table row. */ + UVector *fSafeTable; + RBBITableBuilder(const RBBITableBuilder &other); // forbid copying of this class RBBITableBuilder &operator=(const RBBITableBuilder &other); // forbid copying of this class diff --git a/deps/icu-small/source/common/serv.cpp b/deps/icu-small/source/common/serv.cpp index 8913b21e69457d..35e362b71a8fc2 100644 --- a/deps/icu-small/source/common/serv.cpp +++ b/deps/icu-small/source/common/serv.cpp @@ -547,16 +547,15 @@ ICUService::getKey(ICUServiceKey& key, UnicodeString* actualReturn, const ICUSer if (putInCache && cacheResult) { serviceCache->put(result->actualDescriptor, result, status); if (U_FAILURE(status)) { - delete result; return NULL; } if (cacheDescriptorList._obj != NULL) { for (int32_t i = cacheDescriptorList._obj->size(); --i >= 0;) { UnicodeString* desc = (UnicodeString*)cacheDescriptorList._obj->elementAt(i); + serviceCache->put(*desc, result, status); if (U_FAILURE(status)) { - delete result; return NULL; } diff --git a/deps/icu-small/source/common/static_unicode_sets.cpp b/deps/icu-small/source/common/static_unicode_sets.cpp new file mode 100644 index 00000000000000..9e731f5781e215 --- /dev/null +++ b/deps/icu-small/source/common/static_unicode_sets.cpp @@ -0,0 +1,222 @@ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +// Allow implicit conversion from char16_t* to UnicodeString for this file: +// Helpful in toString methods and elsewhere. +#define UNISTR_FROM_STRING_EXPLICIT + +#include "static_unicode_sets.h" +#include "umutex.h" +#include "ucln_cmn.h" +#include "unicode/uniset.h" +#include "uresimp.h" +#include "cstring.h" +#include "uassert.h" + +using namespace icu; +using namespace icu::unisets; + + +namespace { + +UnicodeSet* gUnicodeSets[COUNT] = {}; + +// Save the empty instance in static memory to have well-defined behavior if a +// regular UnicodeSet cannot be allocated. +char gEmptyUnicodeSet[sizeof(UnicodeSet)]; + +// Whether the gEmptyUnicodeSet is initialized and ready to use. +UBool gEmptyUnicodeSetInitialized = FALSE; + +inline UnicodeSet* getImpl(Key key) { + UnicodeSet* candidate = gUnicodeSets[key]; + if (candidate == nullptr) { + return reinterpret_cast(gEmptyUnicodeSet); + } + return candidate; +} + +UnicodeSet* computeUnion(Key k1, Key k2) { + UnicodeSet* result = new UnicodeSet(); + if (result == nullptr) { + return nullptr; + } + result->addAll(*getImpl(k1)); + result->addAll(*getImpl(k2)); + result->freeze(); + return result; +} + +UnicodeSet* computeUnion(Key k1, Key k2, Key k3) { + UnicodeSet* result = new UnicodeSet(); + if (result == nullptr) { + return nullptr; + } + result->addAll(*getImpl(k1)); + result->addAll(*getImpl(k2)); + result->addAll(*getImpl(k3)); + result->freeze(); + return result; +} + + +void saveSet(Key key, const UnicodeString& unicodeSetPattern, UErrorCode& status) { + // assert unicodeSets.get(key) == null; + gUnicodeSets[key] = new UnicodeSet(unicodeSetPattern, status); +} + +class ParseDataSink : public ResourceSink { + public: + void put(const char* key, ResourceValue& value, UBool /*noFallback*/, UErrorCode& status) U_OVERRIDE { + ResourceTable contextsTable = value.getTable(status); + if (U_FAILURE(status)) { return; } + for (int i = 0; contextsTable.getKeyAndValue(i, key, value); i++) { + if (uprv_strcmp(key, "date") == 0) { + // ignore + } else { + ResourceTable strictnessTable = value.getTable(status); + if (U_FAILURE(status)) { return; } + for (int j = 0; strictnessTable.getKeyAndValue(j, key, value); j++) { + bool isLenient = (uprv_strcmp(key, "lenient") == 0); + ResourceArray array = value.getArray(status); + if (U_FAILURE(status)) { return; } + for (int k = 0; k < array.getSize(); k++) { + array.getValue(k, value); + UnicodeString str = value.getUnicodeString(status); + if (U_FAILURE(status)) { return; } + // There is both lenient and strict data for comma/period, + // but not for any of the other symbols. + if (str.indexOf(u'.') != -1) { + saveSet(isLenient ? PERIOD : STRICT_PERIOD, str, status); + } else if (str.indexOf(u',') != -1) { + saveSet(isLenient ? COMMA : STRICT_COMMA, str, status); + } else if (str.indexOf(u'+') != -1) { + saveSet(PLUS_SIGN, str, status); + } else if (str.indexOf(u'‒') != -1) { + saveSet(MINUS_SIGN, str, status); + } else if (str.indexOf(u'$') != -1) { + saveSet(DOLLAR_SIGN, str, status); + } else if (str.indexOf(u'£') != -1) { + saveSet(POUND_SIGN, str, status); + } else if (str.indexOf(u'₨') != -1) { + saveSet(RUPEE_SIGN, str, status); + } + if (U_FAILURE(status)) { return; } + } + } + } + } + } +}; + + +icu::UInitOnce gNumberParseUniSetsInitOnce = U_INITONCE_INITIALIZER; + +UBool U_CALLCONV cleanupNumberParseUniSets() { + if (gEmptyUnicodeSetInitialized) { + reinterpret_cast(gEmptyUnicodeSet)->~UnicodeSet(); + gEmptyUnicodeSetInitialized = FALSE; + } + for (int32_t i = 0; i < COUNT; i++) { + delete gUnicodeSets[i]; + gUnicodeSets[i] = nullptr; + } + gNumberParseUniSetsInitOnce.reset(); + return TRUE; +} + +void U_CALLCONV initNumberParseUniSets(UErrorCode& status) { + ucln_common_registerCleanup(UCLN_COMMON_NUMPARSE_UNISETS, cleanupNumberParseUniSets); + + // Initialize the empty instance for well-defined fallback behavior + new(gEmptyUnicodeSet) UnicodeSet(); + reinterpret_cast(gEmptyUnicodeSet)->freeze(); + gEmptyUnicodeSetInitialized = TRUE; + + // These sets were decided after discussion with icu-design@. See tickets #13084 and #13309. + // Zs+TAB is "horizontal whitespace" according to UTS #18 (blank property). + gUnicodeSets[DEFAULT_IGNORABLES] = new UnicodeSet( + u"[[:Zs:][\\u0009][:Bidi_Control:][:Variation_Selector:]]", status); + gUnicodeSets[STRICT_IGNORABLES] = new UnicodeSet(u"[[:Bidi_Control:]]", status); + + LocalUResourceBundlePointer rb(ures_open(nullptr, "root", &status)); + if (U_FAILURE(status)) { return; } + ParseDataSink sink; + ures_getAllItemsWithFallback(rb.getAlias(), "parse", sink, status); + if (U_FAILURE(status)) { return; } + + // NOTE: It is OK for these assertions to fail if there was a no-data build. + U_ASSERT(gUnicodeSets[COMMA] != nullptr); + U_ASSERT(gUnicodeSets[STRICT_COMMA] != nullptr); + U_ASSERT(gUnicodeSets[PERIOD] != nullptr); + U_ASSERT(gUnicodeSets[STRICT_PERIOD] != nullptr); + + gUnicodeSets[OTHER_GROUPING_SEPARATORS] = new UnicodeSet( + u"['٬‘’'\\u0020\\u00A0\\u2000-\\u200A\\u202F\\u205F\\u3000]", status); + gUnicodeSets[ALL_SEPARATORS] = computeUnion(COMMA, PERIOD, OTHER_GROUPING_SEPARATORS); + gUnicodeSets[STRICT_ALL_SEPARATORS] = computeUnion( + STRICT_COMMA, STRICT_PERIOD, OTHER_GROUPING_SEPARATORS); + + U_ASSERT(gUnicodeSets[MINUS_SIGN] != nullptr); + U_ASSERT(gUnicodeSets[PLUS_SIGN] != nullptr); + + gUnicodeSets[PERCENT_SIGN] = new UnicodeSet(u"[%٪]", status); + gUnicodeSets[PERMILLE_SIGN] = new UnicodeSet(u"[‰؉]", status); + gUnicodeSets[INFINITY_KEY] = new UnicodeSet(u"[∞]", status); + + U_ASSERT(gUnicodeSets[DOLLAR_SIGN] != nullptr); + U_ASSERT(gUnicodeSets[POUND_SIGN] != nullptr); + U_ASSERT(gUnicodeSets[RUPEE_SIGN] != nullptr); + gUnicodeSets[YEN_SIGN] = new UnicodeSet(u"[¥\\uffe5]", status); + + gUnicodeSets[DIGITS] = new UnicodeSet(u"[:digit:]", status); + + gUnicodeSets[DIGITS_OR_ALL_SEPARATORS] = computeUnion(DIGITS, ALL_SEPARATORS); + gUnicodeSets[DIGITS_OR_STRICT_ALL_SEPARATORS] = computeUnion(DIGITS, STRICT_ALL_SEPARATORS); + + for (auto* uniset : gUnicodeSets) { + if (uniset != nullptr) { + uniset->freeze(); + } + } +} + +} + +const UnicodeSet* unisets::get(Key key) { + UErrorCode localStatus = U_ZERO_ERROR; + umtx_initOnce(gNumberParseUniSetsInitOnce, &initNumberParseUniSets, localStatus); + if (U_FAILURE(localStatus)) { + return reinterpret_cast(gEmptyUnicodeSet); + } + return getImpl(key); +} + +Key unisets::chooseFrom(UnicodeString str, Key key1) { + return get(key1)->contains(str) ? key1 : NONE; +} + +Key unisets::chooseFrom(UnicodeString str, Key key1, Key key2) { + return get(key1)->contains(str) ? key1 : chooseFrom(str, key2); +} + +//Key unisets::chooseCurrency(UnicodeString str) { +// if (get(DOLLAR_SIGN)->contains(str)) { +// return DOLLAR_SIGN; +// } else if (get(POUND_SIGN)->contains(str)) { +// return POUND_SIGN; +// } else if (get(RUPEE_SIGN)->contains(str)) { +// return RUPEE_SIGN; +// } else if (get(YEN_SIGN)->contains(str)) { +// return YEN_SIGN; +// } else { +// return NONE; +// } +//} + + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/common/static_unicode_sets.h b/deps/icu-small/source/common/static_unicode_sets.h new file mode 100644 index 00000000000000..5f18b3217eae2b --- /dev/null +++ b/deps/icu-small/source/common/static_unicode_sets.h @@ -0,0 +1,119 @@ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +// This file is in common instead of i18n because it is needed by ucurr.cpp. + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING +#ifndef __STATIC_UNICODE_SETS_H__ +#define __STATIC_UNICODE_SETS_H__ + +#include "unicode/uniset.h" +#include "unicode/unistr.h" + +U_NAMESPACE_BEGIN +namespace unisets { + +enum Key { + // NONE is used to indicate null in chooseFrom(). + // EMPTY is used to get an empty UnicodeSet. + NONE = -1, + EMPTY = 0, + + // Ignorables + DEFAULT_IGNORABLES, + STRICT_IGNORABLES, + + // Separators + // Notes: + // - COMMA is a superset of STRICT_COMMA + // - PERIOD is a superset of SCRICT_PERIOD + // - ALL_SEPARATORS is the union of COMMA, PERIOD, and OTHER_GROUPING_SEPARATORS + // - STRICT_ALL_SEPARATORS is the union of STRICT_COMMA, STRICT_PERIOD, and OTHER_GRP_SEPARATORS + COMMA, + PERIOD, + STRICT_COMMA, + STRICT_PERIOD, + OTHER_GROUPING_SEPARATORS, + ALL_SEPARATORS, + STRICT_ALL_SEPARATORS, + + // Symbols + MINUS_SIGN, + PLUS_SIGN, + PERCENT_SIGN, + PERMILLE_SIGN, + INFINITY_KEY, // INFINITY is defined in cmath + + // Currency Symbols + DOLLAR_SIGN, + POUND_SIGN, + RUPEE_SIGN, + YEN_SIGN, // not in CLDR data, but Currency.java wants it + + // Other + DIGITS, + + // Combined Separators with Digits (for lead code points) + DIGITS_OR_ALL_SEPARATORS, + DIGITS_OR_STRICT_ALL_SEPARATORS, + + // The number of elements in the enum. + COUNT +}; + +/** + * Gets the static-allocated UnicodeSet according to the provided key. The + * pointer will be deleted during u_cleanup(); the caller should NOT delete it. + * + * Exported as U_COMMON_API for ucurr.cpp + * + * @param key The desired UnicodeSet according to the enum in this file. + * @return The requested UnicodeSet. Guaranteed to be frozen and non-null, but + * may be empty if an error occurred during data loading. + */ +U_COMMON_API const UnicodeSet* get(Key key); + +/** + * Checks if the UnicodeSet given by key1 contains the given string. + * + * Exported as U_COMMON_API for numparse_decimal.cpp + * + * @param str The string to check. + * @param key1 The set to check. + * @return key1 if the set contains str, or NONE if not. + */ +U_COMMON_API Key chooseFrom(UnicodeString str, Key key1); + +/** + * Checks if the UnicodeSet given by either key1 or key2 contains the string. + * + * Exported as U_COMMON_API for numparse_decimal.cpp + * + * @param str The string to check. + * @param key1 The first set to check. + * @param key2 The second set to check. + * @return key1 if that set contains str; key2 if that set contains str; or + * NONE if neither set contains str. + */ +U_COMMON_API Key chooseFrom(UnicodeString str, Key key1, Key key2); + +// Unused in C++: +// Key chooseCurrency(UnicodeString str); +// Used instead: +static const struct { + Key key; + UChar32 exemplar; +} kCurrencyEntries[] = { + {DOLLAR_SIGN, u'$'}, + {POUND_SIGN, u'£'}, + {RUPEE_SIGN, u'₨'}, + {YEN_SIGN, u'¥'}, +}; + +} // namespace unisets +U_NAMESPACE_END + +#endif //__STATIC_UNICODE_SETS_H__ +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/common/ubidi_props_data.h b/deps/icu-small/source/common/ubidi_props_data.h index 98f21510e7d42c..55e331b612efcb 100644 --- a/deps/icu-small/source/common/ubidi_props_data.h +++ b/deps/icu-small/source/common/ubidi_props_data.h @@ -11,538 +11,539 @@ #ifdef INCLUDED_FROM_UBIDI_PROPS_C -static const UVersionInfo ubidi_props_dataVersion={0xa,0,0,0}; +static const UVersionInfo ubidi_props_dataVersion={0xb,0,0,0}; -static const int32_t ubidi_props_indexes[UBIDI_IX_TOP]={0x10,0x6028,0x5cb0,0x1a,0x620,0x8c0,0x10ac0,0x10af0,0,0,0,0,0,0,0,0x6302b6}; +static const int32_t ubidi_props_indexes[UBIDI_IX_TOP]={0x10,0x647c,0x5e98,0x28,0x620,0x8c0,0x10ac0,0x10d24,0,0,0,0,0,0,0,0x6502b6}; -static const uint16_t ubidi_props_trieIndex[11856]={ -0x36a,0x372,0x37a,0x382,0x39a,0x3a2,0x3aa,0x3b2,0x38a,0x392,0x38a,0x392,0x38a,0x392,0x38a,0x392, -0x38a,0x392,0x38a,0x392,0x3b8,0x3c0,0x3c8,0x3d0,0x3d8,0x3e0,0x3dc,0x3e4,0x3ec,0x3f4,0x3ef,0x3f7, -0x38a,0x392,0x38a,0x392,0x3ff,0x407,0x38a,0x392,0x38a,0x392,0x38a,0x392,0x40d,0x415,0x41d,0x425, -0x42d,0x435,0x43d,0x445,0x44b,0x453,0x45b,0x463,0x46b,0x473,0x479,0x481,0x489,0x491,0x499,0x4a1, -0x4ad,0x4a9,0x4b5,0x4bd,0x41f,0x4cd,0x4d5,0x4c5,0x4dd,0x4df,0x4e7,0x4ef,0x4f7,0x4f8,0x500,0x508, -0x510,0x4f8,0x518,0x51d,0x510,0x4f8,0x525,0x52d,0x4f7,0x535,0x53d,0x4ef,0x542,0x38a,0x54a,0x54e, -0x556,0x557,0x55f,0x567,0x4f7,0x56f,0x577,0x4ef,0x401,0x57b,0x500,0x4ef,0x38a,0x38a,0x583,0x38a, -0x38a,0x589,0x591,0x38a,0x38a,0x595,0x59d,0x38a,0x5a1,0x5a8,0x38a,0x5b0,0x5b8,0x5bf,0x541,0x38a, -0x38a,0x5c7,0x5cf,0x5d7,0x5df,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x5e7,0x38a,0x5ef,0x38a,0x38a,0x38a, -0x5f7,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x5ff,0x38a,0x38a,0x38a,0x607,0x607,0x504,0x504,0x38a,0x60d,0x615,0x5ef, -0x62b,0x61d,0x61d,0x633,0x63a,0x623,0x38a,0x38a,0x38a,0x642,0x64a,0x38a,0x38a,0x38a,0x64c,0x654, -0x65c,0x38a,0x663,0x66b,0x38a,0x673,0x38a,0x38a,0x534,0x67b,0x542,0x683,0x401,0x68b,0x38a,0x692, -0x38a,0x697,0x38a,0x38a,0x38a,0x38a,0x69d,0x6a5,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x3d8,0x6ad, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x6b5,0x6bd,0x6c1, -0x6d9,0x6df,0x6c9,0x6d1,0x6e7,0x6ef,0x6f3,0x5c2,0x6fb,0x703,0x70b,0x38a,0x713,0x654,0x654,0x654, -0x723,0x72b,0x733,0x73b,0x740,0x748,0x750,0x71b,0x758,0x760,0x38a,0x766,0x76d,0x654,0x654,0x654, -0x654,0x56d,0x773,0x654,0x77b,0x38a,0x38a,0x651,0x654,0x654,0x654,0x654,0x654,0x654,0x654,0x654, -0x654,0x654,0x654,0x654,0x654,0x783,0x654,0x654,0x654,0x654,0x654,0x789,0x654,0x654,0x791,0x799, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x654,0x654,0x654,0x654,0x7a9,0x7b0,0x7b8,0x7a1, -0x7c8,0x7d0,0x7d8,0x7df,0x7e7,0x7ef,0x7f6,0x7c0,0x654,0x654,0x654,0x7fe,0x804,0x80a,0x812,0x817, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x81e,0x38a,0x38a,0x38a,0x826,0x38a,0x38a,0x38a,0x3d8, -0x82e,0x836,0x763,0x38a,0x839,0x654,0x654,0x657,0x654,0x654,0x654,0x654,0x654,0x654,0x840,0x846, -0x856,0x84e,0x38a,0x38a,0x85e,0x5f7,0x38a,0x3b1,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x654,0x825, -0x3bf,0x38a,0x866,0x86e,0x38a,0x876,0x817,0x38a,0x38a,0x38a,0x38a,0x87e,0x38a,0x38a,0x64c,0x3b0, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x654,0x654, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x866,0x654,0x56d,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x885,0x38a,0x38a,0x88a,0x557,0x38a,0x38a,0x5a3,0x654,0x64b,0x38a,0x38a,0x892,0x38a,0x38a,0x38a, -0x89a,0x8a1,0x61d,0x8a9,0x38a,0x38a,0x579,0x8b1,0x38a,0x8b8,0x8bf,0x38a,0x4dd,0x8c4,0x38a,0x4f6, -0x38a,0x8cc,0x8d4,0x4f8,0x38a,0x8d8,0x4f7,0x8e0,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x8e7, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x8fb,0x8ef,0x8f3,0x489,0x489,0x489,0x489,0x489, -0x489,0x489,0x489,0x489,0x489,0x489,0x489,0x489,0x489,0x903,0x489,0x489,0x489,0x489,0x90b,0x90f, -0x917,0x91f,0x923,0x92b,0x489,0x489,0x489,0x92f,0x937,0x37a,0x93f,0x947,0x38a,0x38a,0x38a,0x94f, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0xe28,0xe28,0xe68,0xea8,0xe28,0xe28,0xe28,0xe28,0xe28,0xe28,0xee0,0xf20,0xf60,0xf70,0xfb0,0xfbc, -0xe28,0xe28,0xffc,0xe28,0xe28,0xe28,0x1034,0x1074,0x10b4,0x10f4,0x112c,0x116c,0x11ac,0x11e4,0x1224,0x1264, -0xa40,0xa80,0xac0,0xafa,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0xb25,0x1a0,0x1a0, -0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0xb62,0x1a0,0x1a0,0xb97,0xbd7,0xc17,0xc57,0xc97,0xcd7, +static const uint16_t ubidi_props_trieIndex[12100]={ +0x36f,0x377,0x37f,0x387,0x39f,0x3a7,0x3af,0x3b7,0x38f,0x397,0x38f,0x397,0x38f,0x397,0x38f,0x397, +0x38f,0x397,0x38f,0x397,0x3bd,0x3c5,0x3cd,0x3d5,0x3dd,0x3e5,0x3e1,0x3e9,0x3f1,0x3f9,0x3f4,0x3fc, +0x38f,0x397,0x38f,0x397,0x404,0x40c,0x38f,0x397,0x38f,0x397,0x38f,0x397,0x412,0x41a,0x422,0x42a, +0x432,0x43a,0x442,0x44a,0x450,0x458,0x460,0x468,0x470,0x478,0x47e,0x486,0x48e,0x496,0x49e,0x4a6, +0x4b2,0x4ae,0x4ba,0x4c2,0x424,0x4d2,0x4da,0x4ca,0x4e2,0x4e4,0x4ec,0x4f4,0x4fc,0x4fd,0x505,0x50d, +0x515,0x4fd,0x51d,0x522,0x515,0x4fd,0x52a,0x532,0x4fc,0x53a,0x542,0x4f4,0x547,0x38f,0x54f,0x553, +0x55b,0x55d,0x565,0x56d,0x4fc,0x575,0x57d,0x4f4,0x406,0x581,0x505,0x4f4,0x38f,0x38f,0x589,0x38f, +0x38f,0x58f,0x597,0x38f,0x38f,0x59b,0x5a3,0x38f,0x5a7,0x5ae,0x38f,0x5b6,0x5be,0x5c5,0x546,0x38f, +0x38f,0x5cd,0x5d5,0x5dd,0x5e5,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x5ed,0x38f,0x5f5,0x38f,0x38f,0x38f, +0x5fd,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x605,0x38f,0x38f,0x38f,0x60d,0x60d,0x509,0x509,0x38f,0x613,0x61b,0x5f5, +0x631,0x623,0x623,0x639,0x640,0x629,0x38f,0x38f,0x38f,0x648,0x650,0x38f,0x38f,0x38f,0x652,0x65a, +0x662,0x38f,0x669,0x671,0x38f,0x679,0x38f,0x38f,0x539,0x681,0x547,0x689,0x406,0x691,0x38f,0x698, +0x38f,0x69d,0x38f,0x38f,0x38f,0x38f,0x6a3,0x6ab,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x3dd,0x6b3, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x6bb,0x6c3,0x6c7, +0x6df,0x6e5,0x6cf,0x6d7,0x6ed,0x6f5,0x6f9,0x5c8,0x701,0x709,0x711,0x38f,0x719,0x65a,0x65a,0x65a, +0x729,0x731,0x739,0x741,0x746,0x74e,0x756,0x721,0x75e,0x766,0x38f,0x76c,0x773,0x65a,0x65a,0x65a, +0x65a,0x573,0x779,0x65a,0x781,0x38f,0x38f,0x657,0x65a,0x65a,0x65a,0x65a,0x65a,0x65a,0x65a,0x65a, +0x65a,0x65a,0x65a,0x65a,0x65a,0x789,0x65a,0x65a,0x65a,0x65a,0x65a,0x78f,0x65a,0x65a,0x797,0x79f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x65a,0x65a,0x65a,0x65a,0x7af,0x7b7,0x7bf,0x7a7, +0x7cf,0x7d7,0x7df,0x7e6,0x7ed,0x7f5,0x7f9,0x7c7,0x65a,0x65a,0x65a,0x801,0x807,0x65a,0x80d,0x810, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x818,0x38f,0x38f,0x38f,0x820,0x38f,0x38f,0x38f,0x3dd, +0x828,0x830,0x835,0x38f,0x83d,0x65a,0x65a,0x65d,0x65a,0x65a,0x65a,0x65a,0x65a,0x65a,0x844,0x84a, +0x85a,0x852,0x38f,0x38f,0x862,0x5fd,0x38f,0x3b6,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x65a,0x81f, +0x3c4,0x38f,0x839,0x86a,0x38f,0x872,0x87a,0x38f,0x38f,0x38f,0x38f,0x87e,0x38f,0x38f,0x652,0x3b5, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x65a,0x65a, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x839,0x65a,0x573,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x885,0x38f,0x38f,0x88a,0x55d,0x38f,0x38f,0x5a9,0x65a,0x651,0x38f,0x38f,0x892,0x38f,0x38f,0x38f, +0x89a,0x8a1,0x623,0x8a9,0x38f,0x38f,0x57f,0x8b1,0x38f,0x8b9,0x8c0,0x38f,0x4e2,0x8c5,0x38f,0x4fb, +0x38f,0x8cd,0x8d5,0x4fd,0x38f,0x8d9,0x4fc,0x8e1,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x8e8, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x8fc,0x8f0,0x8f4,0x48e,0x48e,0x48e,0x48e,0x48e, +0x48e,0x48e,0x48e,0x48e,0x48e,0x48e,0x48e,0x48e,0x48e,0x904,0x48e,0x48e,0x48e,0x48e,0x90c,0x910, +0x918,0x920,0x924,0x92c,0x48e,0x48e,0x48e,0x930,0x938,0x37f,0x940,0x948,0x38f,0x38f,0x38f,0x950, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0xe3c,0xe3c,0xe7c,0xebc,0xe3c,0xe3c,0xe3c,0xe3c,0xe3c,0xe3c,0xef4,0xf34,0xf74,0xf84,0xfc4,0xfd0, +0xe3c,0xe3c,0x1010,0xe3c,0xe3c,0xe3c,0x1048,0x1088,0x10c8,0x1108,0x1140,0x1180,0x11c0,0x11f8,0x1238,0x1278, +0xa40,0xa80,0xac0,0xaff,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0xb37,0x1a0,0x1a0, +0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0xb74,0x1a0,0x1a0,0xba9,0xbe9,0xc29,0xc69,0xca9,0xce9, 0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0, -0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0xd17, +0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0xd29, 0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0, -0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0xd17, +0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0xd29, 0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0, -0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0xd17, +0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0xd29, 0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0, -0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0xd17, +0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0xd29, 0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0, -0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0xd17, +0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0xd29, 0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0, -0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0xd17, +0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0xd29, 0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0, -0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0xd17, +0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0xd29, 0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0, -0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0xd17, +0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0xd29, 0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0, -0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0xd17, +0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0xd29, 0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0, -0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0xd17, +0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0xd29, 0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0, -0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0xd17, +0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0xd29, 0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0, -0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0xd17, -0xd57,0xd67,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0, -0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0xd17, +0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0xd29, +0xd69,0xd79,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0, +0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0xd29, 0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0, -0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0xd17, +0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0xd29, 0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0, -0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0xd17, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x957,0x38a,0x654,0x654,0x95f,0x5f7,0x38a,0x4f0, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x967,0x38a,0x38a,0x38a,0x96e,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x41f,0x41f,0x41f,0x41f,0x41f,0x41f,0x41f,0x41f,0x976,0x41f,0x41f,0x41f,0x41f,0x41f,0x41f,0x41f, -0x97e,0x982,0x41f,0x41f,0x41f,0x41f,0x992,0x98a,0x41f,0x99a,0x41f,0x41f,0x9a2,0x9a8,0x41f,0x41f, -0x41f,0x41f,0x41f,0x41f,0x41f,0x41f,0x41f,0x41f,0x41f,0x41f,0x41f,0x41f,0x41f,0x41f,0x41f,0x41f, -0x41f,0x41f,0x41f,0x9b0,0x41f,0x41f,0x41f,0x41f,0x41f,0x41f,0x41f,0x41f,0x41f,0x41f,0x41f,0x41f, -0x4f7,0x9b8,0x9bf,0x9c6,0x401,0x9c9,0x38a,0x38a,0x4dd,0x9d1,0x38a,0x9d7,0x401,0x9dc,0x609,0x38a, -0x38a,0x9e4,0x38a,0x38a,0x38a,0x38a,0x826,0x9ec,0x401,0x4f8,0x556,0x9f3,0x38a,0x38a,0x38a,0x38a, -0x38a,0x9b8,0x9fb,0x38a,0x38a,0x9ff,0xa07,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0xa0b,0xa13,0x38a, -0x38a,0xa1b,0x556,0xa23,0x38a,0xa29,0x38a,0x38a,0x5e7,0xa31,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0xa39,0xa3d,0xa45,0x38a,0xa4c,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0xa53,0x38a,0x38a,0xa61,0xa5b, -0x38a,0x38a,0x38a,0xa69,0xa71,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0xa75,0x38a,0xa7b,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0xa81,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x511,0xa89,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0xa90,0xa98,0xa9e,0x38a,0x38a,0x654,0x654,0xaa6,0x38a,0x38a,0x38a,0x38a,0x38a,0x654, -0x654,0xaae,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0xab4,0x38a,0xabb, -0x38a,0xab7,0x38a,0xabe,0x38a,0xac6,0xaca,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x3d8,0xad2,0x3d8,0xad9,0xae0,0xae8,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0xaf0,0xaf8,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x41f,0x41f,0x41f,0x41f,0x41f,0x41f,0xb00,0x41f,0xb08, -0xb08,0xb0f,0x41f,0x41f,0x41f,0x41f,0x41f,0x41f,0x41f,0x41f,0x41f,0x41f,0x41f,0x41f,0x41f,0x41f, -0x41f,0x41f,0x41f,0x41f,0x41f,0x41f,0x41f,0x41f,0x41f,0x41f,0x41f,0x41f,0x41f,0x41f,0x41f,0x41f, -0x41f,0x41f,0x41f,0x41f,0x41f,0x41f,0x41f,0x489,0x489,0x489,0x489,0x489,0x489,0x489,0xb17,0x41f, -0x41f,0x41f,0x41f,0x41f,0x41f,0x41f,0x41f,0x654,0xb1f,0x654,0x654,0x657,0xb24,0xb28,0x840,0xb30, -0x38a,0x38a,0xb36,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x764,0x38a,0x38a,0x38a,0x38a,0x654, -0x654,0x654,0x654,0x654,0x654,0x654,0x654,0x654,0x654,0x654,0x654,0x654,0x654,0x654,0x654,0x654, -0x654,0x654,0x654,0x654,0x654,0x654,0x654,0x654,0x654,0x654,0x654,0x654,0x654,0xb3e,0xb46,0x654, -0x654,0x654,0x657,0x654,0x654,0xb3e,0x38a,0xb1f,0x654,0xb4e,0x654,0xb56,0x842,0x38a,0x38a,0xb1f, -0xb5a,0xb62,0x659,0x656,0x38a,0xb6a,0x56d,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0xb72,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0x38a, -0x38a,0x38a,0x38a,0x38a,0x38a,0x38a,0xb72,0xb82,0xb7a,0xb7a,0xb7a,0xb83,0xb83,0xb83,0xb83,0x3d8, -0x3d8,0x3d8,0x3d8,0x3d8,0x3d8,0x3d8,0xb8b,0xb83,0xb83,0xb83,0xb83,0xb83,0xb83,0xb83,0xb83,0xb83, -0xb83,0xb83,0xb83,0xb83,0xb83,0xb83,0xb83,0xb83,0xb83,0xb83,0xb83,0xb83,0xb83,0xb83,0xb83,0xb83, -0xb83,0xb83,0xb83,0xb83,0xb83,0xb83,0xb83,0xb83,0xb83,0xb83,0xb83,0xb83,0xb83,0xb83,0xb83,0xb83, -0xb83,0xb83,0xb83,0xb83,0xb83,0xb83,0xb83,0xb83,0xb83,0xb83,0xb83,0xb83,0xb83,0xb83,0xb83,0xb83, -0xb83,0xb83,0xb83,0xb83,0xb83,0xb83,0xb83,0x369,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, -0x12,8,7,8,9,7,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, -0x12,0x12,0x12,0x12,7,7,7,8,9,0xa,0xa,4,4,4,0xa,0xa, -0x310a,0xf20a,0xa,3,6,3,6,6,2,2,2,2,2,2,2,2, -2,2,6,0xa,0x500a,0xa,0xd00a,0xa,0xa,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0x510a,0xa,0xd20a,0xa,0xa,0xa,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0x510a,0xa,0xd20a,0xa,0x12,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0x12,0x12,0x12,0x12,0x12,7,0x12,0x12, -0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, -0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,6,0xa,4,4,4,4,0xa,0xa, -0xa,0xa,0,0x900a,0xa,0xb2,0xa,0xa,4,4,2,2,0xa,0,0xa,0xa, -0xa,2,0,0x900a,0xa,0xa,0xa,0xa,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0xa, +0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0x1a0,0xd29, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x958,0x38f,0x65a,0x65a,0x960,0x5fd,0x38f,0x4f5, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x968,0x38f,0x38f,0x38f,0x96f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x424,0x424,0x424,0x424,0x424,0x424,0x424,0x424,0x977,0x424,0x424,0x424,0x424,0x424,0x424,0x424, +0x97f,0x983,0x424,0x424,0x424,0x424,0x993,0x98b,0x424,0x99b,0x424,0x424,0x9a3,0x9a9,0x424,0x424, +0x424,0x424,0x424,0x424,0x424,0x424,0x424,0x424,0x9b9,0x9b1,0x424,0x424,0x424,0x424,0x424,0x424, +0x424,0x424,0x424,0x9c1,0x424,0x424,0x424,0x424,0x424,0x9c9,0x9d0,0x9d6,0x424,0x424,0x424,0x424, +0x4fc,0x9de,0x9e5,0x9ec,0x406,0x9ef,0x38f,0x38f,0x4e2,0x9f6,0x38f,0x9fc,0x406,0xa01,0xa09,0x38f, +0x38f,0xa0e,0x38f,0x38f,0x38f,0x38f,0x820,0xa16,0x406,0x581,0x55c,0xa1d,0x38f,0x38f,0x38f,0x38f, +0x38f,0x9de,0xa25,0x38f,0x38f,0xa2d,0xa35,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0xa39,0xa41,0x38f, +0x38f,0xa49,0x55c,0xa51,0x38f,0xa57,0x38f,0x38f,0x5ed,0xa5f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0xa64,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0xa6c, +0xa70,0xa78,0x38f,0xa7f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0xa86,0x38f,0x38f,0xa94,0xa8e,0x38f,0x38f,0x38f,0xa9c,0xaa4,0x38f,0xaa8,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x583,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0xaae,0x38f, +0xab4,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0xaba,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x516,0xac2,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0xac9,0xad1,0xad7,0x38f,0x38f,0x65a,0x65a,0xadf,0x38f,0x38f,0x38f,0x38f, +0x38f,0x65a,0x65a,0x833,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0xae1, +0x38f,0xae8,0x38f,0xae4,0x38f,0xaeb,0x38f,0xaf3,0xaf7,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x3dd,0xaff,0x3dd,0xb06,0xb0d,0xb15,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0xb1d,0xb25,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x424,0x424,0x424,0x424,0x424,0x424,0xb2d, +0x424,0xb35,0xb35,0xb3c,0x424,0x424,0x424,0x424,0x424,0x424,0x424,0x424,0x424,0x424,0x424,0x424, +0x424,0x424,0x424,0x424,0x424,0x424,0x424,0x424,0x424,0x424,0x424,0x424,0x8f4,0x48e,0x48e,0x424, +0x424,0x424,0x424,0x424,0x424,0x424,0x424,0x424,0x424,0x48e,0x48e,0x48e,0x48e,0x48e,0x48e,0x48e, +0xb44,0x424,0x424,0x424,0x424,0x424,0x424,0x424,0x424,0x65a,0xb4c,0x65a,0x65a,0x65d,0xb51,0xb55, +0x844,0xb5d,0x3b1,0x38f,0xb63,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x76a,0x38f,0x38f,0x38f, +0x38f,0x65a,0x65a,0x65a,0x65a,0x65a,0x65a,0x65a,0x65a,0x65a,0x65a,0x65a,0x65a,0x65a,0x65a,0x65a, +0x65a,0x65a,0x65a,0x65a,0x65a,0x65a,0x65a,0x65a,0x65a,0x65a,0x65a,0x65a,0x65a,0x65a,0x65a,0xb6b, +0xb73,0x65a,0x65a,0x65a,0x65d,0x65a,0x65a,0xb7b,0x38f,0xb4c,0x65a,0xb83,0x65a,0xb8b,0x846,0x38f, +0x38f,0xb4c,0xb8f,0x65a,0xb97,0x65a,0xb9f,0xba7,0x65a,0x38f,0x38f,0x38f,0x846,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0xbaf,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f, +0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0x38f,0xbaf,0xbbf,0xbb7,0xbb7,0xbb7,0xbc0,0xbc0,0xbc0, +0xbc0,0x3dd,0x3dd,0x3dd,0x3dd,0x3dd,0x3dd,0x3dd,0xbc8,0xbc0,0xbc0,0xbc0,0xbc0,0xbc0,0xbc0,0xbc0, +0xbc0,0xbc0,0xbc0,0xbc0,0xbc0,0xbc0,0xbc0,0xbc0,0xbc0,0xbc0,0xbc0,0xbc0,0xbc0,0xbc0,0xbc0,0xbc0, +0xbc0,0xbc0,0xbc0,0xbc0,0xbc0,0xbc0,0xbc0,0xbc0,0xbc0,0xbc0,0xbc0,0xbc0,0xbc0,0xbc0,0xbc0,0xbc0, +0xbc0,0xbc0,0xbc0,0xbc0,0xbc0,0xbc0,0xbc0,0xbc0,0xbc0,0xbc0,0xbc0,0xbc0,0xbc0,0xbc0,0xbc0,0xbc0, +0xbc0,0xbc0,0xbc0,0xbc0,0xbc0,0xbc0,0xbc0,0xbc0,0xbc0,0x36e,0x36e,0x36e,0x12,0x12,0x12,0x12, +0x12,0x12,0x12,0x12,0x12,8,7,8,9,7,0x12,0x12,0x12,0x12,0x12,0x12, +0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,7,7,7,8,9,0xa,0xa,4, +4,4,0xa,0xa,0x310a,0xf20a,0xa,3,6,3,6,6,2,2,2,2, +2,2,2,2,2,2,6,0xa,0x500a,0xa,0xd00a,0xa,0xa,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0xa, +0,0,0,0,0,0,0,0x510a,0xa,0xd20a,0xa,0xa,0xa,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0x510a,0xa,0xd20a,0xa,0x12,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0xa,0xa,0,0,0,0,0, -0,0,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, -0,0,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, -0,0,0,0,0,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0,0xa, -0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, -0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0x12,0x12,0x12,0x12, +0x12,7,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, +0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,6,0xa,4,4, +4,4,0xa,0xa,0xa,0xa,0,0x900a,0xa,0xb2,0xa,0xa,4,4,2,2, +0xa,0,0xa,0xa,0xa,2,0,0x900a,0xa,0xa,0xa,0xa,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0xa,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0xa,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0xa,0xa,0, +0,0,0,0,0,0,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, +0xa,0xa,0xa,0xa,0,0,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, +0xa,0xa,0xa,0xa,0,0,0,0,0,0xa,0xa,0xa,0xa,0xa,0xa,0xa, +0xa,0xa,0,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, +0xa,0xa,0xa,0xa,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, 0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, 0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, 0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, -0,0,0,0,0xa,0xa,0,0,0,0,0,0,0,0,0xa,0, -0,0,0,0,0xa,0xa,0,0xa,0,0,0,0,0,0,0,0, +0xb1,0xb1,0xb1,0xb1,0,0,0,0,0xa,0xa,0,0,0,0,0,0, +0,0,0xa,0,0,0,0,0,0xa,0xa,0,0xa,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0xa,0,0,0,0,0,0,0,0,0,0,0,0,0xb1, -0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0xa,0,0,0,0,0,0,0,0,0, +0,0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0xa,0, -0,0xa,0xa,4,1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0xa,0,0,0xa,0xa,4,1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, 0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, 0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, -0xb1,0xb1,1,0xb1,1,0xb1,0xb1,1,0xb1,0xb1,1,0xb1,1,1,1,1, +0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,1,0xb1,1,0xb1,0xb1,1,0xb1,0xb1,1,0xb1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,5,5,5,5,5,5,0xa,0xa,0xd,4,4,0xd, -6,0xd,0xa,0xa,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xd, -0x8ad,0xd,0xd,0xd,0x4d,0xd,0x8d,0x8d,0x8d,0x8d,0x4d,0x8d,0x4d,0x8d,0x4d,0x4d, -0x4d,0x4d,0x4d,0x8d,0x8d,0x8d,0x8d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d, -0x4d,0x4d,0x4d,0x4d,0x2d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x8d,0x4d,0x4d,0xb1, -0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, -0xb1,0xb1,0xb1,0xb1,5,5,5,5,5,5,5,5,5,5,4,5, -5,0xd,0x4d,0x4d,0xb1,0x8d,0x8d,0x8d,0xd,0x8d,0x8d,0x8d,0x4d,0x4d,0x4d,0x4d, -0x4d,0x4d,0x4d,0x4d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d, -0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d, +1,1,1,1,1,1,1,1,5,5,5,5,5,5,0xa,0xa, +0xd,4,4,0xd,6,0xd,0xa,0xa,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, +0xb1,0xb1,0xb1,0xd,0x8ad,0xd,0xd,0xd,0x4d,0xd,0x8d,0x8d,0x8d,0x8d,0x4d,0x8d, +0x4d,0x8d,0x4d,0x4d,0x4d,0x4d,0x4d,0x8d,0x8d,0x8d,0x8d,0x4d,0x4d,0x4d,0x4d,0x4d, +0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x2d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d, +0x8d,0x4d,0x4d,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, +0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,5,5,5,5,5,5,5,5, +5,5,4,5,5,0xd,0x4d,0x4d,0xb1,0x8d,0x8d,0x8d,0xd,0x8d,0x8d,0x8d, +0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d, +0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d, 0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d, -0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x8d,0x4d,0x4d,0x8d, -0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x4d,0x8d,0x4d,0x8d,0x4d,0x4d,0x8d,0x8d, -0xd,0x8d,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,5,0xa,0xb1,0xb1,0xb1,0xb1,0xb1, -0xb1,0xd,0xd,0xb1,0xb1,0xa,0xb1,0xb1,0xb1,0xb1,0x8d,0x8d,2,2,2,2, -2,2,2,2,2,2,0x4d,0x4d,0x4d,0xd,0xd,0x4d,0xd,0xd,0xd,0xd, -0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xad,0x8d,0xb1,0x4d,0x4d, -0x4d,0x8d,0x8d,0x8d,0x8d,0x8d,0x4d,0x4d,0x4d,0x4d,0x8d,0x4d,0x4d,0x4d,0x4d,0x4d, -0x4d,0x4d,0x4d,0x4d,0x8d,0x4d,0x8d,0x4d,0x8d,0x4d,0x4d,0x8d,0xb1,0xb1,0xb1,0xb1, -0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xd, -0xd,0x8d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x8d,0x8d,0x8d, -0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x8d, -0x8d,0x4d,0x4d,0x4d,0x4d,0x8d,0x4d,0x8d,0x8d,0x4d,0x4d,0x4d,0x8d,0x8d,0x4d,0x4d, -0x4d,0x4d,0x4d,0x4d,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd, +0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d, +0x8d,0x4d,0x4d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x8d,0x4d,0x8d,0x4d,0x8d, +0x4d,0x4d,0x8d,0x8d,0xd,0x8d,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,5,0xa,0xb1, +0xb1,0xb1,0xb1,0xb1,0xb1,0xd,0xd,0xb1,0xb1,0xa,0xb1,0xb1,0xb1,0xb1,0x8d,0x8d, +2,2,2,2,2,2,2,2,2,2,0x4d,0x4d,0x4d,0xd,0xd,0x4d, +0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xad, +0x8d,0xb1,0x4d,0x4d,0x4d,0x8d,0x8d,0x8d,0x8d,0x8d,0x4d,0x4d,0x4d,0x4d,0x8d,0x4d, +0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x8d,0x4d,0x8d,0x4d,0x8d,0x4d,0x4d,0x8d, +0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, +0xb1,0xb1,0xb1,0xd,0xd,0x8d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d, +0x4d,0x8d,0x8d,0x8d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d, +0x4d,0x4d,0x4d,0x8d,0x8d,0x4d,0x4d,0x4d,0x4d,0x8d,0x4d,0x8d,0x8d,0x4d,0x4d,0x4d, +0x8d,0x8d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd, 0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd, -0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, -0xb1,0xb1,0xb1,0xb1,0xb1,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd, -0xd,0xd,0xd,0xd,1,1,1,1,1,1,1,1,1,1,0x41,0x41, +0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xb1,0xb1, +0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xd,0xd,0xd,0xd,0xd,0xd,0xd, +0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,1,1,1,1,1,1,1,1, +1,1,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41, 0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41, -0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0xb1, -0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,1,1,0xa,0xa,0xa,0xa,0x21,1, -1,1,1,1,0xb1,0xb1,0xb1,0xb1,1,0xb1,0xb1,0xb1,1,0xb1,0xb1,0xb1, -0xb1,0xb1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,0xb1,0xb1,0xb1,0xb1,1,0xb1, -0xb1,0xb1,0xb1,0xb1,0x81,0x41,0x41,0x41,0x41,0x41,0x81,0x81,0x41,0x81,0x41,0x41, -0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x81,0x41,1,1,1,0xb1,0xb1,0xb1, -1,1,1,1,0x4d,0xd,0x4d,0x4d,0x4d,0x4d,0xd,0x8d,0x4d,0x8d,0x8d,0xd, -0xd,0xd,0xd,0xd,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,0xb1,0xb1,5,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, +0x41,0x41,0x41,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,1,1,0xa,0xa, +0xa,0xa,0x21,1,1,0xb1,1,1,0xb1,0xb1,0xb1,0xb1,1,0xb1,0xb1,0xb1, +1,0xb1,0xb1,0xb1,0xb1,0xb1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,0xb1,0xb1, +0xb1,0xb1,1,0xb1,0xb1,0xb1,0xb1,0xb1,0x81,0x41,0x41,0x41,0x41,0x41,0x81,0x81, +0x41,0x81,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x81,0x41,1,1, +1,0xb1,0xb1,0xb1,1,1,1,1,0x4d,0xd,0x4d,0x4d,0x4d,0x4d,0xd,0x8d, +0x4d,0x8d,0x8d,0xd,0xd,0xd,0xd,0xd,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,0xb1,0xb1,5,0xb1,0xb1,0xb1,0xb1,0xb1, 0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, -0xb1,0xb1,0xb1,0xb1,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x8d,0x8d, -0x8d,0xd,0x8d,0x4d,0x4d,0x8d,0x8d,0x4d,0x4d,0xd,0x4d,0x4d,0x4d,0x8d,0x4d,0x4d, -0x4d,0x4d,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd, -0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, -0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0xb1,0,0xb1,0,0,0,0,0xb1,0xb1,0xb1, -0xb1,0xb1,0xb1,0xb1,0xb1,0,0,0,0,0xb1,0,0,0,0xb1,0xb1,0xb1, -0xb1,0xb1,0xb1,0xb1,0,0,0,0,0,0,0,0,0,0,0xb1,0xb1, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0xb1,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0xb1,0,0,0, -0,0xb1,0xb1,0xb1,0xb1,0,0,0,0,0,0,0,0,0xb1,0,0, +0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d, +0x4d,0x4d,0x8d,0x8d,0x8d,0xd,0x8d,0x4d,0x4d,0x8d,0x8d,0x4d,0x4d,0xd,0x4d,0x4d, +0x4d,0x8d,0x4d,0x4d,0x4d,0x4d,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd, +0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xb1,0xb1,0xb1,0xb1,0xb1, +0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0xb1,0,0xb1,0,0,0, +0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0,0,0,0xb1,0,0, +0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0,0,0,0,0,0,0, 0,0,0xb1,0xb1,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,4,4,0,0,0,0,0,0,0,4,0,0,0,0, -0,0xb1,0xb1,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0xb1,0xb1,0,0,0,0,0xb1,0xb1,0,0,0xb1,0xb1,0xb1,0,0, 0,0xb1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0xb1,0xb1,0,0,0,0xb1,0,0,0,0,0,0, -0,0,0,0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0,0xb1,0xb1,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0xb1,0,0,0,0,0xb1,0xb1,0xb1,0xb1,0,0,0,0,0,0,0, 0,0xb1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0xb1,0xb1,0,0,0,0,0,0,0,0, -0,0,0,0,0,4,0,0,0,0,0,0,0,0,0xb1,0xb1, -0xb1,0xb1,0xb1,0xb1,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0xb1,0,0,0xb1,0,0xb1,0xb1,0xb1,0xb1,0,0,0,0,0,0,0, -0,0xb1,0,0,0,0,0,0,0,0,0xb1,0,0,0,0,0, +0,0,0,0,0,0,4,4,0,0,0,0,0,0,0,4, +0,0,0xb1,0,0,0xb1,0xb1,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0xb1,0,0,0,0,0,0,0, -0,0,0,0,0,0xb1,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0xa,0xa,0xa,0xa,0xa, -0xa,4,0xa,0,0,0,0,0,0xb1,0,0,0,0,0,0,0, +0,0,0,0,0,0xb1,0xb1,0,0,0,0,0xb1,0xb1,0,0,0xb1, +0xb1,0xb1,0,0,0,0xb1,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0xb1,0xb1,0,0,0,0xb1,0,0, +0,0,0,0,0,0,0,0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0,0xb1, +0xb1,0,0,0,0,0xb1,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0xb1,0xb1,0,0,0,0, +0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0, +0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0xb1,0xb1,0xb1,0,0,0, -0,0,0xb1,0xb1,0xb1,0,0xb1,0xb1,0xb1,0xb1,0,0,0,0,0,0, -0,0xb1,0xb1,0,0,0,0,0,0,0,0,0,0,0,0xb1,0xb1, +0,0,0,0,0xb1,0,0,0xb1,0,0xb1,0xb1,0xb1,0xb1,0,0,0, +0,0,0,0,0,0xb1,0,0,0,0,0,0,0,0,0xb1,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0xb1,0,0,0, +0,0,0,0,0,0,0,0,0,0xb1,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0xa, +0xa,0xa,0xa,0xa,0xa,4,0xa,0,0,0,0,0,0xb1,0,0,0, +0xb1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0xb1,0,0,0xa0,0,0,0,0, -0,0,0xa0,0,0,0,0,0,0xb1,0xb1,0,0,0,0,0,0, +0,0,0xb1,0xb1,0xb1,0,0,0,0,0,0xb1,0xb1,0xb1,0,0xb1,0xb1, +0xb1,0xb1,0,0,0,0,0,0,0,0xb1,0xb1,0,0,0,0,0, +0,0,0,0,0,0,0xb1,0xb1,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0xa,0xa,0xa,0xa, +0xa,0xa,0xa,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0xb1,0xb1,0,0,0,0,0,0,0, -0,0,0,0,0,0,0xb1,0,0,0,0,0,0,0,0xb1,0xb1, -0xb1,0,0xb1,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0xb1,0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0, -0,0,0,4,0,0,0,0,0,0,0,0xb1,0xb1,0xb1,0xb1,0xb1, -0xb1,0xb1,0xb1,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0xb1,0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0xb1, -0xb1,0,0,0,0,0,0,0,0,0,0,0,0xb1,0xb1,0xb1,0xb1, +0xb1,0,0,0xa0,0,0,0,0,0,0,0xa0,0,0,0,0,0, 0xb1,0xb1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0xb1,0xb1,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0xb1,0,0xb1,0,0xb1,0x310a,0xf20a,0x310a,0xf20a,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0, -0xb1,0xb1,0xb1,0xb1,0xb1,0,0xb1,0xb1,0,0,0,0,0,0xb1,0xb1,0xb1, -0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, -0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, -0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0xb1,0xb1,0xb1,0xb1,0,0xb1,0xb1, -0xb1,0xb1,0xb1,0xb1,0,0xb1,0xb1,0,0,0xb1,0xb1,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0xb1,0xb1,0,0,0,0,0xb1,0xb1,0xb1,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0xb1,0xb1,0xb1, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0xb1, 0xb1,0,0,0,0,0,0,0,0,0,0,0,0,0,0xb1,0, -0,0xb1,0xb1,0,0,0,0,0,0,0xb1,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0xb1,0,0,0,0,0,0, +0,0,0,0,0,0,0xb1,0xb1,0xb1,0,0xb1,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0xb1,0,0, +0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0,0,0,4,0,0,0,0, +0,0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0xb1,0,0, +0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0xb1,0xb1,0,0,0,0,0,0,0, +0,0,0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0xb1,0xb1,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0xb1,0,0xb1, +0,0xb1,0x310a,0xf20a,0x310a,0xf20a,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, +0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0xb1,0xb1,0xb1,0xb1,0xb1,0,0xb1,0xb1, +0,0,0,0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, +0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, +0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, +0xb1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0xb1,0xb1,0xb1,0xb1,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0xb1,0xb1,0, +0,0xb1,0xb1,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0xb1,0xb1,0,0, +0,0,0xb1,0xb1,0xb1,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0xb1,0xb1,0xb1,0xb1,0,0,0,0,0,0,0, +0,0,0,0,0,0,0xb1,0,0,0xb1,0xb1,0,0,0,0,0, +0,0xb1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0xb1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0xb1,0xb1,0xb1,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0xa,0xa,0xa,0xa, -0xa,0xa,0xa,0xa,0xa,0xa,0,0,0,0,0,0,0xa,0,0,0, +0,0xb1,0xb1,0xb1,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0,0, +0,0,0,0,0xa,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0, +0,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0x310a, +0xf20a,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0xb1,0xb1,0xb1,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0x310a,0xf20a,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0xb1,0xb1, -0xb1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0xb1,0xb1,0,0xb1,0xb1,0xb1,0xb1,0xb1, -0xb1,0xb1,0,0,0,0,0,0,0,0,0xb1,0,0,0xb1,0xb1,0xb1, -0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0,0,0,0,0,0,4, -0,0xb1,0,0,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, -0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, -0x40,0x40,0x40,0x40,0x40,0xb1,0x40,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0xa,0xa,0xa,0xa, -0xa,0xa,0xa,0x4a,0xa,0xa,0x2a,0xb1,0xb1,0xb1,0x12,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0x40,0x40,0x40,0x40, +0xb1,0xb1,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0,0,0,0,0, +0,0,0xb1,0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, +0,0,0,0,0,0,0,4,0,0xb1,0,0,0x40,0x40,0x40,0x40, 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, -0x40,0x40,0x40,0x40,0,0,0,0,0,0,0,0,0,0xb1,0xb1,0x40, +0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0xb1,0x40,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0x4a,0xa,0xa,0x2a,0xb1, +0xb1,0xb1,0x12,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, +0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0,0,0, +0,0,0,0,0,0xb1,0xb1,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, -0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0xb1,0xb1,0xb1,0,0,0,0,0xb1, -0xb1,0,0,0,0,0,0,0,0,0,0xb1,0,0,0,0,0, -0,0xb1,0xb1,0xb1,0,0,0,0,0xa,0,0,0,0xa,0xa,0,0, +0xb1,0xb1,0xb1,0,0,0,0,0xb1,0xb1,0,0,0,0,0,0,0, +0,0,0xb1,0,0,0,0,0,0,0xb1,0xb1,0xb1,0,0,0,0, +0xa,0,0,0,0xa,0xa,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0xa,0xa, -0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, +0,0,0,0,0,0,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, 0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0xb1,0xb1,0,0,0xb1,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0xb1,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0xb1,0,0xb1,0, -0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0,0,0,0,0,0xb1, -0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0,0xb1,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0xb1,0xb1,0xb1,0xb1, -0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0xb1,0,0xb1,0xb1,0xb1,0xb1,0xb1,0,0xb1,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0xb1,0xb1, -0xb1,0xb1,0,0,0xb1,0xb1,0,0xb1,0xb1,0xb1,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0xb1,0, -0xb1,0xb1,0,0,0,0xb1,0,0xb1,0xb1,0xb1,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, -0,0,0xb1,0xb1,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0xb1,0xb1,0xb1,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, -0xb1,0xb1,0xb1,0xb1,0xb1,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0,0, -0,0xb1,0,0,0,0,0,0,0xb1,0,0,0,0xb1,0xb1,0,0, +0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0xb1, +0xb1,0,0,0xb1,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0xb1,0,0xb1,0xb1,0xb1,0xb1, +0xb1,0xb1,0xb1,0,0xb1,0,0xb1,0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, +0xb1,0,0,0,0,0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, +0xb1,0,0,0xb1,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, -0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0xb1, -0xb1,0xb1,0xb1,0xb1,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0xa,0,0xa,0xa,0xa,0,0,0,0,0,0,0,0,0,0, -0,0xa,0xa,0xa,0,0,0,0,0,0,0,0,0,0,0,0, -0,0xa,0xa,0xa,0,0,0,0,0,0,0,0,0,0,0,0, -0,0xa,0xa,0,0xa,0xa,0xa,0xa,6,0x310a,0xf20a,0xa,0xa,0xa,0xa,0xa, -0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, -0xa,0xa,0xa,9,0xb2,0xb2,0xb2,0xb2,0xb2,0x12,0x814,0x815,0x813,0x816,0xb2,0xb2, -0xb2,0xb2,0xb2,0xb2,2,0,0,0,2,2,2,2,2,2,3,3, -0xa,0x310a,0xf20a,0,9,9,9,9,9,9,9,9,9,9,9,0xb2, -0x412,0x432,0x8a0,0x8a1,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, -0xa,0xa,0xa,0xa,9,7,0x8ab,0x8ae,0x8b0,0x8ac,0x8af,6,4,4,4,4, -4,0xa,0xa,0xa,0xa,0x300a,0xf00a,0xa,0xa,0xa,0xa,0xa,2,2,2,2, -2,2,2,2,2,2,3,3,0xa,0x310a,0xf20a,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,4,4,4,4, +0xb1,0xb1,0xb1,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0xb1,0,0xb1,0xb1,0xb1,0xb1,0xb1,0, +0xb1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0xb1, +0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0xb1,0xb1,0xb1,0xb1,0,0,0xb1,0xb1,0,0xb1, +0xb1,0xb1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0xb1,0,0xb1,0xb1,0,0,0,0xb1,0,0xb1, +0xb1,0xb1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0,0xb1,0xb1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0xb1,0xb1,0xb1,0, +0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0xb1,0xb1, +0xb1,0xb1,0xb1,0xb1,0xb1,0,0,0,0,0xb1,0,0,0,0,0,0, +0xb1,0,0,0,0xb1,0xb1,0,0,0,0,0,0,0xb1,0xb1,0xb1,0xb1, +0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, +0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0xb1,0xb1,0xb1,0xb1,0xb1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0xa,0,0xa,0xa,0xa,0,0, +0,0,0,0,0,0,0,0,0,0xa,0xa,0xa,0,0,0,0, +0,0,0,0,0,0,0,0,0,0xa,0xa,0xa,0,0,0,0, +0,0,0,0,0,0,0,0,0,0xa,0xa,0,0xa,0xa,0xa,0xa, +6,0x310a,0xf20a,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, +0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,9,0xb2,0xb2,0xb2,0xb2, +0xb2,0x12,0x814,0x815,0x813,0x816,0xb2,0xb2,0xb2,0xb2,0xb2,0xb2,2,0,0,0, +2,2,2,2,2,2,3,3,0xa,0x310a,0xf20a,0,9,9,9,9, +9,9,9,9,9,9,9,0xb2,0x412,0x432,0x8a0,0x8a1,0xa,0xa,0xa,0xa, +0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,9,7,0x8ab,0x8ae, +0x8b0,0x8ac,0x8af,6,4,4,4,4,4,0xa,0xa,0xa,0xa,0x300a,0xf00a,0xa, +0xa,0xa,0xa,0xa,2,2,2,2,2,2,2,2,2,2,3,3, +0xa,0x310a,0xf20a,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,4,4,4,4,4,4,4,4,4,4,4,4, 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, -4,4,4,4,4,4,4,4,4,4,4,4,0xb1,0xb1,0xb1,0xb1, -0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xa,0xa,0,0xa, -0xa,0xa,0xa,0,0xa,0xa,0,0,0,0,0,0,0,0,0,0, -0xa,0,0xa,0xa,0xa,0,0,0,0,0,0xa,0xa,0xa,0xa,0xa,0xa, -0,0xa,0,0xa,0,0xa,0,0,0,0,4,0,0,0,0,0, -0,0,0,0,0,0,0xa,0xa,0,0,0,0,0x100a,0xa,0xa,0xa, -0xa,0,0,0,0,0,0xa,0xa,0xa,0xa,0,0,0xa,0xa,0xa,0xa, -0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0,0,0,0, -0,0,0,0,0,0xa,0xa,0xa,0,0,0,0,0xa,0xa,0xa,0xa, -0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0x300a,0xf00a,0x300a,0xf00a, -0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0xa,0xa,0x300a,0xf00a,0x900a,0x900a, -0x900a,0x100a,0x900a,0x900a,0x100a,0x100a,0x900a,0x900a,0x900a,0x900a,0x900a,0x100a,0xa,0x100a,0x100a,0x100a, -0x100a,0xa,0xa,0xa,0x700a,0x700a,0x700a,0xb00a,0xb00a,0xb00a,0xa,0xa,0xa,0x100a,3,4, -0xa,0x900a,0x100a,0xa,0xa,0xa,0x100a,0x100a,0x100a,0x100a,0xa,0x100a,0x100a,0x100a,0x100a,0xa, -0x100a,0xa,0x100a,0xa,0xa,0xa,0xa,0x100a,0x100a,0x100a,0x100a,0x100a,0x100a,0x100a,0x100a,0x100a, -0xa,0xa,0xa,0xa,0xa,0x100a,0xa,0x100a,0x300a,0xf00a,0x100a,0x100a,0x100a,0x100a,0x100a,0x900a, -0x100a,0x100a,0x100a,0x100a,0x100a,0x100a,0x100a,0x100a,0x100a,0xa,0xa,0xa,0xa,0xa,0x300a,0xf00a, -0x300a,0xf00a,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0x100a,0x100a,0xa,0x100a,0xa, -0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0xa,0xa,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a, -0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x100a,0xa,0xa,0x300a, -0xf00a,0x300a,0xf00a,0xa,0xa,0xa,0xa,0xa,0x900a,0xa,0xa,0xa,0xa,0xa,0xa,0xa, -0xa,0xa,0x300a,0xf00a,0xa,0xa,0x900a,0x100a,0x900a,0x900a,0x100a,0x900a,0x100a,0x100a,0x100a,0x100a, -0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x100a,0xa,0xa,0xa,0xa,0xa,0x100a,0x100a, -0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0x300a,0xf00a,0x300a,0xf00a,0x900a,0xa,0xa, -0x300a,0xf00a,0xa,0xa,0xa,0xa,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a, -0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0x310a,0xf20a,0x310a,0xf20a,0xa,0xa,0xa,0xa, +4,4,4,4,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, +0xb1,0xb1,0xb1,0xb1,0xa,0xa,0,0xa,0xa,0xa,0xa,0,0xa,0xa,0,0, +0,0,0,0,0,0,0,0,0xa,0,0xa,0xa,0xa,0,0,0, +0,0,0xa,0xa,0xa,0xa,0xa,0xa,0,0xa,0,0xa,0,0xa,0,0, +0,0,4,0,0,0,0,0,0,0,0,0,0,0,0xa,0xa, +0,0,0,0,0x100a,0xa,0xa,0xa,0xa,0,0,0,0,0,0xa,0xa, +0xa,0xa,0,0,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, +0xa,0xa,0xa,0xa,0,0,0,0,0,0,0,0,0,0xa,0xa,0xa, +0,0,0,0,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, +0xa,0xa,0xa,0xa,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a, +0x300a,0xf00a,0xa,0xa,0x300a,0xf00a,0x900a,0x900a,0x900a,0x100a,0x900a,0x900a,0x100a,0x100a,0x900a,0x900a, +0x900a,0x900a,0x900a,0x100a,0xa,0x100a,0x100a,0x100a,0x100a,0xa,0xa,0xa,0x700a,0x700a,0x700a,0xb00a, +0xb00a,0xb00a,0xa,0xa,0xa,0x100a,3,4,0xa,0x900a,0x100a,0xa,0xa,0xa,0x100a,0x100a, +0x100a,0x100a,0xa,0x900a,0x900a,0x900a,0x900a,0xa,0x900a,0xa,0x100a,0xa,0xa,0xa,0xa,0x100a, +0x100a,0x100a,0x100a,0x100a,0x100a,0x100a,0x100a,0x100a,0xa,0xa,0xa,0xa,0xa,0x100a,0xa,0x100a, +0x300a,0xf00a,0x100a,0x100a,0x100a,0x100a,0x100a,0x900a,0x100a,0x900a,0x100a,0x100a,0x100a,0x100a,0x100a,0x100a, +0x900a,0xa,0xa,0xa,0xa,0xa,0x300a,0xf00a,0x300a,0xf00a,0xa,0xa,0xa,0xa,0xa,0xa, +0xa,0xa,0xa,0x100a,0x100a,0xa,0x100a,0xa,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a, +0xa,0xa,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a, +0x300a,0xf00a,0x300a,0xf00a,0x100a,0xa,0xa,0x300a,0xf00a,0x300a,0xf00a,0xa,0xa,0xa,0xa,0xa, +0x900a,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0x300a,0xf00a,0xa,0xa,0x900a,0x100a, +0x900a,0x900a,0x100a,0x900a,0x100a,0x100a,0x100a,0x100a,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a, +0x900a,0xa,0xa,0xa,0xa,0xa,0x100a,0x100a,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, +0xa,0x300a,0xf00a,0x300a,0xf00a,0x900a,0xa,0xa,0x300a,0xf00a,0xa,0xa,0xa,0xa,0x300a,0xf00a, +0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, +0x310a,0xf20a,0x310a,0xf20a,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, +0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0x100a,0x100a,0xa,0xa,0xa,0xa,0xa,0xa, +0xa,0x310a,0xf20a,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0xa,0xa,0xa,0xa,0xa, 0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, -0x100a,0x100a,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0x310a,0xf20a,0xa,0xa,0xa,0xa,0xa, -0xa,0xa,0xa,0xa,0xa,0xa,0,0,0,0,0,0,0,0,0,0, +0xa,0,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, -0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0,0xa,0xa,0xa,0xa,0xa,0xa, -0xa,0xa,0xa,0xa,0xa,0xa,0xa,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0xa,0xa,0xa,0xa, -0xa,0xa,0xa,0xa,2,2,2,2,2,2,2,2,2,2,2,2, -2,2,2,2,2,2,2,2,0,0,0,0,0xa,0xa,0xa,0xa, -0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0,0xa,0xa,0xa,0xa,0xa,0xa,0xa, -0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0x310a,0xf20a,0x310a,0xf20a, -0x310a,0xf20a,0x310a,0xf20a,0x310a,0xf20a,0x310a,0xf20a,0x310a,0xf20a,0xa,0xa,0xa,0xa,0xa,0xa, -0xa,0xa,0xa,0xa,0x100a,0xa,0xa,0x300a,0xf00a,0x310a,0xf20a,0xa,0x300a,0xf00a,0xa,0x500a, -0x100a,0xd00a,0xa,0xa,0xa,0xa,0xa,0x100a,0x100a,0x300a,0xf00a,0xa,0xa,0xa,0xa,0xa, -0x100a,0x300a,0xf00a,0xa,0xa,0xa,0x300a,0xf00a,0x300a,0xf00a,0x310a,0xf20a,0x310a,0xf20a,0x310a,0xf20a, -0x310a,0xf20a,0x310a,0xf20a,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, -0xa,0xa,0xa,0xa,0xa,0x100a,0xa,0x100a,0x100a,0x100a,0xa,0xa,0x100a,0x100a,0xa,0xa, -0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0x100a,0x900a,0x100a,0x100a,0x300a,0xf00a,0xa,0xa, -0x310a,0xf20a,0xa,0xa,0xa,0xa,0xa,0x310a,0xf20a,0x310a,0xf20a,0x310a,0xf20a,0x310a,0xf20a,0x310a, -0xf20a,0x710a,0x320a,0xf10a,0xb20a,0x310a,0xf20a,0x310a,0xf20a,0x310a,0xf20a,0x310a,0xf20a,0xa,0xa,0x100a, -0x100a,0x100a,0x100a,0x100a,0x100a,0x100a,0x100a,0x100a,0x100a,0x100a,0x100a,0x100a,0x100a,0x100a,0x100a,0x100a, -0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0x900a,0xa,0xa,0xa,0xa,0xa,0xa,0xa, -0x300a,0xf00a,0x100a,0x100a,0x300a,0xf00a,0xa,0xa,0xa,0x100a,0xa,0xa,0xa,0xa,0x100a,0x300a, -0xf00a,0x300a,0xf00a,0xa,0x300a,0xf00a,0xa,0xa,0x310a,0xf20a,0x310a,0xf20a,0x100a,0xa,0xa,0xa, -0xa,0xa,0x100a,0x900a,0x900a,0x900a,0x100a,0xa,0xa,0xa,0xa,0xa,0x300a,0xf00a,0x100a,0xa, -0xa,0xa,0xa,0x100a,0xa,0xa,0xa,0x300a,0xf00a,0x300a,0xf00a,0x100a,0xa,0x100a,0xa,0xa, -0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0x100a,0x100a,0x100a,0x100a,0x100a,0x100a, -0x100a,0x100a,0x100a,0x100a,0x100a,0x100a,0x100a,0x100a,0x100a,0x100a,0x100a,0x100a,0x100a,0xa,0x100a,0x100a, -0x100a,0x100a,0xa,0xa,0x100a,0xa,0x100a,0xa,0xa,0x100a,0xa,0x300a,0xf00a,0x300a,0xf00a,0xa, -0xa,0xa,0xa,0xa,0x300a,0xf00a,0xa,0xa,0xa,0xa,0xa,0xa,0x300a,0xf00a,0x100a,0xa, +0,0,0,0,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,2,2,2,2, +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +0,0,0,0,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, +0,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, +0xa,0xa,0xa,0xa,0x310a,0xf20a,0x310a,0xf20a,0x310a,0xf20a,0x310a,0xf20a,0x310a,0xf20a,0x310a,0xf20a, +0x310a,0xf20a,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0x100a,0xa,0xa,0x300a, +0xf00a,0x310a,0xf20a,0xa,0x300a,0xf00a,0xa,0x500a,0x100a,0xd00a,0xa,0xa,0xa,0xa,0xa,0x100a, +0x100a,0x300a,0xf00a,0xa,0xa,0xa,0xa,0xa,0x900a,0x300a,0xf00a,0xa,0xa,0xa,0x300a,0xf00a, +0x300a,0xf00a,0x310a,0xf20a,0x310a,0xf20a,0x310a,0xf20a,0x310a,0xf20a,0x310a,0xf20a,0xa,0xa,0xa,0xa, +0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0x100a,0xa,0x100a, +0x100a,0x100a,0xa,0xa,0x300a,0xf00a,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, +0x100a,0x900a,0x100a,0x100a,0x300a,0xf00a,0xa,0xa,0x310a,0xf20a,0xa,0xa,0xa,0xa,0xa,0x310a, +0xf20a,0x310a,0xf20a,0x310a,0xf20a,0x310a,0xf20a,0x310a,0xf20a,0x710a,0x320a,0xf10a,0xb20a,0x310a,0xf20a,0x310a, +0xf20a,0x310a,0xf20a,0x310a,0xf20a,0xa,0xa,0x900a,0x100a,0x100a,0x100a,0x100a,0x900a,0xa,0x100a,0x900a, +0x300a,0xf00a,0x100a,0x100a,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0xa,0xa,0xa,0xa, +0xa,0xa,0xa,0xa,0x900a,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0x300a,0xf00a,0x100a,0x100a, +0x300a,0xf00a,0xa,0xa,0xa,0x100a,0xa,0xa,0xa,0xa,0x100a,0x300a,0xf00a,0x300a,0xf00a,0xa, +0x300a,0xf00a,0xa,0xa,0x310a,0xf20a,0x310a,0xf20a,0x100a,0xa,0xa,0xa,0xa,0xa,0x100a,0x900a, +0x900a,0x900a,0x100a,0xa,0xa,0xa,0xa,0xa,0x300a,0xf00a,0x900a,0xa,0xa,0xa,0xa,0x100a, +0xa,0xa,0xa,0x300a,0xf00a,0x300a,0xf00a,0x100a,0xa,0x100a,0xa,0xa,0xa,0xa,0xa,0xa, +0xa,0xa,0xa,0xa,0xa,0xa,0x100a,0x100a,0x100a,0x100a,0x100a,0x100a,0x100a,0x100a,0x100a,0x100a, +0x100a,0x100a,0x100a,0x100a,0x100a,0x100a,0x100a,0x100a,0x100a,0xa,0x100a,0x100a,0x100a,0x100a,0xa,0xa, +0x100a,0xa,0x100a,0xa,0xa,0x100a,0xa,0x300a,0xf00a,0x300a,0xf00a,0xa,0xa,0xa,0xa,0xa, +0x300a,0xf00a,0xa,0xa,0xa,0xa,0xa,0xa,0x300a,0xf00a,0x100a,0xa,0xa,0xa,0xa,0xa, 0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, -0xa,0xa,0xa,0xa,0xa,0xa,0xa,0x100a,0x100a,0xa,0xa,0xa,0xa,0xa,0xa,0xa, -0x300a,0xf00a,0xa,0xa,0xa,0xa,0x100a,0x100a,0x100a,0x100a,0xa,0x100a,0x100a,0xa,0xa,0x100a, -0x100a,0xa,0xa,0xa,0xa,0x300a,0xf00a,0x100a,0x100a,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x300a, -0xf00a,0x100a,0x100a,0x100a,0x100a,0x100a,0x100a,0x300a,0xf00a,0x100a,0x100a,0x100a,0x100a,0x300a,0xf00a,0x300a, -0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x100a,0x100a,0x100a,0x100a,0x300a,0xf00a,0x100a, -0xa,0xa,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0xa,0x300a,0xf00a,0x100a,0x100a,0x300a, -0xf00a,0x100a,0x100a,0x100a,0x100a,0x100a,0x100a,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x100a, -0x100a,0x100a,0x100a,0x100a,0x100a,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0xa, -0xa,0xa,0xa,0xa,0x100a,0xa,0x900a,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, -0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0,0,0xa,0xa, +0xa,0xa,0xa,0x100a,0x100a,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0x300a,0xf00a,0xa,0xa, +0xa,0xa,0x100a,0x100a,0x100a,0x100a,0xa,0x100a,0x100a,0xa,0xa,0x100a,0x100a,0xa,0xa,0xa, +0xa,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x300a, +0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x300a, +0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x100a,0xa,0xa,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a, +0x300a,0xf00a,0xa,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x300a, +0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0x300a,0xf00a,0xa,0xa,0xa,0xa,0xa, +0x100a,0xa,0x900a,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, +0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0,0,0xa,0xa,0xa,0xa,0xa,0xa, 0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, -0xa,0xa,0xa,0xa,0xa,0xa,0,0,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, +0xa,0xa,0,0,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0,0xa,0xa, +0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, +0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0x900a,0, +0,0,0,0,0,0xa,0xa,0xa,0xa,0xa,0xa,0,0,0,0,0xb1, +0xb1,0xb1,0,0,0,0,0,0,0,0xa,0xa,0xa,0xa,0xa,0xa,0xa, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0xb1, +0xa,0xa,0x300a,0xf00a,0x300a,0xf00a,0xa,0xa,0xa,0x300a,0xf00a,0xa,0x300a,0xf00a,0xa,0xa, +0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0x300a,0xf00a,0xa,0xa, +0x300a,0xf00a,0x310a,0xf20a,0x310a,0xf20a,0x310a,0xf20a,0x310a,0xf20a,0xa,0xa,0xa,0xa,0xa,0xa, 0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, -0xa,0xa,0,0,0,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, -0xa,0,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0,0,0,0,0, -0,0,0,0,0,0,0,0,0xa,0xa,0xa,0xa,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0xa,0xa,0xa, -0xa,0xa,0xa,0,0,0,0,0xb1,0xb1,0xb1,0,0,0,0,0,0, -0,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0xb1,0xa,0xa,0x300a,0xf00a,0x300a,0xf00a,0xa,0xa, -0xa,0x300a,0xf00a,0xa,0x300a,0xf00a,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, -0xa,0xa,0xa,0xa,0x300a,0xf00a,0xa,0xa,0x300a,0xf00a,0x310a,0xf20a,0x310a,0xf20a,0x310a,0xf20a, -0x310a,0xf20a,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, +0xa,0xa,0xa,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, 0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0,0xa, 0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, 0xa,0xa,0xa,0xa,0xa,0xa,0,0,0,0,0,0,0,0,0,0, @@ -554,12 +555,11 @@ static const uint16_t ubidi_props_trieIndex[11856]={ 0x310a,0xf20a,0x310a,0xf20a,0xa,0xa,0xa,0xa,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0xb1,0xb1,0xa,0xa,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, -0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0xa,0xa,0xa,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0xa,0xa,0xa,0xa,0xa,0xa,0xa, 0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0,0,0,0,0,0,0,0, +0,0,0,0,0xa,0xa,0xa,0xa,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0xa, 0xa,0xa,0xa,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0xa,0xa,0xa,0,0,0,0,0,0,0,0,0,0,0,0, @@ -574,194 +574,211 @@ static const uint16_t ubidi_props_trieIndex[11856]={ 0x40,0x40,0x40,0x40,0x40,0x40,0x60,0,0xa,0xa,0xa,0xa,0,0,0,0, 0,0,0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, 0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0, +0,0,0,0xb1,0,0,0,0,0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, +0xb1,0xb1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, +0xb1,0xb1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0xb1,0,0,0xb1,0xb1,0xb1,0xb1,0,0, +0xb1,0,0,0,0,0,0,0,0,0,0,0,0,0xb1,0xb1,0xb1, +0xb1,0xb1,0xb1,0,0,0xb1,0xb1,0,0,0xb1,0xb1,0,0,0,0,0, +0,0,0,0,0,0,0,0xb1,0,0,0,0,0,0,0,0, +0xb1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0xb1,0,0xb1,0xb1,0xb1,0,0,0xb1,0xb1,0,0,0, +0,0,0xb1,0xb1,0,0,0,0,0,0,0,0,0,0,0,0, +0xb1,0xb1,0,0,0,0,0,0,0,0,0xb1,0,0,0,0,0, +0,0,0,0,0,0xb1,0,0,0xb1,0,0,0,0,0xb1,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0xb1,0,0,0xb1,0xb1,0xb1,0xb1,0,0,0xb1,0,0,0, -0,0,0,0,0,0,0,0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0, -0,0xb1,0xb1,0,0,0xb1,0xb1,0,0,0,0,0,0,0,0,0, -0,0,0,0xb1,0,0,0,0,0,0,0,0,0xb1,0,0,0, +1,1,1,1,1,1,1,1,1,3,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0xb1,0,0xb1,0xb1,0xb1,0,0,0xb1,0xb1,0,0,0,0,0,0xb1,0xb1, -0,0,0,0,0,0,0,0,0,0,0,0,0xb1,0xb1,0,0, -0,0,0,0,0,0,0xb1,0,0,0,0,0,0,0,0,0, -0,0xb1,0,0,0xb1,0,0,0,0,0xb1,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1, -1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,0xd,0xd,0xd,0xd, -0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,1,0xb1,1,0xd,0xd,0xd,0xd, +0,0,0,0,0,0,0,0,0,0,0,0,0,1,0xb1,1, 0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd, -0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xa,0xa,0xd,0xd,0xd,0xd, -0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0x12,0x12,0x12,0x12, -0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0xd,0xd,0xd,0xd, -0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xa,0xd,0xd,0xb1,0xb1,0xb1,0xb1, -0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xa,0xa,0xa,0xa, -0xa,0xa,0xa,0xa,0xa,0xa,0,0,0,0,0,0,0xb1,0xb1,0xb1,0xb1, -0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xa,0xa,0xa,0xa, -0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,6,0xa,6,0, -0xa,6,0xa,0xa,0xa,0x310a,0xf20a,0x310a,0xf20a,0x310a,0xf20a,4,0xa,0xa,3,3, -0x300a,0xf00a,0xa,0,0xa,4,4,0xa,0,0,0,0,0xd,0xd,0xd,0xd, +0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xa,0xa, 0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd, -0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xb2,0,0xa,0xa,4, -4,4,0xa,0xa,0x310a,0xf20a,0xa,3,6,3,6,6,2,2,2,2, -2,2,2,2,2,2,6,0xa,0x500a,0xa,0xd00a,0xa,0xa,0,0,0, +0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, +0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xa,0xd,0xd, +0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, +0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0,0,0,0,0,0, +0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, +0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, +6,0xa,6,0,0xa,6,0xa,0xa,0xa,0x310a,0xf20a,0x310a,0xf20a,0x310a,0xf20a,4, +0xa,0xa,3,3,0x300a,0xf00a,0xa,0,0xa,4,4,0xa,0,0,0,0, +0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd, +0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xb2, +0,0xa,0xa,4,4,4,0xa,0xa,0x310a,0xf20a,0xa,3,6,3,6,6, +2,2,2,2,2,2,2,2,2,2,6,0xa,0x500a,0xa,0xd00a,0xa, +0xa,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0x510a,0xa,0xd20a,0xa,0x310a, +0xf20a,0xa,0x310a,0xf20a,0xa,0xa,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0x510a,0xa,0xd20a,0xa,0x310a,0xf20a,0xa,0x310a,0xf20a, -0xa,0xa,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,4,4,0xa,0xa, -0xa,4,4,0,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0,0x12,0x12,0x12,0x12, -0x12,0x12,0x12,0x12,0x12,0xaa,0xaa,0xaa,0xa,0xa,0x12,0x12,0,0xa,0,0, +4,4,0xa,0xa,0xa,4,4,0,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0, +0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0xaa,0xaa,0xaa,0xa,0xa,0x12,0x12, +0,0xa,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0xa,0xa,0xa,0xa, -0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0,0,0,0xa,0xa,0xa,0xa, -0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0,0,0,0,0xb1,2,2,2, -2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, -2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0xb1,0xb1, -0xb1,0xb1,0xb1,0,0,0,0,0,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,0xa,1,0xb1,0xb1,0xb1,1,0xb1,0xb1,1, -1,1,1,1,0xb1,0xb1,0xb1,0xb1,1,1,1,1,1,1,1,1, +0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0,0,0, +0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0,0,0,0, +0xb1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0,0,0,0,0,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -0xb1,0xb1,0xb1,1,1,1,1,0xb1,0x41,0x81,1,1,0x81,0xb1,0xb1,1, -1,1,1,0x41,0x41,0x41,0x41,0x81,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,0x41,0x41,0x41,0x41,0x41,0x81,1,0x81, -1,0x81,0x81,1,1,0x61,0x81,0x81,0x81,0x81,0x81,0x41,0x41,0x41,0x41,0x61, -0x41,0x41,0x41,0x41,0x41,0x81,0x41,0x41,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,0xa,1,0xb1,0xb1,0xb1, +1,0xb1,0xb1,1,1,1,1,1,0xb1,0xb1,0xb1,0xb1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -1,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0x41,0x81,0x41,0x81,0x81,0x81,0x41,0x41, -0x41,0x81,0x41,0x41,0x81,0x41,0x81,0x81,0x41,0x81,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,0x81,0x81,0x81,0x81,0x41,0x41,1, +1,1,1,1,0xb1,0xb1,0xb1,1,1,1,1,0xb1,0x41,0x81,1,1, +0x81,0xb1,0xb1,1,1,1,1,0x41,0x41,0x41,0x41,0x81,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,0x41,0x41,0x41,0x41, +0x41,0x81,1,0x81,1,0x81,0x81,1,1,0x61,0x81,0x81,0x81,0x81,0x81,0x41, +0x41,0x41,0x41,0x61,0x41,0x41,0x41,0x41,0x41,0x81,0x41,0x41,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0x41,0x81,0x41,0x81, +0x81,0x81,0x41,0x41,0x41,0x81,0x41,0x41,0x81,0x41,0x81,0x81,0x41,0x81,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,0x81,0x81,0x81, +0x81,0x41,0x41,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,0x4d,0x4d,0x8d,0x4d,0xb1,0xb1,0xb1,0xb1,0xd,0xd,0xd,0xd, +0xd,0xd,0xd,0xd,5,5,5,5,5,5,5,5,5,5,0xd,0xd, +0xd,0xd,0xd,0xd,0x6d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d, +0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d, +0x4d,0x4d,0x4d,0x4d,5,5,5,5,5,5,5,5,5,5,5,5, 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, -5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,1, +5,5,5,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,0x4d,0x4d,0x4d,0x8d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d,0x4d, +0x4d,0x4d,0x4d,0x4d,0x4d,0xd,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, +0xb1,0x4d,0x4d,0x4d,0x8d,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd, +0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, -0xb1,0xb1,0xb1,0,0,0,0,0,0,0,0,0,0,0,0xa,0xa, -0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0xb1,0xb1,0xb1,0xb1,0,0,0xb1,0xb1,0, -0,0xa0,0,0,0,0,0,0,0,0,0,0xb1,0xb1,0xb1,0xb1,0xb1, -0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0,0,0,0,0,0, +0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0,0,0,0, +0,0,0,0,0,0,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, +0xa,0xa,0xa,0xa,0xa,0xa,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0xb1, +0xb1,0xb1,0xb1,0,0,0xb1,0xb1,0,0,0,0,0,0,0,0,0xb1, +0xb1,0xb1,0xb1,0xb1,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0xb1, -0xb1,0xb1,0,0,0xb1,0,0xb1,0xb1,0,0,0,0,0,0,0xb1,0, -0,0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0,0,0,0, +0,0,0,0xb1,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, +0xb1,0xb1,0xb1,0,0,0,0,0,0,0,0,0,0,0xb1,0xb1,0xb1, +0xb1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0xb1,0xb1,0xb1,0,0,0xb1,0,0xb1,0xb1, +0,0,0,0,0,0,0xb1,0,0,0,0,0xb1,0xb1,0xb1,0xb1,0xb1, +0xb1,0xb1,0xb1,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, +0xb1,0,0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0,0,0,0,0,0,0, +0,0,0,0,0,0,0xb1,0xb1,0xb1,0,0xb1,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0,0,0xb1,0xb1,0xb1,0xb1, -0xb1,0,0,0,0,0,0,0,0,0,0,0,0,0,0xb1,0xb1, -0xb1,0,0xb1,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0xb1, -0xb1,0xb1,0xb1,0xb1,0xb1,0,0xb1,0,0,0,0,0xb1,0xb1,0,0xb1,0xb1, +0,0,0xb1,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0xb1,0, +0,0,0,0xb1,0xb1,0,0xb1,0xb1,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0xb1,0xb1, -0xb1,0xb1,0,0,0,0,0,0,0xb1,0xb1,0,0xb1,0xb1,0,0,0, +0,0,0,0,0,0,0xb1,0xb1,0xb1,0xb1,0,0,0,0,0,0, +0xb1,0xb1,0,0xb1,0xb1,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0xb1,0xb1,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0xb1, -0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0,0xb1,0,0xb1,0xa,0xa,0xa,0xa, -0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0,0,0,0,0,0,0, +0xb1,0xb1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0, +0,0xb1,0,0xb1,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, +0xa,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0xb1,0,0xb1,0,0,0xb1,0xb1,0xb1,0xb1, +0xb1,0xb1,0,0xb1,0,0,0,0,0,0,0,0,0,0,0xb1,0xb1, +0xb1,0xb1,0,0xb1,0xb1,0xb1,0xb1,0xb1,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0xb1, -0,0xb1,0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0xb1,0,0,0,0, -0,0,0,0,0,0,0xb1,0xb1,0xb1,0xb1,0,0xb1,0xb1,0xb1,0xb1,0xb1, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0,0xb1,0xb1,0, +0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0xb1,0xb1,0,0,0,0,0, +0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xa0,0xa0,0xb1,0xb1,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0,0xb1, -0xb1,0xb1,0xb1,0,0,0,0,0,0,0,0,0xb1,0,0,0,0, -0,0,0,0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0,0xb1,0xb1,0xb1, -0,0,0,0,0,0,0,0,0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, -0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0xb1,0xb1,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0xb1,0xb1,0xb1,0xb1, -0xb1,0xb1,0xb1,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0xa0,0xb1,0xb1,0xb1,0xb1, -0xb1,0xb1,0xb1,0xb1,0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0xb1,0xb1, -0,0xb1,0xb1,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0,0xb1,0xb1,0xb1,0xb1,0, +0,0,0,0,0,0,0,0xb1,0,0,0,0,0,0,0,0, +0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0,0xb1,0xb1,0xb1,0,0,0,0, 0,0,0,0,0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, -0xb1,0xb1,0xb1,0xb1,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0,0,0xb1,0, -0xb1,0xb1,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0xb1,0,0,0,0, +0xb1,0xb1,0xb1,0,0xb1,0xb1,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0, +0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0xa0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, +0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0xb1,0xb1,0,0xb1,0xb1,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0xb1,0xb1,0xb1,0xb1, -0xb1,0xb1,0xb1,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0xb1,0xb1,0xb1,0xb1,0,0,0,0,0,0,0,0,0, -0,0,0,0,0xb2,0xb2,0xb2,0xb2,0,0,0,0,0,0,0,0, +0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0xb1,0xb1,0xb1,0,0,0,0,0,0, -0,0,0,0xb2,0xb2,0xb2,0xb2,0xb2,0xb2,0xb2,0xb2,0xb1,0xb1,0xb1,0xb1,0xb1, -0xb1,0xb1,0xb1,0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0,0,0, +0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0,0,0xb1,0,0xb1,0xb1,0,0xb1, +0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0xb1,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0xb1,0xb1,0xb1,0xb1,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0xa,0xa,0xb1,0xb1,0xb1,0xa,0,0, +0xb1,0xb1,0,0,0,0xb1,0,0xb1,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, -0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0, +0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0xb1,0xb1,0xb1,0xb1,0,0,0,0,0, +0,0,0,0,0,0,0,0,0xb2,0xb2,0xb2,0xb2,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0x100a,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0xb1,0xb1,0xb1,0,0, +0,0,0,0,0,0,0,0xb2,0xb2,0xb2,0xb2,0xb2,0xb2,0xb2,0xb2,0xb1, +0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0x100a,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0x100a,0,0,0,0, -0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2, -2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, -2,2,2,2,2,2,2,2,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, -0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0, -0,0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, -0xb1,0,0,0,0,0,0,0,0,0xb1,0,0,0,0,0,0, -0,0,0,0,0xb1,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0xb1,0xb1,0xb1,0xb1,0xb1, -0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, +0,0,0,0,0,0,0xb1,0xb1,0xb1,0xb1,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0xa,0xa,0xb1,0xb1, +0xb1,0xa,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0x100a, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, -0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0,0xb1,0xb1,0xb1,0xb1,0xb1, -0xb1,0xb1,0,0xb1,0xb1,0,0xb1,0xb1,0xb1,0xb1,0xb1,0,0,0,0,0, +0,0,0,0,0,0x100a,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0x100a, +0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2, +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,2,2,2,2,2,2,2,2,0xb1,0xb1,0xb1,0xb1, +0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, +0xb1,0xb1,0xb1,0,0,0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, +0xb1,0xb1,0xb1,0xb1,0xb1,0,0,0,0,0,0,0,0,0xb1,0,0, +0,0,0,0,0,0,0,0,0xb1,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0xb1, +0xb1,0xb1,0xb1,0xb1,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, +0xb1,0xb1,0xb1,0xb1,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0xb1,0xb1,0xb1,0xb1, +0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0,0xb1, +0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0,0xb1,0xb1,0,0xb1,0xb1,0xb1,0xb1,0xb1,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, -0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,1,1,1,1,1,1,1,1,1, -0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41, +0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,1,1,1,1,1, +1,1,1,1,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41, 0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41, -0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,1,1,1,1,1,1,1,1,1, -1,1,1,1,1,1,1,1,1,1,1,1,0xd,0xd,0xd,0xd, -0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xa,0xa,0xd,0xd, -0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xa,0xa,0xa,0xa, -0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0,0,0,0,0xa,0xa,0xa,0xa, -0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0, -0,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, -0,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, -2,2,2,2,2,2,2,2,2,2,2,0xa,0xa,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0xa,0xa,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, -0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0,0,0, -0,0,0,0,0,0,0,0,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, -0xa,0xa,0xa,0xa,0xa,0,0,0,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, -0xa,0,0,0,0,0,0,0,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, -0,0,0,0,0,0,0,0,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, -0xa,0xa,0,0,0,0,0,0,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, -0,0,0,0,0,0,0,0,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, +0x41,0x41,0x41,0x41,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd, +0xa,0xa,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd, +0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0,0,0,0, 0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, -0xa,0xa,0xa,0xa,0xa,0xa,0xa,0,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, -0xa,0xa,0xa,0xa,0xa,0,0,0,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, -0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, -0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0,0,0,0,0,0,0,0, +0xa,0xa,0xa,0,0,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, +0xa,0xa,0xa,0xa,0,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, +0xa,0xa,0xa,0xa,2,2,2,2,2,2,2,2,2,2,2,0xa, +0xa,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0xa,0xa,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0xa,0xa,0xa,0xa, +0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, +0xa,0,0,0,0,0,0,0,0,0,0,0,0xa,0xa,0xa,0xa, +0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0,0,0,0xa,0xa,0xa,0xa, +0xa,0xa,0xa,0xa,0xa,0xa,0,0,0,0,0,0,0xa,0xa,0xa,0xa, +0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, +0xa,0xa,0xa,0xa,0xa,0,0,0,0,0,0,0,0xa,0xa,0xa,0xa, +0xa,0xa,0xa,0xa,0,0,0,0,0,0,0,0,0xa,0xa,0xa,0xa, +0xa,0xa,0xa,0xa,0xa,0xa,0,0,0,0,0,0,0xa,0xa,0xa,0xa, +0xa,0xa,0xa,0xa,0,0,0,0,0,0,0,0,0xa,0xa,0xa,0xa, +0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa, +0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0,0xa,0xa,0xa,0xa, +0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0,0,0xa, +0xa,0xa,0xa,0,0,0,0xa,0,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0, +0,0,0,0,0,0,0,0,0,0,0,0,0xa,0xa,0xa,0xa, +0xa,0xa,0xa,0xa,0xa,0xa,0,0,0,0,0,0,0xa,0xa,0xa,0, +0,0,0,0,0,0,0,0,0,0,0,0,0xa,0xa,0xa,0xa, +0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0x12,0x12,0xb2,0xb2,0xb2,0xb2,0xb2,0xb2,0xb2,0xb2, +0,0,0,0,0,0,0,0,0,0,0x12,0x12,0xb2,0xb2,0xb2,0xb2, 0xb2,0xb2,0xb2,0xb2,0xb2,0xb2,0xb2,0xb2,0xb2,0xb2,0xb2,0xb2,0xb2,0xb2,0xb2,0xb2, -0xb2,0xb2,0xb2,0xb2,0xb2,0xb2,0xb2,0xb2,0x12,0xb2,0x12,0x12,0x12,0x12,0x12,0x12, +0xb2,0xb2,0xb2,0xb2,0xb2,0xb2,0xb2,0xb2,0xb2,0xb2,0xb2,0xb2,0x12,0xb2,0x12,0x12, 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, -0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0xb1,0xb1,0xb1,0xb1, -0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0x12,0x12,0x12,0x12, -0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0,0,0,0 +0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, +0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1,0xb1, +0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, +0,0,0,0 }; -static const uint32_t ubidi_props_mirrors[26]={ -0x2000ab,0xbb,0x2a02215,0x1202243,0x2802298,0x2c022a6,0x30022a8,0x2e022a9,0x32022ab,0x6022cd,0x1e022f2,0x20022f3,0x22022f4,0x24022f6,0x26022f7,0x14022fa, -0x16022fb,0x18022fc,0x1a022fd,0x1c022fe,0x8029b8,0x4029f5,0xa02ade,0xe02ae3,0xc02ae4,0x1002ae5 +static const uint32_t ubidi_props_mirrors[40]={ +0x2000ab,0xbb,0x4202215,0x4e0221f,0x3e02220,0x3a02221,0x3c02222,0x4c02224,0x2202243,0x1402245,0x120224c,0x4002298,0x44022a6,0x48022a8,0x46022a9,0x4a022ab, +0x38022b8,0x10022cd,0x2e022f2,0x30022f3,0x32022f4,0x34022f6,0x36022f7,0x24022fa,0x26022fb,0x28022fc,0x2a022fd,0x2c022fe,0x20027dc,0xa0299b,0xc029a0,0x8029a3, +0x16029b8,0x4029f5,0x1802ade,0x1c02ae3,0x1a02ae4,0x1e02ae5,0xe02aee,0x602bfe }; static const uint8_t ubidi_props_jgArray[672]={ @@ -809,10 +826,46 @@ static const uint8_t ubidi_props_jgArray[672]={ 0xb,0x55,0x1f,1,0x13,0,4,4,4,0x1f,0x2d,0x56,0x58,0x57,0,0 }; -static const uint8_t ubidi_props_jgArray2[48]={ +static const uint8_t ubidi_props_jgArray2[612]={ 0x3a,0x3c,0x3c,0x40,0x40,0x3d,0,0x52,0,0x54,0x54,0,0,0x41,0x4f,0x53, 0x43,0x43,0x43,0x44,0x3e,0x50,0x45,0x46,0x4c,0x3b,0x3b,0x48,0x48,0x4b,0x49,0x49, -0x49,0x4a,0,0,0x4d,0,0,0,0,0,0,0x47,0x3f,0x4e,0x51,0x42 +0x49,0x4a,0,0,0x4d,0,0,0,0,0,0,0x47,0x3f,0x4e,0x51,0x42, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0x65,0,0,0,0,0,0,0x65,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0x64,0,0,0x65,0,0x64,0, +0x64,0,0,0x64 }; static const UBiDiProps ubidi_props_singleton={ @@ -823,16 +876,16 @@ static const UBiDiProps ubidi_props_singleton={ ubidi_props_jgArray2, { ubidi_props_trieIndex, - ubidi_props_trieIndex+3496, + ubidi_props_trieIndex+3516, NULL, - 3496, - 8360, + 3516, + 8584, 0x1a0, - 0xe28, + 0xe3c, 0x0, 0x0, 0x110000, - 0x2e4c, + 0x2f40, NULL, 0, FALSE, FALSE, 0, NULL }, { 2,2,0,0 } diff --git a/deps/icu-small/source/common/ucase.cpp b/deps/icu-small/source/common/ucase.cpp index 28d5a4cac62ba6..43c57f896e9348 100644 --- a/deps/icu-small/source/common/ucase.cpp +++ b/deps/icu-small/source/common/ucase.cpp @@ -138,6 +138,11 @@ ucase_tolower(UChar32 c) { } else { const uint16_t *pe=GET_EXCEPTIONS(&ucase_props_singleton, props); uint16_t excWord=*pe++; + if(HAS_SLOT(excWord, UCASE_EXC_DELTA) && UCASE_IS_UPPER_OR_TITLE(props)) { + int32_t delta; + GET_SLOT_VALUE(excWord, UCASE_EXC_DELTA, pe, delta); + return (excWord&UCASE_EXC_DELTA_IS_NEGATIVE)==0 ? c+delta : c-delta; + } if(HAS_SLOT(excWord, UCASE_EXC_LOWER)) { GET_SLOT_VALUE(excWord, UCASE_EXC_LOWER, pe, c); } @@ -155,6 +160,11 @@ ucase_toupper(UChar32 c) { } else { const uint16_t *pe=GET_EXCEPTIONS(&ucase_props_singleton, props); uint16_t excWord=*pe++; + if(HAS_SLOT(excWord, UCASE_EXC_DELTA) && UCASE_GET_TYPE(props)==UCASE_LOWER) { + int32_t delta; + GET_SLOT_VALUE(excWord, UCASE_EXC_DELTA, pe, delta); + return (excWord&UCASE_EXC_DELTA_IS_NEGATIVE)==0 ? c+delta : c-delta; + } if(HAS_SLOT(excWord, UCASE_EXC_UPPER)) { GET_SLOT_VALUE(excWord, UCASE_EXC_UPPER, pe, c); } @@ -172,6 +182,11 @@ ucase_totitle(UChar32 c) { } else { const uint16_t *pe=GET_EXCEPTIONS(&ucase_props_singleton, props); uint16_t excWord=*pe++; + if(HAS_SLOT(excWord, UCASE_EXC_DELTA) && UCASE_GET_TYPE(props)==UCASE_LOWER) { + int32_t delta; + GET_SLOT_VALUE(excWord, UCASE_EXC_DELTA, pe, delta); + return (excWord&UCASE_EXC_DELTA_IS_NEGATIVE)==0 ? c+delta : c-delta; + } int32_t idx; if(HAS_SLOT(excWord, UCASE_EXC_TITLE)) { idx=UCASE_EXC_TITLE; @@ -254,6 +269,11 @@ ucase_addCaseClosure(UChar32 c, const USetAdder *sa) { sa->add(sa->set, c); } } + if(HAS_SLOT(excWord, UCASE_EXC_DELTA)) { + int32_t delta; + GET_SLOT_VALUE(excWord, UCASE_EXC_DELTA, pe, delta); + sa->add(sa->set, (excWord&UCASE_EXC_DELTA_IS_NEGATIVE)==0 ? c+delta : c-delta); + } /* get the closure string pointer & length */ if(HAS_SLOT(excWord, UCASE_EXC_CLOSURE)) { @@ -590,7 +610,12 @@ ucase_isSoftDotted(UChar32 c) { U_CAPI UBool U_EXPORT2 ucase_isCaseSensitive(UChar32 c) { uint16_t props=UTRIE2_GET16(&ucase_props_singleton.trie, c); - return (UBool)((props&UCASE_SENSITIVE)!=0); + if(!UCASE_HAS_EXCEPTION(props)) { + return (UBool)((props&UCASE_SENSITIVE)!=0); + } else { + const uint16_t *pe=GET_EXCEPTIONS(&ucase_props_singleton, props); + return (UBool)((*pe&UCASE_EXC_SENSITIVE)!=0); + } } /* string casing ------------------------------------------------------------ */ @@ -1140,6 +1165,11 @@ ucase_toFullLower(UChar32 c, } } + if(HAS_SLOT(excWord, UCASE_EXC_DELTA) && UCASE_IS_UPPER_OR_TITLE(props)) { + int32_t delta; + GET_SLOT_VALUE(excWord, UCASE_EXC_DELTA, pe, delta); + return (excWord&UCASE_EXC_DELTA_IS_NEGATIVE)==0 ? c+delta : c-delta; + } if(HAS_SLOT(excWord, UCASE_EXC_LOWER)) { GET_SLOT_VALUE(excWord, UCASE_EXC_LOWER, pe2, result); } @@ -1229,6 +1259,11 @@ toUpperOrTitle(UChar32 c, } } + if(HAS_SLOT(excWord, UCASE_EXC_DELTA) && UCASE_GET_TYPE(props)==UCASE_LOWER) { + int32_t delta; + GET_SLOT_VALUE(excWord, UCASE_EXC_DELTA, pe, delta); + return (excWord&UCASE_EXC_DELTA_IS_NEGATIVE)==0 ? c+delta : c-delta; + } if(!upperNotTitle && HAS_SLOT(excWord, UCASE_EXC_TITLE)) { idx=UCASE_EXC_TITLE; } else if(HAS_SLOT(excWord, UCASE_EXC_UPPER)) { @@ -1334,6 +1369,14 @@ ucase_fold(UChar32 c, uint32_t options) { } } } + if((excWord&UCASE_EXC_NO_SIMPLE_CASE_FOLDING)!=0) { + return c; + } + if(HAS_SLOT(excWord, UCASE_EXC_DELTA) && UCASE_IS_UPPER_OR_TITLE(props)) { + int32_t delta; + GET_SLOT_VALUE(excWord, UCASE_EXC_DELTA, pe, delta); + return (excWord&UCASE_EXC_DELTA_IS_NEGATIVE)==0 ? c+delta : c-delta; + } if(HAS_SLOT(excWord, UCASE_EXC_FOLD)) { idx=UCASE_EXC_FOLD; } else if(HAS_SLOT(excWord, UCASE_EXC_LOWER)) { @@ -1421,6 +1464,14 @@ ucase_toFullFolding(UChar32 c, } } + if((excWord&UCASE_EXC_NO_SIMPLE_CASE_FOLDING)!=0) { + return ~c; + } + if(HAS_SLOT(excWord, UCASE_EXC_DELTA) && UCASE_IS_UPPER_OR_TITLE(props)) { + int32_t delta; + GET_SLOT_VALUE(excWord, UCASE_EXC_DELTA, pe, delta); + return (excWord&UCASE_EXC_DELTA_IS_NEGATIVE)==0 ? c+delta : c-delta; + } if(HAS_SLOT(excWord, UCASE_EXC_FOLD)) { idx=UCASE_EXC_FOLD; } else if(HAS_SLOT(excWord, UCASE_EXC_LOWER)) { diff --git a/deps/icu-small/source/common/ucase.h b/deps/icu-small/source/common/ucase.h index a7a8c9f00d1e93..b0a453b87e8afa 100644 --- a/deps/icu-small/source/common/ucase.h +++ b/deps/icu-small/source/common/ucase.h @@ -354,8 +354,8 @@ enum { #define UCASE_IS_UPPER_OR_TITLE(props) ((props)&2) #define UCASE_IGNORABLE 4 -#define UCASE_SENSITIVE 8 -#define UCASE_EXCEPTION 0x10 +#define UCASE_EXCEPTION 8 +#define UCASE_SENSITIVE 0x10 #define UCASE_HAS_EXCEPTION(props) ((props)&UCASE_EXCEPTION) @@ -379,9 +379,9 @@ enum { # define UCASE_GET_DELTA(props) (int16_t)(((props)&0x8000) ? (((props)>>UCASE_DELTA_SHIFT)|0xfe00) : ((uint16_t)(props)>>UCASE_DELTA_SHIFT)) #endif -/* exception: bits 15..5 are an unsigned 11-bit index into the exceptions array */ -#define UCASE_EXC_SHIFT 5 -#define UCASE_EXC_MASK 0xffe0 +/* exception: bits 15..4 are an unsigned 12-bit index into the exceptions array */ +#define UCASE_EXC_SHIFT 4 +#define UCASE_EXC_MASK 0xfff0 #define UCASE_MAX_EXCEPTIONS ((UCASE_EXC_MASK>>UCASE_EXC_SHIFT)+1) /* definitions for 16-bit main exceptions word ------------------------------ */ @@ -392,7 +392,7 @@ enum { UCASE_EXC_FOLD, UCASE_EXC_UPPER, UCASE_EXC_TITLE, - UCASE_EXC_4, /* reserved */ + UCASE_EXC_DELTA, UCASE_EXC_5, /* reserved */ UCASE_EXC_CLOSURE, UCASE_EXC_FULL_MAPPINGS, @@ -402,7 +402,11 @@ enum { /* each slot is 2 uint16_t instead of 1 */ #define UCASE_EXC_DOUBLE_SLOTS 0x100 -/* reserved: exception bits 11..9 */ +enum { + UCASE_EXC_NO_SIMPLE_CASE_FOLDING=0x200, + UCASE_EXC_DELTA_IS_NEGATIVE=0x400, + UCASE_EXC_SENSITIVE=0x800 +}; /* UCASE_EXC_DOT_MASK=UCASE_DOT_MASK< *maxMatchLen && len <= textLen && uprv_memcmp(currencyNames[index].currencyName, text, len * sizeof(UChar)) == 0) { + *partialMatchLen = MAX(*partialMatchLen, len); *maxMatchIndex = index; *maxMatchLen = len; #ifdef UCURR_DEBUG printf("maxMatchIndex = %d, maxMatchLen = %d\n", *maxMatchIndex, *maxMatchLen); #endif + } else { + // Check for partial matches. + for (int32_t i=initialPartialMatchLen; icurrencyNames; - total_currency_name_count = cacheEntry->totalCurrencyNameCount; - currencySymbols = cacheEntry->currencySymbols; - total_currency_symbol_count = cacheEntry->totalCurrencySymbolCount; ++(cacheEntry->refCount); } umtx_unlock(&gCurrencyCacheMutex); if (found == -1) { collectCurrencyNames(locale, ¤cyNames, &total_currency_name_count, ¤cySymbols, &total_currency_symbol_count, ec); if (U_FAILURE(ec)) { - return; + return NULL; } umtx_lock(&gCurrencyCacheMutex); // check again. @@ -1500,20 +1500,50 @@ uprv_parseCurrency(const char* locale, cacheEntry->totalCurrencySymbolCount = total_currency_symbol_count; cacheEntry->refCount = 2; // one for cache, one for reference currentCacheEntryIndex = (currentCacheEntryIndex + 1) % CURRENCY_NAME_CACHE_NUM; - ucln_common_registerCleanup(UCLN_COMMON_CURRENCY, currency_cache_cleanup); + ucln_common_registerCleanup(UCLN_COMMON_CURRENCY, currency_cleanup); } else { deleteCurrencyNames(currencyNames, total_currency_name_count); deleteCurrencyNames(currencySymbols, total_currency_symbol_count); cacheEntry = currCache[found]; - currencyNames = cacheEntry->currencyNames; - total_currency_name_count = cacheEntry->totalCurrencyNameCount; - currencySymbols = cacheEntry->currencySymbols; - total_currency_symbol_count = cacheEntry->totalCurrencySymbolCount; ++(cacheEntry->refCount); } umtx_unlock(&gCurrencyCacheMutex); } + return cacheEntry; +} + +static void releaseCacheEntry(CurrencyNameCacheEntry* cacheEntry) { + umtx_lock(&gCurrencyCacheMutex); + --(cacheEntry->refCount); + if (cacheEntry->refCount == 0) { // remove + deleteCacheEntry(cacheEntry); + } + umtx_unlock(&gCurrencyCacheMutex); +} + +U_CAPI void +uprv_parseCurrency(const char* locale, + const icu::UnicodeString& text, + icu::ParsePosition& pos, + int8_t type, + int32_t* partialMatchLen, + UChar* result, + UErrorCode& ec) { + U_NAMESPACE_USE + if (U_FAILURE(ec)) { + return; + } + CurrencyNameCacheEntry* cacheEntry = getCacheEntry(locale, ec); + if (U_FAILURE(ec)) { + return; + } + + int32_t total_currency_name_count = cacheEntry->totalCurrencyNameCount; + CurrencyNameStruct* currencyNames = cacheEntry->currencyNames; + int32_t total_currency_symbol_count = cacheEntry->totalCurrencySymbolCount; + CurrencyNameStruct* currencySymbols = cacheEntry->currencySymbols; + int32_t start = pos.getIndex(); UChar inputText[MAX_CURRENCY_NAME_LEN]; @@ -1523,11 +1553,14 @@ uprv_parseCurrency(const char* locale, UErrorCode ec1 = U_ZERO_ERROR; textLen = u_strToUpper(upperText, MAX_CURRENCY_NAME_LEN, inputText, textLen, locale, &ec1); + // Make sure partialMatchLen is initialized + *partialMatchLen = 0; + int32_t max = 0; int32_t matchIndex = -1; // case in-sensitive comparision against currency names searchCurrencyName(currencyNames, total_currency_name_count, - upperText, textLen, &max, &matchIndex); + upperText, textLen, partialMatchLen, &max, &matchIndex); #ifdef UCURR_DEBUG printf("search in names, max = %d, matchIndex = %d\n", max, matchIndex); @@ -1539,6 +1572,7 @@ uprv_parseCurrency(const char* locale, // case sensitive comparison against currency symbols and ISO code. searchCurrencyName(currencySymbols, total_currency_symbol_count, inputText, textLen, + partialMatchLen, &maxInSymbol, &matchIndexInSymbol); } @@ -1558,12 +1592,35 @@ uprv_parseCurrency(const char* locale, } // decrease reference count - umtx_lock(&gCurrencyCacheMutex); - --(cacheEntry->refCount); - if (cacheEntry->refCount == 0) { // remove - deleteCacheEntry(cacheEntry); + releaseCacheEntry(cacheEntry); +} + +void uprv_currencyLeads(const char* locale, icu::UnicodeSet& result, UErrorCode& ec) { + U_NAMESPACE_USE + if (U_FAILURE(ec)) { + return; } - umtx_unlock(&gCurrencyCacheMutex); + CurrencyNameCacheEntry* cacheEntry = getCacheEntry(locale, ec); + if (U_FAILURE(ec)) { + return; + } + + for (int32_t i=0; itotalCurrencySymbolCount; i++) { + const CurrencyNameStruct& info = cacheEntry->currencySymbols[i]; + UChar32 cp; + U16_GET(info.currencyName, 0, 0, info.currencyNameLen, cp); + result.add(cp); + } + + for (int32_t i=0; itotalCurrencyNameCount; i++) { + const CurrencyNameStruct& info = cacheEntry->currencyNames[i]; + UChar32 cp; + U16_GET(info.currencyName, 0, 0, info.currencyNameLen, cp); + result.add(cp); + } + + // decrease reference count + releaseCacheEntry(cacheEntry); } @@ -1729,7 +1786,8 @@ static const struct CurrencyList { {"BUK", UCURR_COMMON|UCURR_DEPRECATED}, {"BWP", UCURR_COMMON|UCURR_NON_DEPRECATED}, {"BYB", UCURR_COMMON|UCURR_DEPRECATED}, - {"BYR", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"BYN", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"BYR", UCURR_COMMON|UCURR_DEPRECATED}, {"BZD", UCURR_COMMON|UCURR_NON_DEPRECATED}, {"CAD", UCURR_COMMON|UCURR_NON_DEPRECATED}, {"CDF", UCURR_COMMON|UCURR_NON_DEPRECATED}, @@ -1739,6 +1797,7 @@ static const struct CurrencyList { {"CLE", UCURR_COMMON|UCURR_DEPRECATED}, {"CLF", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, {"CLP", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"CNH", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, {"CNX", UCURR_UNCOMMON|UCURR_DEPRECATED}, {"CNY", UCURR_COMMON|UCURR_NON_DEPRECATED}, {"COP", UCURR_COMMON|UCURR_NON_DEPRECATED}, @@ -1761,7 +1820,7 @@ static const struct CurrencyList { {"ECV", UCURR_UNCOMMON|UCURR_DEPRECATED}, {"EEK", UCURR_COMMON|UCURR_DEPRECATED}, {"EGP", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"EQE", UCURR_COMMON|UCURR_DEPRECATED}, + {"EQE", UCURR_COMMON|UCURR_DEPRECATED}, // questionable, remove? {"ERN", UCURR_COMMON|UCURR_NON_DEPRECATED}, {"ESA", UCURR_UNCOMMON|UCURR_DEPRECATED}, {"ESB", UCURR_UNCOMMON|UCURR_DEPRECATED}, @@ -1785,7 +1844,7 @@ static const struct CurrencyList { {"GRD", UCURR_COMMON|UCURR_DEPRECATED}, {"GTQ", UCURR_COMMON|UCURR_NON_DEPRECATED}, {"GWE", UCURR_COMMON|UCURR_DEPRECATED}, - {"GWP", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"GWP", UCURR_COMMON|UCURR_DEPRECATED}, {"GYD", UCURR_COMMON|UCURR_NON_DEPRECATED}, {"HKD", UCURR_COMMON|UCURR_NON_DEPRECATED}, {"HNL", UCURR_COMMON|UCURR_NON_DEPRECATED}, @@ -1823,13 +1882,13 @@ static const struct CurrencyList { {"LKR", UCURR_COMMON|UCURR_NON_DEPRECATED}, {"LRD", UCURR_COMMON|UCURR_NON_DEPRECATED}, {"LSL", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"LSM", UCURR_COMMON|UCURR_DEPRECATED}, - {"LTL", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"LSM", UCURR_COMMON|UCURR_DEPRECATED}, // questionable, remove? + {"LTL", UCURR_COMMON|UCURR_DEPRECATED}, {"LTT", UCURR_COMMON|UCURR_DEPRECATED}, {"LUC", UCURR_UNCOMMON|UCURR_DEPRECATED}, {"LUF", UCURR_COMMON|UCURR_DEPRECATED}, {"LUL", UCURR_UNCOMMON|UCURR_DEPRECATED}, - {"LVL", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"LVL", UCURR_COMMON|UCURR_DEPRECATED}, {"LVR", UCURR_COMMON|UCURR_DEPRECATED}, {"LYD", UCURR_COMMON|UCURR_NON_DEPRECATED}, {"MAD", UCURR_COMMON|UCURR_NON_DEPRECATED}, @@ -1845,18 +1904,19 @@ static const struct CurrencyList { {"MMK", UCURR_COMMON|UCURR_NON_DEPRECATED}, {"MNT", UCURR_COMMON|UCURR_NON_DEPRECATED}, {"MOP", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"MRO", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"MRO", UCURR_COMMON|UCURR_DEPRECATED}, + {"MRU", UCURR_COMMON|UCURR_NON_DEPRECATED}, {"MTL", UCURR_COMMON|UCURR_DEPRECATED}, {"MTP", UCURR_COMMON|UCURR_DEPRECATED}, {"MUR", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"MVP", UCURR_COMMON|UCURR_DEPRECATED}, + {"MVP", UCURR_COMMON|UCURR_DEPRECATED}, // questionable, remove? {"MVR", UCURR_COMMON|UCURR_NON_DEPRECATED}, {"MWK", UCURR_COMMON|UCURR_NON_DEPRECATED}, {"MXN", UCURR_COMMON|UCURR_NON_DEPRECATED}, {"MXP", UCURR_COMMON|UCURR_DEPRECATED}, {"MXV", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, {"MYR", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"MZE", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"MZE", UCURR_COMMON|UCURR_DEPRECATED}, {"MZM", UCURR_COMMON|UCURR_DEPRECATED}, {"MZN", UCURR_COMMON|UCURR_NON_DEPRECATED}, {"NAD", UCURR_COMMON|UCURR_NON_DEPRECATED}, @@ -1897,15 +1957,16 @@ static const struct CurrencyList { {"SGD", UCURR_COMMON|UCURR_NON_DEPRECATED}, {"SHP", UCURR_COMMON|UCURR_NON_DEPRECATED}, {"SIT", UCURR_COMMON|UCURR_DEPRECATED}, - {"SKK", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"SKK", UCURR_COMMON|UCURR_DEPRECATED}, {"SLL", UCURR_COMMON|UCURR_NON_DEPRECATED}, {"SOS", UCURR_COMMON|UCURR_NON_DEPRECATED}, {"SRD", UCURR_COMMON|UCURR_NON_DEPRECATED}, {"SRG", UCURR_COMMON|UCURR_DEPRECATED}, {"SSP", UCURR_COMMON|UCURR_NON_DEPRECATED}, - {"STD", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"STD", UCURR_COMMON|UCURR_DEPRECATED}, + {"STN", UCURR_COMMON|UCURR_NON_DEPRECATED}, {"SUR", UCURR_COMMON|UCURR_DEPRECATED}, - {"SVC", UCURR_COMMON|UCURR_NON_DEPRECATED}, + {"SVC", UCURR_COMMON|UCURR_DEPRECATED}, {"SYP", UCURR_COMMON|UCURR_NON_DEPRECATED}, {"SZL", UCURR_COMMON|UCURR_NON_DEPRECATED}, {"THB", UCURR_COMMON|UCURR_NON_DEPRECATED}, @@ -1954,7 +2015,7 @@ static const struct CurrencyList { {"XPD", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, {"XPF", UCURR_COMMON|UCURR_NON_DEPRECATED}, {"XPT", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, - {"XRE", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, + {"XRE", UCURR_UNCOMMON|UCURR_DEPRECATED}, {"XSU", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, {"XTS", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, {"XUA", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, @@ -1965,15 +2026,15 @@ static const struct CurrencyList { {"YUM", UCURR_COMMON|UCURR_DEPRECATED}, {"YUN", UCURR_COMMON|UCURR_DEPRECATED}, {"YUR", UCURR_COMMON|UCURR_DEPRECATED}, - {"ZAL", UCURR_UNCOMMON|UCURR_NON_DEPRECATED}, + {"ZAL", UCURR_UNCOMMON|UCURR_DEPRECATED}, {"ZAR", UCURR_COMMON|UCURR_NON_DEPRECATED}, {"ZMK", UCURR_COMMON|UCURR_DEPRECATED}, {"ZMW", UCURR_COMMON|UCURR_NON_DEPRECATED}, {"ZRN", UCURR_COMMON|UCURR_DEPRECATED}, {"ZRZ", UCURR_COMMON|UCURR_DEPRECATED}, + {"ZWD", UCURR_COMMON|UCURR_DEPRECATED}, {"ZWL", UCURR_COMMON|UCURR_DEPRECATED}, {"ZWR", UCURR_COMMON|UCURR_DEPRECATED}, - {"ZWD", UCURR_COMMON|UCURR_DEPRECATED}, { NULL, 0 } // Leave here to denote the end of the list. }; @@ -2144,16 +2205,20 @@ static void U_CALLCONV initIsoCodes(UErrorCode &status) { } static void populateCurrSymbolsEquiv(icu::Hashtable *hash, UErrorCode &status) { - if (U_FAILURE(status)) { - return; - } - int32_t length = UPRV_LENGTHOF(EQUIV_CURRENCY_SYMBOLS); - for (int32_t i = 0; i < length; ++i) { - icu::UnicodeString lhs(EQUIV_CURRENCY_SYMBOLS[i][0], -1, US_INV); - icu::UnicodeString rhs(EQUIV_CURRENCY_SYMBOLS[i][1], -1, US_INV); - makeEquivalent(lhs.unescape(), rhs.unescape(), hash, status); - if (U_FAILURE(status)) { - return; + if (U_FAILURE(status)) { return; } + for (auto& entry : unisets::kCurrencyEntries) { + UnicodeString exemplar(entry.exemplar); + const UnicodeSet* set = unisets::get(entry.key); + if (set == nullptr) { return; } + UnicodeSetIterator it(*set); + while (it.next()) { + UnicodeString value = it.getString(); + if (value == exemplar) { + // No need to mark the exemplar character as an equivalent + continue; + } + makeEquivalent(exemplar, value, hash, status); + if (U_FAILURE(status)) { return; } } } } diff --git a/deps/icu-small/source/common/ucurrimp.h b/deps/icu-small/source/common/ucurrimp.h index 6e468fd4c94299..6d9588295df7bb 100644 --- a/deps/icu-small/source/common/ucurrimp.h +++ b/deps/icu-small/source/common/ucurrimp.h @@ -13,6 +13,7 @@ #include "unicode/utypes.h" #include "unicode/unistr.h" #include "unicode/parsepos.h" +#include "unicode/uniset.h" /** * Internal method. Given a currency ISO code and a locale, return @@ -36,6 +37,8 @@ uprv_getStaticCurrencyName(const UChar* iso, const char* loc, * match, then the display name is preferred, unless it's length * is less than 3. * + * The parameters must not be NULL. + * * @param locale the locale of the display names to match * @param text the text to parse * @param pos input-output position; on input, the position within @@ -43,6 +46,8 @@ uprv_getStaticCurrencyName(const UChar* iso, const char* loc, * on output, the position after the last matched character. If * the parse fails, the position in unchanged upon output. * @param type currency type to parse against, LONG_NAME only or not + * @param partialMatchLen The length of the longest matching prefix; + * this may be nonzero even if no full currency was matched. * @return the ISO 4217 code, as a string, of the best match, or * null if there is no match * @@ -53,9 +58,21 @@ uprv_parseCurrency(const char* locale, const icu::UnicodeString& text, icu::ParsePosition& pos, int8_t type, + int32_t* partialMatchLen, UChar* result, UErrorCode& ec); +/** + * Puts all possible first-characters of a currency into the + * specified UnicodeSet. + * + * @param locale the locale of the display names of interest + * @param result the UnicodeSet to which to add the starting characters + */ +void uprv_currencyLeads(const char* locale, icu::UnicodeSet& result, UErrorCode& ec); + + + #endif /* #ifndef _UCURR_IMP_H_ */ //eof diff --git a/deps/icu-small/source/common/unicode/brkiter.h b/deps/icu-small/source/common/unicode/brkiter.h index 607f3ec625ab1f..5faeedfa93ecb8 100644 --- a/deps/icu-small/source/common/unicode/brkiter.h +++ b/deps/icu-small/source/common/unicode/brkiter.h @@ -638,7 +638,7 @@ class U_COMMON_API BreakIterator : public UObject { private: - /** @internal */ + /** @internal (private) */ char actualLocale[ULOC_FULLNAME_CAPACITY]; char validLocale[ULOC_FULLNAME_CAPACITY]; }; diff --git a/deps/icu-small/source/common/unicode/bytestriebuilder.h b/deps/icu-small/source/common/unicode/bytestriebuilder.h index 7a806bb7f02096..b164e3bbd685f7 100644 --- a/deps/icu-small/source/common/unicode/bytestriebuilder.h +++ b/deps/icu-small/source/common/unicode/bytestriebuilder.h @@ -143,7 +143,7 @@ class U_COMMON_API BytesTrieBuilder : public StringTrieBuilder { virtual int32_t getMaxLinearMatchLength() const { return BytesTrie::kMaxLinearMatchLength; } /** - * @internal + * @internal (private) */ class BTLinearMatchNode : public LinearMatchNode { public: diff --git a/deps/icu-small/source/common/unicode/caniter.h b/deps/icu-small/source/common/unicode/caniter.h index 543341f42c5fe2..b47e35da07bb7f 100644 --- a/deps/icu-small/source/common/unicode/caniter.h +++ b/deps/icu-small/source/common/unicode/caniter.h @@ -153,13 +153,13 @@ class U_COMMON_API CanonicalIterator U_FINAL : public UObject { /** * Copy constructor. Private for now. - * @internal + * @internal (private) */ CanonicalIterator(const CanonicalIterator& other); /** * Assignment operator. Private for now. - * @internal + * @internal (private) */ CanonicalIterator& operator=(const CanonicalIterator& other); diff --git a/deps/icu-small/source/common/unicode/docmain.h b/deps/icu-small/source/common/unicode/docmain.h index 3e645aee4a87fb..91e5ae3fa2e2e6 100644 --- a/deps/icu-small/source/common/unicode/docmain.h +++ b/deps/icu-small/source/common/unicode/docmain.h @@ -139,7 +139,7 @@ * * * Number Formatting - * unum.h + * unumberformatter.h, unum.h * icu::number::NumberFormatter (ICU 60+) or icu::NumberFormat (older versions) * * diff --git a/deps/icu-small/source/common/unicode/edits.h b/deps/icu-small/source/common/unicode/edits.h index 5a72574c140db6..f767a8d3b494c3 100644 --- a/deps/icu-small/source/common/unicode/edits.h +++ b/deps/icu-small/source/common/unicode/edits.h @@ -17,10 +17,57 @@ U_NAMESPACE_BEGIN +class UnicodeString; + /** - * Records lengths of string edits but not replacement text. - * Supports replacements, insertions, deletions in linear progression. - * Does not support moving/reordering of text. + * Records lengths of string edits but not replacement text. Supports replacements, insertions, deletions + * in linear progression. Does not support moving/reordering of text. + * + * There are two types of edits: change edits and no-change edits. Add edits to + * instances of this class using {@link #addReplace(int, int)} (for change edits) and + * {@link #addUnchanged(int)} (for no-change edits). Change edits are retained with full granularity, + * whereas adjacent no-change edits are always merged together. In no-change edits, there is a one-to-one + * mapping between code points in the source and destination strings. + * + * After all edits have been added, instances of this class should be considered immutable, and an + * {@link Edits::Iterator} can be used for queries. + * + * There are four flavors of Edits::Iterator: + * + *
    + *
  • {@link #getFineIterator()} retains full granularity of change edits. + *
  • {@link #getFineChangesIterator()} retains full granularity of change edits, and when calling + * next() on the iterator, skips over no-change edits (unchanged regions). + *
  • {@link #getCoarseIterator()} treats adjacent change edits as a single edit. (Adjacent no-change + * edits are automatically merged during the construction phase.) + *
  • {@link #getCoarseChangesIterator()} treats adjacent change edits as a single edit, and when + * calling next() on the iterator, skips over no-change edits (unchanged regions). + *
+ * + * For example, consider the string "abcßDeF", which case-folds to "abcssdef". This string has the + * following fine edits: + *
    + *
  • abc ⇨ abc (no-change) + *
  • ß ⇨ ss (change) + *
  • D ⇨ d (change) + *
  • e ⇨ e (no-change) + *
  • F ⇨ f (change) + *
+ * and the following coarse edits (note how adjacent change edits get merged together): + *
    + *
  • abc ⇨ abc (no-change) + *
  • ßD ⇨ ssd (change) + *
  • e ⇨ e (no-change) + *
  • F ⇨ f (change) + *
+ * + * The "fine changes" and "coarse changes" iterators will step through only the change edits when their + * {@link Edits::Iterator#next()} methods are called. They are identical to the non-change iterators when + * their {@link Edits::Iterator#findSourceIndex(int)} or {@link Edits::Iterator#findDestinationIndex(int)} + * methods are used to walk through the string. + * + * For examples of how to use this class, see the test TestCaseMapEditsIteratorDocs in + * UCharacterCaseTest.java. * * An Edits object tracks a separate UErrorCode, but ICU string transformation functions * (e.g., case mapping functions) merge any such errors into their API's UErrorCode. @@ -91,13 +138,13 @@ class U_COMMON_API Edits U_FINAL : public UMemory { void reset() U_NOEXCEPT; /** - * Adds a record for an unchanged segment of text. + * Adds a no-change edit: a record for an unchanged segment of text. * Normally called from inside ICU string transformation functions, not user code. * @stable ICU 59 */ void addUnchanged(int32_t unchangedLength); /** - * Adds a record for a text replacement/insertion/deletion. + * Adds a change edit: a record for a text replacement/insertion/deletion. * Normally called from inside ICU string transformation functions, not user code. * @stable ICU 59 */ @@ -136,6 +183,18 @@ class U_COMMON_API Edits U_FINAL : public UMemory { /** * Access to the list of edits. + * + * At any moment in time, an instance of this class points to a single edit: a "window" into a span + * of the source string and the corresponding span of the destination string. The source string span + * starts at {@link #sourceIndex()} and runs for {@link #oldLength()} chars; the destination string + * span starts at {@link #destinationIndex()} and runs for {@link #newLength()} chars. + * + * The iterator can be moved between edits using the {@link #next()}, {@link #findSourceIndex(int)}, + * and {@link #findDestinationIndex(int)} methods. Calling any of these methods mutates the iterator + * to make it point to the corresponding edit. + * + * For more information, see the documentation for {@link Edits}. + * * @see getCoarseIterator * @see getFineIterator * @stable ICU 59 @@ -162,7 +221,7 @@ class U_COMMON_API Edits U_FINAL : public UMemory { Iterator &operator=(const Iterator &other) = default; /** - * Advances to the next edit. + * Advances the iterator to the next edit. * @param errorCode ICU error code. Its input value must pass the U_SUCCESS() test, * or else the function returns immediately. Check for U_FAILURE() * on output or use with function chaining. (See User Guide for details.) @@ -172,9 +231,9 @@ class U_COMMON_API Edits U_FINAL : public UMemory { UBool next(UErrorCode &errorCode) { return next(onlyChanges_, errorCode); } /** - * Finds the edit that contains the source index. - * The source index may be found in a non-change - * even if normal iteration would skip non-changes. + * Moves the iterator to the edit that contains the source index. + * The source index may be found in a no-change edit + * even if normal iteration would skip no-change edits. * Normal iteration can continue from a found edit. * * The iterator state before this search logically does not matter. @@ -196,9 +255,9 @@ class U_COMMON_API Edits U_FINAL : public UMemory { #ifndef U_HIDE_DRAFT_API /** - * Finds the edit that contains the destination index. - * The destination index may be found in a non-change - * even if normal iteration would skip non-changes. + * Moves the iterator to the edit that contains the destination index. + * The destination index may be found in a no-change edit + * even if normal iteration would skip no-change edits. * Normal iteration can continue from a found edit. * * The iterator state before this search logically does not matter. @@ -219,7 +278,7 @@ class U_COMMON_API Edits U_FINAL : public UMemory { } /** - * Returns the destination index corresponding to the given source index. + * Computes the destination index corresponding to the given source index. * If the source index is inside a change edit (not at its start), * then the destination index at the end of that edit is returned, * since there is no information about index mapping inside a change edit. @@ -243,7 +302,7 @@ class U_COMMON_API Edits U_FINAL : public UMemory { int32_t destinationIndexFromSourceIndex(int32_t i, UErrorCode &errorCode); /** - * Returns the source index corresponding to the given destination index. + * Computes the source index corresponding to the given destination index. * If the destination index is inside a change edit (not at its start), * then the source index at the end of that edit is returned, * since there is no information about index mapping inside a change edit. @@ -268,17 +327,27 @@ class U_COMMON_API Edits U_FINAL : public UMemory { #endif // U_HIDE_DRAFT_API /** + * Returns whether the edit currently represented by the iterator is a change edit. + * * @return TRUE if this edit replaces oldLength() units with newLength() different ones. * FALSE if oldLength units remain unchanged. * @stable ICU 59 */ UBool hasChange() const { return changed; } + /** + * The length of the current span in the source string, which starts at {@link #sourceIndex}. + * * @return the number of units in the original string which are replaced or remain unchanged. * @stable ICU 59 */ int32_t oldLength() const { return oldLength_; } + /** + * The length of the current span in the destination string, which starts at + * {@link #destinationIndex}, or in the replacement string, which starts at + * {@link #replacementIndex}. + * * @return the number of units in the modified string, if hasChange() is TRUE. * Same as oldLength if hasChange() is FALSE. * @stable ICU 59 @@ -286,22 +355,52 @@ class U_COMMON_API Edits U_FINAL : public UMemory { int32_t newLength() const { return newLength_; } /** + * The start index of the current span in the source string; the span has length + * {@link #oldLength}. + * * @return the current index into the source string * @stable ICU 59 */ int32_t sourceIndex() const { return srcIndex; } + /** + * The start index of the current span in the replacement string; the span has length + * {@link #newLength}. Well-defined only if the current edit is a change edit. + *

+ * The replacement string is the concatenation of all substrings of the destination + * string corresponding to change edits. + *

+ * This method is intended to be used together with operations that write only replacement + * characters (e.g., {@link CaseMap#omitUnchangedText()}). The source string can then be modified + * in-place. + * * @return the current index into the replacement-characters-only string, * not counting unchanged spans * @stable ICU 59 */ - int32_t replacementIndex() const { return replIndex; } + int32_t replacementIndex() const { + // TODO: Throw an exception if we aren't in a change edit? + return replIndex; + } + /** + * The start index of the current span in the destination string; the span has length + * {@link #newLength}. + * * @return the current index into the full destination string * @stable ICU 59 */ int32_t destinationIndex() const { return destIndex; } +#ifndef U_HIDE_INTERNAL_API + /** + * A string representation of the current edit represented by the iterator for debugging. You + * should not depend on the contents of the return string. + * @internal + */ + UnicodeString& toString(UnicodeString& appendTo) const; +#endif // U_HIDE_INTERNAL_API + private: friend class Edits; @@ -330,8 +429,10 @@ class U_COMMON_API Edits U_FINAL : public UMemory { }; /** - * Returns an Iterator for coarse-grained changes for simple string updates. - * Skips non-changes. + * Returns an Iterator for coarse-grained change edits + * (adjacent change edits are treated as one). + * Can be used to perform simple string updates. + * Skips no-change edits. * @return an Iterator that merges adjacent changes. * @stable ICU 59 */ @@ -340,7 +441,10 @@ class U_COMMON_API Edits U_FINAL : public UMemory { } /** - * Returns an Iterator for coarse-grained changes and non-changes for simple string updates. + * Returns an Iterator for coarse-grained change and no-change edits + * (adjacent change edits are treated as one). + * Can be used to perform simple string updates. + * Adjacent change edits are treated as one edit. * @return an Iterator that merges adjacent changes. * @stable ICU 59 */ @@ -349,8 +453,10 @@ class U_COMMON_API Edits U_FINAL : public UMemory { } /** - * Returns an Iterator for fine-grained changes for modifying styled text. - * Skips non-changes. + * Returns an Iterator for fine-grained change edits + * (full granularity of change edits is retained). + * Can be used for modifying styled text. + * Skips no-change edits. * @return an Iterator that separates adjacent changes. * @stable ICU 59 */ @@ -359,7 +465,9 @@ class U_COMMON_API Edits U_FINAL : public UMemory { } /** - * Returns an Iterator for fine-grained changes and non-changes for modifying styled text. + * Returns an Iterator for fine-grained change and no-change edits + * (full granularity of change edits is retained). + * Can be used for modifying styled text. * @return an Iterator that separates adjacent changes. * @stable ICU 59 */ diff --git a/deps/icu-small/source/common/unicode/platform.h b/deps/icu-small/source/common/unicode/platform.h index a3f8d32f89d2cd..d9636580c309b5 100644 --- a/deps/icu-small/source/common/unicode/platform.h +++ b/deps/icu-small/source/common/unicode/platform.h @@ -196,20 +196,6 @@ # define U_PLATFORM U_PF_UNKNOWN #endif -/** - * \def UPRV_INCOMPLETE_CPP11_SUPPORT - * This switch turns off ICU 60 NumberFormatter code. - * By default, this switch is enabled on AIX and z/OS, - * which have poor C++11 support. - * - * NOTE: This switch is intended to be temporary; see #13393. - * - * @internal - */ -#ifndef UPRV_INCOMPLETE_CPP11_SUPPORT -# define UPRV_INCOMPLETE_CPP11_SUPPORT (U_PLATFORM == U_PF_AIX || U_PLATFORM == U_PF_OS390 || U_PLATFORM == U_PF_SOLARIS ) -#endif - /** * \def CYGWINMSVC * Defined if this is Windows with Cygwin, but using MSVC rather than gcc. diff --git a/deps/icu-small/source/common/unicode/rbbi.h b/deps/icu-small/source/common/unicode/rbbi.h index 0c41d69d235ccb..e9b82cd520736b 100644 --- a/deps/icu-small/source/common/unicode/rbbi.h +++ b/deps/icu-small/source/common/unicode/rbbi.h @@ -55,7 +55,7 @@ class U_COMMON_API RuleBasedBreakIterator /*U_FINAL*/ : public BreakIterator { private: /** * The UText through which this BreakIterator accesses the text - * @internal + * @internal (private) */ UText fText; @@ -70,13 +70,6 @@ class U_COMMON_API RuleBasedBreakIterator /*U_FINAL*/ : public BreakIterator { RBBIDataWrapper *fData; private: - /** - * The iteration state - current position, rule status for the current position, - * and whether the iterator ran off the end, yielding UBRK_DONE. - * Current position is pinned to be 0 < position <= text.length. - * Current position is always set to a boundary. - * @internal - */ /** * The current position of the iterator. Pinned, 0 < fPosition <= text.length. * Never has the value UBRK_DONE (-1). @@ -628,25 +621,26 @@ class U_COMMON_API RuleBasedBreakIterator /*U_FINAL*/ : public BreakIterator { /** * Dumps caches and performs other actions associated with a complete change * in text or iteration position. - * @internal + * @internal (private) */ void reset(void); /** * Common initialization function, used by constructors and bufferClone. - * @internal + * @internal (private) */ void init(UErrorCode &status); /** - * Iterate backwards from an arbitrary position in the input text using the Safe Reverse rules. + * Iterate backwards from an arbitrary position in the input text using the + * synthesized Safe Reverse rules. * This locates a "Safe Position" from which the forward break rules * will operate correctly. A Safe Position is not necessarily a boundary itself. * * @param fromPosition the position in the input text to begin the iteration. - * @internal + * @internal (private) */ - int32_t handlePrevious(int32_t fromPosition); + int32_t handleSafePrevious(int32_t fromPosition); /** * Find a rule-based boundary by running the state machine. @@ -658,7 +652,7 @@ class U_COMMON_API RuleBasedBreakIterator /*U_FINAL*/ : public BreakIterator { * If > 0, the segment will be further subdivided * fRuleStatusIndex Info from the state table indicating which rules caused the boundary. * - * @internal + * @internal (private) */ int32_t handleNext(); @@ -667,7 +661,7 @@ class U_COMMON_API RuleBasedBreakIterator /*U_FINAL*/ : public BreakIterator { * This function returns the appropriate LanguageBreakEngine for a * given character c. * @param c A character in the dictionary set - * @internal + * @internal (private) */ const LanguageBreakEngine *getLanguageBreakEngine(UChar32 c); diff --git a/deps/icu-small/source/common/unicode/uchar.h b/deps/icu-small/source/common/unicode/uchar.h index 4b72ecfc26bf9a..6d31083e66ee0d 100644 --- a/deps/icu-small/source/common/unicode/uchar.h +++ b/deps/icu-small/source/common/unicode/uchar.h @@ -42,7 +42,7 @@ U_CDECL_BEGIN * @see u_getUnicodeVersion * @stable ICU 2.0 */ -#define U_UNICODE_VERSION "10.0" +#define U_UNICODE_VERSION "11.0" /** * \file @@ -446,6 +446,13 @@ typedef enum UProperty { * @stable ICU 60 */ UCHAR_PREPENDED_CONCATENATION_MARK=63, + /** + * Binary property Extended_Pictographic. + * See http://www.unicode.org/reports/tr51/#Emoji_Properties + * + * @stable ICU 62 + */ + UCHAR_EXTENDED_PICTOGRAPHIC=64, #ifndef U_HIDE_DEPRECATED_API /** * One more than the last constant for binary Unicode properties. @@ -1683,6 +1690,31 @@ enum UBlockCode { /** @stable ICU 60 */ UBLOCK_ZANABAZAR_SQUARE = 280, /*[11A00]*/ + // New blocks in Unicode 11.0 + + /** @stable ICU 62 */ + UBLOCK_CHESS_SYMBOLS = 281, /*[1FA00]*/ + /** @stable ICU 62 */ + UBLOCK_DOGRA = 282, /*[11800]*/ + /** @stable ICU 62 */ + UBLOCK_GEORGIAN_EXTENDED = 283, /*[1C90]*/ + /** @stable ICU 62 */ + UBLOCK_GUNJALA_GONDI = 284, /*[11D60]*/ + /** @stable ICU 62 */ + UBLOCK_HANIFI_ROHINGYA = 285, /*[10D00]*/ + /** @stable ICU 62 */ + UBLOCK_INDIC_SIYAQ_NUMBERS = 286, /*[1EC70]*/ + /** @stable ICU 62 */ + UBLOCK_MAKASAR = 287, /*[11EE0]*/ + /** @stable ICU 62 */ + UBLOCK_MAYAN_NUMERALS = 288, /*[1D2E0]*/ + /** @stable ICU 62 */ + UBLOCK_MEDEFAIDRIN = 289, /*[16E40]*/ + /** @stable ICU 62 */ + UBLOCK_OLD_SOGDIAN = 290, /*[10F00]*/ + /** @stable ICU 62 */ + UBLOCK_SOGDIAN = 291, /*[10F30]*/ + #ifndef U_HIDE_DEPRECATED_API /** * One more than the highest normal UBlockCode value. @@ -1690,7 +1722,7 @@ enum UBlockCode { * * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. */ - UBLOCK_COUNT = 281, + UBLOCK_COUNT = 292, #endif // U_HIDE_DEPRECATED_API /** @stable ICU 2.0 */ @@ -1979,6 +2011,9 @@ typedef enum UJoiningGroup { U_JG_MALAYALAM_SSA, /**< @stable ICU 60 */ U_JG_MALAYALAM_TTA, /**< @stable ICU 60 */ + U_JG_HANIFI_ROHINGYA_KINNA_YA, /**< @stable ICU 62 */ + U_JG_HANIFI_ROHINGYA_PA, /**< @stable ICU 62 */ + #ifndef U_HIDE_DEPRECATED_API /** * One more than the highest normal UJoiningGroup value. @@ -2029,6 +2064,7 @@ typedef enum UGraphemeClusterBreak { U_GCB_GLUE_AFTER_ZWJ = 16, /*[GAZ]*/ /** @stable ICU 58 */ U_GCB_ZWJ = 17, /*[ZWJ]*/ + #ifndef U_HIDE_DEPRECATED_API /** * One more than the highest normal UGraphemeClusterBreak value. @@ -2090,6 +2126,9 @@ typedef enum UWordBreakValues { U_WB_GLUE_AFTER_ZWJ = 20, /*[GAZ]*/ /** @stable ICU 58 */ U_WB_ZWJ = 21, /*[ZWJ]*/ + /** @stable ICU 62 */ + U_WB_WSEGSPACE = 22, /*[WSEGSPACE]*/ + #ifndef U_HIDE_DEPRECATED_API /** * One more than the highest normal UWordBreakValues value. @@ -2097,7 +2136,7 @@ typedef enum UWordBreakValues { * * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. */ - U_WB_COUNT = 22 + U_WB_COUNT = 23 #endif // U_HIDE_DEPRECATED_API } UWordBreakValues; diff --git a/deps/icu-small/source/common/unicode/unistr.h b/deps/icu-small/source/common/unicode/unistr.h index d0b271754b660e..b84f40bd449ced 100644 --- a/deps/icu-small/source/common/unicode/unistr.h +++ b/deps/icu-small/source/common/unicode/unistr.h @@ -1892,7 +1892,7 @@ class U_COMMON_API UnicodeString : public Replaceable UnicodeString &fastCopyFrom(const UnicodeString &src); /** - * Move assignment operator, might leave src in bogus state. + * Move assignment operator; might leave src in bogus state. * This string will have the same contents and state that the source string had. * The behavior is undefined if *this and src are the same object. * @param src source string @@ -1905,7 +1905,7 @@ class U_COMMON_API UnicodeString : public Replaceable // do not use #ifndef U_HIDE_DRAFT_API for moveFrom, needed by non-draft API /** - * Move assignment, might leave src in bogus state. + * Move assignment; might leave src in bogus state. * This string will have the same contents and state that the source string had. * The behavior is undefined if *this and src are the same object. * @@ -3314,7 +3314,7 @@ class U_COMMON_API UnicodeString : public Replaceable UnicodeString(const UnicodeString& that); /** - * Move constructor, might leave src in bogus state. + * Move constructor; might leave src in bogus state. * This string will have the same contents and state that the source string had. * @param src source string * @stable ICU 56 diff --git a/deps/icu-small/source/common/unicode/urename.h b/deps/icu-small/source/common/unicode/urename.h index d8ab85091f5721..4175e527f404a7 100644 --- a/deps/icu-small/source/common/unicode/urename.h +++ b/deps/icu-small/source/common/unicode/urename.h @@ -613,6 +613,7 @@ #define ucnv_createConverterFromPackage U_ICU_ENTRY_POINT_RENAME(ucnv_createConverterFromPackage) #define ucnv_createConverterFromSharedData U_ICU_ENTRY_POINT_RENAME(ucnv_createConverterFromSharedData) #define ucnv_detectUnicodeSignature U_ICU_ENTRY_POINT_RENAME(ucnv_detectUnicodeSignature) +#define ucnv_enableCleanup U_ICU_ENTRY_POINT_RENAME(ucnv_enableCleanup) #define ucnv_extContinueMatchFromU U_ICU_ENTRY_POINT_RENAME(ucnv_extContinueMatchFromU) #define ucnv_extContinueMatchToU U_ICU_ENTRY_POINT_RENAME(ucnv_extContinueMatchToU) #define ucnv_extGetUnicodeSet U_ICU_ENTRY_POINT_RENAME(ucnv_extGetUnicodeSet) @@ -1170,6 +1171,16 @@ #define unum_setSymbol U_ICU_ENTRY_POINT_RENAME(unum_setSymbol) #define unum_setTextAttribute U_ICU_ENTRY_POINT_RENAME(unum_setTextAttribute) #define unum_toPattern U_ICU_ENTRY_POINT_RENAME(unum_toPattern) +#define unumf_close U_ICU_ENTRY_POINT_RENAME(unumf_close) +#define unumf_closeResult U_ICU_ENTRY_POINT_RENAME(unumf_closeResult) +#define unumf_formatDecimal U_ICU_ENTRY_POINT_RENAME(unumf_formatDecimal) +#define unumf_formatDouble U_ICU_ENTRY_POINT_RENAME(unumf_formatDouble) +#define unumf_formatInt U_ICU_ENTRY_POINT_RENAME(unumf_formatInt) +#define unumf_openForSkeletonAndLocale U_ICU_ENTRY_POINT_RENAME(unumf_openForSkeletonAndLocale) +#define unumf_openResult U_ICU_ENTRY_POINT_RENAME(unumf_openResult) +#define unumf_resultGetAllFieldPositions U_ICU_ENTRY_POINT_RENAME(unumf_resultGetAllFieldPositions) +#define unumf_resultNextFieldPosition U_ICU_ENTRY_POINT_RENAME(unumf_resultNextFieldPosition) +#define unumf_resultToString U_ICU_ENTRY_POINT_RENAME(unumf_resultToString) #define unumsys_close U_ICU_ENTRY_POINT_RENAME(unumsys_close) #define unumsys_getDescription U_ICU_ENTRY_POINT_RENAME(unumsys_getDescription) #define unumsys_getName U_ICU_ENTRY_POINT_RENAME(unumsys_getName) @@ -1209,6 +1220,7 @@ #define uplug_setPlugNoUnload U_ICU_ENTRY_POINT_RENAME(uplug_setPlugNoUnload) #define uprops_getSource U_ICU_ENTRY_POINT_RENAME(uprops_getSource) #define upropsvec_addPropertyStarts U_ICU_ENTRY_POINT_RENAME(upropsvec_addPropertyStarts) +#define uprv_add32_overflow U_ICU_ENTRY_POINT_RENAME(uprv_add32_overflow) #define uprv_aestrncpy U_ICU_ENTRY_POINT_RENAME(uprv_aestrncpy) #define uprv_asciiFromEbcdic U_ICU_ENTRY_POINT_RENAME(uprv_asciiFromEbcdic) #define uprv_asciitolower U_ICU_ENTRY_POINT_RENAME(uprv_asciitolower) @@ -1343,6 +1355,7 @@ #define uprv_maximumPtr U_ICU_ENTRY_POINT_RENAME(uprv_maximumPtr) #define uprv_min U_ICU_ENTRY_POINT_RENAME(uprv_min) #define uprv_modf U_ICU_ENTRY_POINT_RENAME(uprv_modf) +#define uprv_mul32_overflow U_ICU_ENTRY_POINT_RENAME(uprv_mul32_overflow) #define uprv_parseCurrency U_ICU_ENTRY_POINT_RENAME(uprv_parseCurrency) #define uprv_pathIsAbsolute U_ICU_ENTRY_POINT_RENAME(uprv_pathIsAbsolute) #define uprv_pow U_ICU_ENTRY_POINT_RENAME(uprv_pow) diff --git a/deps/icu-small/source/common/unicode/uscript.h b/deps/icu-small/source/common/unicode/uscript.h index 0befa1cd422c20..faf9edf8ae2694 100644 --- a/deps/icu-small/source/common/unicode/uscript.h +++ b/deps/icu-small/source/common/unicode/uscript.h @@ -451,6 +451,21 @@ typedef enum UScriptCode { /** @stable ICU 60 */ USCRIPT_ZANABAZAR_SQUARE = 177,/* Zanb */ + /** @stable ICU 62 */ + USCRIPT_DOGRA = 178,/* Dogr */ + /** @stable ICU 62 */ + USCRIPT_GUNJALA_GONDI = 179,/* Gong */ + /** @stable ICU 62 */ + USCRIPT_MAKASAR = 180,/* Maka */ + /** @stable ICU 62 */ + USCRIPT_MEDEFAIDRIN = 181,/* Medf */ + /** @stable ICU 62 */ + USCRIPT_HANIFI_ROHINGYA = 182,/* Rohg */ + /** @stable ICU 62 */ + USCRIPT_SOGDIAN = 183,/* Sogd */ + /** @stable ICU 62 */ + USCRIPT_OLD_SOGDIAN = 184,/* Sogo */ + #ifndef U_HIDE_DEPRECATED_API /** * One more than the highest normal UScriptCode value. @@ -458,7 +473,7 @@ typedef enum UScriptCode { * * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. */ - USCRIPT_CODE_LIMIT = 178 + USCRIPT_CODE_LIMIT = 185 #endif // U_HIDE_DEPRECATED_API } UScriptCode; diff --git a/deps/icu-small/source/common/unicode/utypes.h b/deps/icu-small/source/common/unicode/utypes.h index b6cf4965112a16..f43056b6f2fd10 100644 --- a/deps/icu-small/source/common/unicode/utypes.h +++ b/deps/icu-small/source/common/unicode/utypes.h @@ -542,12 +542,15 @@ typedef enum UErrorCode { #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 */ #endif // U_HIDE_DRAFT_API +#ifndef U_HIDE_DRAFT_API + 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 /* diff --git a/deps/icu-small/source/common/unicode/uvernum.h b/deps/icu-small/source/common/unicode/uvernum.h index 0427bcb03db4e3..2240661112c1ad 100644 --- a/deps/icu-small/source/common/unicode/uvernum.h +++ b/deps/icu-small/source/common/unicode/uvernum.h @@ -58,7 +58,7 @@ * This value will change in the subsequent releases of ICU * @stable ICU 2.4 */ -#define U_ICU_VERSION_MAJOR_NUM 61 +#define U_ICU_VERSION_MAJOR_NUM 62 /** The current ICU minor version as an integer. * This value will change in the subsequent releases of ICU @@ -84,7 +84,7 @@ * This value will change in the subsequent releases of ICU * @stable ICU 2.6 */ -#define U_ICU_VERSION_SUFFIX _61 +#define U_ICU_VERSION_SUFFIX _62 /** * \def U_DEF2_ICU_ENTRY_POINT_RENAME @@ -119,7 +119,7 @@ * This value will change in the subsequent releases of ICU * @stable ICU 2.4 */ -#define U_ICU_VERSION "61.1" +#define U_ICU_VERSION "62.1" /** * The current ICU library major version number as a string, for library name suffixes. @@ -132,13 +132,13 @@ * * @stable ICU 2.6 */ -#define U_ICU_VERSION_SHORT "61" +#define U_ICU_VERSION_SHORT "62" #ifndef U_HIDE_INTERNAL_API /** Data version in ICU4C. * @internal ICU 4.4 Internal Use Only **/ -#define U_ICU_DATA_VERSION "61.1" +#define U_ICU_DATA_VERSION "62.1" #endif /* U_HIDE_INTERNAL_API */ /*=========================================================================== diff --git a/deps/icu-small/source/common/uprops.cpp b/deps/icu-small/source/common/uprops.cpp index b76896db1b704f..21723b32aa7c8d 100644 --- a/deps/icu-small/source/common/uprops.cpp +++ b/deps/icu-small/source/common/uprops.cpp @@ -282,6 +282,7 @@ static const BinaryProperty binProps[UCHAR_BINARY_LIMIT]={ { 2, U_MASK(UPROPS_2_EMOJI_COMPONENT), defaultContains }, { 2, 0, isRegionalIndicator }, { 1, U_MASK(UPROPS_PREPENDED_CONCATENATION_MARK), defaultContains }, + { 2, U_MASK(UPROPS_2_EXTENDED_PICTOGRAPHIC), defaultContains }, }; U_CAPI UBool U_EXPORT2 diff --git a/deps/icu-small/source/common/uprops.h b/deps/icu-small/source/common/uprops.h index 6f67756cd91033..2078384c3e47dd 100644 --- a/deps/icu-small/source/common/uprops.h +++ b/deps/icu-small/source/common/uprops.h @@ -196,8 +196,7 @@ enum { /* * Properties in vector word 2 * Bits - * 31..27 http://www.unicode.org/reports/tr51/#Emoji_Properties - * 26 reserved + * 31..26 http://www.unicode.org/reports/tr51/#Emoji_Properties * 25..20 Line Break * 19..15 Sentence Break * 14..10 Word Break @@ -205,7 +204,8 @@ enum { * 4.. 0 Decomposition Type */ enum { - UPROPS_2_EMOJI_COMPONENT=27, + UPROPS_2_EXTENDED_PICTOGRAPHIC=26, + UPROPS_2_EMOJI_COMPONENT, UPROPS_2_EMOJI, UPROPS_2_EMOJI_PRESENTATION, UPROPS_2_EMOJI_MODIFIER, diff --git a/deps/icu-small/source/common/uscript_props.cpp b/deps/icu-small/source/common/uscript_props.cpp index 7998c52c7f02c5..bfdb68c7a9c998 100644 --- a/deps/icu-small/source/common/uscript_props.cpp +++ b/deps/icu-small/source/common/uscript_props.cpp @@ -71,7 +71,7 @@ const int32_t SCRIPT_PROPS[] = { 0x0EA5 | RECOMMENDED | LB_LETTERS, // Laoo 0x004C | RECOMMENDED | CASED, // Latn 0x0D15 | RECOMMENDED, // Mlym - 0x1826 | LIMITED_USE, // Mong + 0x1826 | EXCLUSION, // Mong 0x1000 | RECOMMENDED | LB_LETTERS, // Mymr 0x168F | EXCLUSION, // Ogam 0x10300 | EXCLUSION, // Ital @@ -222,6 +222,13 @@ const int32_t SCRIPT_PROPS[] = { 0x11D10 | EXCLUSION, // Gonm 0x11A5C | EXCLUSION, // Soyo 0x11A0B | EXCLUSION, // Zanb + 0x1180B | EXCLUSION, // Dogr + 0x11D71 | LIMITED_USE, // Gong + 0x11EE5 | EXCLUSION, // Maka + 0x16E40 | EXCLUSION | CASED, // Medf + 0x10D12 | LIMITED_USE | RTL, // Rohg + 0x10F42 | EXCLUSION | RTL, // Sogd + 0x10F19 | EXCLUSION | RTL, // Sogo // End copy-paste from parsescriptmetadata.py }; diff --git a/deps/icu-small/source/common/ustr_cnv.cpp b/deps/icu-small/source/common/ustr_cnv.cpp index 951864f4a6c7db..eb37232c25d50b 100644 --- a/deps/icu-small/source/common/ustr_cnv.cpp +++ b/deps/icu-small/source/common/ustr_cnv.cpp @@ -28,6 +28,7 @@ #include "cmemory.h" #include "umutex.h" #include "ustr_cnv.h" +#include "ucnv_bld.h" /* mutexed access to a shared default converter ----------------------------- */ @@ -68,8 +69,8 @@ u_releaseDefaultConverter(UConverter *converter) if (converter != NULL) { ucnv_reset(converter); } + ucnv_enableCleanup(); umtx_lock(NULL); - if(gDefaultConverter == NULL) { gDefaultConverter = converter; converter = NULL; diff --git a/deps/icu-small/source/common/util.h b/deps/icu-small/source/common/util.h index 7af9a32d8ffe53..92cdc9ef69a58a 100644 --- a/deps/icu-small/source/common/util.h +++ b/deps/icu-small/source/common/util.h @@ -46,6 +46,13 @@ class U_COMMON_API ICU_Utility /* not : public UObject because all methods are s int32_t radix = 10, int32_t minDigits = 1); + /** Returns a bogus UnicodeString by value. */ + static inline UnicodeString makeBogusString() { + UnicodeString result; + result.setToBogus(); + return result; + } + /** * Return true if the character is NOT printable ASCII. * diff --git a/deps/icu-small/source/common/utypes.cpp b/deps/icu-small/source/common/utypes.cpp index 5d6a0504ba682a..7531e465683342 100644 --- a/deps/icu-small/source/common/utypes.cpp +++ b/deps/icu-small/source/common/utypes.cpp @@ -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 diff --git a/deps/icu-small/source/common/wintz.cpp b/deps/icu-small/source/common/wintz.cpp index c30a5dbc606894..3708925b38f0cd 100644 --- a/deps/icu-small/source/common/wintz.cpp +++ b/deps/icu-small/source/common/wintz.cpp @@ -49,7 +49,7 @@ typedef struct /** * Various registry keys and key fragments. */ -static const char CURRENT_ZONE_REGKEY[] = "SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation\\"; +static const wchar_t CURRENT_ZONE_REGKEY[] = L"SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation\\"; static const char STANDARD_TIME_REGKEY[] = " Standard Time"; static const char TZI_REGKEY[] = "TZI"; static const char STD_REGKEY[] = "Std"; @@ -121,27 +121,39 @@ static LONG getSTDName(const char *winid, char *regStdName, int32_t length) return result; } -static LONG getTZKeyName(char* tzKeyName, int32_t length) +static LONG getTZKeyName(char* tzKeyName, int32_t tzKeyNamelength) { HKEY hkey; LONG result = FALSE; - DWORD cbData = length; + WCHAR timeZoneKeyNameData[128]; + DWORD timeZoneKeyNameLength = static_cast(sizeof(timeZoneKeyNameData)); - if(ERROR_SUCCESS == RegOpenKeyExA( + if(ERROR_SUCCESS == RegOpenKeyExW( HKEY_LOCAL_MACHINE, CURRENT_ZONE_REGKEY, 0, KEY_QUERY_VALUE, &hkey)) { - result = RegQueryValueExA( + if (ERROR_SUCCESS == RegQueryValueExW( hkey, - "TimeZoneKeyName", + L"TimeZoneKeyName", NULL, NULL, - (LPBYTE)tzKeyName, - &cbData); + (LPBYTE)timeZoneKeyNameData, + &timeZoneKeyNameLength)) + { + // Ensure null termination. + timeZoneKeyNameData[UPRV_LENGTHOF(timeZoneKeyNameData) - 1] = L'\0'; + // Convert the UTF-16 string to UTF-8. + UErrorCode status = U_ZERO_ERROR; + u_strToUTF8(tzKeyName, tzKeyNamelength, NULL, reinterpret_cast(timeZoneKeyNameData), -1, &status); + if (U_ZERO_ERROR == status) + { + result = ERROR_SUCCESS; + } + } RegCloseKey(hkey); } diff --git a/deps/icu-small/source/data/in/icudt61l.dat b/deps/icu-small/source/data/in/icudt62l.dat similarity index 65% rename from deps/icu-small/source/data/in/icudt61l.dat rename to deps/icu-small/source/data/in/icudt62l.dat index e9c24d8d1a7882..a6ac7ebb374980 100644 Binary files a/deps/icu-small/source/data/in/icudt61l.dat and b/deps/icu-small/source/data/in/icudt62l.dat differ diff --git a/deps/icu-small/source/i18n/affixpatternparser.cpp b/deps/icu-small/source/i18n/affixpatternparser.cpp deleted file mode 100644 index d9e122953af53e..00000000000000 --- a/deps/icu-small/source/i18n/affixpatternparser.cpp +++ /dev/null @@ -1,698 +0,0 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - * Copyright (C) 2015, International Business Machines - * Corporation and others. All Rights Reserved. - * - * file name: affixpatternparser.cpp - */ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/dcfmtsym.h" -#include "unicode/plurrule.h" -#include "unicode/strenum.h" -#include "unicode/ucurr.h" -#include "unicode/ustring.h" -#include "affixpatternparser.h" -#include "charstr.h" -#include "precision.h" -#include "uassert.h" -#include "unistrappender.h" - -static const UChar gDefaultSymbols[] = {0xa4, 0xa4, 0xa4}; - -static const UChar gPercent = 0x25; -static const UChar gPerMill = 0x2030; -static const UChar gNegative = 0x2D; -static const UChar gPositive = 0x2B; - -#define PACK_TOKEN_AND_LENGTH(t, l) ((UChar) (((t) << 8) | (l & 0xFF))) - -#define UNPACK_TOKEN(c) ((AffixPattern::ETokenType) (((c) >> 8) & 0x7F)) - -#define UNPACK_LONG(c) (((c) >> 8) & 0x80) - -#define UNPACK_LENGTH(c) ((c) & 0xFF) - -U_NAMESPACE_BEGIN - -static int32_t -nextToken(const UChar *buffer, int32_t idx, int32_t len, UChar *token) { - if (buffer[idx] != 0x27 || idx + 1 == len) { - *token = buffer[idx]; - return 1; - } - *token = buffer[idx + 1]; - if (buffer[idx + 1] == 0xA4) { - int32_t i = 2; - for (; idx + i < len && i < 4 && buffer[idx + i] == buffer[idx + 1]; ++i) - ; - return i; - } - return 2; -} - -static int32_t -nextUserToken(const UChar *buffer, int32_t idx, int32_t len, UChar *token) { - *token = buffer[idx]; - int32_t max; - switch (buffer[idx]) { - case 0x27: - max = 2; - break; - case 0xA4: - max = 3; - break; - default: - max = 1; - break; - } - int32_t i = 1; - for (; idx + i < len && i < max && buffer[idx + i] == buffer[idx]; ++i) - ; - return i; -} - -CurrencyAffixInfo::CurrencyAffixInfo() - : fSymbol(gDefaultSymbols, 1), - fISO(gDefaultSymbols, 2), - fLong(DigitAffix(gDefaultSymbols, 3)), - fIsDefault(TRUE) { -} - -void -CurrencyAffixInfo::set( - const char *locale, - const PluralRules *rules, - const UChar *currency, - UErrorCode &status) { - if (U_FAILURE(status)) { - return; - } - fIsDefault = FALSE; - if (currency == NULL) { - fSymbol.setTo(gDefaultSymbols, 1); - fISO.setTo(gDefaultSymbols, 2); - fLong.remove(); - fLong.append(gDefaultSymbols, 3); - fIsDefault = TRUE; - return; - } - int32_t len; - UBool unusedIsChoice; - const UChar *symbol = ucurr_getName( - currency, locale, UCURR_SYMBOL_NAME, &unusedIsChoice, - &len, &status); - if (U_FAILURE(status)) { - return; - } - fSymbol.setTo(symbol, len); - fISO.setTo(currency, u_strlen(currency)); - fLong.remove(); - StringEnumeration* keywords = rules->getKeywords(status); - if (U_FAILURE(status)) { - return; - } - const UnicodeString* pluralCount; - while ((pluralCount = keywords->snext(status)) != NULL) { - CharString pCount; - pCount.appendInvariantChars(*pluralCount, status); - const UChar *pluralName = ucurr_getPluralName( - currency, locale, &unusedIsChoice, pCount.data(), - &len, &status); - fLong.setVariant(pCount.data(), UnicodeString(pluralName, len), status); - } - delete keywords; -} - -void -CurrencyAffixInfo::adjustPrecision( - const UChar *currency, const UCurrencyUsage usage, - FixedPrecision &precision, UErrorCode &status) { - if (U_FAILURE(status)) { - return; - } - - int32_t digitCount = ucurr_getDefaultFractionDigitsForUsage( - currency, usage, &status); - precision.fMin.setFracDigitCount(digitCount); - precision.fMax.setFracDigitCount(digitCount); - double increment = ucurr_getRoundingIncrementForUsage( - currency, usage, &status); - if (increment == 0.0) { - precision.fRoundingIncrement.clear(); - } else { - precision.fRoundingIncrement.set(increment); - // guard against round-off error - precision.fRoundingIncrement.round(6); - } -} - -void -AffixPattern::addLiteral( - const UChar *literal, int32_t start, int32_t len) { - char32Count += u_countChar32(literal + start, len); - literals.append(literal, start, len); - int32_t tlen = tokens.length(); - // Takes 4 UChars to encode maximum literal length. - UChar *tokenChars = tokens.getBuffer(tlen + 4); - - // find start of literal size. May be tlen if there is no literal. - // While finding start of literal size, compute literal length - int32_t literalLength = 0; - int32_t tLiteralStart = tlen; - while (tLiteralStart > 0 && UNPACK_TOKEN(tokenChars[tLiteralStart - 1]) == kLiteral) { - tLiteralStart--; - literalLength <<= 8; - literalLength |= UNPACK_LENGTH(tokenChars[tLiteralStart]); - } - // Add number of chars we just added to literal - literalLength += len; - - // Now encode the new length starting at tLiteralStart - tlen = tLiteralStart; - tokenChars[tlen++] = PACK_TOKEN_AND_LENGTH(kLiteral, literalLength & 0xFF); - literalLength >>= 8; - while (literalLength) { - tokenChars[tlen++] = PACK_TOKEN_AND_LENGTH(kLiteral | 0x80, literalLength & 0xFF); - literalLength >>= 8; - } - tokens.releaseBuffer(tlen); -} - -void -AffixPattern::add(ETokenType t) { - add(t, 1); -} - -void -AffixPattern::addCurrency(uint8_t count) { - add(kCurrency, count); -} - -void -AffixPattern::add(ETokenType t, uint8_t count) { - U_ASSERT(t != kLiteral); - char32Count += count; - switch (t) { - case kCurrency: - hasCurrencyToken = TRUE; - break; - case kPercent: - hasPercentToken = TRUE; - break; - case kPerMill: - hasPermillToken = TRUE; - break; - default: - // Do nothing - break; - } - tokens.append(PACK_TOKEN_AND_LENGTH(t, count)); -} - -AffixPattern & -AffixPattern::append(const AffixPattern &other) { - AffixPatternIterator iter; - other.iterator(iter); - UnicodeString literal; - while (iter.nextToken()) { - switch (iter.getTokenType()) { - case kLiteral: - iter.getLiteral(literal); - addLiteral(literal.getBuffer(), 0, literal.length()); - break; - case kCurrency: - addCurrency(static_cast(iter.getTokenLength())); - break; - default: - add(iter.getTokenType()); - break; - } - } - return *this; -} - -void -AffixPattern::remove() { - tokens.remove(); - literals.remove(); - hasCurrencyToken = FALSE; - hasPercentToken = FALSE; - hasPermillToken = FALSE; - char32Count = 0; -} - -// escapes literals for strings where special characters are NOT escaped -// except for apostrophe. -static void escapeApostropheInLiteral( - const UnicodeString &literal, UnicodeStringAppender &appender) { - int32_t len = literal.length(); - const UChar *buffer = literal.getBuffer(); - for (int32_t i = 0; i < len; ++i) { - UChar ch = buffer[i]; - switch (ch) { - case 0x27: - appender.append((UChar) 0x27); - appender.append((UChar) 0x27); - break; - default: - appender.append(ch); - break; - } - } -} - - -// escapes literals for user strings where special characters in literals -// are escaped with apostrophe. -static void escapeLiteral( - const UnicodeString &literal, UnicodeStringAppender &appender) { - int32_t len = literal.length(); - const UChar *buffer = literal.getBuffer(); - for (int32_t i = 0; i < len; ++i) { - UChar ch = buffer[i]; - switch (ch) { - case 0x27: - appender.append((UChar) 0x27); - appender.append((UChar) 0x27); - break; - case 0x25: - appender.append((UChar) 0x27); - appender.append((UChar) 0x25); - appender.append((UChar) 0x27); - break; - case 0x2030: - appender.append((UChar) 0x27); - appender.append((UChar) 0x2030); - appender.append((UChar) 0x27); - break; - case 0xA4: - appender.append((UChar) 0x27); - appender.append((UChar) 0xA4); - appender.append((UChar) 0x27); - break; - case 0x2D: - appender.append((UChar) 0x27); - appender.append((UChar) 0x2D); - appender.append((UChar) 0x27); - break; - case 0x2B: - appender.append((UChar) 0x27); - appender.append((UChar) 0x2B); - appender.append((UChar) 0x27); - break; - default: - appender.append(ch); - break; - } - } -} - -UnicodeString & -AffixPattern::toString(UnicodeString &appendTo) const { - AffixPatternIterator iter; - iterator(iter); - UnicodeStringAppender appender(appendTo); - UnicodeString literal; - while (iter.nextToken()) { - switch (iter.getTokenType()) { - case kLiteral: - escapeApostropheInLiteral(iter.getLiteral(literal), appender); - break; - case kPercent: - appender.append((UChar) 0x27); - appender.append((UChar) 0x25); - break; - case kPerMill: - appender.append((UChar) 0x27); - appender.append((UChar) 0x2030); - break; - case kCurrency: - { - appender.append((UChar) 0x27); - int32_t cl = iter.getTokenLength(); - for (int32_t i = 0; i < cl; ++i) { - appender.append((UChar) 0xA4); - } - } - break; - case kNegative: - appender.append((UChar) 0x27); - appender.append((UChar) 0x2D); - break; - case kPositive: - appender.append((UChar) 0x27); - appender.append((UChar) 0x2B); - break; - default: - U_ASSERT(FALSE); - break; - } - } - return appendTo; -} - -UnicodeString & -AffixPattern::toUserString(UnicodeString &appendTo) const { - AffixPatternIterator iter; - iterator(iter); - UnicodeStringAppender appender(appendTo); - UnicodeString literal; - while (iter.nextToken()) { - switch (iter.getTokenType()) { - case kLiteral: - escapeLiteral(iter.getLiteral(literal), appender); - break; - case kPercent: - appender.append((UChar) 0x25); - break; - case kPerMill: - appender.append((UChar) 0x2030); - break; - case kCurrency: - { - int32_t cl = iter.getTokenLength(); - for (int32_t i = 0; i < cl; ++i) { - appender.append((UChar) 0xA4); - } - } - break; - case kNegative: - appender.append((UChar) 0x2D); - break; - case kPositive: - appender.append((UChar) 0x2B); - break; - default: - U_ASSERT(FALSE); - break; - } - } - return appendTo; -} - -class AffixPatternAppender : public UMemory { -public: - AffixPatternAppender(AffixPattern &dest) : fDest(&dest), fIdx(0) { } - - inline void append(UChar x) { - if (fIdx == UPRV_LENGTHOF(fBuffer)) { - fDest->addLiteral(fBuffer, 0, fIdx); - fIdx = 0; - } - fBuffer[fIdx++] = x; - } - - inline void append(UChar32 x) { - if (fIdx >= UPRV_LENGTHOF(fBuffer) - 1) { - fDest->addLiteral(fBuffer, 0, fIdx); - fIdx = 0; - } - U16_APPEND_UNSAFE(fBuffer, fIdx, x); - } - - inline void flush() { - if (fIdx) { - fDest->addLiteral(fBuffer, 0, fIdx); - } - fIdx = 0; - } - - /** - * flush the buffer when we go out of scope. - */ - ~AffixPatternAppender() { - flush(); - } -private: - AffixPattern *fDest; - int32_t fIdx; - UChar fBuffer[32]; - AffixPatternAppender(const AffixPatternAppender &other); - AffixPatternAppender &operator=(const AffixPatternAppender &other); -}; - - -AffixPattern & -AffixPattern::parseUserAffixString( - const UnicodeString &affixStr, - AffixPattern &appendTo, - UErrorCode &status) { - if (U_FAILURE(status)) { - return appendTo; - } - int32_t len = affixStr.length(); - const UChar *buffer = affixStr.getBuffer(); - // 0 = not quoted; 1 = quoted. - int32_t state = 0; - AffixPatternAppender appender(appendTo); - for (int32_t i = 0; i < len; ) { - UChar token; - int32_t tokenSize = nextUserToken(buffer, i, len, &token); - i += tokenSize; - if (token == 0x27 && tokenSize == 1) { // quote - state = 1 - state; - continue; - } - if (state == 0) { - switch (token) { - case 0x25: - appender.flush(); - appendTo.add(kPercent, 1); - break; - case 0x27: // double quote - appender.append((UChar) 0x27); - break; - case 0x2030: - appender.flush(); - appendTo.add(kPerMill, 1); - break; - case 0x2D: - appender.flush(); - appendTo.add(kNegative, 1); - break; - case 0x2B: - appender.flush(); - appendTo.add(kPositive, 1); - break; - case 0xA4: - appender.flush(); - appendTo.add(kCurrency, static_cast(tokenSize)); - break; - default: - appender.append(token); - break; - } - } else { - switch (token) { - case 0x27: // double quote - appender.append((UChar) 0x27); - break; - case 0xA4: // included b/c tokenSize can be > 1 - for (int32_t j = 0; j < tokenSize; ++j) { - appender.append((UChar) 0xA4); - } - break; - default: - appender.append(token); - break; - } - } - } - return appendTo; -} - -AffixPattern & -AffixPattern::parseAffixString( - const UnicodeString &affixStr, - AffixPattern &appendTo, - UErrorCode &status) { - if (U_FAILURE(status)) { - return appendTo; - } - int32_t len = affixStr.length(); - const UChar *buffer = affixStr.getBuffer(); - for (int32_t i = 0; i < len; ) { - UChar token; - int32_t tokenSize = nextToken(buffer, i, len, &token); - if (tokenSize == 1) { - int32_t literalStart = i; - ++i; - while (i < len && (tokenSize = nextToken(buffer, i, len, &token)) == 1) { - ++i; - } - appendTo.addLiteral(buffer, literalStart, i - literalStart); - - // If we reached end of string, we are done - if (i == len) { - return appendTo; - } - } - i += tokenSize; - switch (token) { - case 0x25: - appendTo.add(kPercent, 1); - break; - case 0x2030: - appendTo.add(kPerMill, 1); - break; - case 0x2D: - appendTo.add(kNegative, 1); - break; - case 0x2B: - appendTo.add(kPositive, 1); - break; - case 0xA4: - { - if (tokenSize - 1 > 3) { - status = U_PARSE_ERROR; - return appendTo; - } - appendTo.add(kCurrency, tokenSize - 1); - } - break; - default: - appendTo.addLiteral(&token, 0, 1); - break; - } - } - return appendTo; -} - -AffixPatternIterator & -AffixPattern::iterator(AffixPatternIterator &result) const { - result.nextLiteralIndex = 0; - result.lastLiteralLength = 0; - result.nextTokenIndex = 0; - result.tokens = &tokens; - result.literals = &literals; - return result; -} - -UBool -AffixPatternIterator::nextToken() { - int32_t tlen = tokens->length(); - if (nextTokenIndex == tlen) { - return FALSE; - } - ++nextTokenIndex; - const UChar *tokenBuffer = tokens->getBuffer(); - if (UNPACK_TOKEN(tokenBuffer[nextTokenIndex - 1]) == - AffixPattern::kLiteral) { - while (nextTokenIndex < tlen && - UNPACK_LONG(tokenBuffer[nextTokenIndex])) { - ++nextTokenIndex; - } - lastLiteralLength = 0; - int32_t i = nextTokenIndex - 1; - for (; UNPACK_LONG(tokenBuffer[i]); --i) { - lastLiteralLength <<= 8; - lastLiteralLength |= UNPACK_LENGTH(tokenBuffer[i]); - } - lastLiteralLength <<= 8; - lastLiteralLength |= UNPACK_LENGTH(tokenBuffer[i]); - nextLiteralIndex += lastLiteralLength; - } - return TRUE; -} - -AffixPattern::ETokenType -AffixPatternIterator::getTokenType() const { - return UNPACK_TOKEN(tokens->charAt(nextTokenIndex - 1)); -} - -UnicodeString & -AffixPatternIterator::getLiteral(UnicodeString &result) const { - const UChar *buffer = literals->getBuffer(); - result.setTo(buffer + (nextLiteralIndex - lastLiteralLength), lastLiteralLength); - return result; -} - -int32_t -AffixPatternIterator::getTokenLength() const { - const UChar *tokenBuffer = tokens->getBuffer(); - AffixPattern::ETokenType type = UNPACK_TOKEN(tokenBuffer[nextTokenIndex - 1]); - return type == AffixPattern::kLiteral ? lastLiteralLength : UNPACK_LENGTH(tokenBuffer[nextTokenIndex - 1]); -} - -AffixPatternParser::AffixPatternParser() - : fPercent(gPercent), fPermill(gPerMill), fNegative(gNegative), fPositive(gPositive) { -} - -AffixPatternParser::AffixPatternParser( - const DecimalFormatSymbols &symbols) { - setDecimalFormatSymbols(symbols); -} - -void -AffixPatternParser::setDecimalFormatSymbols( - const DecimalFormatSymbols &symbols) { - fPercent = symbols.getConstSymbol(DecimalFormatSymbols::kPercentSymbol); - fPermill = symbols.getConstSymbol(DecimalFormatSymbols::kPerMillSymbol); - fNegative = symbols.getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol); - fPositive = symbols.getConstSymbol(DecimalFormatSymbols::kPlusSignSymbol); -} - -PluralAffix & -AffixPatternParser::parse( - const AffixPattern &affixPattern, - const CurrencyAffixInfo ¤cyAffixInfo, - PluralAffix &appendTo, - UErrorCode &status) const { - if (U_FAILURE(status)) { - return appendTo; - } - AffixPatternIterator iter; - affixPattern.iterator(iter); - UnicodeString literal; - while (iter.nextToken()) { - switch (iter.getTokenType()) { - case AffixPattern::kPercent: - appendTo.append(fPercent, UNUM_PERCENT_FIELD); - break; - case AffixPattern::kPerMill: - appendTo.append(fPermill, UNUM_PERMILL_FIELD); - break; - case AffixPattern::kNegative: - appendTo.append(fNegative, UNUM_SIGN_FIELD); - break; - case AffixPattern::kPositive: - appendTo.append(fPositive, UNUM_SIGN_FIELD); - break; - case AffixPattern::kCurrency: - switch (iter.getTokenLength()) { - case 1: - appendTo.append( - currencyAffixInfo.getSymbol(), UNUM_CURRENCY_FIELD); - break; - case 2: - appendTo.append( - currencyAffixInfo.getISO(), UNUM_CURRENCY_FIELD); - break; - case 3: - appendTo.append( - currencyAffixInfo.getLong(), UNUM_CURRENCY_FIELD, status); - break; - default: - U_ASSERT(FALSE); - break; - } - break; - case AffixPattern::kLiteral: - appendTo.append(iter.getLiteral(literal)); - break; - default: - U_ASSERT(FALSE); - break; - } - } - return appendTo; -} - - -U_NAMESPACE_END -#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/affixpatternparser.h b/deps/icu-small/source/i18n/affixpatternparser.h deleted file mode 100644 index b54c749c700816..00000000000000 --- a/deps/icu-small/source/i18n/affixpatternparser.h +++ /dev/null @@ -1,402 +0,0 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2015, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* affixpatternparser.h -* -* created on: 2015jan06 -* created by: Travis Keep -*/ - -#ifndef __AFFIX_PATTERN_PARSER_H__ -#define __AFFIX_PATTERN_PARSER_H__ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/unistr.h" -#include "unicode/uobject.h" -#include "pluralaffix.h" - -U_NAMESPACE_BEGIN - -class PluralRules; -class FixedPrecision; -class DecimalFormatSymbols; - -/** - * A representation of the various forms of a particular currency according - * to some locale and usage context. - * - * Includes the symbol, ISO code form, and long form(s) of the currency name - * for each plural variation. - */ -class U_I18N_API CurrencyAffixInfo : public UMemory { -public: - /** - * Symbol is \u00a4; ISO form is \u00a4\u00a4; - * long form is \u00a4\u00a4\u00a4. - */ - CurrencyAffixInfo(); - - const UnicodeString &getSymbol() const { return fSymbol; } - const UnicodeString &getISO() const { return fISO; } - const PluralAffix &getLong() const { return fLong; } - void setSymbol(const UnicodeString &symbol) { - fSymbol = symbol; - fIsDefault = FALSE; - } - void setISO(const UnicodeString &iso) { - fISO = iso; - fIsDefault = FALSE; - } - UBool - equals(const CurrencyAffixInfo &other) const { - return (fSymbol == other.fSymbol) - && (fISO == other.fISO) - && (fLong.equals(other.fLong)) - && (fIsDefault == other.fIsDefault); - } - - /** - * Intializes this instance. - * - * @param locale the locale for the currency forms. - * @param rules The plural rules for the locale. - * @param currency the null terminated, 3 character ISO code of the - * currency. If NULL, resets this instance as if it were just created. - * In this case, the first 2 parameters may be NULL as well. - * @param status any error returned here. - */ - void set( - const char *locale, const PluralRules *rules, - const UChar *currency, UErrorCode &status); - - /** - * Returns true if this instance is the default. That is has no real - * currency. For instance never initialized with set() - * or reset with set(NULL, NULL, NULL, status). - */ - UBool isDefault() const { return fIsDefault; } - - /** - * Adjusts the precision used for a particular currency. - * @param currency the null terminated, 3 character ISO code of the - * currency. - * @param usage the usage of the currency - * @param precision min/max fraction digits and rounding increment - * adjusted. - * @params status any error reported here. - */ - static void adjustPrecision( - const UChar *currency, const UCurrencyUsage usage, - FixedPrecision &precision, UErrorCode &status); - -private: - /** - * The symbol form of the currency. - */ - UnicodeString fSymbol; - - /** - * The ISO form of the currency, usually three letter abbreviation. - */ - UnicodeString fISO; - - /** - * The long forms of the currency keyed by plural variation. - */ - PluralAffix fLong; - - UBool fIsDefault; - -}; - -class AffixPatternIterator; - -/** - * A locale agnostic representation of an affix pattern. - */ -class U_I18N_API AffixPattern : public UMemory { -public: - - /** - * The token types that can appear in an affix pattern. - */ - enum ETokenType { - kLiteral, - kPercent, - kPerMill, - kCurrency, - kNegative, - kPositive - }; - - /** - * An empty affix pattern. - */ - AffixPattern() - : tokens(), literals(), hasCurrencyToken(FALSE), - hasPercentToken(FALSE), hasPermillToken(FALSE), char32Count(0) { - } - - /** - * Adds a string literal to this affix pattern. - */ - void addLiteral(const UChar *, int32_t start, int32_t len); - - /** - * Adds a token to this affix pattern. t must not be kLiteral as - * the addLiteral() method adds literals. - * @param t the token type to add - */ - void add(ETokenType t); - - /** - * Adds a currency token with specific count to this affix pattern. - * @param count the token count. Used to distinguish between - * one, two, or three currency symbols. Note that adding a currency - * token with count=2 (Use ISO code) is different than adding two - * currency tokens each with count=1 (two currency symbols). - */ - void addCurrency(uint8_t count); - - /** - * Makes this instance be an empty affix pattern. - */ - void remove(); - - /** - * Provides an iterator over the tokens in this instance. - * @param result this is initialized to point just before the - * first token of this instance. Caller must call nextToken() - * on the iterator once it is set up to have it actually point - * to the first token. This first call to nextToken() will return - * FALSE if the AffixPattern being iterated over is empty. - * @return result - */ - AffixPatternIterator &iterator(AffixPatternIterator &result) const; - - /** - * Returns TRUE if this instance has currency tokens in it. - */ - UBool usesCurrency() const { - return hasCurrencyToken; - } - - UBool usesPercent() const { - return hasPercentToken; - } - - UBool usesPermill() const { - return hasPermillToken; - } - - /** - * Returns the number of code points a string of this instance - * would have if none of the special tokens were escaped. - * Used to compute the padding size. - */ - int32_t countChar32() const { - return char32Count; - } - - /** - * Appends other to this instance mutating this instance in place. - * @param other The pattern appended to the end of this one. - * @return a reference to this instance for chaining. - */ - AffixPattern &append(const AffixPattern &other); - - /** - * Converts this AffixPattern back into a user string. - * It is the inverse of parseUserAffixString. - */ - UnicodeString &toUserString(UnicodeString &appendTo) const; - - /** - * Converts this AffixPattern back into a string. - * It is the inverse of parseAffixString. - */ - UnicodeString &toString(UnicodeString &appendTo) const; - - /** - * Parses an affix pattern string appending it to an AffixPattern. - * Parses affix pattern strings produced from using - * DecimalFormatPatternParser to parse a format pattern. Affix patterns - * include the positive prefix and suffix and the negative prefix - * and suffix. This method expects affix patterns strings to be in the - * same format that DecimalFormatPatternParser produces. Namely special - * characters in the affix that correspond to a field type must be - * prefixed with an apostrophe ('). These special character sequences - * inluce minus (-), percent (%), permile (U+2030), plus (+), - * short currency (U+00a4), medium currency (u+00a4 * 2), - * long currency (u+a4 * 3), and apostrophe (') - * (apostrophe does not correspond to a field type but has to be escaped - * because it itself is the escape character). - * Since the expansion of these special character - * sequences is locale dependent, these sequences are not expanded in - * an AffixPattern instance. - * If these special characters are not prefixed with an apostrophe in - * the affix pattern string, then they are treated verbatim just as - * any other character. If an apostrophe prefixes a non special - * character in the affix pattern, the apostrophe is simply ignored. - * - * @param affixStr the string from DecimalFormatPatternParser - * @param appendTo parsed result appended here. - * @param status any error parsing returned here. - */ - static AffixPattern &parseAffixString( - const UnicodeString &affixStr, - AffixPattern &appendTo, - UErrorCode &status); - - /** - * Parses an affix pattern string appending it to an AffixPattern. - * Parses affix pattern strings as the user would supply them. - * In this function, quoting makes special characters like normal - * characters whereas in parseAffixString, quoting makes special - * characters special. - * - * @param affixStr the string from the user - * @param appendTo parsed result appended here. - * @param status any error parsing returned here. - */ - static AffixPattern &parseUserAffixString( - const UnicodeString &affixStr, - AffixPattern &appendTo, - UErrorCode &status); - - UBool equals(const AffixPattern &other) const { - return (tokens == other.tokens) - && (literals == other.literals) - && (hasCurrencyToken == other.hasCurrencyToken) - && (hasPercentToken == other.hasPercentToken) - && (hasPermillToken == other.hasPermillToken) - && (char32Count == other.char32Count); - } - -private: - /* - * Tokens stored here. Each UChar generally stands for one token. A - * Each token is of form 'etttttttllllllll' llllllll is the length of - * the token and ranges from 0-255. ttttttt is the token type and ranges - * from 0-127. If e is set it means this is an extendo token (to be - * described later). To accomodate token lengths above 255, each normal - * token (e=0) can be followed by 0 or more extendo tokens (e=1) with - * the same type. Right now only kLiteral Tokens have extendo tokens. - * Each extendo token provides the next 8 higher bits for the length. - * If a kLiteral token is followed by 2 extendo tokens then, then the - * llllllll of the next extendo token contains bits 8-15 of the length - * and the last extendo token contains bits 16-23 of the length. - */ - UnicodeString tokens; - - /* - * The characters of the kLiteral tokens are concatenated together here. - * The first characters go with the first kLiteral token, the next - * characters go with the next kLiteral token etc. - */ - UnicodeString literals; - UBool hasCurrencyToken; - UBool hasPercentToken; - UBool hasPermillToken; - int32_t char32Count; - void add(ETokenType t, uint8_t count); - -}; - -/** - * An iterator over the tokens in an AffixPattern instance. - */ -class U_I18N_API AffixPatternIterator : public UMemory { -public: - - /** - * Using an iterator without first calling iterator on an AffixPattern - * instance to initialize the iterator results in - * undefined behavior. - */ - AffixPatternIterator() : nextLiteralIndex(0), lastLiteralLength(0), nextTokenIndex(0), tokens(NULL), literals(NULL) { } - /** - * Advances this iterator to the next token. Returns FALSE when there - * are no more tokens. Calling the other methods after nextToken() - * returns FALSE results in undefined behavior. - */ - UBool nextToken(); - - /** - * Returns the type of token. - */ - AffixPattern::ETokenType getTokenType() const; - - /** - * For literal tokens, returns the literal string. Calling this for - * other token types results in undefined behavior. - * @param result replaced with a read-only alias to the literal string. - * @return result - */ - UnicodeString &getLiteral(UnicodeString &result) const; - - /** - * Returns the token length. Usually 1, but for currency tokens may - * be 2 for ISO code and 3 for long form. - */ - int32_t getTokenLength() const; -private: - int32_t nextLiteralIndex; - int32_t lastLiteralLength; - int32_t nextTokenIndex; - const UnicodeString *tokens; - const UnicodeString *literals; - friend class AffixPattern; - AffixPatternIterator(const AffixPatternIterator &); - AffixPatternIterator &operator=(const AffixPatternIterator &); -}; - -/** - * A locale aware class that converts locale independent AffixPattern - * instances into locale dependent PluralAffix instances. - */ -class U_I18N_API AffixPatternParser : public UMemory { -public: -AffixPatternParser(); -AffixPatternParser(const DecimalFormatSymbols &symbols); -void setDecimalFormatSymbols(const DecimalFormatSymbols &symbols); - -/** - * Parses affixPattern appending the result to appendTo. - * @param affixPattern The affix pattern. - * @param currencyAffixInfo contains the currency forms. - * @param appendTo The result of parsing affixPattern is appended here. - * @param status any error returned here. - * @return appendTo. - */ -PluralAffix &parse( - const AffixPattern &affixPattern, - const CurrencyAffixInfo ¤cyAffixInfo, - PluralAffix &appendTo, - UErrorCode &status) const; - -UBool equals(const AffixPatternParser &other) const { - return (fPercent == other.fPercent) - && (fPermill == other.fPermill) - && (fNegative == other.fNegative) - && (fPositive == other.fPositive); -} - -private: -UnicodeString fPercent; -UnicodeString fPermill; -UnicodeString fNegative; -UnicodeString fPositive; -}; - - -U_NAMESPACE_END -#endif /* #if !UCONFIG_NO_FORMATTING */ -#endif // __AFFIX_PATTERN_PARSER_H__ diff --git a/deps/icu-small/source/i18n/collationfcd.cpp b/deps/icu-small/source/i18n/collationfcd.cpp index 19841ee6487ad2..1aff936dee1d2a 100644 --- a/deps/icu-small/source/i18n/collationfcd.cpp +++ b/deps/icu-small/source/i18n/collationfcd.cpp @@ -22,27 +22,27 @@ const uint8_t CollationFCD::lcccIndex[2048]={ 0,0,0,0,0,0,0,0,1,1,2,3,0,0,0,0, 0,0,0,0,4,0,0,0,0,0,0,0,5,6,7,0, 8,0,9,0xa,0,0,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0x10, -0x11,0x12,0x13,0,0,0,0x14,0x15,0,0x16,0x17,0,0,0x16,0x18,0, +0x11,0x12,0x13,0,0,0,0x14,0x15,0,0x16,0x17,0,0,0x16,0x18,0x19, 0,0x16,0x18,0,0,0x16,0x18,0,0,0x16,0x18,0,0,0,0x18,0, -0,0,0x19,0,0,0x16,0x18,0,0,0x1a,0x18,0,0,0,0x1b,0, -0,0x1c,0x1d,0,0,0x1e,0x1d,0,0x1e,0x1f,0,0x20,0x21,0,0x22,0, -0,0x23,0,0,0x18,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0x24,0,0,0,0,0, +0,0,0x1a,0,0,0x16,0x18,0,0,0x1b,0x18,0,0,0,0x1c,0, +0,0x1d,0x1e,0,0,0x1f,0x1e,0,0x1f,0x20,0,0x21,0x22,0,0x23,0, +0,0x24,0,0,0x18,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0x25,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0x25,0x25,0,0,0,0,0x26,0, -0,0,0,0,0,0x27,0,0,0,0x13,0,0,0,0,0,0, -0x28,0,0,0x29,0,0x2a,0,0,0,0x25,0x2b,0x10,0,0x2c,0,0x2d, -0,0x2e,0,0,0,0,0x2f,0x30,0,0,0,0,0,0,1,0x31, +0,0,0,0,0,0,0,0,0x26,0x26,0,0,0,0,0x27,0, +0,0,0,0,0,0x28,0,0,0,0x13,0,0,0,0,0,0, +0x29,0,0,0x2a,0,0x2b,0,0,0,0x26,0x2c,0x2d,0,0x2e,0,0x2f, +0,0x30,0,0,0,0,0x31,0x32,0,0,0,0,0,0,1,0x33, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0x32,0x33,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0x34,0x35,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0x34,0,0,0,0x35,0,0,0,1, +0,0,0,0,0,0,0,0x36,0,0,0,0x37,0,0,0,1, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0x36,0,0,0x37,0,0,0,0,0,0,0,0,0,0,0, +0,0x38,0,0,0x39,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, @@ -101,9 +101,9 @@ const uint8_t CollationFCD::lcccIndex[2048]={ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0x38,0x39,0,0,0x3a,0,0,0,0,0,0,0,0, -0x22,0,0,0,0,0,0x2b,0x3b,0,0x3c,0x3d,0,0,0x3d,0x3e,0, -0,0,0,0,0,0x3f,0x40,0x41,0,0,0,0,0,0,0,0x18, +0,0,0,0x3a,0x3b,0,0,0x3c,0,0,0,0,0,0,0,0, +0x23,0,0,0,0,0,0x2c,0x3d,0,0x3e,0x3f,0,0,0x3f,0x40,0, +0,0,0,0,0,0x41,0x42,0x43,0,0,0,0,0,0,0,0x18, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, @@ -126,7 +126,7 @@ const uint8_t CollationFCD::lcccIndex[2048]={ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0x42,0x43,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0x44,0x45,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, @@ -143,17 +143,17 @@ const uint8_t CollationFCD::lcccIndex[2048]={ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0x44,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0x19,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; -const uint32_t CollationFCD::lcccBits[69]={ +const uint32_t CollationFCD::lcccBits[70]={ 0,0xffffffff,0xffff7fff,0xffff,0xf8,0xfffe0000,0xbfffffff,0xb6,0x7ff0000,0xfffff800,0x10000,0x9fc00000,0x3d9f,0x20000,0xffff0000,0x7ff, -0xff800,0xfbc00000,0x3eef,0xe000000,0xfff00000,0xfffffffb,0x10000000,0x1e2000,0x2000,0x602000,0x18000000,0x400,0x7000000,0xf00,0x3000000,0x2a00000, -0x3c3e0000,0xdf,0x40,0x6800000,0xe0000000,0x100000,0x20040000,0x200,0x1800000,0x9fe00001,0x3fff0000,0x10,0xc00,0xc0040,0x800000,0xfff70000, -0x31021fd,0xfbffffff,0x1fff0000,0x1ffe2,0x38000,0x80000000,0xfc00,0x6000000,0x3ff08000,0xc0000000,0x30000,0x3ffff,0x3800,0x80000,1,0xc19d0000, -2,0x400000,0x40000f5,0x5108000,0x40000000 +0x200ff800,0xfbc00000,0x3eef,0xe000000,0xfff80000,0xfffffffb,0x10000000,0x1e2000,0x2000,0x40000000,0x602000,0x18000000,0x400,0x7000000,0xf00,0x3000000, +0x2a00000,0x3c3e0000,0xdf,0x40,0x6800000,0xe0000000,0x100000,0x20040000,0x200,0x1800000,0x9fe00001,0x3fff0000,0x10,0xff800,0xc00,0xc0040, +0x800000,0xfff70000,0x31021fd,0xfbffffff,0x1fff0000,0x1ffe2,0x38000,0x80000000,0xfc00,0x6000000,0x3ff08000,0xc0000000,0x30000,0x3ffff,0x3800,0x80000, +1,0xc19d0000,2,0x400000,0x40000fd,0x5108000 }; const uint8_t CollationFCD::tcccIndex[2048]={ @@ -161,27 +161,27 @@ const uint8_t CollationFCD::tcccIndex[2048]={ 0xb,0xc,0,0,0,0,0,0,1,1,0xd,0xe,0xf,0x10,0x11,0, 0x12,0x13,0x14,0x15,0x16,0,0x17,0x18,0,0,0,0,0x19,0x1a,0x1b,0, 0x1c,0x1d,0x1e,0x1f,0,0,0x20,0x21,0x22,0x23,0x24,0,0,0,0,0x25, -0x26,0x27,0x28,0,0,0,0x29,0x2a,0,0x2b,0x2c,0,0,0x2d,0x2e,0, -0,0x2f,0x30,0,0,0x2d,0x31,0,0,0x2d,0x32,0,0,0,0x31,0, -0,0,0x33,0,0,0x2d,0x31,0,0,0x34,0x31,0,0,0,0x35,0, -0,0x36,0x37,0,0,0x38,0x37,0,0x38,0x39,0,0x3a,0x3b,0,0x3c,0, -0,0x3d,0,0,0x31,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0x3e,0,0,0,0,0, +0x26,0x27,0x28,0,0,0,0x29,0x2a,0,0x2b,0x2c,0,0,0x2d,0x2e,0x2f, +0,0x30,0x31,0,0,0x2d,0x32,0,0,0x2d,0x33,0,0,0,0x32,0, +0,0,0x34,0,0,0x2d,0x32,0,0,0x35,0x32,0,0,0,0x36,0, +0,0x37,0x38,0,0,0x39,0x38,0,0x39,0x3a,0,0x3b,0x3c,0,0x3d,0, +0,0x3e,0,0,0x32,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0x3f,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0x3f,0x3f,0,0,0,0,0x40,0, -0,0,0,0,0,0x41,0,0,0,0x28,0,0,0,0,0,0, -0x42,0,0,0x43,0,0x44,0,0,0,0x3f,0x45,0x25,0,0x46,0,0x47, -0,0x48,0,0,0,0,0x49,0x4a,0,0,0,0,0,0,1,0x4b, -1,1,1,1,0x4c,1,1,0x4d,0x4e,1,0x4f,0x50,1,0x51,0x52,0x53, -0,0,0,0,0,0,0x54,0x55,0,0x56,0,0,0x57,0x58,0x59,0, -0x5a,0x5b,0x5c,0x5d,0x5e,0x5f,0,0x60,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0x40,0x40,0,0,0,0,0x41,0, +0,0,0,0,0,0x42,0,0,0,0x28,0,0,0,0,0,0, +0x43,0,0,0x44,0,0x45,0,0,0,0x40,0x46,0x47,0,0x48,0,0x49, +0,0x4a,0,0,0,0,0x4b,0x4c,0,0,0,0,0,0,1,0x4d, +1,1,1,1,0x4e,1,1,0x4f,0x50,1,0x51,0x52,1,0x53,0x54,0x55, +0,0,0,0,0,0,0x56,0x57,0,0x58,0,0,0x59,0x5a,0x5b,0, +0x5c,0x5d,0x5e,0x5f,0x60,0x61,0,0x62,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0x2d,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0x61,0,0,0,0x62,0,0,0,1, +0,0,0,0,0,0,0,0x63,0,0,0,0x64,0,0,0,1, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0x63,0x64,0x65,0x66,0x64,0x65,0x67,0,0,0,0,0,0,0,0, +0,0x65,0x66,0x67,0x68,0x66,0x67,0x69,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, @@ -240,9 +240,9 @@ const uint8_t CollationFCD::tcccIndex[2048]={ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0x68,0x69,0,0,0x6a,0,0,0,0,0,0,0,0, -0x3c,0,0,0,0,0,0x45,0x6b,0,0x6c,0x6d,0,0,0x6d,0x6e,0, -0,0,0,0,0,0x6f,0x70,0x71,0,0,0,0,0,0,0,0x31, +0,0,0,0x6a,0x6b,0,0,0x6c,0,0,0,0,0,0,0,0, +0x3d,0,0,0,0,0,0x46,0x6d,0,0x6e,0x6f,0,0,0x6f,0x70,0, +0,0,0,0,0,0x71,0x72,0x73,0,0,0,0,0,0,0,0x32, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, @@ -265,7 +265,7 @@ const uint8_t CollationFCD::tcccIndex[2048]={ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0x72,0x73,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0x74,0x75,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, @@ -282,20 +282,20 @@ const uint8_t CollationFCD::tcccIndex[2048]={ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0x3e,0x74,0x75,0,0,0,0,0, +0,0,0,0,0,0,0,0,0x3f,0x76,0x77,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0xe,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; -const uint32_t CollationFCD::tcccBits[118]={ +const uint32_t CollationFCD::tcccBits[120]={ 0,0xffffffff,0x3e7effbf,0xbe7effbf,0xfffcffff,0x7ef1ff3f,0xfff3f1f8,0x7fffff3f,0x18003,0xdfffe000,0xff31ffcf,0xcfffffff,0xfffc0,0xffff7fff,0xffff,0x1d760, 0x1fc00,0x187c00,0x200708b,0x2000000,0x708b0000,0xc00000,0xf8,0xfccf0006,0x33ffcfc,0xfffe0000,0xbfffffff,0xb6,0x7ff0000,0x7c,0xfffff800,0x10000, -0x9fc80005,0x3d9f,0x20000,0xffff0000,0x7ff,0xff800,0xfbc00000,0x3eef,0xe000000,0xfff00000,0xfffffffb,0x10120200,0xff1e2000,0x10000000,0xb0002000,0x10480000, -0x4e002000,0x2000,0x30002000,0x602100,0x18000000,0x24000400,0x7000000,0xf00,0x3000000,0x2a00000,0x3d7e0000,0xdf,0x40,0x6800000,0xe0000000,0x100000, -0x20040000,0x200,0x1800000,0x9fe00001,0x3fff0000,0x10,0xc00,0xc0040,0x800000,0xfff70000,0x31021fd,0xfbffffff,0xbffffff,0x3ffffff,0x3f3fffff,0xaaff3f3f, -0x3fffffff,0x1fdfffff,0xefcfffde,0x1fdc7fff,0x1fff0000,0x1ffe2,0x800,0xc000000,0x4000,0xe000,0x1210,0x50,0x292,0x333e005,0x333,0xf000, -0x3c0f,0x38000,0x80000000,0xfc00,0x55555000,0x36db02a5,0x46100000,0x47900000,0x3ff08000,0xc0000000,0x30000,0x3ffff,0x3800,0x80000,1,0xc19d0000, -2,0x400000,0x40000f5,0x5108000,0x5f7ffc00,0x7fdb +0x9fc80005,0x3d9f,0x20000,0xffff0000,0x7ff,0x200ff800,0xfbc00000,0x3eef,0xe000000,0xfff80000,0xfffffffb,0x10120200,0xff1e2000,0x10000000,0xb0002000,0x40000000, +0x10480000,0x4e002000,0x2000,0x30002000,0x602100,0x18000000,0x24000400,0x7000000,0xf00,0x3000000,0x2a00000,0x3d7e0000,0xdf,0x40,0x6800000,0xe0000000, +0x100000,0x20040000,0x200,0x1800000,0x9fe00001,0x3fff0000,0x10,0xff800,0xc00,0xc0040,0x800000,0xfff70000,0x31021fd,0xfbffffff,0xbffffff,0x3ffffff, +0x3f3fffff,0xaaff3f3f,0x3fffffff,0x1fdfffff,0xefcfffde,0x1fdc7fff,0x1fff0000,0x1ffe2,0x800,0xc000000,0x4000,0xe000,0x1210,0x50,0x292,0x333e005, +0x333,0xf000,0x3c0f,0x38000,0x80000000,0xfc00,0x55555000,0x36db02a5,0x46100000,0x47900000,0x3ff08000,0xc0000000,0x30000,0x3ffff,0x3800,0x80000, +1,0xc19d0000,2,0x400000,0x40000fd,0x5108000,0x5f7ffc00,0x7fdb }; U_NAMESPACE_END diff --git a/deps/icu-small/source/i18n/compactdecimalformat.cpp b/deps/icu-small/source/i18n/compactdecimalformat.cpp index b2aacc45cda49c..4dd2241b23d0a6 100644 --- a/deps/icu-small/source/i18n/compactdecimalformat.cpp +++ b/deps/icu-small/source/i18n/compactdecimalformat.cpp @@ -1,1013 +1,75 @@ -// © 2016 and later: Unicode, Inc. and others. +// © 2018 and later: Unicode, Inc. and others. // License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 1997-2015, International Business Machines Corporation and * -* others. All Rights Reserved. * -******************************************************************************* -* -* File COMPACTDECIMALFORMAT.CPP -* -******************************************************************************** -*/ + #include "unicode/utypes.h" #if !UCONFIG_NO_FORMATTING -#include "charstr.h" -#include "cstring.h" -#include "digitlst.h" -#include "mutex.h" -#include "unicode/compactdecimalformat.h" -#include "unicode/numsys.h" -#include "unicode/plurrule.h" -#include "unicode/ures.h" -#include "ucln_in.h" -#include "uhash.h" -#include "umutex.h" -#include "unicode/ures.h" -#include "uresimp.h" - -// Maps locale name to CDFLocaleData struct. -static UHashtable* gCompactDecimalData = NULL; -static UMutex gCompactDecimalMetaLock = U_MUTEX_INITIALIZER; - -U_NAMESPACE_BEGIN - -static const int32_t MAX_DIGITS = 15; -static const char gOther[] = "other"; -static const char gLatnTag[] = "latn"; -static const char gNumberElementsTag[] = "NumberElements"; -static const char gDecimalFormatTag[] = "decimalFormat"; -static const char gPatternsShort[] = "patternsShort"; -static const char gPatternsLong[] = "patternsLong"; -static const char gLatnPath[] = "NumberElements/latn"; - -static const UChar u_0 = 0x30; -static const UChar u_apos = 0x27; - -static const UChar kZero[] = {u_0}; - -// Used to unescape single quotes. -enum QuoteState { - OUTSIDE, - INSIDE_EMPTY, - INSIDE_FULL -}; - -enum FallbackFlags { - ANY = 0, - MUST = 1, - NOT_ROOT = 2 - // Next one will be 4 then 6 etc. -}; - - -// CDFUnit represents a prefix-suffix pair for a particular variant -// and log10 value. -struct CDFUnit : public UMemory { - UnicodeString prefix; - UnicodeString suffix; - inline CDFUnit() : prefix(), suffix() { - prefix.setToBogus(); - } - inline ~CDFUnit() {} - inline UBool isSet() const { - return !prefix.isBogus(); - } - inline void markAsSet() { - prefix.remove(); - } -}; - -// CDFLocaleStyleData contains formatting data for a particular locale -// and style. -class CDFLocaleStyleData : public UMemory { - public: - // What to divide by for each log10 value when formatting. These values - // will be powers of 10. For English, would be: - // 1, 1, 1, 1000, 1000, 1000, 1000000, 1000000, 1000000, 1000000000 ... - double divisors[MAX_DIGITS]; - // Maps plural variants to CDFUnit[MAX_DIGITS] arrays. - // To format a number x, - // first compute log10(x). Compute displayNum = (x / divisors[log10(x)]). - // Compute the plural variant for displayNum - // (e.g zero, one, two, few, many, other). - // Compute cdfUnits = unitsByVariant[pluralVariant]. - // Prefix and suffix to use at cdfUnits[log10(x)] - UHashtable* unitsByVariant; - // A flag for whether or not this CDFLocaleStyleData was loaded from the - // Latin numbering system as a fallback from the locale numbering system. - // This value is meaningless if the object is bogus or empty. - UBool fromFallback; - inline CDFLocaleStyleData() : unitsByVariant(NULL), fromFallback(FALSE) { - uprv_memset(divisors, 0, sizeof(divisors)); - } - ~CDFLocaleStyleData(); - // Init initializes this object. - void Init(UErrorCode& status); - inline UBool isBogus() const { - return unitsByVariant == NULL; - } - void setToBogus(); - UBool isEmpty() { - return unitsByVariant == NULL || unitsByVariant->count == 0; - } - private: - CDFLocaleStyleData(const CDFLocaleStyleData&); - CDFLocaleStyleData& operator=(const CDFLocaleStyleData&); -}; - -// CDFLocaleData contains formatting data for a particular locale. -struct CDFLocaleData : public UMemory { - CDFLocaleStyleData shortData; - CDFLocaleStyleData longData; - inline CDFLocaleData() : shortData(), longData() { } - inline ~CDFLocaleData() { } - // Init initializes this object. - void Init(UErrorCode& status); -}; - -U_NAMESPACE_END - -U_CDECL_BEGIN - -static UBool U_CALLCONV cdf_cleanup(void) { - if (gCompactDecimalData != NULL) { - uhash_close(gCompactDecimalData); - gCompactDecimalData = NULL; - } - return TRUE; -} - -static void U_CALLCONV deleteCDFUnits(void* ptr) { - delete [] (icu::CDFUnit*) ptr; -} - -static void U_CALLCONV deleteCDFLocaleData(void* ptr) { - delete (icu::CDFLocaleData*) ptr; -} - -U_CDECL_END +// Allow implicit conversion from char16_t* to UnicodeString for this file: +// Helpful in toString methods and elsewhere. +#define UNISTR_FROM_STRING_EXPLICIT -U_NAMESPACE_BEGIN +#include "unicode/compactdecimalformat.h" +#include "number_mapper.h" +#include "number_decimfmtprops.h" -static UBool divisors_equal(const double* lhs, const double* rhs); -static const CDFLocaleStyleData* getCDFLocaleStyleData(const Locale& inLocale, UNumberCompactStyle style, UErrorCode& status); +using namespace icu; -static const CDFLocaleStyleData* extractDataByStyleEnum(const CDFLocaleData& data, UNumberCompactStyle style, UErrorCode& status); -static CDFLocaleData* loadCDFLocaleData(const Locale& inLocale, UErrorCode& status); -static void load(const Locale& inLocale, CDFLocaleData* result, UErrorCode& status); -static int32_t populatePrefixSuffix(const char* variant, int32_t log10Value, const UnicodeString& formatStr, UHashtable* result, UBool overwrite, UErrorCode& status); -static double calculateDivisor(double power10, int32_t numZeros); -static UBool onlySpaces(UnicodeString u); -static void fixQuotes(UnicodeString& s); -static void checkForOtherVariants(CDFLocaleStyleData* result, UErrorCode& status); -static void fillInMissing(CDFLocaleStyleData* result); -static int32_t computeLog10(double x, UBool inRange); -static CDFUnit* createCDFUnit(const char* variant, int32_t log10Value, UHashtable* table, UErrorCode& status); -static const CDFUnit* getCDFUnitFallback(const UHashtable* table, const UnicodeString& variant, int32_t log10Value); UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CompactDecimalFormat) -CompactDecimalFormat::CompactDecimalFormat( - const DecimalFormat& decimalFormat, - const UHashtable* unitsByVariant, - const double* divisors, - PluralRules* pluralRules) - : DecimalFormat(decimalFormat), _unitsByVariant(unitsByVariant), _divisors(divisors), _pluralRules(pluralRules) { -} - -CompactDecimalFormat::CompactDecimalFormat(const CompactDecimalFormat& source) - : DecimalFormat(source), _unitsByVariant(source._unitsByVariant), _divisors(source._divisors), _pluralRules(source._pluralRules->clone()) { -} - -CompactDecimalFormat* U_EXPORT2 -CompactDecimalFormat::createInstance( - const Locale& inLocale, UNumberCompactStyle style, UErrorCode& status) { - LocalPointer decfmt((DecimalFormat*) NumberFormat::makeInstance(inLocale, UNUM_DECIMAL, TRUE, status)); - if (U_FAILURE(status)) { - return NULL; - } - LocalPointer pluralRules(PluralRules::forLocale(inLocale, status)); - if (U_FAILURE(status)) { - return NULL; - } - const CDFLocaleStyleData* data = getCDFLocaleStyleData(inLocale, style, status); - if (U_FAILURE(status)) { - return NULL; - } - CompactDecimalFormat* result = - new CompactDecimalFormat(*decfmt, data->unitsByVariant, data->divisors, pluralRules.getAlias()); - if (result == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - pluralRules.orphan(); - result->setMaximumSignificantDigits(3); - result->setSignificantDigitsUsed(TRUE); - result->setGroupingUsed(FALSE); - return result; -} - -CompactDecimalFormat& -CompactDecimalFormat::operator=(const CompactDecimalFormat& rhs) { - if (this != &rhs) { - DecimalFormat::operator=(rhs); - _unitsByVariant = rhs._unitsByVariant; - _divisors = rhs._divisors; - delete _pluralRules; - _pluralRules = rhs._pluralRules->clone(); - } - return *this; -} - -CompactDecimalFormat::~CompactDecimalFormat() { - delete _pluralRules; -} - -Format* -CompactDecimalFormat::clone(void) const { - return new CompactDecimalFormat(*this); +CompactDecimalFormat* +CompactDecimalFormat::createInstance(const Locale& inLocale, UNumberCompactStyle style, + UErrorCode& status) { + return new CompactDecimalFormat(inLocale, style, status); } -UBool -CompactDecimalFormat::operator==(const Format& that) const { - if (this == &that) { - return TRUE; - } - return (DecimalFormat::operator==(that) && eqHelper((const CompactDecimalFormat&) that)); -} - -UBool -CompactDecimalFormat::eqHelper(const CompactDecimalFormat& that) const { - return uhash_equals(_unitsByVariant, that._unitsByVariant) && divisors_equal(_divisors, that._divisors) && (*_pluralRules == *that._pluralRules); -} - -UnicodeString& -CompactDecimalFormat::format( - double number, - UnicodeString& appendTo, - FieldPosition& pos) const { - UErrorCode status = U_ZERO_ERROR; - return format(number, appendTo, pos, status); -} - -UnicodeString& -CompactDecimalFormat::format( - double number, - UnicodeString& appendTo, - FieldPosition& pos, - UErrorCode &status) const { - if (U_FAILURE(status)) { - return appendTo; - } - DigitList orig, rounded; - orig.set(number); - UBool isNegative; - _round(orig, rounded, isNegative, status); - if (U_FAILURE(status)) { - return appendTo; - } - double roundedDouble = rounded.getDouble(); - if (isNegative) { - roundedDouble = -roundedDouble; - } - int32_t baseIdx = computeLog10(roundedDouble, TRUE); - double numberToFormat = roundedDouble / _divisors[baseIdx]; - UnicodeString variant = _pluralRules->select(numberToFormat); - if (isNegative) { - numberToFormat = -numberToFormat; - } - const CDFUnit* unit = getCDFUnitFallback(_unitsByVariant, variant, baseIdx); - appendTo += unit->prefix; - DecimalFormat::format(numberToFormat, appendTo, pos); - appendTo += unit->suffix; - return appendTo; -} - -UnicodeString& -CompactDecimalFormat::format( - double /* number */, - UnicodeString& appendTo, - FieldPositionIterator* /* posIter */, - UErrorCode& status) const { - status = U_UNSUPPORTED_ERROR; - return appendTo; -} - -UnicodeString& -CompactDecimalFormat::format( - int32_t number, - UnicodeString& appendTo, - FieldPosition& pos) const { - return format((double) number, appendTo, pos); -} - -UnicodeString& -CompactDecimalFormat::format( - int32_t number, - UnicodeString& appendTo, - FieldPosition& pos, - UErrorCode &status) const { - return format((double) number, appendTo, pos, status); -} - -UnicodeString& -CompactDecimalFormat::format( - int32_t /* number */, - UnicodeString& appendTo, - FieldPositionIterator* /* posIter */, - UErrorCode& status) const { - status = U_UNSUPPORTED_ERROR; - return appendTo; -} - -UnicodeString& -CompactDecimalFormat::format( - int64_t number, - UnicodeString& appendTo, - FieldPosition& pos) const { - return format((double) number, appendTo, pos); -} - -UnicodeString& -CompactDecimalFormat::format( - int64_t number, - UnicodeString& appendTo, - FieldPosition& pos, - UErrorCode &status) const { - return format((double) number, appendTo, pos, status); +CompactDecimalFormat::CompactDecimalFormat(const Locale& inLocale, UNumberCompactStyle style, + UErrorCode& status) + : DecimalFormat(new DecimalFormatSymbols(inLocale, status), status) { + if (U_FAILURE(status)) return; + // Minimal properties: let the non-shim code path do most of the logic for us. + fields->properties->compactStyle = style; + fields->properties->groupingSize = -2; // do not forward grouping information + fields->properties->minimumGroupingDigits = 2; + touch(status); } -UnicodeString& -CompactDecimalFormat::format( - int64_t /* number */, - UnicodeString& appendTo, - FieldPositionIterator* /* posIter */, - UErrorCode& status) const { - status = U_UNSUPPORTED_ERROR; - return appendTo; -} +CompactDecimalFormat::CompactDecimalFormat(const CompactDecimalFormat& source) = default; -UnicodeString& -CompactDecimalFormat::format( - StringPiece /* number */, - UnicodeString& appendTo, - FieldPositionIterator* /* posIter */, - UErrorCode& status) const { - status = U_UNSUPPORTED_ERROR; - return appendTo; -} +CompactDecimalFormat::~CompactDecimalFormat() = default; -UnicodeString& -CompactDecimalFormat::format( - const DigitList& /* number */, - UnicodeString& appendTo, - FieldPositionIterator* /* posIter */, - UErrorCode& status) const { - status = U_UNSUPPORTED_ERROR; - return appendTo; +CompactDecimalFormat& CompactDecimalFormat::operator=(const CompactDecimalFormat& rhs) { + DecimalFormat::operator=(rhs); + return *this; } -UnicodeString& -CompactDecimalFormat::format(const DigitList& /* number */, - UnicodeString& appendTo, - FieldPosition& /* pos */, - UErrorCode& status) const { - status = U_UNSUPPORTED_ERROR; - return appendTo; +Format* CompactDecimalFormat::clone() const { + return new CompactDecimalFormat(*this); } void CompactDecimalFormat::parse( - const UnicodeString& /* text */, - Formattable& /* result */, - ParsePosition& /* parsePosition */) const { + const UnicodeString& /* text */, + Formattable& /* result */, + ParsePosition& /* parsePosition */) const { } void CompactDecimalFormat::parse( - const UnicodeString& /* text */, - Formattable& /* result */, - UErrorCode& status) const { - status = U_UNSUPPORTED_ERROR; + const UnicodeString& /* text */, + Formattable& /* result */, + UErrorCode& status) const { + status = U_UNSUPPORTED_ERROR; } CurrencyAmount* CompactDecimalFormat::parseCurrency( - const UnicodeString& /* text */, - ParsePosition& /* pos */) const { - return NULL; -} - -void CDFLocaleStyleData::Init(UErrorCode& status) { - if (unitsByVariant != NULL) { - return; - } - unitsByVariant = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status); - if (U_FAILURE(status)) { - return; - } - uhash_setKeyDeleter(unitsByVariant, uprv_free); - uhash_setValueDeleter(unitsByVariant, deleteCDFUnits); -} - -CDFLocaleStyleData::~CDFLocaleStyleData() { - setToBogus(); -} - -void CDFLocaleStyleData::setToBogus() { - if (unitsByVariant != NULL) { - uhash_close(unitsByVariant); - unitsByVariant = NULL; - } -} - -void CDFLocaleData::Init(UErrorCode& status) { - shortData.Init(status); - if (U_FAILURE(status)) { - return; - } - longData.Init(status); -} - -// Helper method for operator= -static UBool divisors_equal(const double* lhs, const double* rhs) { - for (int32_t i = 0; i < MAX_DIGITS; ++i) { - if (lhs[i] != rhs[i]) { - return FALSE; - } - } - return TRUE; -} - -// getCDFLocaleStyleData returns pointer to formatting data for given locale and -// style within the global cache. On cache miss, getCDFLocaleStyleData loads -// the data from CLDR into the global cache before returning the pointer. If a -// UNUM_LONG data is requested for a locale, and that locale does not have -// UNUM_LONG data, getCDFLocaleStyleData will fall back to UNUM_SHORT data for -// that locale. -static const CDFLocaleStyleData* getCDFLocaleStyleData(const Locale& inLocale, UNumberCompactStyle style, UErrorCode& status) { - if (U_FAILURE(status)) { - return NULL; - } - CDFLocaleData* result = NULL; - const char* key = inLocale.getName(); - { - Mutex lock(&gCompactDecimalMetaLock); - if (gCompactDecimalData == NULL) { - gCompactDecimalData = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status); - if (U_FAILURE(status)) { - return NULL; - } - uhash_setKeyDeleter(gCompactDecimalData, uprv_free); - uhash_setValueDeleter(gCompactDecimalData, deleteCDFLocaleData); - ucln_i18n_registerCleanup(UCLN_I18N_CDFINFO, cdf_cleanup); - } else { - result = (CDFLocaleData*) uhash_get(gCompactDecimalData, key); - } - } - if (result != NULL) { - return extractDataByStyleEnum(*result, style, status); - } - - result = loadCDFLocaleData(inLocale, status); - if (U_FAILURE(status)) { - return NULL; - } - - { - Mutex lock(&gCompactDecimalMetaLock); - CDFLocaleData* temp = (CDFLocaleData*) uhash_get(gCompactDecimalData, key); - if (temp != NULL) { - delete result; - result = temp; - } else { - uhash_put(gCompactDecimalData, uprv_strdup(key), (void*) result, &status); - if (U_FAILURE(status)) { - return NULL; - } - } - } - return extractDataByStyleEnum(*result, style, status); -} - -static const CDFLocaleStyleData* extractDataByStyleEnum(const CDFLocaleData& data, UNumberCompactStyle style, UErrorCode& status) { - switch (style) { - case UNUM_SHORT: - return &data.shortData; - case UNUM_LONG: - if (!data.longData.isBogus()) { - return &data.longData; - } - return &data.shortData; - default: - status = U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } -} - -// loadCDFLocaleData loads formatting data from CLDR for a given locale. The -// caller owns the returned pointer. -static CDFLocaleData* loadCDFLocaleData(const Locale& inLocale, UErrorCode& status) { - if (U_FAILURE(status)) { - return NULL; - } - CDFLocaleData* result = new CDFLocaleData; - if (result == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - result->Init(status); - if (U_FAILURE(status)) { - delete result; - return NULL; - } - - load(inLocale, result, status); - - if (U_FAILURE(status)) { - delete result; - return NULL; - } - return result; -} - -namespace { - -struct CmptDecDataSink : public ResourceSink { - - CDFLocaleData& dataBundle; // Where to save values when they are read - UBool isLatin; // Whether or not we are traversing the Latin tree - UBool isFallback; // Whether or not we are traversing the Latin tree as fallback - - enum EPatternsTableKey { PATTERNS_SHORT, PATTERNS_LONG }; - enum EFormatsTableKey { DECIMAL_FORMAT, CURRENCY_FORMAT }; - - /* - * NumberElements{ <-- top (numbering system table) - * latn{ <-- patternsTable (one per numbering system) - * patternsLong{ <-- formatsTable (one per pattern) - * decimalFormat{ <-- powersOfTenTable (one per format) - * 1000{ <-- pluralVariantsTable (one per power of ten) - * one{"0 thousand"} <-- plural variant and template - */ - - CmptDecDataSink(CDFLocaleData& _dataBundle) - : dataBundle(_dataBundle), isLatin(FALSE), isFallback(FALSE) {} - virtual ~CmptDecDataSink(); - - virtual void put(const char *key, ResourceValue &value, UBool isRoot, UErrorCode &errorCode) { - // SPECIAL CASE: Don't consume root in the non-Latin numbering system - if (isRoot && !isLatin) { return; } - - ResourceTable patternsTable = value.getTable(errorCode); - if (U_FAILURE(errorCode)) { return; } - for (int i1 = 0; patternsTable.getKeyAndValue(i1, key, value); ++i1) { - - // Check for patternsShort or patternsLong - EPatternsTableKey patternsTableKey; - if (uprv_strcmp(key, gPatternsShort) == 0) { - patternsTableKey = PATTERNS_SHORT; - } else if (uprv_strcmp(key, gPatternsLong) == 0) { - patternsTableKey = PATTERNS_LONG; - } else { - continue; - } - - // Traverse into the formats table - ResourceTable formatsTable = value.getTable(errorCode); - if (U_FAILURE(errorCode)) { return; } - for (int i2 = 0; formatsTable.getKeyAndValue(i2, key, value); ++i2) { - - // Check for decimalFormat or currencyFormat - EFormatsTableKey formatsTableKey; - if (uprv_strcmp(key, gDecimalFormatTag) == 0) { - formatsTableKey = DECIMAL_FORMAT; - // TODO: Enable this statement when currency support is added - // } else if (uprv_strcmp(key, gCurrencyFormat) == 0) { - // formatsTableKey = CURRENCY_FORMAT; - } else { - continue; - } - - // Set the current style and destination based on the two keys - UNumberCompactStyle style; - CDFLocaleStyleData* destination = NULL; - if (patternsTableKey == PATTERNS_LONG - && formatsTableKey == DECIMAL_FORMAT) { - style = UNUM_LONG; - destination = &dataBundle.longData; - } else if (patternsTableKey == PATTERNS_SHORT - && formatsTableKey == DECIMAL_FORMAT) { - style = UNUM_SHORT; - destination = &dataBundle.shortData; - // TODO: Enable the following statements when currency support is added - // } else if (patternsTableKey == PATTERNS_SHORT - // && formatsTableKey == CURRENCY_FORMAT) { - // style = UNUM_SHORT_CURRENCY; // or whatever the enum gets named - // destination = &dataBundle.shortCurrencyData; - // } else { - // // Silently ignore this case - // continue; - } - - // SPECIAL CASE: RULES FOR WHETHER OR NOT TO CONSUME THIS TABLE: - // 1) Don't consume longData if shortData was consumed from the non-Latin - // locale numbering system - // 2) Don't consume longData for the first time if this is the root bundle and - // shortData is already populated from a more specific locale. Note that if - // both longData and shortData are both only in root, longData will be - // consumed since it is alphabetically before shortData in the bundle. - if (isFallback - && style == UNUM_LONG - && !dataBundle.shortData.isEmpty() - && !dataBundle.shortData.fromFallback) { - continue; - } - if (isRoot - && style == UNUM_LONG - && dataBundle.longData.isEmpty() - && !dataBundle.shortData.isEmpty()) { - continue; - } - - // Set the "fromFallback" flag on the data object - destination->fromFallback = isFallback; - - // Traverse into the powers of ten table - ResourceTable powersOfTenTable = value.getTable(errorCode); - if (U_FAILURE(errorCode)) { return; } - for (int i3 = 0; powersOfTenTable.getKeyAndValue(i3, key, value); ++i3) { - - // The key will always be some even power of 10. e.g 10000. - char* endPtr = NULL; - double power10 = uprv_strtod(key, &endPtr); - if (*endPtr != 0) { - errorCode = U_INTERNAL_PROGRAM_ERROR; - return; - } - int32_t log10Value = computeLog10(power10, FALSE); - - // Silently ignore divisors that are too big. - if (log10Value >= MAX_DIGITS) continue; - - // Iterate over the plural variants ("one", "other", etc) - ResourceTable pluralVariantsTable = value.getTable(errorCode); - if (U_FAILURE(errorCode)) { return; } - for (int i4 = 0; pluralVariantsTable.getKeyAndValue(i4, key, value); ++i4) { - const char* pluralVariant = key; - const UnicodeString formatStr = value.getUnicodeString(errorCode); - - // Copy the data into the in-memory data bundle (do not overwrite - // existing values) - int32_t numZeros = populatePrefixSuffix( - pluralVariant, log10Value, formatStr, - destination->unitsByVariant, FALSE, errorCode); - - // If populatePrefixSuffix returns -1, it means that this key has been - // encountered already. - if (numZeros < 0) { - continue; - } - - // Set the divisor, which is based on the number of zeros in the template - // string. If the divisor from here is different from the one previously - // stored, it means that the number of zeros in different plural variants - // differs; throw an exception. - // TODO: How should I check for floating-point errors here? - // Is there a good reason why "divisor" is double and not long like Java? - double divisor = calculateDivisor(power10, numZeros); - if (destination->divisors[log10Value] != 0.0 - && destination->divisors[log10Value] != divisor) { - errorCode = U_INTERNAL_PROGRAM_ERROR; - return; - } - destination->divisors[log10Value] = divisor; - } - } - } - } - } -}; - -// Virtual destructors must be defined out of line. -CmptDecDataSink::~CmptDecDataSink() {} - -} // namespace - -static void load(const Locale& inLocale, CDFLocaleData* result, UErrorCode& status) { - LocalPointer ns(NumberingSystem::createInstance(inLocale, status)); - if (U_FAILURE(status)) { - return; - } - const char* nsName = ns->getName(); - - LocalUResourceBundlePointer resource(ures_open(NULL, inLocale.getName(), &status)); - if (U_FAILURE(status)) { - return; - } - CmptDecDataSink sink(*result); - sink.isFallback = FALSE; - - // First load the number elements data if nsName is not Latin. - if (uprv_strcmp(nsName, gLatnTag) != 0) { - sink.isLatin = FALSE; - CharString path; - path.append(gNumberElementsTag, status) - .append('/', status) - .append(nsName, status); - ures_getAllItemsWithFallback(resource.getAlias(), path.data(), sink, status); - if (status == U_MISSING_RESOURCE_ERROR) { - // Silently ignore and use Latin - status = U_ZERO_ERROR; - } else if (U_FAILURE(status)) { - return; - } - sink.isFallback = TRUE; - } - - // Now load Latin. - sink.isLatin = TRUE; - ures_getAllItemsWithFallback(resource.getAlias(), gLatnPath, sink, status); - if (U_FAILURE(status)) return; - - // If longData is empty, default it to be equal to shortData - if (result->longData.isEmpty()) { - result->longData.setToBogus(); - } - - // Check for "other" variants in each of the three data classes, and resolve missing elements. - - if (!result->longData.isBogus()) { - checkForOtherVariants(&result->longData, status); - if (U_FAILURE(status)) return; - fillInMissing(&result->longData); - } - - checkForOtherVariants(&result->shortData, status); - if (U_FAILURE(status)) return; - fillInMissing(&result->shortData); - - // TODO: Enable this statement when currency support is added - // checkForOtherVariants(&result->shortCurrencyData, status); - // if (U_FAILURE(status)) return; - // fillInMissing(&result->shortCurrencyData); -} - -// populatePrefixSuffix Adds a specific prefix-suffix pair to result for a -// given variant and log10 value. -// variant is 'zero', 'one', 'two', 'few', 'many', or 'other'. -// formatStr is the format string from which the prefix and suffix are -// extracted. It is usually of form 'Pefix 000 suffix'. -// populatePrefixSuffix returns the number of 0's found in formatStr -// before the decimal point. -// In the special case that formatStr contains only spaces for prefix -// and suffix, populatePrefixSuffix returns log10Value + 1. -static int32_t populatePrefixSuffix( - const char* variant, int32_t log10Value, const UnicodeString& formatStr, UHashtable* result, UBool overwrite, UErrorCode& status) { - if (U_FAILURE(status)) { - return 0; - } - - // ICU 59 HACK: Ignore negative part of format string, mimicking ICU 58 behavior. - // TODO(sffc): Make sure this is fixed during the overhaul port in ICU 60. - int32_t semiPos = formatStr.indexOf(';', 0); - if (semiPos == -1) { - semiPos = formatStr.length(); - } - UnicodeString positivePart = formatStr.tempSubString(0, semiPos); - - int32_t firstIdx = positivePart.indexOf(kZero, UPRV_LENGTHOF(kZero), 0); - // We must have 0's in format string. - if (firstIdx == -1) { - status = U_INTERNAL_PROGRAM_ERROR; - return 0; - } - int32_t lastIdx = positivePart.lastIndexOf(kZero, UPRV_LENGTHOF(kZero), firstIdx); - CDFUnit* unit = createCDFUnit(variant, log10Value, result, status); - if (U_FAILURE(status)) { - return 0; - } - - // Return -1 if we are not overwriting an existing value - if (unit->isSet() && !overwrite) { - return -1; - } - unit->markAsSet(); - - // Everything up to first 0 is the prefix - unit->prefix = positivePart.tempSubString(0, firstIdx); - fixQuotes(unit->prefix); - // Everything beyond the last 0 is the suffix - unit->suffix = positivePart.tempSubString(lastIdx + 1); - fixQuotes(unit->suffix); - - // If there is effectively no prefix or suffix, ignore the actual number of - // 0's and act as if the number of 0's matches the size of the number. - if (onlySpaces(unit->prefix) && onlySpaces(unit->suffix)) { - return log10Value + 1; - } - - // Calculate number of zeros before decimal point - int32_t idx = firstIdx + 1; - while (idx <= lastIdx && positivePart.charAt(idx) == u_0) { - ++idx; - } - return (idx - firstIdx); -} - -// Calculate a divisor based on the magnitude and number of zeros in the -// template string. -static double calculateDivisor(double power10, int32_t numZeros) { - double divisor = power10; - for (int32_t i = 1; i < numZeros; ++i) { - divisor /= 10.0; - } - return divisor; -} - -static UBool onlySpaces(UnicodeString u) { - return u.trim().length() == 0; + const UnicodeString& /* text */, + ParsePosition& /* pos */) const { + return nullptr; } -// fixQuotes unescapes single quotes. Don''t -> Don't. Letter 'j' -> Letter j. -// Modifies s in place. -static void fixQuotes(UnicodeString& s) { - QuoteState state = OUTSIDE; - int32_t len = s.length(); - int32_t dest = 0; - for (int32_t i = 0; i < len; ++i) { - UChar ch = s.charAt(i); - if (ch == u_apos) { - if (state == INSIDE_EMPTY) { - s.setCharAt(dest, ch); - ++dest; - } - } else { - s.setCharAt(dest, ch); - ++dest; - } - - // Update state - switch (state) { - case OUTSIDE: - state = ch == u_apos ? INSIDE_EMPTY : OUTSIDE; - break; - case INSIDE_EMPTY: - case INSIDE_FULL: - state = ch == u_apos ? OUTSIDE : INSIDE_FULL; - break; - default: - break; - } - } - s.truncate(dest); -} - -// Checks to make sure that an "other" variant is present in all -// powers of 10. -static void checkForOtherVariants(CDFLocaleStyleData* result, - UErrorCode& status) { - if (result == NULL || result->unitsByVariant == NULL) { - return; - } - - const CDFUnit* otherByBase = - (const CDFUnit*) uhash_get(result->unitsByVariant, gOther); - if (otherByBase == NULL) { - status = U_INTERNAL_PROGRAM_ERROR; - return; - } - - // Check all other plural variants, and make sure that if - // any of them are populated, then other is also populated - int32_t pos = UHASH_FIRST; - const UHashElement* element; - while ((element = uhash_nextElement(result->unitsByVariant, &pos)) != NULL) { - CDFUnit* variantsByBase = (CDFUnit*) element->value.pointer; - if (variantsByBase == otherByBase) continue; - for (int32_t log10Value = 0; log10Value < MAX_DIGITS; ++log10Value) { - if (variantsByBase[log10Value].isSet() - && !otherByBase[log10Value].isSet()) { - status = U_INTERNAL_PROGRAM_ERROR; - return; - } - } - } -} - -// fillInMissing ensures that the data in result is complete. -// result data is complete if for each variant in result, there exists -// a prefix-suffix pair for each log10 value and there also exists -// a divisor for each log10 value. -// -// First this function figures out for which log10 values, the other -// variant already had data. These are the same log10 values defined -// in CLDR. -// -// For each log10 value not defined in CLDR, it uses the divisor for -// the last defined log10 value or 1. -// -// Then for each variant, it does the following. For each log10 -// value not defined in CLDR, copy the prefix-suffix pair from the -// previous log10 value. If log10 value is defined in CLDR but is -// missing from given variant, copy the prefix-suffix pair for that -// log10 value from the 'other' variant. -static void fillInMissing(CDFLocaleStyleData* result) { - const CDFUnit* otherUnits = - (const CDFUnit*) uhash_get(result->unitsByVariant, gOther); - UBool definedInCLDR[MAX_DIGITS]; - double lastDivisor = 1.0; - for (int32_t i = 0; i < MAX_DIGITS; ++i) { - if (!otherUnits[i].isSet()) { - result->divisors[i] = lastDivisor; - definedInCLDR[i] = FALSE; - } else { - lastDivisor = result->divisors[i]; - definedInCLDR[i] = TRUE; - } - } - // Iterate over each variant. - int32_t pos = UHASH_FIRST; - const UHashElement* element = uhash_nextElement(result->unitsByVariant, &pos); - for (;element != NULL; element = uhash_nextElement(result->unitsByVariant, &pos)) { - CDFUnit* units = (CDFUnit*) element->value.pointer; - for (int32_t i = 0; i < MAX_DIGITS; ++i) { - if (definedInCLDR[i]) { - if (!units[i].isSet()) { - units[i] = otherUnits[i]; - } - } else { - if (i == 0) { - units[0].markAsSet(); - } else { - units[i] = units[i - 1]; - } - } - } - } -} - -// computeLog10 computes floor(log10(x)). If inRange is TRUE, the biggest -// value computeLog10 will return MAX_DIGITS -1 even for -// numbers > 10^MAX_DIGITS. If inRange is FALSE, computeLog10 will return -// up to MAX_DIGITS. -static int32_t computeLog10(double x, UBool inRange) { - int32_t result = 0; - int32_t max = inRange ? MAX_DIGITS - 1 : MAX_DIGITS; - while (x >= 10.0) { - x /= 10.0; - ++result; - if (result == max) { - break; - } - } - return result; -} - -// createCDFUnit returns a pointer to the prefix-suffix pair for a given -// variant and log10 value within table. If no such prefix-suffix pair is -// stored in table, one is created within table before returning pointer. -static CDFUnit* createCDFUnit(const char* variant, int32_t log10Value, UHashtable* table, UErrorCode& status) { - if (U_FAILURE(status)) { - return NULL; - } - CDFUnit *cdfUnit = (CDFUnit*) uhash_get(table, variant); - if (cdfUnit == NULL) { - cdfUnit = new CDFUnit[MAX_DIGITS]; - if (cdfUnit == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - uhash_put(table, uprv_strdup(variant), cdfUnit, &status); - if (U_FAILURE(status)) { - return NULL; - } - } - CDFUnit* result = &cdfUnit[log10Value]; - return result; -} - -// getCDFUnitFallback returns a pointer to the prefix-suffix pair for a given -// variant and log10 value within table. If the given variant doesn't exist, it -// falls back to the OTHER variant. Therefore, this method will always return -// some non-NULL value. -static const CDFUnit* getCDFUnitFallback(const UHashtable* table, const UnicodeString& variant, int32_t log10Value) { - CharString cvariant; - UErrorCode status = U_ZERO_ERROR; - const CDFUnit *cdfUnit = NULL; - cvariant.appendInvariantChars(variant, status); - if (!U_FAILURE(status)) { - cdfUnit = (const CDFUnit*) uhash_get(table, cvariant.data()); - } - if (cdfUnit == NULL) { - cdfUnit = (const CDFUnit*) uhash_get(table, gOther); - } - return &cdfUnit[log10Value]; -} -U_NAMESPACE_END -#endif +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/currpinf.cpp b/deps/icu-small/source/i18n/currpinf.cpp index 5d3ca620891500..6b1efd5f4da721 100644 --- a/deps/icu-small/source/i18n/currpinf.cpp +++ b/deps/icu-small/source/i18n/currpinf.cpp @@ -203,6 +203,9 @@ CurrencyPluralInfo::setCurrencyPluralPattern(const UnicodeString& pluralCount, const UnicodeString& pattern, UErrorCode& status) { if (U_SUCCESS(status)) { + UnicodeString* oldValue = static_cast( + fPluralCountToCurrencyUnitPattern->get(pluralCount)); + delete oldValue; fPluralCountToCurrencyUnitPattern->put(pluralCount, new UnicodeString(pattern), status); } } diff --git a/deps/icu-small/source/i18n/currunit.cpp b/deps/icu-small/source/i18n/currunit.cpp index 83429c01694acc..6d8d1cd6c6f97f 100644 --- a/deps/icu-small/source/i18n/currunit.cpp +++ b/deps/icu-small/source/i18n/currunit.cpp @@ -17,21 +17,32 @@ #include "unicode/currunit.h" #include "unicode/ustring.h" #include "cstring.h" +#include "uinvchar.h" + +static constexpr char16_t kDefaultCurrency[] = u"XXX"; U_NAMESPACE_BEGIN CurrencyUnit::CurrencyUnit(ConstChar16Ptr _isoCode, UErrorCode& ec) { - *isoCode = 0; - if (U_SUCCESS(ec)) { - if (_isoCode != nullptr && u_strlen(_isoCode)==3) { - u_strcpy(isoCode, _isoCode); - char simpleIsoCode[4]; - u_UCharsToChars(isoCode, simpleIsoCode, 4); - initCurrency(simpleIsoCode); - } else { - ec = U_ILLEGAL_ARGUMENT_ERROR; - } + // The constructor always leaves the CurrencyUnit in a valid state (with a 3-character currency code). + // Note: in ICU4J Currency.getInstance(), we check string length for 3, but in ICU4C we allow a + // non-NUL-terminated string to be passed as an argument, so it is not possible to check length. + const char16_t* isoCodeToUse; + if (U_FAILURE(ec) || _isoCode == nullptr) { + isoCodeToUse = kDefaultCurrency; + } else if (!uprv_isInvariantUString(_isoCode, 3)) { + // TODO: Perform a more strict ASCII check like in ICU4J isAlpha3Code? + isoCodeToUse = kDefaultCurrency; + ec = U_INVARIANT_CONVERSION_ERROR; + } else { + isoCodeToUse = _isoCode; } + // TODO: Perform uppercasing here like in ICU4J Currency.getInstance()? + uprv_memcpy(isoCode, isoCodeToUse, sizeof(UChar) * 3); + isoCode[3] = 0; + char simpleIsoCode[4]; + u_UCharsToChars(isoCode, simpleIsoCode, 4); + initCurrency(simpleIsoCode); } CurrencyUnit::CurrencyUnit(const CurrencyUnit& other) : MeasureUnit(other) { @@ -52,7 +63,7 @@ CurrencyUnit::CurrencyUnit(const MeasureUnit& other, UErrorCode& ec) : MeasureUn } CurrencyUnit::CurrencyUnit() : MeasureUnit() { - u_strcpy(isoCode, u"XXX"); + u_strcpy(isoCode, kDefaultCurrency); char simpleIsoCode[4]; u_UCharsToChars(isoCode, simpleIsoCode, 4); initCurrency(simpleIsoCode); diff --git a/deps/icu-small/source/i18n/dcfmtimp.h b/deps/icu-small/source/i18n/dcfmtimp.h deleted file mode 100644 index e582efb344b3b8..00000000000000 --- a/deps/icu-small/source/i18n/dcfmtimp.h +++ /dev/null @@ -1,54 +0,0 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************** -* Copyright (C) 2012-2014, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************************/ - -#ifndef DCFMTIMP_H -#define DCFMTIMP_H - -#include "unicode/utypes.h" - - -#if UCONFIG_FORMAT_FASTPATHS_49 - -U_NAMESPACE_BEGIN - -enum EDecimalFormatFastpathStatus { - kFastpathNO = 0, - kFastpathYES = 1, - kFastpathUNKNOWN = 2, /* not yet set */ - kFastpathMAYBE = 3 /* depends on value being formatted. */ -}; - -/** - * Must be smaller than DecimalFormat::fReserved - */ -struct DecimalFormatInternal { - uint8_t fFastFormatStatus; - uint8_t fFastParseStatus; - - DecimalFormatInternal &operator=(const DecimalFormatInternal& rhs) { - fFastParseStatus = rhs.fFastParseStatus; - fFastFormatStatus = rhs.fFastFormatStatus; - return *this; - } -#ifdef FMT_DEBUG - void dump() const { - printf("DecimalFormatInternal: fFastFormatStatus=%c, fFastParseStatus=%c\n", - "NY?"[(int)fFastFormatStatus&3], - "NY?"[(int)fFastParseStatus&3] - ); - } -#endif -}; - - - -U_NAMESPACE_END - -#endif - -#endif diff --git a/deps/icu-small/source/i18n/dcfmtsym.cpp b/deps/icu-small/source/i18n/dcfmtsym.cpp index 680c3120a1e0f9..5a432aec8e4df0 100644 --- a/deps/icu-small/source/i18n/dcfmtsym.cpp +++ b/deps/icu-small/source/i18n/dcfmtsym.cpp @@ -66,7 +66,7 @@ static const UChar INTL_CURRENCY_SYMBOL_STR[] = {0xa4, 0xa4, 0}; static const char *gNumberElementKeys[DecimalFormatSymbols::kFormatSymbolCount] = { "decimal", "group", - "list", + NULL, /* #11897: the symbol is NOT the pattern separator symbol */ "percentSign", NULL, /* Native zero digit is deprecated from CLDR - get it from the numbering system */ NULL, /* Pattern digit character is deprecated from CLDR - use # by default always */ @@ -98,7 +98,7 @@ static const char *gNumberElementKeys[DecimalFormatSymbols::kFormatSymbolCount] // Initializes this with the decimal format symbols in the default locale. DecimalFormatSymbols::DecimalFormatSymbols(UErrorCode& status) - : UObject(), locale() { + : UObject(), locale(), currPattern(NULL) { initialize(locale, status, TRUE); } @@ -106,12 +106,12 @@ DecimalFormatSymbols::DecimalFormatSymbols(UErrorCode& status) // Initializes this with the decimal format symbols in the desired locale. DecimalFormatSymbols::DecimalFormatSymbols(const Locale& loc, UErrorCode& status) - : UObject(), locale(loc) { + : UObject(), locale(loc), currPattern(NULL) { initialize(locale, status); } DecimalFormatSymbols::DecimalFormatSymbols(const Locale& loc, const NumberingSystem& ns, UErrorCode& status) - : UObject(), locale(loc) { + : UObject(), locale(loc), currPattern(NULL) { initialize(locale, status, FALSE, &ns); } @@ -349,7 +349,6 @@ DecimalFormatSymbols::initialize(const Locale& loc, UErrorCode& status, { if (U_FAILURE(status)) { return; } *validLocale = *actualLocale = 0; - currPattern = NULL; // First initialize all the symbols to the fallbacks for anything we can't find initialize(); @@ -477,6 +476,7 @@ DecimalFormatSymbols::initialize(const Locale& loc, UErrorCode& status, UErrorCode localStatus = U_ZERO_ERROR; uccLen = ucurr_forLocale(locName, ucc, uccLen, &localStatus); + // TODO: Currency pattern data loading is duplicated in number_formatimpl.cpp if(U_SUCCESS(localStatus) && uccLen > 0) { char cc[4]={0}; u_UCharsToChars(ucc, cc, uccLen); diff --git a/deps/icu-small/source/i18n/decfmtst.cpp b/deps/icu-small/source/i18n/decfmtst.cpp deleted file mode 100644 index 5943affad4eb06..00000000000000 --- a/deps/icu-small/source/i18n/decfmtst.cpp +++ /dev/null @@ -1,251 +0,0 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2009-2016, International Business Machines Corporation and -* others. All Rights Reserved. -******************************************************************************* -* -* This file contains the class DecimalFormatStaticSets -* -* DecimalFormatStaticSets holds the UnicodeSets that are needed for lenient -* parsing of decimal and group separators. -******************************************************************************** -*/ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/unistr.h" -#include "unicode/uniset.h" -#include "unicode/uchar.h" -#include "cmemory.h" -#include "cstring.h" -#include "uassert.h" -#include "ucln_in.h" -#include "umutex.h" - -#include "decfmtst.h" - -U_NAMESPACE_BEGIN - - -//------------------------------------------------------------------------------ -// -// Unicode Set pattern strings for all of the required constant sets. -// Initialized with hex values for portability to EBCDIC based machines. -// Really ugly, but there's no good way to avoid it. -// -//------------------------------------------------------------------------------ - -static const UChar gDotEquivalentsPattern[] = { - // [ . \u2024 \u3002 \uFE12 \uFE52 \uFF0E \uFF61 ] - 0x005B, 0x002E, 0x2024, 0x3002, 0xFE12, 0xFE52, 0xFF0E, 0xFF61, 0x005D, 0x0000}; - -static const UChar gCommaEquivalentsPattern[] = { - // [ , \u060C \u066B \u3001 \uFE10 \uFE11 \uFE50 \uFE51 \uFF0C \uFF64 ] - 0x005B, 0x002C, 0x060C, 0x066B, 0x3001, 0xFE10, 0xFE11, 0xFE50, 0xFE51, 0xFF0C, 0xFF64, 0x005D, 0x0000}; - -static const UChar gOtherGroupingSeparatorsPattern[] = { - // [ \ SPACE ' NBSP \u066C \u2000 - \u200A \u2018 \u2019 \u202F \u205F \u3000 \uFF07 ] - 0x005B, 0x005C, 0x0020, 0x0027, 0x00A0, 0x066C, 0x2000, 0x002D, 0x200A, 0x2018, 0x2019, 0x202F, 0x205F, 0x3000, 0xFF07, 0x005D, 0x0000}; - -static const UChar gDashEquivalentsPattern[] = { - // [ \ - HYPHEN F_DASH N_DASH MINUS ] - 0x005B, 0x005C, 0x002D, 0x2010, 0x2012, 0x2013, 0x2212, 0x005D, 0x0000}; - -static const UChar gStrictDotEquivalentsPattern[] = { - // [ . \u2024 \uFE52 \uFF0E \uFF61 ] - 0x005B, 0x002E, 0x2024, 0xFE52, 0xFF0E, 0xFF61, 0x005D, 0x0000}; - -static const UChar gStrictCommaEquivalentsPattern[] = { - // [ , \u066B \uFE10 \uFE50 \uFF0C ] - 0x005B, 0x002C, 0x066B, 0xFE10, 0xFE50, 0xFF0C, 0x005D, 0x0000}; - -static const UChar gStrictOtherGroupingSeparatorsPattern[] = { - // [ \ SPACE ' NBSP \u066C \u2000 - \u200A \u2018 \u2019 \u202F \u205F \u3000 \uFF07 ] - 0x005B, 0x005C, 0x0020, 0x0027, 0x00A0, 0x066C, 0x2000, 0x002D, 0x200A, 0x2018, 0x2019, 0x202F, 0x205F, 0x3000, 0xFF07, 0x005D, 0x0000}; - -static const UChar gStrictDashEquivalentsPattern[] = { - // [ \ - MINUS ] - 0x005B, 0x005C, 0x002D, 0x2212, 0x005D, 0x0000}; - -static const UChar32 gMinusSigns[] = { - 0x002D, - 0x207B, - 0x208B, - 0x2212, - 0x2796, - 0xFE63, - 0xFF0D}; - -static const UChar32 gPlusSigns[] = { - 0x002B, - 0x207A, - 0x208A, - 0x2795, - 0xfB29, - 0xFE62, - 0xFF0B}; - -static void initUnicodeSet(const UChar32 *raw, int32_t len, UnicodeSet *s) { - for (int32_t i = 0; i < len; ++i) { - s->add(raw[i]); - } -} - -DecimalFormatStaticSets::DecimalFormatStaticSets(UErrorCode &status) -: fDotEquivalents(NULL), - fCommaEquivalents(NULL), - fOtherGroupingSeparators(NULL), - fDashEquivalents(NULL), - fStrictDotEquivalents(NULL), - fStrictCommaEquivalents(NULL), - fStrictOtherGroupingSeparators(NULL), - fStrictDashEquivalents(NULL), - fDefaultGroupingSeparators(NULL), - fStrictDefaultGroupingSeparators(NULL), - fMinusSigns(NULL), - fPlusSigns(NULL) -{ - fDotEquivalents = new UnicodeSet(UnicodeString(TRUE, gDotEquivalentsPattern, -1), status); - fCommaEquivalents = new UnicodeSet(UnicodeString(TRUE, gCommaEquivalentsPattern, -1), status); - fOtherGroupingSeparators = new UnicodeSet(UnicodeString(TRUE, gOtherGroupingSeparatorsPattern, -1), status); - fDashEquivalents = new UnicodeSet(UnicodeString(TRUE, gDashEquivalentsPattern, -1), status); - - fStrictDotEquivalents = new UnicodeSet(UnicodeString(TRUE, gStrictDotEquivalentsPattern, -1), status); - fStrictCommaEquivalents = new UnicodeSet(UnicodeString(TRUE, gStrictCommaEquivalentsPattern, -1), status); - fStrictOtherGroupingSeparators = new UnicodeSet(UnicodeString(TRUE, gStrictOtherGroupingSeparatorsPattern, -1), status); - fStrictDashEquivalents = new UnicodeSet(UnicodeString(TRUE, gStrictDashEquivalentsPattern, -1), status); - - - fDefaultGroupingSeparators = new UnicodeSet(*fDotEquivalents); - fDefaultGroupingSeparators->addAll(*fCommaEquivalents); - fDefaultGroupingSeparators->addAll(*fOtherGroupingSeparators); - - fStrictDefaultGroupingSeparators = new UnicodeSet(*fStrictDotEquivalents); - fStrictDefaultGroupingSeparators->addAll(*fStrictCommaEquivalents); - fStrictDefaultGroupingSeparators->addAll(*fStrictOtherGroupingSeparators); - - fMinusSigns = new UnicodeSet(); - fPlusSigns = new UnicodeSet(); - - // Check for null pointers - if (fDotEquivalents == NULL || fCommaEquivalents == NULL || fOtherGroupingSeparators == NULL || fDashEquivalents == NULL || - fStrictDotEquivalents == NULL || fStrictCommaEquivalents == NULL || fStrictOtherGroupingSeparators == NULL || fStrictDashEquivalents == NULL || - fDefaultGroupingSeparators == NULL || fStrictOtherGroupingSeparators == NULL || - fMinusSigns == NULL || fPlusSigns == NULL) { - cleanup(); - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - - initUnicodeSet( - gMinusSigns, - UPRV_LENGTHOF(gMinusSigns), - fMinusSigns); - initUnicodeSet( - gPlusSigns, - UPRV_LENGTHOF(gPlusSigns), - fPlusSigns); - - // Freeze all the sets - fDotEquivalents->freeze(); - fCommaEquivalents->freeze(); - fOtherGroupingSeparators->freeze(); - fDashEquivalents->freeze(); - fStrictDotEquivalents->freeze(); - fStrictCommaEquivalents->freeze(); - fStrictOtherGroupingSeparators->freeze(); - fStrictDashEquivalents->freeze(); - fDefaultGroupingSeparators->freeze(); - fStrictDefaultGroupingSeparators->freeze(); - fMinusSigns->freeze(); - fPlusSigns->freeze(); -} - -DecimalFormatStaticSets::~DecimalFormatStaticSets() { - cleanup(); -} - -void DecimalFormatStaticSets::cleanup() { // Be sure to clean up newly added fields! - delete fDotEquivalents; fDotEquivalents = NULL; - delete fCommaEquivalents; fCommaEquivalents = NULL; - delete fOtherGroupingSeparators; fOtherGroupingSeparators = NULL; - delete fDashEquivalents; fDashEquivalents = NULL; - delete fStrictDotEquivalents; fStrictDotEquivalents = NULL; - delete fStrictCommaEquivalents; fStrictCommaEquivalents = NULL; - delete fStrictOtherGroupingSeparators; fStrictOtherGroupingSeparators = NULL; - delete fStrictDashEquivalents; fStrictDashEquivalents = NULL; - delete fDefaultGroupingSeparators; fDefaultGroupingSeparators = NULL; - delete fStrictDefaultGroupingSeparators; fStrictDefaultGroupingSeparators = NULL; - delete fStrictOtherGroupingSeparators; fStrictOtherGroupingSeparators = NULL; - delete fMinusSigns; fMinusSigns = NULL; - delete fPlusSigns; fPlusSigns = NULL; -} - -static DecimalFormatStaticSets *gStaticSets; -static icu::UInitOnce gStaticSetsInitOnce = U_INITONCE_INITIALIZER; - - -//------------------------------------------------------------------------------ -// -// decfmt_cleanup Memory cleanup function, free/delete all -// cached memory. Called by ICU's u_cleanup() function. -// -//------------------------------------------------------------------------------ -U_CDECL_BEGIN -static UBool U_CALLCONV -decimfmt_cleanup(void) -{ - delete gStaticSets; - gStaticSets = NULL; - gStaticSetsInitOnce.reset(); - return TRUE; -} - -static void U_CALLCONV initSets(UErrorCode &status) { - U_ASSERT(gStaticSets == NULL); - ucln_i18n_registerCleanup(UCLN_I18N_DECFMT, decimfmt_cleanup); - gStaticSets = new DecimalFormatStaticSets(status); - if (U_FAILURE(status)) { - delete gStaticSets; - gStaticSets = NULL; - return; - } - if (gStaticSets == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - } -} -U_CDECL_END - -const DecimalFormatStaticSets *DecimalFormatStaticSets::getStaticSets(UErrorCode &status) { - umtx_initOnce(gStaticSetsInitOnce, initSets, status); - return gStaticSets; -} - - -const UnicodeSet *DecimalFormatStaticSets::getSimilarDecimals(UChar32 decimal, UBool strictParse) -{ - UErrorCode status = U_ZERO_ERROR; - umtx_initOnce(gStaticSetsInitOnce, initSets, status); - if (U_FAILURE(status)) { - return NULL; - } - - if (gStaticSets->fDotEquivalents->contains(decimal)) { - return strictParse ? gStaticSets->fStrictDotEquivalents : gStaticSets->fDotEquivalents; - } - - if (gStaticSets->fCommaEquivalents->contains(decimal)) { - return strictParse ? gStaticSets->fStrictCommaEquivalents : gStaticSets->fCommaEquivalents; - } - - // if there is no match, return NULL - return NULL; -} - - -U_NAMESPACE_END -#endif // !UCONFIG_NO_FORMATTING diff --git a/deps/icu-small/source/i18n/decfmtst.h b/deps/icu-small/source/i18n/decfmtst.h deleted file mode 100644 index 63ae50c6df904a..00000000000000 --- a/deps/icu-small/source/i18n/decfmtst.h +++ /dev/null @@ -1,69 +0,0 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2009-2016, International Business Machines Corporation and -* others. All Rights Reserved. -******************************************************************************* -* -* This file contains declarations for the class DecimalFormatStaticSets -* -* DecimalFormatStaticSets holds the UnicodeSets that are needed for lenient -* parsing of decimal and group separators. -******************************************************************************** -*/ - -#ifndef DECFMTST_H -#define DECFMTST_H - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/uobject.h" - -U_NAMESPACE_BEGIN - -class UnicodeSet; - - -class DecimalFormatStaticSets : public UMemory -{ -public: - // Constructor and Destructor not for general use. - // Public to permit access from plain C implementation functions. - DecimalFormatStaticSets(UErrorCode &status); - ~DecimalFormatStaticSets(); - - /** - * Return a pointer to a lazy-initialized singleton instance of this class. - */ - static const DecimalFormatStaticSets *getStaticSets(UErrorCode &status); - - static const UnicodeSet *getSimilarDecimals(UChar32 decimal, UBool strictParse); - - UnicodeSet *fDotEquivalents; - UnicodeSet *fCommaEquivalents; - UnicodeSet *fOtherGroupingSeparators; - UnicodeSet *fDashEquivalents; - - UnicodeSet *fStrictDotEquivalents; - UnicodeSet *fStrictCommaEquivalents; - UnicodeSet *fStrictOtherGroupingSeparators; - UnicodeSet *fStrictDashEquivalents; - - UnicodeSet *fDefaultGroupingSeparators; - UnicodeSet *fStrictDefaultGroupingSeparators; - - UnicodeSet *fMinusSigns; - UnicodeSet *fPlusSigns; -private: - void cleanup(); - -}; - - -U_NAMESPACE_END - -#endif // !UCONFIG_NO_FORMATTING -#endif // DECFMTST_H diff --git a/deps/icu-small/source/i18n/decimalformatpattern.cpp b/deps/icu-small/source/i18n/decimalformatpattern.cpp deleted file mode 100644 index 80a1870f33ef8d..00000000000000 --- a/deps/icu-small/source/i18n/decimalformatpattern.cpp +++ /dev/null @@ -1,656 +0,0 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 1997-2015, International Business Machines Corporation and * -* others. All Rights Reserved. * -******************************************************************************* -*/ - -#include "uassert.h" -#include "decimalformatpattern.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/dcfmtsym.h" -#include "unicode/format.h" -#include "unicode/utf16.h" -#include "decimalformatpatternimpl.h" - - -#ifdef FMT_DEBUG -#define debug(x) printf("%s:%d: %s\n", __FILE__,__LINE__, x); -#else -#define debug(x) -#endif - -U_NAMESPACE_BEGIN - -// TODO: Travis Keep: Copied from numfmt.cpp -static int32_t kDoubleIntegerDigits = 309; -static int32_t kDoubleFractionDigits = 340; - - -// TODO: Travis Keep: Copied from numfmt.cpp -static int32_t gDefaultMaxIntegerDigits = 2000000000; - -// TODO: Travis Keep: This function was copied from format.cpp -static void syntaxError(const UnicodeString& pattern, - int32_t pos, - UParseError& parseError) { - parseError.offset = pos; - parseError.line=0; // we are not using line number - - // for pre-context - int32_t start = (pos < U_PARSE_CONTEXT_LEN)? 0 : (pos - (U_PARSE_CONTEXT_LEN-1 - /* subtract 1 so that we have room for null*/)); - int32_t stop = pos; - pattern.extract(start,stop-start,parseError.preContext,0); - //null terminate the buffer - parseError.preContext[stop-start] = 0; - - //for post-context - start = pattern.moveIndex32(pos, 1); - stop = pos + U_PARSE_CONTEXT_LEN - 1; - if (stop > pattern.length()) { - stop = pattern.length(); - } - pattern.extract(start, stop - start, parseError.postContext, 0); - //null terminate the buffer - parseError.postContext[stop-start]= 0; -} - -DecimalFormatPattern::DecimalFormatPattern() - : fMinimumIntegerDigits(1), - fMaximumIntegerDigits(gDefaultMaxIntegerDigits), - fMinimumFractionDigits(0), - fMaximumFractionDigits(3), - fUseSignificantDigits(FALSE), - fMinimumSignificantDigits(1), - fMaximumSignificantDigits(6), - fUseExponentialNotation(FALSE), - fMinExponentDigits(0), - fExponentSignAlwaysShown(FALSE), - fCurrencySignCount(fgCurrencySignCountZero), - fGroupingUsed(TRUE), - fGroupingSize(0), - fGroupingSize2(0), - fMultiplier(1), - fDecimalSeparatorAlwaysShown(FALSE), - fFormatWidth(0), - fRoundingIncrementUsed(FALSE), - fRoundingIncrement(), - fPad(kDefaultPad), - fNegPatternsBogus(TRUE), - fPosPatternsBogus(TRUE), - fNegPrefixPattern(), - fNegSuffixPattern(), - fPosPrefixPattern(), - fPosSuffixPattern(), - fPadPosition(DecimalFormatPattern::kPadBeforePrefix) { -} - - -DecimalFormatPatternParser::DecimalFormatPatternParser() : - fZeroDigit(kPatternZeroDigit), - fSigDigit(kPatternSignificantDigit), - fGroupingSeparator((UChar)kPatternGroupingSeparator), - fDecimalSeparator((UChar)kPatternDecimalSeparator), - fPercent((UChar)kPatternPercent), - fPerMill((UChar)kPatternPerMill), - fDigit((UChar)kPatternDigit), - fSeparator((UChar)kPatternSeparator), - fExponent((UChar)kPatternExponent), - fPlus((UChar)kPatternPlus), - fMinus((UChar)kPatternMinus), - fPadEscape((UChar)kPatternPadEscape) { -} - -void DecimalFormatPatternParser::useSymbols( - const DecimalFormatSymbols& symbols) { - fZeroDigit = symbols.getConstSymbol( - DecimalFormatSymbols::kZeroDigitSymbol).char32At(0); - fSigDigit = symbols.getConstSymbol( - DecimalFormatSymbols::kSignificantDigitSymbol).char32At(0); - fGroupingSeparator = symbols.getConstSymbol( - DecimalFormatSymbols::kGroupingSeparatorSymbol); - fDecimalSeparator = symbols.getConstSymbol( - DecimalFormatSymbols::kDecimalSeparatorSymbol); - fPercent = symbols.getConstSymbol( - DecimalFormatSymbols::kPercentSymbol); - fPerMill = symbols.getConstSymbol( - DecimalFormatSymbols::kPerMillSymbol); - fDigit = symbols.getConstSymbol( - DecimalFormatSymbols::kDigitSymbol); - fSeparator = symbols.getConstSymbol( - DecimalFormatSymbols::kPatternSeparatorSymbol); - fExponent = symbols.getConstSymbol( - DecimalFormatSymbols::kExponentialSymbol); - fPlus = symbols.getConstSymbol( - DecimalFormatSymbols::kPlusSignSymbol); - fMinus = symbols.getConstSymbol( - DecimalFormatSymbols::kMinusSignSymbol); - fPadEscape = symbols.getConstSymbol( - DecimalFormatSymbols::kPadEscapeSymbol); -} - -void -DecimalFormatPatternParser::applyPatternWithoutExpandAffix( - const UnicodeString& pattern, - DecimalFormatPattern& out, - UParseError& parseError, - UErrorCode& status) { - if (U_FAILURE(status)) - { - return; - } - out = DecimalFormatPattern(); - - // Clear error struct - parseError.offset = -1; - parseError.preContext[0] = parseError.postContext[0] = (UChar)0; - - // TODO: Travis Keep: This won't always work. - UChar nineDigit = (UChar)(fZeroDigit + 9); - int32_t digitLen = fDigit.length(); - int32_t groupSepLen = fGroupingSeparator.length(); - int32_t decimalSepLen = fDecimalSeparator.length(); - - int32_t pos = 0; - int32_t patLen = pattern.length(); - // Part 0 is the positive pattern. Part 1, if present, is the negative - // pattern. - for (int32_t part=0; part<2 && pos 0 || sigDigitCount > 0) { - ++digitRightCount; - } else { - ++digitLeftCount; - } - if (groupingCount >= 0 && decimalPos < 0) { - ++groupingCount; - } - pos += digitLen; - } else if ((ch >= fZeroDigit && ch <= nineDigit) || - ch == fSigDigit) { - if (digitRightCount > 0) { - // Unexpected '0' - debug("Unexpected '0'") - status = U_UNEXPECTED_TOKEN; - syntaxError(pattern,pos,parseError); - return; - } - if (ch == fSigDigit) { - ++sigDigitCount; - } else { - if (ch != fZeroDigit && roundingPos < 0) { - roundingPos = digitLeftCount + zeroDigitCount; - } - if (roundingPos >= 0) { - roundingInc.append((char)(ch - fZeroDigit + '0')); - } - ++zeroDigitCount; - } - if (groupingCount >= 0 && decimalPos < 0) { - ++groupingCount; - } - pos += U16_LENGTH(ch); - } else if (pattern.compare(pos, groupSepLen, fGroupingSeparator) == 0) { - if (decimalPos >= 0) { - // Grouping separator after decimal - debug("Grouping separator after decimal") - status = U_UNEXPECTED_TOKEN; - syntaxError(pattern,pos,parseError); - return; - } - groupingCount2 = groupingCount; - groupingCount = 0; - pos += groupSepLen; - } else if (pattern.compare(pos, decimalSepLen, fDecimalSeparator) == 0) { - if (decimalPos >= 0) { - // Multiple decimal separators - debug("Multiple decimal separators") - status = U_MULTIPLE_DECIMAL_SEPARATORS; - syntaxError(pattern,pos,parseError); - return; - } - // Intentionally incorporate the digitRightCount, - // even though it is illegal for this to be > 0 - // at this point. We check pattern syntax below. - decimalPos = digitLeftCount + zeroDigitCount + digitRightCount; - pos += decimalSepLen; - } else { - if (pattern.compare(pos, fExponent.length(), fExponent) == 0) { - if (expDigits >= 0) { - // Multiple exponential symbols - debug("Multiple exponential symbols") - status = U_MULTIPLE_EXPONENTIAL_SYMBOLS; - syntaxError(pattern,pos,parseError); - return; - } - if (groupingCount >= 0) { - // Grouping separator in exponential pattern - debug("Grouping separator in exponential pattern") - status = U_MALFORMED_EXPONENTIAL_PATTERN; - syntaxError(pattern,pos,parseError); - return; - } - pos += fExponent.length(); - // Check for positive prefix - if (pos < patLen - && pattern.compare(pos, fPlus.length(), fPlus) == 0) { - expSignAlways = TRUE; - pos += fPlus.length(); - } - // Use lookahead to parse out the exponential part of the - // pattern, then jump into suffix subpart. - expDigits = 0; - while (pos < patLen && - pattern.char32At(pos) == fZeroDigit) { - ++expDigits; - pos += U16_LENGTH(fZeroDigit); - } - - // 1. Require at least one mantissa pattern digit - // 2. Disallow "#+ @" in mantissa - // 3. Require at least one exponent pattern digit - if (((digitLeftCount + zeroDigitCount) < 1 && - (sigDigitCount + digitRightCount) < 1) || - (sigDigitCount > 0 && digitLeftCount > 0) || - expDigits < 1) { - // Malformed exponential pattern - debug("Malformed exponential pattern") - status = U_MALFORMED_EXPONENTIAL_PATTERN; - syntaxError(pattern,pos,parseError); - return; - } - } - // Transition to suffix subpart - subpart = 2; // suffix subpart - affix = &suffix; - sub0Limit = pos; - continue; - } - break; - case 1: // Prefix subpart - case 2: // Suffix subpart - // Process the prefix / suffix characters - // Process unquoted characters seen in prefix or suffix - // subpart. - - // Several syntax characters implicitly begins the - // next subpart if we are in the prefix; otherwise - // they are illegal if unquoted. - if (!pattern.compare(pos, digitLen, fDigit) || - !pattern.compare(pos, groupSepLen, fGroupingSeparator) || - !pattern.compare(pos, decimalSepLen, fDecimalSeparator) || - (ch >= fZeroDigit && ch <= nineDigit) || - ch == fSigDigit) { - if (subpart == 1) { // prefix subpart - subpart = 0; // pattern proper subpart - sub0Start = pos; // Reprocess this character - continue; - } else { - status = U_UNQUOTED_SPECIAL; - syntaxError(pattern,pos,parseError); - return; - } - } else if (ch == kCurrencySign) { - affix->append(kQuote); // Encode currency - // Use lookahead to determine if the currency sign is - // doubled or not. - U_ASSERT(U16_LENGTH(kCurrencySign) == 1); - if ((pos+1) < pattern.length() && pattern[pos+1] == kCurrencySign) { - affix->append(kCurrencySign); - ++pos; // Skip over the doubled character - if ((pos+1) < pattern.length() && - pattern[pos+1] == kCurrencySign) { - affix->append(kCurrencySign); - ++pos; // Skip over the doubled character - out.fCurrencySignCount = fgCurrencySignCountInPluralFormat; - } else { - out.fCurrencySignCount = fgCurrencySignCountInISOFormat; - } - } else { - out.fCurrencySignCount = fgCurrencySignCountInSymbolFormat; - } - // Fall through to append(ch) - } else if (ch == kQuote) { - // A quote outside quotes indicates either the opening - // quote or two quotes, which is a quote literal. That is, - // we have the first quote in 'do' or o''clock. - U_ASSERT(U16_LENGTH(kQuote) == 1); - ++pos; - if (pos < pattern.length() && pattern[pos] == kQuote) { - affix->append(kQuote); // Encode quote - // Fall through to append(ch) - } else { - subpart += 2; // open quote - continue; - } - } else if (pattern.compare(pos, fSeparator.length(), fSeparator) == 0) { - // Don't allow separators in the prefix, and don't allow - // separators in the second pattern (part == 1). - if (subpart == 1 || part == 1) { - // Unexpected separator - debug("Unexpected separator") - status = U_UNEXPECTED_TOKEN; - syntaxError(pattern,pos,parseError); - return; - } - sub2Limit = pos; - isPartDone = TRUE; // Go to next part - pos += fSeparator.length(); - break; - } else if (pattern.compare(pos, fPercent.length(), fPercent) == 0) { - // Next handle characters which are appended directly. - if (multiplier != 1) { - // Too many percent/perMill characters - debug("Too many percent characters") - status = U_MULTIPLE_PERCENT_SYMBOLS; - syntaxError(pattern,pos,parseError); - return; - } - affix->append(kQuote); // Encode percent/perMill - affix->append(kPatternPercent); // Use unlocalized pattern char - multiplier = 100; - pos += fPercent.length(); - break; - } else if (pattern.compare(pos, fPerMill.length(), fPerMill) == 0) { - // Next handle characters which are appended directly. - if (multiplier != 1) { - // Too many percent/perMill characters - debug("Too many perMill characters") - status = U_MULTIPLE_PERMILL_SYMBOLS; - syntaxError(pattern,pos,parseError); - return; - } - affix->append(kQuote); // Encode percent/perMill - affix->append(kPatternPerMill); // Use unlocalized pattern char - multiplier = 1000; - pos += fPerMill.length(); - break; - } else if (pattern.compare(pos, fPadEscape.length(), fPadEscape) == 0) { - if (padPos >= 0 || // Multiple pad specifiers - (pos+1) == pattern.length()) { // Nothing after padEscape - debug("Multiple pad specifiers") - status = U_MULTIPLE_PAD_SPECIFIERS; - syntaxError(pattern,pos,parseError); - return; - } - padPos = pos; - pos += fPadEscape.length(); - padChar = pattern.char32At(pos); - pos += U16_LENGTH(padChar); - break; - } else if (pattern.compare(pos, fMinus.length(), fMinus) == 0) { - affix->append(kQuote); // Encode minus - affix->append(kPatternMinus); - pos += fMinus.length(); - break; - } else if (pattern.compare(pos, fPlus.length(), fPlus) == 0) { - affix->append(kQuote); // Encode plus - affix->append(kPatternPlus); - pos += fPlus.length(); - break; - } - // Unquoted, non-special characters fall through to here, as - // well as other code which needs to append something to the - // affix. - affix->append(ch); - pos += U16_LENGTH(ch); - break; - case 3: // Prefix subpart, in quote - case 4: // Suffix subpart, in quote - // A quote within quotes indicates either the closing - // quote or two quotes, which is a quote literal. That is, - // we have the second quote in 'do' or 'don''t'. - if (ch == kQuote) { - ++pos; - if (pos < pattern.length() && pattern[pos] == kQuote) { - affix->append(kQuote); // Encode quote - // Fall through to append(ch) - } else { - subpart -= 2; // close quote - continue; - } - } - affix->append(ch); - pos += U16_LENGTH(ch); - break; - } - } - - if (sub0Limit == 0) { - sub0Limit = pattern.length(); - } - - if (sub2Limit == 0) { - sub2Limit = pattern.length(); - } - - /* Handle patterns with no '0' pattern character. These patterns - * are legal, but must be recodified to make sense. "##.###" -> - * "#0.###". ".###" -> ".0##". - * - * We allow patterns of the form "####" to produce a zeroDigitCount - * of zero (got that?); although this seems like it might make it - * possible for format() to produce empty strings, format() checks - * for this condition and outputs a zero digit in this situation. - * Having a zeroDigitCount of zero yields a minimum integer digits - * of zero, which allows proper round-trip patterns. We don't want - * "#" to become "#0" when toPattern() is called (even though that's - * what it really is, semantically). - */ - if (zeroDigitCount == 0 && sigDigitCount == 0 && - digitLeftCount > 0 && decimalPos >= 0) { - // Handle "###.###" and "###." and ".###" - int n = decimalPos; - if (n == 0) - ++n; // Handle ".###" - digitRightCount = digitLeftCount - n; - digitLeftCount = n - 1; - zeroDigitCount = 1; - } - - // Do syntax checking on the digits, decimal points, and quotes. - if ((decimalPos < 0 && digitRightCount > 0 && sigDigitCount == 0) || - (decimalPos >= 0 && - (sigDigitCount > 0 || - decimalPos < digitLeftCount || - decimalPos > (digitLeftCount + zeroDigitCount))) || - groupingCount == 0 || groupingCount2 == 0 || - (sigDigitCount > 0 && zeroDigitCount > 0) || - subpart > 2) - { // subpart > 2 == unmatched quote - debug("Syntax error") - status = U_PATTERN_SYNTAX_ERROR; - syntaxError(pattern,pos,parseError); - return; - } - - // Make sure pad is at legal position before or after affix. - if (padPos >= 0) { - if (padPos == start) { - padPos = DecimalFormatPattern::kPadBeforePrefix; - } else if (padPos+2 == sub0Start) { - padPos = DecimalFormatPattern::kPadAfterPrefix; - } else if (padPos == sub0Limit) { - padPos = DecimalFormatPattern::kPadBeforeSuffix; - } else if (padPos+2 == sub2Limit) { - padPos = DecimalFormatPattern::kPadAfterSuffix; - } else { - // Illegal pad position - debug("Illegal pad position") - status = U_ILLEGAL_PAD_POSITION; - syntaxError(pattern,pos,parseError); - return; - } - } - - if (part == 0) { - out.fPosPatternsBogus = FALSE; - out.fPosPrefixPattern = prefix; - out.fPosSuffixPattern = suffix; - out.fNegPatternsBogus = TRUE; - out.fNegPrefixPattern.remove(); - out.fNegSuffixPattern.remove(); - - out.fUseExponentialNotation = (expDigits >= 0); - if (out.fUseExponentialNotation) { - out.fMinExponentDigits = expDigits; - } - out.fExponentSignAlwaysShown = expSignAlways; - int32_t digitTotalCount = digitLeftCount + zeroDigitCount + digitRightCount; - // The effectiveDecimalPos is the position the decimal is at or - // would be at if there is no decimal. Note that if - // decimalPos<0, then digitTotalCount == digitLeftCount + - // zeroDigitCount. - int32_t effectiveDecimalPos = decimalPos >= 0 ? decimalPos : digitTotalCount; - UBool isSigDig = (sigDigitCount > 0); - out.fUseSignificantDigits = isSigDig; - if (isSigDig) { - out.fMinimumSignificantDigits = sigDigitCount; - out.fMaximumSignificantDigits = sigDigitCount + digitRightCount; - } else { - int32_t minInt = effectiveDecimalPos - digitLeftCount; - out.fMinimumIntegerDigits = minInt; - out.fMaximumIntegerDigits = out.fUseExponentialNotation - ? digitLeftCount + out.fMinimumIntegerDigits - : gDefaultMaxIntegerDigits; - out.fMaximumFractionDigits = decimalPos >= 0 - ? (digitTotalCount - decimalPos) : 0; - out.fMinimumFractionDigits = decimalPos >= 0 - ? (digitLeftCount + zeroDigitCount - decimalPos) : 0; - } - out.fGroupingUsed = groupingCount > 0; - out.fGroupingSize = (groupingCount > 0) ? groupingCount : 0; - out.fGroupingSize2 = (groupingCount2 > 0 && groupingCount2 != groupingCount) - ? groupingCount2 : 0; - out.fMultiplier = multiplier; - out.fDecimalSeparatorAlwaysShown = decimalPos == 0 - || decimalPos == digitTotalCount; - if (padPos >= 0) { - out.fPadPosition = (DecimalFormatPattern::EPadPosition) padPos; - // To compute the format width, first set up sub0Limit - - // sub0Start. Add in prefix/suffix length later. - - // fFormatWidth = prefix.length() + suffix.length() + - // sub0Limit - sub0Start; - out.fFormatWidth = sub0Limit - sub0Start; - out.fPad = padChar; - } else { - out.fFormatWidth = 0; - } - if (roundingPos >= 0) { - out.fRoundingIncrementUsed = TRUE; - roundingInc.setDecimalAt(effectiveDecimalPos - roundingPos); - out.fRoundingIncrement = roundingInc; - } else { - out.fRoundingIncrementUsed = FALSE; - } - } else { - out.fNegPatternsBogus = FALSE; - out.fNegPrefixPattern = prefix; - out.fNegSuffixPattern = suffix; - } - } - - if (pattern.length() == 0) { - out.fNegPatternsBogus = TRUE; - out.fNegPrefixPattern.remove(); - out.fNegSuffixPattern.remove(); - out.fPosPatternsBogus = FALSE; - out.fPosPrefixPattern.remove(); - out.fPosSuffixPattern.remove(); - - out.fMinimumIntegerDigits = 0; - out.fMaximumIntegerDigits = kDoubleIntegerDigits; - out.fMinimumFractionDigits = 0; - out.fMaximumFractionDigits = kDoubleFractionDigits; - - out.fUseExponentialNotation = FALSE; - out.fCurrencySignCount = fgCurrencySignCountZero; - out.fGroupingUsed = FALSE; - out.fGroupingSize = 0; - out.fGroupingSize2 = 0; - out.fMultiplier = 1; - out.fDecimalSeparatorAlwaysShown = FALSE; - out.fFormatWidth = 0; - out.fRoundingIncrementUsed = FALSE; - } - - // If there was no negative pattern, or if the negative pattern is - // identical to the positive pattern, then prepend the minus sign to the - // positive pattern to form the negative pattern. - if (out.fNegPatternsBogus || - (out.fNegPrefixPattern == out.fPosPrefixPattern - && out.fNegSuffixPattern == out.fPosSuffixPattern)) { - out.fNegPatternsBogus = FALSE; - out.fNegSuffixPattern = out.fPosSuffixPattern; - out.fNegPrefixPattern.remove(); - out.fNegPrefixPattern.append(kQuote).append(kPatternMinus) - .append(out.fPosPrefixPattern); - } - // TODO: Deprecate/Remove out.fNegSuffixPattern and 3 other fields. - AffixPattern::parseAffixString( - out.fNegSuffixPattern, out.fNegSuffixAffix, status); - AffixPattern::parseAffixString( - out.fPosSuffixPattern, out.fPosSuffixAffix, status); - AffixPattern::parseAffixString( - out.fNegPrefixPattern, out.fNegPrefixAffix, status); - AffixPattern::parseAffixString( - out.fPosPrefixPattern, out.fPosPrefixAffix, status); -} - -U_NAMESPACE_END - -#endif /* !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/decimalformatpattern.h b/deps/icu-small/source/i18n/decimalformatpattern.h deleted file mode 100644 index 1c297575ead1e7..00000000000000 --- a/deps/icu-small/source/i18n/decimalformatpattern.h +++ /dev/null @@ -1,106 +0,0 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 1997-2015, International Business Machines Corporation and * -* others. All Rights Reserved. * -******************************************************************************* -*/ -#ifndef _DECIMAL_FORMAT_PATTERN -#define _DECIMAL_FORMAT_PATTERN - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/uobject.h" -#include "unicode/unistr.h" -#include "digitlst.h" -#include "affixpatternparser.h" - -U_NAMESPACE_BEGIN - -// currency sign count -enum CurrencySignCount { - fgCurrencySignCountZero, - fgCurrencySignCountInSymbolFormat, - fgCurrencySignCountInISOFormat, - fgCurrencySignCountInPluralFormat -}; - -class DecimalFormatSymbols; - -struct DecimalFormatPattern : public UMemory { - enum EPadPosition { - kPadBeforePrefix, - kPadAfterPrefix, - kPadBeforeSuffix, - kPadAfterSuffix - }; - - DecimalFormatPattern(); - - int32_t fMinimumIntegerDigits; - int32_t fMaximumIntegerDigits; - int32_t fMinimumFractionDigits; - int32_t fMaximumFractionDigits; - UBool fUseSignificantDigits; - int32_t fMinimumSignificantDigits; - int32_t fMaximumSignificantDigits; - UBool fUseExponentialNotation; - int32_t fMinExponentDigits; - UBool fExponentSignAlwaysShown; - int32_t fCurrencySignCount; - UBool fGroupingUsed; - int32_t fGroupingSize; - int32_t fGroupingSize2; - int32_t fMultiplier; - UBool fDecimalSeparatorAlwaysShown; - int32_t fFormatWidth; - UBool fRoundingIncrementUsed; - DigitList fRoundingIncrement; - UChar32 fPad; - UBool fNegPatternsBogus; - UBool fPosPatternsBogus; - UnicodeString fNegPrefixPattern; - UnicodeString fNegSuffixPattern; - UnicodeString fPosPrefixPattern; - UnicodeString fPosSuffixPattern; - AffixPattern fNegPrefixAffix; - AffixPattern fNegSuffixAffix; - AffixPattern fPosPrefixAffix; - AffixPattern fPosSuffixAffix; - EPadPosition fPadPosition; -}; - -class DecimalFormatPatternParser : public UMemory { - public: - DecimalFormatPatternParser(); - void useSymbols(const DecimalFormatSymbols& symbols); - - void applyPatternWithoutExpandAffix( - const UnicodeString& pattern, - DecimalFormatPattern& out, - UParseError& parseError, - UErrorCode& status); - private: - DecimalFormatPatternParser(const DecimalFormatPatternParser&); - DecimalFormatPatternParser& operator=(DecimalFormatPatternParser& rhs); - UChar32 fZeroDigit; - UChar32 fSigDigit; - UnicodeString fGroupingSeparator; - UnicodeString fDecimalSeparator; - UnicodeString fPercent; - UnicodeString fPerMill; - UnicodeString fDigit; - UnicodeString fSeparator; - UnicodeString fExponent; - UnicodeString fPlus; - UnicodeString fMinus; - UnicodeString fPadEscape; -}; - -U_NAMESPACE_END - -#endif /* !UCONFIG_NO_FORMATTING */ -#endif diff --git a/deps/icu-small/source/i18n/decimalformatpatternimpl.h b/deps/icu-small/source/i18n/decimalformatpatternimpl.h deleted file mode 100644 index 8cecc8cca02c72..00000000000000 --- a/deps/icu-small/source/i18n/decimalformatpatternimpl.h +++ /dev/null @@ -1,35 +0,0 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************** -* Copyright (C) 2015, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************** -* -* File decimalformatpatternimpl.h -******************************************************************************** -*/ - -#ifndef DECIMALFORMATPATTERNIMPL_H -#define DECIMALFORMATPATTERNIMPL_H - -#include "unicode/utypes.h" - -#define kPatternZeroDigit ((UChar)0x0030) /*'0'*/ -#define kPatternSignificantDigit ((UChar)0x0040) /*'@'*/ -#define kPatternGroupingSeparator ((UChar)0x002C) /*','*/ -#define kPatternDecimalSeparator ((UChar)0x002E) /*'.'*/ -#define kPatternPerMill ((UChar)0x2030) -#define kPatternPercent ((UChar)0x0025) /*'%'*/ -#define kPatternDigit ((UChar)0x0023) /*'#'*/ -#define kPatternSeparator ((UChar)0x003B) /*';'*/ -#define kPatternExponent ((UChar)0x0045) /*'E'*/ -#define kPatternPlus ((UChar)0x002B) /*'+'*/ -#define kPatternMinus ((UChar)0x002D) /*'-'*/ -#define kPatternPadEscape ((UChar)0x002A) /*'*'*/ -#define kQuote ((UChar)0x0027) /*'\''*/ - -#define kCurrencySign ((UChar)0x00A4) -#define kDefaultPad ((UChar)0x0020) /* */ - -#endif diff --git a/deps/icu-small/source/i18n/decimfmt.cpp b/deps/icu-small/source/i18n/decimfmt.cpp index 80a9446e146f6c..a2638bb74298e3 100644 --- a/deps/icu-small/source/i18n/decimfmt.cpp +++ b/deps/icu-small/source/i18n/decimfmt.cpp @@ -1,3295 +1,1383 @@ -// © 2016 and later: Unicode, Inc. and others. +// © 2018 and later: Unicode, Inc. and others. // License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 1997-2015, International Business Machines Corporation and * -* others. All Rights Reserved. * -******************************************************************************* -* -* File DECIMFMT.CPP -* -* Modification History: -* -* Date Name Description -* 02/19/97 aliu Converted from java. -* 03/20/97 clhuang Implemented with new APIs. -* 03/31/97 aliu Moved isLONG_MIN to DigitList, and fixed it. -* 04/3/97 aliu Rewrote parsing and formatting completely, and -* cleaned up and debugged. Actually works now. -* Implemented NAN and INF handling, for both parsing -* and formatting. Extensive testing & debugging. -* 04/10/97 aliu Modified to compile on AIX. -* 04/16/97 aliu Rewrote to use DigitList, which has been resurrected. -* Changed DigitCount to int per code review. -* 07/09/97 helena Made ParsePosition into a class. -* 08/26/97 aliu Extensive changes to applyPattern; completely -* rewritten from the Java. -* 09/09/97 aliu Ported over support for exponential formats. -* 07/20/98 stephen JDK 1.2 sync up. -* Various instances of '0' replaced with 'NULL' -* Check for grouping size in subFormat() -* Brought subParse() in line with Java 1.2 -* Added method appendAffix() -* 08/24/1998 srl Removed Mutex calls. This is not a thread safe class! -* 02/22/99 stephen Removed character literals for EBCDIC safety -* 06/24/99 helena Integrated Alan's NF enhancements and Java2 bug fixes -* 06/28/99 stephen Fixed bugs in toPattern(). -* 06/29/99 stephen Fixed operator= to copy fFormatWidth, fPad, -* fPadPosition -******************************************************************************** -*/ #include "unicode/utypes.h" #if !UCONFIG_NO_FORMATTING -#include "unicode/uniset.h" -#include "unicode/currpinf.h" -#include "unicode/plurrule.h" -#include "unicode/utf16.h" -#include "unicode/numsys.h" -#include "unicode/localpointer.h" -#include "unicode/ustring.h" -#include "uresimp.h" -#include "ucurrimp.h" -#include "charstr.h" -#include "patternprops.h" -#include "cstring.h" -#include "uassert.h" -#include "hash.h" -#include "decfmtst.h" -#include "plurrule_impl.h" -#include "decimalformatpattern.h" -#include "fmtableimp.h" -#include "decimfmtimpl.h" -#include "visibledigits.h" - -/* - * On certain platforms, round is a macro defined in math.h - * This undefine is to avoid conflict between the macro and - * the function defined below. - */ -#ifdef round -#undef round -#endif - - -U_NAMESPACE_BEGIN - -#ifdef FMT_DEBUG -#include -static void _debugout(const char *f, int l, const UnicodeString& s) { - char buf[2000]; - s.extract((int32_t) 0, s.length(), buf, "utf-8"); - printf("%s:%d: %s\n", f,l, buf); -} -#define debugout(x) _debugout(__FILE__,__LINE__,x) -#define debug(x) printf("%s:%d: %s\n", __FILE__,__LINE__, x); -static const UnicodeString dbg_null("",""); -#define DEREFSTR(x) ((x!=NULL)?(*x):(dbg_null)) +// Allow implicit conversion from char16_t* to UnicodeString for this file: +// Helpful in toString methods and elsewhere. +#define UNISTR_FROM_STRING_EXPLICIT + +#include +#include +#include +#include "unicode/errorcode.h" +#include "unicode/decimfmt.h" +#include "number_decimalquantity.h" +#include "number_types.h" +#include "numparse_impl.h" +#include "number_mapper.h" +#include "number_patternstring.h" +#include "putilimp.h" +#include "number_utils.h" +#include "number_utypes.h" + +using namespace icu; +using namespace icu::number; +using namespace icu::number::impl; +using namespace icu::numparse; +using namespace icu::numparse::impl; +using ERoundingMode = icu::DecimalFormat::ERoundingMode; +using EPadPosition = icu::DecimalFormat::EPadPosition; + +// MSVC warns C4805 when comparing bool with UBool +// TODO: Move this macro into a better place? +#if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN +#define UBOOL_TO_BOOL(b) static_cast(b) #else -#define debugout(x) -#define debug(x) -#endif - - -/* For currency parsing purose, - * Need to remember all prefix patterns and suffix patterns of - * every currency format pattern, - * including the pattern of default currecny style - * and plural currency style. And the patterns are set through applyPattern. - */ -struct AffixPatternsForCurrency : public UMemory { - // negative prefix pattern - UnicodeString negPrefixPatternForCurrency; - // negative suffix pattern - UnicodeString negSuffixPatternForCurrency; - // positive prefix pattern - UnicodeString posPrefixPatternForCurrency; - // positive suffix pattern - UnicodeString posSuffixPatternForCurrency; - int8_t patternType; - - AffixPatternsForCurrency(const UnicodeString& negPrefix, - const UnicodeString& negSuffix, - const UnicodeString& posPrefix, - const UnicodeString& posSuffix, - int8_t type) { - negPrefixPatternForCurrency = negPrefix; - negSuffixPatternForCurrency = negSuffix; - posPrefixPatternForCurrency = posPrefix; - posSuffixPatternForCurrency = posSuffix; - patternType = type; - } -#ifdef FMT_DEBUG - void dump() const { - debugout( UnicodeString("AffixPatternsForCurrency( -=\"") + - negPrefixPatternForCurrency + (UnicodeString)"\"/\"" + - negSuffixPatternForCurrency + (UnicodeString)"\" +=\"" + - posPrefixPatternForCurrency + (UnicodeString)"\"/\"" + - posSuffixPatternForCurrency + (UnicodeString)"\" )"); - } +#define UBOOL_TO_BOOL(b) b #endif -}; - -/* affix for currency formatting when the currency sign in the pattern - * equals to 3, such as the pattern contains 3 currency sign or - * the formatter style is currency plural format style. - */ -struct AffixesForCurrency : public UMemory { - // negative prefix - UnicodeString negPrefixForCurrency; - // negative suffix - UnicodeString negSuffixForCurrency; - // positive prefix - UnicodeString posPrefixForCurrency; - // positive suffix - UnicodeString posSuffixForCurrency; - - int32_t formatWidth; - - AffixesForCurrency(const UnicodeString& negPrefix, - const UnicodeString& negSuffix, - const UnicodeString& posPrefix, - const UnicodeString& posSuffix) { - negPrefixForCurrency = negPrefix; - negSuffixForCurrency = negSuffix; - posPrefixForCurrency = posPrefix; - posSuffixForCurrency = posSuffix; - } -#ifdef FMT_DEBUG - void dump() const { - debugout( UnicodeString("AffixesForCurrency( -=\"") + - negPrefixForCurrency + (UnicodeString)"\"/\"" + - negSuffixForCurrency + (UnicodeString)"\" +=\"" + - posPrefixForCurrency + (UnicodeString)"\"/\"" + - posSuffixForCurrency + (UnicodeString)"\" )"); - } -#endif -}; - -U_CDECL_BEGIN - -/** - * @internal ICU 4.2 - */ -static UBool U_CALLCONV decimfmtAffixPatternValueComparator(UHashTok val1, UHashTok val2); - - -static UBool -U_CALLCONV decimfmtAffixPatternValueComparator(UHashTok val1, UHashTok val2) { - const AffixPatternsForCurrency* affix_1 = - (AffixPatternsForCurrency*)val1.pointer; - const AffixPatternsForCurrency* affix_2 = - (AffixPatternsForCurrency*)val2.pointer; - return affix_1->negPrefixPatternForCurrency == - affix_2->negPrefixPatternForCurrency && - affix_1->negSuffixPatternForCurrency == - affix_2->negSuffixPatternForCurrency && - affix_1->posPrefixPatternForCurrency == - affix_2->posPrefixPatternForCurrency && - affix_1->posSuffixPatternForCurrency == - affix_2->posSuffixPatternForCurrency && - affix_1->patternType == affix_2->patternType; -} - -U_CDECL_END - - -// ***************************************************************************** -// class DecimalFormat -// ***************************************************************************** - UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DecimalFormat) -// Constants for characters used in programmatic (unlocalized) patterns. -#define kPatternZeroDigit ((UChar)0x0030) /*'0'*/ -#define kPatternSignificantDigit ((UChar)0x0040) /*'@'*/ -#define kPatternGroupingSeparator ((UChar)0x002C) /*','*/ -#define kPatternDecimalSeparator ((UChar)0x002E) /*'.'*/ -#define kPatternPerMill ((UChar)0x2030) -#define kPatternPercent ((UChar)0x0025) /*'%'*/ -#define kPatternDigit ((UChar)0x0023) /*'#'*/ -#define kPatternSeparator ((UChar)0x003B) /*';'*/ -#define kPatternExponent ((UChar)0x0045) /*'E'*/ -#define kPatternPlus ((UChar)0x002B) /*'+'*/ -#define kPatternMinus ((UChar)0x002D) /*'-'*/ -#define kPatternPadEscape ((UChar)0x002A) /*'*'*/ -#define kQuote ((UChar)0x0027) /*'\''*/ -/** - * The CURRENCY_SIGN is the standard Unicode symbol for currency. It - * is used in patterns and substitued with either the currency symbol, - * or if it is doubled, with the international currency symbol. If the - * CURRENCY_SIGN is seen in a pattern, then the decimal separator is - * replaced with the monetary decimal separator. - */ -#define kCurrencySign ((UChar)0x00A4) -#define kDefaultPad ((UChar)0x0020) /* */ - -const int32_t DecimalFormat::kDoubleIntegerDigits = 309; -const int32_t DecimalFormat::kDoubleFractionDigits = 340; - -const int32_t DecimalFormat::kMaxScientificIntegerDigits = 8; - -/** - * These are the tags we expect to see in normal resource bundle files associated - * with a locale. - */ -const char DecimalFormat::fgNumberPatterns[]="NumberPatterns"; // Deprecated - not used -static const char fgNumberElements[]="NumberElements"; -static const char fgLatn[]="latn"; -static const char fgPatterns[]="patterns"; -static const char fgDecimalFormat[]="decimalFormat"; -static const char fgCurrencyFormat[]="currencyFormat"; - -inline int32_t _min(int32_t a, int32_t b) { return (a adoptedSymbols(symbolsToAdopt); - if (U_FAILURE(status)) - return; - - if (adoptedSymbols.isNull()) - { - adoptedSymbols.adoptInstead( - new DecimalFormatSymbols(Locale::getDefault(), status)); - if (adoptedSymbols.isNull() && U_SUCCESS(status)) { - status = U_MEMORY_ALLOCATION_ERROR; - } - if (U_FAILURE(status)) { - return; - } - } - fStaticSets = DecimalFormatStaticSets::getStaticSets(status); - if (U_FAILURE(status)) { - return; - } - - UnicodeString str; - // Uses the default locale's number format pattern if there isn't - // one specified. - if (pattern == NULL) - { - UErrorCode nsStatus = U_ZERO_ERROR; - LocalPointer ns( - NumberingSystem::createInstance(nsStatus)); - if (U_FAILURE(nsStatus)) { - status = nsStatus; - return; - } - - int32_t len = 0; - UResourceBundle *top = ures_open(NULL, Locale::getDefault().getName(), &status); - - UResourceBundle *resource = ures_getByKeyWithFallback(top, fgNumberElements, NULL, &status); - resource = ures_getByKeyWithFallback(resource, ns->getName(), resource, &status); - resource = ures_getByKeyWithFallback(resource, fgPatterns, resource, &status); - const UChar *resStr = ures_getStringByKeyWithFallback(resource, fgDecimalFormat, &len, &status); - if ( status == U_MISSING_RESOURCE_ERROR && uprv_strcmp(fgLatn,ns->getName())) { - status = U_ZERO_ERROR; - resource = ures_getByKeyWithFallback(top, fgNumberElements, resource, &status); - resource = ures_getByKeyWithFallback(resource, fgLatn, resource, &status); - resource = ures_getByKeyWithFallback(resource, fgPatterns, resource, &status); - resStr = ures_getStringByKeyWithFallback(resource, fgDecimalFormat, &len, &status); - } - str.setTo(TRUE, resStr, len); - pattern = &str; - ures_close(resource); - ures_close(top); - } - - fImpl = new DecimalFormatImpl(this, *pattern, adoptedSymbols.getAlias(), parseErr, status); - if (fImpl) { - adoptedSymbols.orphan(); - } else if (U_SUCCESS(status)) { - status = U_MEMORY_ALLOCATION_ERROR; - } - if (U_FAILURE(status)) { - return; - } - - if (U_FAILURE(status)) - { - return; - } - - const UnicodeString* patternUsed; - UnicodeString currencyPluralPatternForOther; - // apply pattern - if (fStyle == UNUM_CURRENCY_PLURAL) { - fCurrencyPluralInfo = new CurrencyPluralInfo(fImpl->fSymbols->getLocale(), status); - if (U_FAILURE(status)) { - return; - } - - // the pattern used in format is not fixed until formatting, - // in which, the number is known and - // will be used to pick the right pattern based on plural count. - // Here, set the pattern as the pattern of plural count == "other". - // For most locale, the patterns are probably the same for all - // plural count. If not, the right pattern need to be re-applied - // during format. - fCurrencyPluralInfo->getCurrencyPluralPattern(UNICODE_STRING("other", 5), currencyPluralPatternForOther); - // TODO(refactor): Revisit, we are setting the pattern twice. - fImpl->applyPatternFavorCurrencyPrecision( - currencyPluralPatternForOther, status); - patternUsed = ¤cyPluralPatternForOther; +DecimalFormat::DecimalFormat(UErrorCode& status) + : DecimalFormat(nullptr, status) { + // Use the default locale and decimal pattern. + const char* localeName = Locale::getDefault().getName(); + LocalPointer ns(NumberingSystem::createInstance(status)); + UnicodeString patternString = utils::getPatternForStyle( + localeName, + ns->getName(), + CLDR_PATTERN_STYLE_DECIMAL, + status); + setPropertiesFromPattern(patternString, IGNORE_ROUNDING_IF_CURRENCY, status); + touch(status); +} + +DecimalFormat::DecimalFormat(const UnicodeString& pattern, UErrorCode& status) + : DecimalFormat(nullptr, status) { + setPropertiesFromPattern(pattern, IGNORE_ROUNDING_IF_CURRENCY, status); + touch(status); +} + +DecimalFormat::DecimalFormat(const UnicodeString& pattern, DecimalFormatSymbols* symbolsToAdopt, + UErrorCode& status) + : DecimalFormat(symbolsToAdopt, status) { + setPropertiesFromPattern(pattern, IGNORE_ROUNDING_IF_CURRENCY, status); + touch(status); +} + +DecimalFormat::DecimalFormat(const UnicodeString& pattern, DecimalFormatSymbols* symbolsToAdopt, + UNumberFormatStyle style, UErrorCode& status) + : DecimalFormat(symbolsToAdopt, status) { + // If choice is a currency type, ignore the rounding information. + if (style == UNumberFormatStyle::UNUM_CURRENCY || style == UNumberFormatStyle::UNUM_CURRENCY_ISO || + style == UNumberFormatStyle::UNUM_CURRENCY_ACCOUNTING || + style == UNumberFormatStyle::UNUM_CASH_CURRENCY || + style == UNumberFormatStyle::UNUM_CURRENCY_STANDARD || + style == UNumberFormatStyle::UNUM_CURRENCY_PLURAL) { + setPropertiesFromPattern(pattern, IGNORE_ROUNDING_ALWAYS, status); } else { - patternUsed = pattern; + setPropertiesFromPattern(pattern, IGNORE_ROUNDING_IF_CURRENCY, status); } - - if (patternUsed->indexOf(kCurrencySign) != -1) { - // initialize for currency, not only for plural format, - // but also for mix parsing - handleCurrencySignInPattern(status); + // Note: in Java, CurrencyPluralInfo is set in NumberFormat.java, but in C++, it is not set there, + // so we have to set it here. + if (style == UNumberFormatStyle::UNUM_CURRENCY_PLURAL) { + LocalPointer cpi( + new CurrencyPluralInfo(fields->symbols->getLocale(), status), + status); + if (U_FAILURE(status)) { return; } + fields->properties->currencyPluralInfo.fPtr.adoptInstead(cpi.orphan()); } + touch(status); } -void -DecimalFormat::handleCurrencySignInPattern(UErrorCode& status) { - // initialize for currency, not only for plural format, - // but also for mix parsing +DecimalFormat::DecimalFormat(const DecimalFormatSymbols* symbolsToAdopt, UErrorCode& status) { + LocalPointer adoptedSymbols(symbolsToAdopt); + fields = new DecimalFormatFields(); if (U_FAILURE(status)) { return; } - if (fCurrencyPluralInfo == NULL) { - fCurrencyPluralInfo = new CurrencyPluralInfo(fImpl->fSymbols->getLocale(), status); - if (U_FAILURE(status)) { - return; - } - } - // need it for mix parsing - if (fAffixPatternsForCurrency == NULL) { - setupCurrencyAffixPatterns(status); - } -} - -static void -applyPatternWithNoSideEffects( - const UnicodeString& pattern, - UParseError& parseError, - UnicodeString &negPrefix, - UnicodeString &negSuffix, - UnicodeString &posPrefix, - UnicodeString &posSuffix, - UErrorCode& status) { - if (U_FAILURE(status)) - { + if (fields == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; return; } - DecimalFormatPatternParser patternParser; - DecimalFormatPattern out; - patternParser.applyPatternWithoutExpandAffix( - pattern, - out, - parseError, - status); - if (U_FAILURE(status)) { - return; + fields->properties.adoptInsteadAndCheckErrorCode(new DecimalFormatProperties(), status); + fields->exportedProperties.adoptInsteadAndCheckErrorCode(new DecimalFormatProperties(), status); + if (adoptedSymbols.isNull()) { + fields->symbols.adoptInsteadAndCheckErrorCode(new DecimalFormatSymbols(status), status); + } else { + fields->symbols.adoptInsteadAndCheckErrorCode(adoptedSymbols.orphan(), status); } - negPrefix = out.fNegPrefixPattern; - negSuffix = out.fNegSuffixPattern; - posPrefix = out.fPosPrefixPattern; - posSuffix = out.fPosSuffixPattern; } -void -DecimalFormat::setupCurrencyAffixPatterns(UErrorCode& status) { - if (U_FAILURE(status)) { - return; - } - UParseError parseErr; - fAffixPatternsForCurrency = initHashForAffixPattern(status); - if (U_FAILURE(status)) { - return; - } - - NumberingSystem *ns = NumberingSystem::createInstance(fImpl->fSymbols->getLocale(),status); - if (U_FAILURE(status)) { - return; - } +#if UCONFIG_HAVE_PARSEALLINPUT - // Save the default currency patterns of this locale. - // Here, chose onlyApplyPatternWithoutExpandAffix without - // expanding the affix patterns into affixes. - UnicodeString currencyPattern; - UErrorCode error = U_ZERO_ERROR; - - UResourceBundle *resource = ures_open(NULL, fImpl->fSymbols->getLocale().getName(), &error); - UResourceBundle *numElements = ures_getByKeyWithFallback(resource, fgNumberElements, NULL, &error); - resource = ures_getByKeyWithFallback(numElements, ns->getName(), resource, &error); - resource = ures_getByKeyWithFallback(resource, fgPatterns, resource, &error); - int32_t patLen = 0; - const UChar *patResStr = ures_getStringByKeyWithFallback(resource, fgCurrencyFormat, &patLen, &error); - if ( error == U_MISSING_RESOURCE_ERROR && uprv_strcmp(ns->getName(),fgLatn)) { - error = U_ZERO_ERROR; - resource = ures_getByKeyWithFallback(numElements, fgLatn, resource, &error); - resource = ures_getByKeyWithFallback(resource, fgPatterns, resource, &error); - patResStr = ures_getStringByKeyWithFallback(resource, fgCurrencyFormat, &patLen, &error); - } - ures_close(numElements); - ures_close(resource); - delete ns; - - if (U_SUCCESS(error)) { - UnicodeString negPrefix; - UnicodeString negSuffix; - UnicodeString posPrefix; - UnicodeString posSuffix; - applyPatternWithNoSideEffects(UnicodeString(patResStr, patLen), - parseErr, - negPrefix, negSuffix, posPrefix, posSuffix, status); - AffixPatternsForCurrency* affixPtn = new AffixPatternsForCurrency( - negPrefix, - negSuffix, - posPrefix, - posSuffix, - UCURR_SYMBOL_NAME); - fAffixPatternsForCurrency->put(UNICODE_STRING("default", 7), affixPtn, status); - } - - // save the unique currency plural patterns of this locale. - Hashtable* pluralPtn = fCurrencyPluralInfo->fPluralCountToCurrencyUnitPattern; - const UHashElement* element = NULL; - int32_t pos = UHASH_FIRST; - Hashtable pluralPatternSet; - while ((element = pluralPtn->nextElement(pos)) != NULL) { - const UHashTok valueTok = element->value; - const UnicodeString* value = (UnicodeString*)valueTok.pointer; - const UHashTok keyTok = element->key; - const UnicodeString* key = (UnicodeString*)keyTok.pointer; - if (pluralPatternSet.geti(*value) != 1) { - UnicodeString negPrefix; - UnicodeString negSuffix; - UnicodeString posPrefix; - UnicodeString posSuffix; - pluralPatternSet.puti(*value, 1, status); - applyPatternWithNoSideEffects( - *value, parseErr, - negPrefix, negSuffix, posPrefix, posSuffix, status); - AffixPatternsForCurrency* affixPtn = new AffixPatternsForCurrency( - negPrefix, - negSuffix, - posPrefix, - posSuffix, - UCURR_LONG_NAME); - fAffixPatternsForCurrency->put(*key, affixPtn, status); - } - } +void DecimalFormat::setParseAllInput(UNumberFormatAttributeValue value) { + if (value == fields->properties->parseAllInput) { return; } + fields->properties->parseAllInput = value; } +#endif -//------------------------------------------------------------------------------ +DecimalFormat& +DecimalFormat::setAttribute(UNumberFormatAttribute attr, int32_t newValue, UErrorCode& status) { + if (U_FAILURE(status)) { return *this; } -DecimalFormat::~DecimalFormat() -{ - deleteHashForAffixPattern(); - delete fCurrencyPluralInfo; - delete fImpl; -} + switch (attr) { + case UNUM_LENIENT_PARSE: + setLenient(newValue != 0); + break; -//------------------------------------------------------------------------------ -// copy constructor + case UNUM_PARSE_INT_ONLY: + setParseIntegerOnly(newValue != 0); + break; -DecimalFormat::DecimalFormat(const DecimalFormat &source) : - NumberFormat(source) { - init(); - *this = source; -} + case UNUM_GROUPING_USED: + setGroupingUsed(newValue != 0); + break; -//------------------------------------------------------------------------------ -// assignment operator + case UNUM_DECIMAL_ALWAYS_SHOWN: + setDecimalSeparatorAlwaysShown(newValue != 0); + break; -template -static void _clone_ptr(T** pdest, const T* source) { - delete *pdest; - if (source == NULL) { - *pdest = NULL; - } else { - *pdest = static_cast(source->clone()); - } -} + case UNUM_MAX_INTEGER_DIGITS: + setMaximumIntegerDigits(newValue); + break; -DecimalFormat& -DecimalFormat::operator=(const DecimalFormat& rhs) -{ - if(this != &rhs) { - UErrorCode status = U_ZERO_ERROR; - NumberFormat::operator=(rhs); - if (fImpl == NULL) { - fImpl = new DecimalFormatImpl(this, *rhs.fImpl, status); - } else { - fImpl->assign(*rhs.fImpl, status); - } - fStaticSets = DecimalFormatStaticSets::getStaticSets(status); - fStyle = rhs.fStyle; - _clone_ptr(&fCurrencyPluralInfo, rhs.fCurrencyPluralInfo); - deleteHashForAffixPattern(); - if (rhs.fAffixPatternsForCurrency) { - UErrorCode status = U_ZERO_ERROR; - fAffixPatternsForCurrency = initHashForAffixPattern(status); - copyHashForAffixPattern(rhs.fAffixPatternsForCurrency, - fAffixPatternsForCurrency, status); - } - } + case UNUM_MIN_INTEGER_DIGITS: + setMinimumIntegerDigits(newValue); + break; - return *this; -} + case UNUM_INTEGER_DIGITS: + setMinimumIntegerDigits(newValue); + setMaximumIntegerDigits(newValue); + break; -//------------------------------------------------------------------------------ + case UNUM_MAX_FRACTION_DIGITS: + setMaximumFractionDigits(newValue); + break; -UBool -DecimalFormat::operator==(const Format& that) const -{ - if (this == &that) - return TRUE; + case UNUM_MIN_FRACTION_DIGITS: + setMinimumFractionDigits(newValue); + break; - // NumberFormat::operator== guarantees this cast is safe - const DecimalFormat* other = (DecimalFormat*)&that; + case UNUM_FRACTION_DIGITS: + setMinimumFractionDigits(newValue); + setMaximumFractionDigits(newValue); + break; - return ( - NumberFormat::operator==(that) && - fBoolFlags.getAll() == other->fBoolFlags.getAll() && - *fImpl == *other->fImpl); + case UNUM_SIGNIFICANT_DIGITS_USED: + setSignificantDigitsUsed(newValue != 0); + break; -} + case UNUM_MAX_SIGNIFICANT_DIGITS: + setMaximumSignificantDigits(newValue); + break; -//------------------------------------------------------------------------------ + case UNUM_MIN_SIGNIFICANT_DIGITS: + setMinimumSignificantDigits(newValue); + break; -Format* -DecimalFormat::clone() const -{ - return new DecimalFormat(*this); -} + case UNUM_MULTIPLIER: + setMultiplier(newValue); + break; + case UNUM_SCALE: + setMultiplierScale(newValue); + break; -FixedDecimal -DecimalFormat::getFixedDecimal(double number, UErrorCode &status) const { - VisibleDigitsWithExponent digits; - initVisibleDigitsWithExponent(number, digits, status); - if (U_FAILURE(status)) { - return FixedDecimal(); - } - return FixedDecimal(digits.getMantissa()); -} + case UNUM_GROUPING_SIZE: + setGroupingSize(newValue); + break; -VisibleDigitsWithExponent & -DecimalFormat::initVisibleDigitsWithExponent( - double number, - VisibleDigitsWithExponent &digits, - UErrorCode &status) const { - return fImpl->initVisibleDigitsWithExponent(number, digits, status); -} + case UNUM_ROUNDING_MODE: + setRoundingMode((DecimalFormat::ERoundingMode) newValue); + break; -FixedDecimal -DecimalFormat::getFixedDecimal(const Formattable &number, UErrorCode &status) const { - VisibleDigitsWithExponent digits; - initVisibleDigitsWithExponent(number, digits, status); - if (U_FAILURE(status)) { - return FixedDecimal(); - } - return FixedDecimal(digits.getMantissa()); -} + case UNUM_FORMAT_WIDTH: + setFormatWidth(newValue); + break; -VisibleDigitsWithExponent & -DecimalFormat::initVisibleDigitsWithExponent( - const Formattable &number, - VisibleDigitsWithExponent &digits, - UErrorCode &status) const { - if (U_FAILURE(status)) { - return digits; - } - if (!number.isNumeric()) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return digits; - } + case UNUM_PADDING_POSITION: + /** The position at which padding will take place. */ + setPadPosition((DecimalFormat::EPadPosition) newValue); + break; - DigitList *dl = number.getDigitList(); - if (dl != NULL) { - DigitList dlCopy(*dl); - return fImpl->initVisibleDigitsWithExponent( - dlCopy, digits, status); - } + case UNUM_SECONDARY_GROUPING_SIZE: + setSecondaryGroupingSize(newValue); + break; - Formattable::Type type = number.getType(); - if (type == Formattable::kDouble || type == Formattable::kLong) { - return fImpl->initVisibleDigitsWithExponent( - number.getDouble(status), digits, status); - } - return fImpl->initVisibleDigitsWithExponent( - number.getInt64(), digits, status); -} +#if UCONFIG_HAVE_PARSEALLINPUT + case UNUM_PARSE_ALL_INPUT: + setParseAllInput((UNumberFormatAttributeValue) newValue); + break; +#endif + case UNUM_PARSE_NO_EXPONENT: + setParseNoExponent((UBool) newValue); + break; -// Create a fixed decimal from a DigitList. -// The digit list may be modified. -// Internal function only. -FixedDecimal -DecimalFormat::getFixedDecimal(DigitList &number, UErrorCode &status) const { - VisibleDigitsWithExponent digits; - initVisibleDigitsWithExponent(number, digits, status); - if (U_FAILURE(status)) { - return FixedDecimal(); - } - return FixedDecimal(digits.getMantissa()); -} + case UNUM_PARSE_DECIMAL_MARK_REQUIRED: + setDecimalPatternMatchRequired((UBool) newValue); + break; -VisibleDigitsWithExponent & -DecimalFormat::initVisibleDigitsWithExponent( - DigitList &number, - VisibleDigitsWithExponent &digits, - UErrorCode &status) const { - return fImpl->initVisibleDigitsWithExponent( - number, digits, status); -} + case UNUM_CURRENCY_USAGE: + setCurrencyUsage((UCurrencyUsage) newValue, &status); + break; + case UNUM_MINIMUM_GROUPING_DIGITS: + setMinimumGroupingDigits(newValue); + break; -//------------------------------------------------------------------------------ + case UNUM_PARSE_CASE_SENSITIVE: + setParseCaseSensitive(static_cast(newValue)); + break; -UnicodeString& -DecimalFormat::format(int32_t number, - UnicodeString& appendTo, - FieldPosition& fieldPosition) const -{ - UErrorCode status = U_ZERO_ERROR; - return fImpl->format(number, appendTo, fieldPosition, status); -} + case UNUM_SIGN_ALWAYS_SHOWN: + setSignAlwaysShown(static_cast(newValue)); + break; -UnicodeString& -DecimalFormat::format(int32_t number, - UnicodeString& appendTo, - FieldPosition& fieldPosition, - UErrorCode& status) const -{ - return fImpl->format(number, appendTo, fieldPosition, status); -} + case UNUM_FORMAT_FAIL_IF_MORE_THAN_MAX_DIGITS: + setFormatFailIfMoreThanMaxDigits(static_cast(newValue)); + break; -UnicodeString& -DecimalFormat::format(int32_t number, - UnicodeString& appendTo, - FieldPositionIterator* posIter, - UErrorCode& status) const -{ - return fImpl->format(number, appendTo, posIter, status); + default: + status = U_UNSUPPORTED_ERROR; + break; + } + return *this; } +int32_t DecimalFormat::getAttribute(UNumberFormatAttribute attr, UErrorCode& status) const { + if (U_FAILURE(status)) { return -1; } + switch (attr) { + case UNUM_LENIENT_PARSE: + return isLenient(); -//------------------------------------------------------------------------------ + case UNUM_PARSE_INT_ONLY: + return isParseIntegerOnly(); -UnicodeString& -DecimalFormat::format(int64_t number, - UnicodeString& appendTo, - FieldPosition& fieldPosition) const -{ - UErrorCode status = U_ZERO_ERROR; /* ignored */ - return fImpl->format(number, appendTo, fieldPosition, status); -} + case UNUM_GROUPING_USED: + return isGroupingUsed(); -UnicodeString& -DecimalFormat::format(int64_t number, - UnicodeString& appendTo, - FieldPosition& fieldPosition, - UErrorCode& status) const -{ - return fImpl->format(number, appendTo, fieldPosition, status); -} + case UNUM_DECIMAL_ALWAYS_SHOWN: + return isDecimalSeparatorAlwaysShown(); -UnicodeString& -DecimalFormat::format(int64_t number, - UnicodeString& appendTo, - FieldPositionIterator* posIter, - UErrorCode& status) const -{ - return fImpl->format(number, appendTo, posIter, status); -} + case UNUM_MAX_INTEGER_DIGITS: + return getMaximumIntegerDigits(); -//------------------------------------------------------------------------------ + case UNUM_MIN_INTEGER_DIGITS: + return getMinimumIntegerDigits(); -UnicodeString& -DecimalFormat::format( double number, - UnicodeString& appendTo, - FieldPosition& fieldPosition) const -{ - UErrorCode status = U_ZERO_ERROR; /* ignored */ - return fImpl->format(number, appendTo, fieldPosition, status); -} + case UNUM_INTEGER_DIGITS: + // TBD: what should this return? + return getMinimumIntegerDigits(); -UnicodeString& -DecimalFormat::format( double number, - UnicodeString& appendTo, - FieldPosition& fieldPosition, - UErrorCode& status) const -{ - return fImpl->format(number, appendTo, fieldPosition, status); -} + case UNUM_MAX_FRACTION_DIGITS: + return getMaximumFractionDigits(); -UnicodeString& -DecimalFormat::format( double number, - UnicodeString& appendTo, - FieldPositionIterator* posIter, - UErrorCode& status) const -{ - return fImpl->format(number, appendTo, posIter, status); -} + case UNUM_MIN_FRACTION_DIGITS: + return getMinimumFractionDigits(); -//------------------------------------------------------------------------------ + case UNUM_FRACTION_DIGITS: + // TBD: what should this return? + return getMinimumFractionDigits(); + case UNUM_SIGNIFICANT_DIGITS_USED: + return areSignificantDigitsUsed(); -UnicodeString& -DecimalFormat::format(StringPiece number, - UnicodeString &toAppendTo, - FieldPositionIterator *posIter, - UErrorCode &status) const -{ - return fImpl->format(number, toAppendTo, posIter, status); -} + case UNUM_MAX_SIGNIFICANT_DIGITS: + return getMaximumSignificantDigits(); + case UNUM_MIN_SIGNIFICANT_DIGITS: + return getMinimumSignificantDigits(); -UnicodeString& -DecimalFormat::format(const DigitList &number, - UnicodeString &appendTo, - FieldPositionIterator *posIter, - UErrorCode &status) const { - return fImpl->format(number, appendTo, posIter, status); -} + case UNUM_MULTIPLIER: + return getMultiplier(); + case UNUM_SCALE: + return getMultiplierScale(); -UnicodeString& -DecimalFormat::format(const DigitList &number, - UnicodeString& appendTo, - FieldPosition& pos, - UErrorCode &status) const { - return fImpl->format(number, appendTo, pos, status); -} + case UNUM_GROUPING_SIZE: + return getGroupingSize(); -UnicodeString& -DecimalFormat::format(const VisibleDigitsWithExponent &number, - UnicodeString &appendTo, - FieldPositionIterator *posIter, - UErrorCode &status) const { - return fImpl->format(number, appendTo, posIter, status); -} + case UNUM_ROUNDING_MODE: + return getRoundingMode(); + case UNUM_FORMAT_WIDTH: + return getFormatWidth(); -UnicodeString& -DecimalFormat::format(const VisibleDigitsWithExponent &number, - UnicodeString& appendTo, - FieldPosition& pos, - UErrorCode &status) const { - return fImpl->format(number, appendTo, pos, status); -} + case UNUM_PADDING_POSITION: + return getPadPosition(); -DigitList& -DecimalFormat::_round(const DigitList& number, DigitList& adjustedNum, UBool& isNegative, UErrorCode& status) const { - adjustedNum = number; - fImpl->round(adjustedNum, status); - isNegative = !adjustedNum.isPositive(); - return adjustedNum; -} + case UNUM_SECONDARY_GROUPING_SIZE: + return getSecondaryGroupingSize(); -void -DecimalFormat::parse(const UnicodeString& text, - Formattable& result, - ParsePosition& parsePosition) const { - parse(text, result, parsePosition, NULL); -} - -CurrencyAmount* DecimalFormat::parseCurrency(const UnicodeString& text, - ParsePosition& pos) const { - Formattable parseResult; - int32_t start = pos.getIndex(); - UChar curbuf[4] = {}; - parse(text, parseResult, pos, curbuf); - if (pos.getIndex() != start) { - UErrorCode ec = U_ZERO_ERROR; - LocalPointer currAmt(new CurrencyAmount(parseResult, curbuf, ec), ec); - if (U_FAILURE(ec)) { - pos.setIndex(start); // indicate failure - } else { - return currAmt.orphan(); - } - } - return NULL; -} - -/** - * Parses the given text as a number, optionally providing a currency amount. - * @param text the string to parse - * @param result output parameter for the numeric result. - * @param parsePosition input-output position; on input, the - * position within text to match; must have 0 <= pos.getIndex() < - * text.length(); on output, the position after the last matched - * character. If the parse fails, the position in unchanged upon - * output. - * @param currency if non-NULL, it should point to a 4-UChar buffer. - * In this case the text is parsed as a currency format, and the - * ISO 4217 code for the parsed currency is put into the buffer. - * Otherwise the text is parsed as a non-currency format. - */ -void DecimalFormat::parse(const UnicodeString& text, - Formattable& result, - ParsePosition& parsePosition, - UChar* currency) const { - int32_t startIdx, backup; - int32_t i = startIdx = backup = parsePosition.getIndex(); - - // clear any old contents in the result. In particular, clears any DigitList - // that it may be holding. - result.setLong(0); - if (currency != NULL) { - for (int32_t ci=0; ci<4; ci++) { - currency[ci] = 0; - } - } + case UNUM_PARSE_NO_EXPONENT: + return isParseNoExponent(); - // Handle NaN as a special case: - int32_t formatWidth = fImpl->getOldFormatWidth(); + case UNUM_PARSE_DECIMAL_MARK_REQUIRED: + return isDecimalPatternMatchRequired(); - // Skip padding characters, if around prefix - if (formatWidth > 0 && ( - fImpl->fAffixes.fPadPosition == DigitAffixesAndPadding::kPadBeforePrefix || - fImpl->fAffixes.fPadPosition == DigitAffixesAndPadding::kPadAfterPrefix)) { - i = skipPadding(text, i); - } + case UNUM_CURRENCY_USAGE: + return getCurrencyUsage(); - if (isLenient()) { - // skip any leading whitespace - i = backup = skipUWhiteSpace(text, i); - } + case UNUM_MINIMUM_GROUPING_DIGITS: + return getMinimumGroupingDigits(); - // If the text is composed of the representation of NaN, returns NaN.length - const UnicodeString *nan = &fImpl->getConstSymbol(DecimalFormatSymbols::kNaNSymbol); - int32_t nanLen = (text.compare(i, nan->length(), *nan) - ? 0 : nan->length()); - if (nanLen) { - i += nanLen; - if (formatWidth > 0 && (fImpl->fAffixes.fPadPosition == DigitAffixesAndPadding::kPadBeforeSuffix || fImpl->fAffixes.fPadPosition == DigitAffixesAndPadding::kPadAfterSuffix)) { - i = skipPadding(text, i); - } - parsePosition.setIndex(i); - result.setDouble(uprv_getNaN()); - return; - } + case UNUM_PARSE_CASE_SENSITIVE: + return isParseCaseSensitive(); - // NaN parse failed; start over - i = backup; - parsePosition.setIndex(i); + case UNUM_SIGN_ALWAYS_SHOWN: + return isSignAlwaysShown(); - // status is used to record whether a number is infinite. - UBool status[fgStatusLength]; + case UNUM_FORMAT_FAIL_IF_MORE_THAN_MAX_DIGITS: + return isFormatFailIfMoreThanMaxDigits(); - DigitList *digits = result.getInternalDigitList(); // get one from the stack buffer - if (digits == NULL) { - return; // no way to report error from here. + default: + status = U_UNSUPPORTED_ERROR; + break; } - if (fImpl->fMonetary) { - if (!parseForCurrency(text, parsePosition, *digits, - status, currency)) { - return; - } - } else { - if (!subparse(text, - &fImpl->fAffixes.fNegativePrefix.getOtherVariant().toString(), - &fImpl->fAffixes.fNegativeSuffix.getOtherVariant().toString(), - &fImpl->fAffixes.fPositivePrefix.getOtherVariant().toString(), - &fImpl->fAffixes.fPositiveSuffix.getOtherVariant().toString(), - FALSE, UCURR_SYMBOL_NAME, - parsePosition, *digits, status, currency)) { - debug("!subparse(...) - rewind"); - parsePosition.setIndex(startIdx); - return; - } - } + return -1; /* undefined */ +} - // Handle infinity - if (status[fgStatusInfinite]) { - double inf = uprv_getInfinity(); - result.setDouble(digits->isPositive() ? inf : -inf); - // TODO: set the dl to infinity, and let it fall into the code below. - } +void DecimalFormat::setGroupingUsed(UBool enabled) { + if (UBOOL_TO_BOOL(enabled) == fields->properties->groupingUsed) { return; } + NumberFormat::setGroupingUsed(enabled); // to set field for compatibility + fields->properties->groupingUsed = enabled; + touchNoError(); +} - else { +void DecimalFormat::setParseIntegerOnly(UBool value) { + if (UBOOL_TO_BOOL(value) == fields->properties->parseIntegerOnly) { return; } + NumberFormat::setParseIntegerOnly(value); // to set field for compatibility + fields->properties->parseIntegerOnly = value; + touchNoError(); +} - if (!fImpl->fMultiplier.isZero()) { - UErrorCode ec = U_ZERO_ERROR; - digits->div(fImpl->fMultiplier, ec); - } +void DecimalFormat::setLenient(UBool enable) { + ParseMode mode = enable ? PARSE_MODE_LENIENT : PARSE_MODE_STRICT; + if (!fields->properties->parseMode.isNull() && mode == fields->properties->parseMode.getNoError()) { return; } + NumberFormat::setLenient(enable); // to set field for compatibility + fields->properties->parseMode = mode; + touchNoError(); +} - if (fImpl->fScale != 0) { - DigitList ten; - ten.set((int32_t)10); - if (fImpl->fScale > 0) { - for (int32_t i = fImpl->fScale; i > 0; i--) { - UErrorCode ec = U_ZERO_ERROR; - digits->div(ten,ec); - } - } else { - for (int32_t i = fImpl->fScale; i < 0; i++) { - UErrorCode ec = U_ZERO_ERROR; - digits->mult(ten,ec); - } - } - } +DecimalFormat::DecimalFormat(const UnicodeString& pattern, DecimalFormatSymbols* symbolsToAdopt, + UParseError&, UErrorCode& status) + : DecimalFormat(symbolsToAdopt, status) { + // TODO: What is parseError for? + setPropertiesFromPattern(pattern, IGNORE_ROUNDING_IF_CURRENCY, status); + touch(status); +} - // Negative zero special case: - // if parsing integerOnly, change to +0, which goes into an int32 in a Formattable. - // if not parsing integerOnly, leave as -0, which a double can represent. - if (digits->isZero() && !digits->isPositive() && isParseIntegerOnly()) { - digits->setPositive(TRUE); - } - result.adoptDigitList(digits); - } -} - - - -UBool -DecimalFormat::parseForCurrency(const UnicodeString& text, - ParsePosition& parsePosition, - DigitList& digits, - UBool* status, - UChar* currency) const { - UnicodeString positivePrefix; - UnicodeString positiveSuffix; - UnicodeString negativePrefix; - UnicodeString negativeSuffix; - fImpl->fPositivePrefixPattern.toString(positivePrefix); - fImpl->fPositiveSuffixPattern.toString(positiveSuffix); - fImpl->fNegativePrefixPattern.toString(negativePrefix); - fImpl->fNegativeSuffixPattern.toString(negativeSuffix); - - int origPos = parsePosition.getIndex(); - int maxPosIndex = origPos; - int maxErrorPos = -1; - // First, parse against current pattern. - // Since current pattern could be set by applyPattern(), - // it could be an arbitrary pattern, and it may not be the one - // defined in current locale. - UBool tmpStatus[fgStatusLength]; - ParsePosition tmpPos(origPos); - DigitList tmpDigitList; - UBool found; - if (fStyle == UNUM_CURRENCY_PLURAL) { - found = subparse(text, - &negativePrefix, &negativeSuffix, - &positivePrefix, &positiveSuffix, - TRUE, UCURR_LONG_NAME, - tmpPos, tmpDigitList, tmpStatus, currency); - } else { - found = subparse(text, - &negativePrefix, &negativeSuffix, - &positivePrefix, &positiveSuffix, - TRUE, UCURR_SYMBOL_NAME, - tmpPos, tmpDigitList, tmpStatus, currency); - } - if (found) { - if (tmpPos.getIndex() > maxPosIndex) { - maxPosIndex = tmpPos.getIndex(); - for (int32_t i = 0; i < fgStatusLength; ++i) { - status[i] = tmpStatus[i]; - } - digits = tmpDigitList; - } - } else { - maxErrorPos = tmpPos.getErrorIndex(); - } - // Then, parse against affix patterns. - // Those are currency patterns and currency plural patterns. - int32_t pos = UHASH_FIRST; - const UHashElement* element = NULL; - while ( (element = fAffixPatternsForCurrency->nextElement(pos)) != NULL ) { - const UHashTok valueTok = element->value; - const AffixPatternsForCurrency* affixPtn = (AffixPatternsForCurrency*)valueTok.pointer; - UBool tmpStatus[fgStatusLength]; - ParsePosition tmpPos(origPos); - DigitList tmpDigitList; - -#ifdef FMT_DEBUG - debug("trying affix for currency.."); - affixPtn->dump(); -#endif +DecimalFormat::DecimalFormat(const UnicodeString& pattern, const DecimalFormatSymbols& symbols, + UErrorCode& status) + : DecimalFormat(new DecimalFormatSymbols(symbols), status) { + setPropertiesFromPattern(pattern, IGNORE_ROUNDING_IF_CURRENCY, status); + touch(status); +} - UBool result = subparse(text, - &affixPtn->negPrefixPatternForCurrency, - &affixPtn->negSuffixPatternForCurrency, - &affixPtn->posPrefixPatternForCurrency, - &affixPtn->posSuffixPatternForCurrency, - TRUE, affixPtn->patternType, - tmpPos, tmpDigitList, tmpStatus, currency); - if (result) { - found = true; - if (tmpPos.getIndex() > maxPosIndex) { - maxPosIndex = tmpPos.getIndex(); - for (int32_t i = 0; i < fgStatusLength; ++i) { - status[i] = tmpStatus[i]; - } - digits = tmpDigitList; - } - } else { - maxErrorPos = (tmpPos.getErrorIndex() > maxErrorPos) ? - tmpPos.getErrorIndex() : maxErrorPos; - } +DecimalFormat::DecimalFormat(const DecimalFormat& source) : NumberFormat(source) { + // Note: it is not safe to copy fields->formatter or fWarehouse directly because fields->formatter might have + // dangling pointers to fields inside fWarehouse. The safe thing is to re-construct fields->formatter from + // the property bag, despite being somewhat slower. + fields = new DecimalFormatFields(); + if (fields == nullptr) { + return; } - // Finally, parse against simple affix to find the match. - // For example, in TestMonster suite, - // if the to-be-parsed text is "-\u00A40,00". - // complexAffixCompare will not find match, - // since there is no ISO code matches "\u00A4", - // and the parse stops at "\u00A4". - // We will just use simple affix comparison (look for exact match) - // to pass it. - // - // TODO: We should parse against simple affix first when - // output currency is not requested. After the complex currency - // parsing implementation was introduced, the default currency - // instance parsing slowed down because of the new code flow. - // I filed #10312 - Yoshito - UBool tmpStatus_2[fgStatusLength]; - ParsePosition tmpPos_2(origPos); - DigitList tmpDigitList_2; - - // Disable complex currency parsing and try it again. - UBool result = subparse(text, - &fImpl->fAffixes.fNegativePrefix.getOtherVariant().toString(), - &fImpl->fAffixes.fNegativeSuffix.getOtherVariant().toString(), - &fImpl->fAffixes.fPositivePrefix.getOtherVariant().toString(), - &fImpl->fAffixes.fPositiveSuffix.getOtherVariant().toString(), - FALSE /* disable complex currency parsing */, UCURR_SYMBOL_NAME, - tmpPos_2, tmpDigitList_2, tmpStatus_2, - currency); - if (result) { - if (tmpPos_2.getIndex() > maxPosIndex) { - maxPosIndex = tmpPos_2.getIndex(); - for (int32_t i = 0; i < fgStatusLength; ++i) { - status[i] = tmpStatus_2[i]; - } - digits = tmpDigitList_2; - } - found = true; - } else { - maxErrorPos = (tmpPos_2.getErrorIndex() > maxErrorPos) ? - tmpPos_2.getErrorIndex() : maxErrorPos; + fields->properties.adoptInstead(new DecimalFormatProperties(*source.fields->properties)); + fields->symbols.adoptInstead(new DecimalFormatSymbols(*source.fields->symbols)); + fields->exportedProperties.adoptInstead(new DecimalFormatProperties()); + if (fields->properties == nullptr || fields->symbols == nullptr || fields->exportedProperties == nullptr) { + return; } + touchNoError(); +} - if (!found) { - //parsePosition.setIndex(origPos); - parsePosition.setErrorIndex(maxErrorPos); - } else { - parsePosition.setIndex(maxPosIndex); - parsePosition.setErrorIndex(-1); - } - return found; -} - - -/** - * Parse the given text into a number. The text is parsed beginning at - * parsePosition, until an unparseable character is seen. - * @param text the string to parse. - * @param negPrefix negative prefix. - * @param negSuffix negative suffix. - * @param posPrefix positive prefix. - * @param posSuffix positive suffix. - * @param complexCurrencyParsing whether it is complex currency parsing or not. - * @param type the currency type to parse against, LONG_NAME only or not. - * @param parsePosition The position at which to being parsing. Upon - * return, the first unparsed character. - * @param digits the DigitList to set to the parsed value. - * @param status output param containing boolean status flags indicating - * whether the value was infinite and whether it was positive. - * @param currency return value for parsed currency, for generic - * currency parsing mode, or NULL for normal parsing. In generic - * currency parsing mode, any currency is parsed, not just the - * currency that this formatter is set to. - */ -UBool DecimalFormat::subparse(const UnicodeString& text, - const UnicodeString* negPrefix, - const UnicodeString* negSuffix, - const UnicodeString* posPrefix, - const UnicodeString* posSuffix, - UBool complexCurrencyParsing, - int8_t type, - ParsePosition& parsePosition, - DigitList& digits, UBool* status, - UChar* currency) const -{ - // The parsing process builds up the number as char string, in the neutral format that - // will be acceptable to the decNumber library, then at the end passes that string - // off for conversion to a decNumber. - UErrorCode err = U_ZERO_ERROR; - CharString parsedNum; - digits.setToZero(); - - int32_t position = parsePosition.getIndex(); - int32_t oldStart = position; - int32_t textLength = text.length(); // One less pointer to follow - UBool strictParse = !isLenient(); - UChar32 zero = fImpl->getConstSymbol(DecimalFormatSymbols::kZeroDigitSymbol).char32At(0); - const UnicodeString *groupingString = &fImpl->getConstSymbol( - !fImpl->fMonetary ? - DecimalFormatSymbols::kGroupingSeparatorSymbol : DecimalFormatSymbols::kMonetaryGroupingSeparatorSymbol); - UChar32 groupingChar = groupingString->char32At(0); - int32_t groupingStringLength = groupingString->length(); - int32_t groupingCharLength = U16_LENGTH(groupingChar); - UBool groupingUsed = isGroupingUsed(); -#ifdef FMT_DEBUG - UChar dbgbuf[300]; - UnicodeString s(dbgbuf,0,300);; - s.append((UnicodeString)"PARSE \"").append(text.tempSubString(position)).append((UnicodeString)"\" " ); -#define DBGAPPD(x) if(x) { s.append(UnicodeString(#x "=")); if(x->isEmpty()) { s.append(UnicodeString("")); } else { s.append(*x); } s.append(UnicodeString(" ")); } else { s.append(UnicodeString(#x "=NULL ")); } - DBGAPPD(negPrefix); - DBGAPPD(negSuffix); - DBGAPPD(posPrefix); - DBGAPPD(posSuffix); - debugout(s); -#endif - - UBool fastParseOk = false; /* TRUE iff fast parse is OK */ - // UBool fastParseHadDecimal = FALSE; /* true if fast parse saw a decimal point. */ - if((fImpl->isParseFastpath()) && !fImpl->fMonetary && - text.length()>0 && - text.length()<32 && - (posPrefix==NULL||posPrefix->isEmpty()) && - (posSuffix==NULL||posSuffix->isEmpty()) && - // (negPrefix==NULL||negPrefix->isEmpty()) && - // (negSuffix==NULL||(negSuffix->isEmpty()) ) && - TRUE) { // optimized path - int j=position; - int l=text.length(); - int digitCount=0; - UChar32 ch = text.char32At(j); - const UnicodeString *decimalString = &fImpl->getConstSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol); - UChar32 decimalChar = 0; - UBool intOnly = FALSE; - UChar32 lookForGroup = (groupingUsed&&intOnly&&strictParse)?groupingChar:0; - - int32_t decimalCount = decimalString->countChar32(0,3); - if(isParseIntegerOnly()) { - decimalChar = 0; // not allowed - intOnly = TRUE; // Don't look for decimals. - } else if(decimalCount==1) { - decimalChar = decimalString->char32At(0); // Look for this decimal - } else if(decimalCount==0) { - decimalChar=0; // NO decimal set - } else { - j=l+1;//Set counter to end of line, so that we break. Unknown decimal situation. - } - -#ifdef FMT_DEBUG - printf("Preparing to do fastpath parse: decimalChar=U+%04X, groupingChar=U+%04X, first ch=U+%04X intOnly=%c strictParse=%c\n", - decimalChar, groupingChar, ch, - (intOnly)?'y':'n', - (strictParse)?'y':'n'); -#endif - if(ch==0x002D) { // '-' - j=l+1;//=break - negative number. - - /* - parsedNum.append('-',err); - j+=U16_LENGTH(ch); - if(j=0 && digit <= 9) { - parsedNum.append((char)(digit + '0'), err); - if((digitCount>0) || digit!=0 || j==(l-1)) { - digitCount++; - } - } else if(ch == 0) { // break out - digitCount=-1; - break; - } else if(ch == decimalChar) { - parsedNum.append((char)('.'), err); - decimalChar=0; // no more decimals. - // fastParseHadDecimal=TRUE; - } else if(ch == lookForGroup) { - // ignore grouping char. No decimals, so it has to be an ignorable grouping sep - } else if(intOnly && (lookForGroup!=0) && !u_isdigit(ch)) { - // parsing integer only and can fall through - } else { - digitCount=-1; // fail - fall through to slow parse - break; - } - j+=U16_LENGTH(ch); - ch = text.char32At(j); // for next - } - if( - ((j==l)||intOnly) // end OR only parsing integer - && (digitCount>0)) { // and have at least one digit - fastParseOk=true; // Fast parse OK! - -#ifdef SKIP_OPT - debug("SKIP_OPT"); - /* for testing, try it the slow way. also */ - fastParseOk=false; - parsedNum.clear(); -#else - parsePosition.setIndex(position=j); - status[fgStatusInfinite]=false; -#endif - } else { - // was not OK. reset, retry -#ifdef FMT_DEBUG - printf("Fall through: j=%d, l=%d, digitCount=%d\n", j, l, digitCount); -#endif - parsedNum.clear(); - } - } else { -#ifdef FMT_DEBUG - printf("Could not fastpath parse. "); - printf("text.length()=%d ", text.length()); - printf("posPrefix=%p posSuffix=%p ", posPrefix, posSuffix); +DecimalFormat& DecimalFormat::operator=(const DecimalFormat& rhs) { + *fields->properties = *rhs.fields->properties; + fields->exportedProperties->clear(); + fields->symbols.adoptInstead(new DecimalFormatSymbols(*rhs.fields->symbols)); + touchNoError(); + return *this; +} - printf("\n"); -#endif - } +DecimalFormat::~DecimalFormat() { + delete fields->atomicParser.exchange(nullptr); + delete fields->atomicCurrencyParser.exchange(nullptr); + delete fields; +} - UnicodeString formatPattern; - toPattern(formatPattern); +Format* DecimalFormat::clone() const { + return new DecimalFormat(*this); +} - if(!fastParseOk -#if UCONFIG_HAVE_PARSEALLINPUT - && fParseAllInput!=UNUM_YES -#endif - ) - { - int32_t formatWidth = fImpl->getOldFormatWidth(); - // Match padding before prefix - if (formatWidth > 0 && fImpl->fAffixes.fPadPosition == DigitAffixesAndPadding::kPadBeforePrefix) { - position = skipPadding(text, position); - } - - // Match positive and negative prefixes; prefer longest match. - int32_t posMatch = compareAffix(text, position, FALSE, TRUE, posPrefix, complexCurrencyParsing, type, currency); - int32_t negMatch = compareAffix(text, position, TRUE, TRUE, negPrefix, complexCurrencyParsing, type, currency); - if (posMatch >= 0 && negMatch >= 0) { - if (posMatch > negMatch) { - negMatch = -1; - } else if (negMatch > posMatch) { - posMatch = -1; - } - } - if (posMatch >= 0) { - position += posMatch; - parsedNum.append('+', err); - } else if (negMatch >= 0) { - position += negMatch; - parsedNum.append('-', err); - } else if (strictParse){ - parsePosition.setErrorIndex(position); - return FALSE; - } else { - // Temporary set positive. This might be changed after checking suffix - parsedNum.append('+', err); +UBool DecimalFormat::operator==(const Format& other) const { + auto* otherDF = dynamic_cast(&other); + if (otherDF == nullptr) { + return false; } + return *fields->properties == *otherDF->fields->properties && *fields->symbols == *otherDF->fields->symbols; +} - // Match padding before prefix - if (formatWidth > 0 && fImpl->fAffixes.fPadPosition == DigitAffixesAndPadding::kPadAfterPrefix) { - position = skipPadding(text, position); +UnicodeString& DecimalFormat::format(double number, UnicodeString& appendTo, FieldPosition& pos) const { + if (pos.getField() == FieldPosition::DONT_CARE && fastFormatDouble(number, appendTo)) { + return appendTo; } + UErrorCode localStatus = U_ZERO_ERROR; + FormattedNumber output = fields->formatter->formatDouble(number, localStatus); + fieldPositionHelper(output, pos, appendTo.length(), localStatus); + auto appendable = UnicodeStringAppendable(appendTo); + output.appendTo(appendable); + return appendTo; +} - if (! strictParse) { - position = skipUWhiteSpace(text, position); +UnicodeString& DecimalFormat::format(double number, UnicodeString& appendTo, FieldPosition& pos, + UErrorCode& status) const { + if (pos.getField() == FieldPosition::DONT_CARE && fastFormatDouble(number, appendTo)) { + return appendTo; } + FormattedNumber output = fields->formatter->formatDouble(number, status); + fieldPositionHelper(output, pos, appendTo.length(), status); + auto appendable = UnicodeStringAppendable(appendTo); + output.appendTo(appendable); + return appendTo; +} - // process digits or Inf, find decimal position - const UnicodeString *inf = &fImpl->getConstSymbol(DecimalFormatSymbols::kInfinitySymbol); - int32_t infLen = (text.compare(position, inf->length(), *inf) - ? 0 : inf->length()); - position += infLen; // infLen is non-zero when it does equal to infinity - status[fgStatusInfinite] = infLen != 0; - - if (infLen != 0) { - parsedNum.append("Infinity", err); - } else { - // We now have a string of digits, possibly with grouping symbols, - // and decimal points. We want to process these into a DigitList. - // We don't want to put a bunch of leading zeros into the DigitList - // though, so we keep track of the location of the decimal point, - // put only significant digits into the DigitList, and adjust the - // exponent as needed. - - - UBool strictFail = FALSE; // did we exit with a strict parse failure? - int32_t lastGroup = -1; // after which digit index did we last see a grouping separator? - int32_t currGroup = -1; // for temporary storage the digit index of the current grouping separator - int32_t gs2 = fImpl->fEffGrouping.fGrouping2 == 0 ? fImpl->fEffGrouping.fGrouping : fImpl->fEffGrouping.fGrouping2; - - const UnicodeString *decimalString; - if (fImpl->fMonetary) { - decimalString = &fImpl->getConstSymbol(DecimalFormatSymbols::kMonetarySeparatorSymbol); - } else { - decimalString = &fImpl->getConstSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol); - } - UChar32 decimalChar = decimalString->char32At(0); - int32_t decimalStringLength = decimalString->length(); - int32_t decimalCharLength = U16_LENGTH(decimalChar); - - UBool sawDecimal = FALSE; - UChar32 sawDecimalChar = 0xFFFF; - UBool sawGrouping = FALSE; - UChar32 sawGroupingChar = 0xFFFF; - UBool sawDigit = FALSE; - int32_t backup = -1; - int32_t digit; - - // equivalent grouping and decimal support - const UnicodeSet *decimalSet = NULL; - const UnicodeSet *groupingSet = NULL; - - if (decimalCharLength == decimalStringLength) { - decimalSet = DecimalFormatStaticSets::getSimilarDecimals(decimalChar, strictParse); - } - - if (groupingCharLength == groupingStringLength) { - if (strictParse) { - groupingSet = fStaticSets->fStrictDefaultGroupingSeparators; - } else { - groupingSet = fStaticSets->fDefaultGroupingSeparators; - } - } - - // We need to test groupingChar and decimalChar separately from groupingSet and decimalSet, if the sets are even initialized. - // If sawDecimal is TRUE, only consider sawDecimalChar and NOT decimalSet - // If a character matches decimalSet, don't consider it to be a member of the groupingSet. - - // We have to track digitCount ourselves, because digits.fCount will - // pin when the maximum allowable digits is reached. - int32_t digitCount = 0; - int32_t integerDigitCount = 0; - - for (; position < textLength; ) - { - UChar32 ch = text.char32At(position); - - /* We recognize all digit ranges, not only the Latin digit range - * '0'..'9'. We do so by using the Character.digit() method, - * which converts a valid Unicode digit to the range 0..9. - * - * The character 'ch' may be a digit. If so, place its value - * from 0 to 9 in 'digit'. First try using the locale digit, - * which may or MAY NOT be a standard Unicode digit range. If - * this fails, try using the standard Unicode digit ranges by - * calling Character.digit(). If this also fails, digit will - * have a value outside the range 0..9. - */ - digit = ch - zero; - if (digit < 0 || digit > 9) - { - digit = u_charDigitValue(ch); - } - - // As a last resort, look through the localized digits if the zero digit - // is not a "standard" Unicode digit. - if ( (digit < 0 || digit > 9) && u_charDigitValue(zero) != 0) { - digit = 0; - if ( fImpl->getConstSymbol((DecimalFormatSymbols::ENumberFormatSymbol)(DecimalFormatSymbols::kZeroDigitSymbol)).char32At(0) == ch ) { - break; - } - for (digit = 1 ; digit < 10 ; digit++ ) { - if ( fImpl->getConstSymbol((DecimalFormatSymbols::ENumberFormatSymbol)(DecimalFormatSymbols::kOneDigitSymbol+digit-1)).char32At(0) == ch ) { - break; - } - } - } - - if (digit >= 0 && digit <= 9) - { - if (strictParse && backup != -1) { - // comma followed by digit, so group before comma is a - // secondary group. If there was a group separator - // before that, the group must == the secondary group - // length, else it can be <= the the secondary group - // length. - if ((lastGroup != -1 && currGroup - lastGroup != gs2) || - (lastGroup == -1 && digitCount - 1 > gs2)) { - strictFail = TRUE; - break; - } - - lastGroup = currGroup; - } - - // Cancel out backup setting (see grouping handler below) - currGroup = -1; - backup = -1; - sawDigit = TRUE; - - // Note: this will append leading zeros - parsedNum.append((char)(digit + '0'), err); - - // count any digit that's not a leading zero - if (digit > 0 || digitCount > 0 || sawDecimal) { - digitCount += 1; - - // count any integer digit that's not a leading zero - if (! sawDecimal) { - integerDigitCount += 1; - } - } - - position += U16_LENGTH(ch); - } - else if (groupingStringLength > 0 && - matchGrouping(groupingChar, sawGrouping, sawGroupingChar, groupingSet, - decimalChar, decimalSet, - ch) && groupingUsed) - { - if (sawDecimal) { - break; - } - - if (strictParse) { - if ((!sawDigit || backup != -1)) { - // leading group, or two group separators in a row - strictFail = TRUE; - break; - } - } - - // Ignore grouping characters, if we are using them, but require - // that they be followed by a digit. Otherwise we backup and - // reprocess them. - currGroup = digitCount; - backup = position; - position += groupingStringLength; - sawGrouping=TRUE; - // Once we see a grouping character, we only accept that grouping character from then on. - sawGroupingChar=ch; - } - else if (matchDecimal(decimalChar,sawDecimal,sawDecimalChar, decimalSet, ch)) - { - if (strictParse) { - if (backup != -1 || - (lastGroup != -1 && digitCount - lastGroup != fImpl->fEffGrouping.fGrouping)) { - strictFail = TRUE; - break; - } - } - - // If we're only parsing integers, or if we ALREADY saw the - // decimal, then don't parse this one. - if (isParseIntegerOnly() || sawDecimal) { - break; - } - - parsedNum.append('.', err); - position += decimalStringLength; - sawDecimal = TRUE; - // Once we see a decimal character, we only accept that decimal character from then on. - sawDecimalChar=ch; - // decimalSet is considered to consist of (ch,ch) - } - else { - - if(!fBoolFlags.contains(UNUM_PARSE_NO_EXPONENT) || // don't parse if this is set unless.. - isScientificNotation()) { // .. it's an exponent format - ignore setting and parse anyways - const UnicodeString *tmp; - tmp = &fImpl->getConstSymbol(DecimalFormatSymbols::kExponentialSymbol); - // TODO: CASE - if (!text.caseCompare(position, tmp->length(), *tmp, U_FOLD_CASE_DEFAULT)) // error code is set below if !sawDigit - { - // Parse sign, if present - int32_t pos = position + tmp->length(); - char exponentSign = '+'; - - if (pos < textLength) - { - tmp = &fImpl->getConstSymbol(DecimalFormatSymbols::kPlusSignSymbol); - if (!text.compare(pos, tmp->length(), *tmp)) - { - pos += tmp->length(); - } - else { - tmp = &fImpl->getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol); - if (!text.compare(pos, tmp->length(), *tmp)) - { - exponentSign = '-'; - pos += tmp->length(); - } - } - } - - UBool sawExponentDigit = FALSE; - while (pos < textLength) { - ch = text.char32At(pos); - digit = ch - zero; - - if (digit < 0 || digit > 9) { - digit = u_charDigitValue(ch); - } - if (0 <= digit && digit <= 9) { - if (!sawExponentDigit) { - parsedNum.append('E', err); - parsedNum.append(exponentSign, err); - sawExponentDigit = TRUE; - } - pos += U16_LENGTH(ch); - parsedNum.append((char)(digit + '0'), err); - } else { - break; - } - } - - if (sawExponentDigit) { - position = pos; // Advance past the exponent - } - - break; // Whether we fail or succeed, we exit this loop - } else { - break; - } - } else { // not parsing exponent - break; - } - } - } - - // if we didn't see a decimal and it is required, check to see if the pattern had one - if(!sawDecimal && isDecimalPatternMatchRequired()) - { - if(formatPattern.indexOf(kPatternDecimalSeparator) != -1) - { - parsePosition.setIndex(oldStart); - parsePosition.setErrorIndex(position); - debug("decimal point match required fail!"); - return FALSE; - } - } - - if (backup != -1) - { - position = backup; - } - - if (strictParse && !sawDecimal) { - if (lastGroup != -1 && digitCount - lastGroup != fImpl->fEffGrouping.fGrouping) { - strictFail = TRUE; - } - } +UnicodeString& +DecimalFormat::format(double number, UnicodeString& appendTo, FieldPositionIterator* posIter, + UErrorCode& status) const { + if (posIter == nullptr && fastFormatDouble(number, appendTo)) { + return appendTo; + } + FormattedNumber output = fields->formatter->formatDouble(number, status); + fieldPositionIteratorHelper(output, posIter, appendTo.length(), status); + auto appendable = UnicodeStringAppendable(appendTo); + output.appendTo(appendable); + return appendTo; +} - if (strictFail) { - // only set with strictParse and a grouping separator error +UnicodeString& DecimalFormat::format(int32_t number, UnicodeString& appendTo, FieldPosition& pos) const { + return format(static_cast (number), appendTo, pos); +} - parsePosition.setIndex(oldStart); - parsePosition.setErrorIndex(position); - debug("strictFail!"); - return FALSE; - } +UnicodeString& DecimalFormat::format(int32_t number, UnicodeString& appendTo, FieldPosition& pos, + UErrorCode& status) const { + return format(static_cast (number), appendTo, pos, status); +} - // If there was no decimal point we have an integer +UnicodeString& +DecimalFormat::format(int32_t number, UnicodeString& appendTo, FieldPositionIterator* posIter, + UErrorCode& status) const { + return format(static_cast (number), appendTo, posIter, status); +} - // If none of the text string was recognized. For example, parse - // "x" with pattern "#0.00" (return index and error index both 0) - // parse "$" with pattern "$#0.00". (return index 0 and error index - // 1). - if (!sawDigit && digitCount == 0) { -#ifdef FMT_DEBUG - debug("none of text rec"); - printf("position=%d\n",position); -#endif - parsePosition.setIndex(oldStart); - parsePosition.setErrorIndex(oldStart); - return FALSE; - } +UnicodeString& DecimalFormat::format(int64_t number, UnicodeString& appendTo, FieldPosition& pos) const { + if (pos.getField() == FieldPosition::DONT_CARE && fastFormatInt64(number, appendTo)) { + return appendTo; } + UErrorCode localStatus = U_ZERO_ERROR; + FormattedNumber output = fields->formatter->formatInt(number, localStatus); + fieldPositionHelper(output, pos, appendTo.length(), localStatus); + auto appendable = UnicodeStringAppendable(appendTo); + output.appendTo(appendable); + return appendTo; +} - // Match padding before suffix - if (formatWidth > 0 && fImpl->fAffixes.fPadPosition == DigitAffixesAndPadding::kPadBeforeSuffix) { - position = skipPadding(text, position); +UnicodeString& DecimalFormat::format(int64_t number, UnicodeString& appendTo, FieldPosition& pos, + UErrorCode& status) const { + if (pos.getField() == FieldPosition::DONT_CARE && fastFormatInt64(number, appendTo)) { + return appendTo; } + FormattedNumber output = fields->formatter->formatInt(number, status); + fieldPositionHelper(output, pos, appendTo.length(), status); + auto appendable = UnicodeStringAppendable(appendTo); + output.appendTo(appendable); + return appendTo; +} - int32_t posSuffixMatch = -1, negSuffixMatch = -1; - - // Match positive and negative suffixes; prefer longest match. - if (posMatch >= 0 || (!strictParse && negMatch < 0)) { - posSuffixMatch = compareAffix(text, position, FALSE, FALSE, posSuffix, complexCurrencyParsing, type, currency); - } - if (negMatch >= 0) { - negSuffixMatch = compareAffix(text, position, TRUE, FALSE, negSuffix, complexCurrencyParsing, type, currency); - } - if (posSuffixMatch >= 0 && negSuffixMatch >= 0) { - if (posSuffixMatch > negSuffixMatch) { - negSuffixMatch = -1; - } else if (negSuffixMatch > posSuffixMatch) { - posSuffixMatch = -1; - } +UnicodeString& +DecimalFormat::format(int64_t number, UnicodeString& appendTo, FieldPositionIterator* posIter, + UErrorCode& status) const { + if (posIter == nullptr && fastFormatInt64(number, appendTo)) { + return appendTo; } + FormattedNumber output = fields->formatter->formatInt(number, status); + fieldPositionIteratorHelper(output, posIter, appendTo.length(), status); + auto appendable = UnicodeStringAppendable(appendTo); + output.appendTo(appendable); + return appendTo; +} - // Fail if neither or both - if (strictParse && ((posSuffixMatch >= 0) == (negSuffixMatch >= 0))) { - parsePosition.setErrorIndex(position); - debug("neither or both"); - return FALSE; - } +UnicodeString& +DecimalFormat::format(StringPiece number, UnicodeString& appendTo, FieldPositionIterator* posIter, + UErrorCode& status) const { + FormattedNumber output = fields->formatter->formatDecimal(number, status); + fieldPositionIteratorHelper(output, posIter, appendTo.length(), status); + auto appendable = UnicodeStringAppendable(appendTo); + output.appendTo(appendable); + return appendTo; +} - position += (posSuffixMatch >= 0 ? posSuffixMatch : (negSuffixMatch >= 0 ? negSuffixMatch : 0)); +UnicodeString& DecimalFormat::format(const DecimalQuantity& number, UnicodeString& appendTo, + FieldPositionIterator* posIter, UErrorCode& status) const { + FormattedNumber output = fields->formatter->formatDecimalQuantity(number, status); + fieldPositionIteratorHelper(output, posIter, appendTo.length(), status); + auto appendable = UnicodeStringAppendable(appendTo); + output.appendTo(appendable); + return appendTo; +} - // Match padding before suffix - if (formatWidth > 0 && fImpl->fAffixes.fPadPosition == DigitAffixesAndPadding::kPadAfterSuffix) { - position = skipPadding(text, position); +UnicodeString& +DecimalFormat::format(const DecimalQuantity& number, UnicodeString& appendTo, FieldPosition& pos, + UErrorCode& status) const { + FormattedNumber output = fields->formatter->formatDecimalQuantity(number, status); + fieldPositionHelper(output, pos, appendTo.length(), status); + auto appendable = UnicodeStringAppendable(appendTo); + output.appendTo(appendable); + return appendTo; +} + +void DecimalFormat::parse(const UnicodeString& text, Formattable& output, + ParsePosition& parsePosition) const { + if (parsePosition.getIndex() < 0 || parsePosition.getIndex() >= text.length()) { + return; } - parsePosition.setIndex(position); - - parsedNum.data()[0] = (posSuffixMatch >= 0 || (!strictParse && negMatch < 0 && negSuffixMatch < 0)) ? '+' : '-'; -#ifdef FMT_DEBUG -printf("PP -> %d, SLOW = [%s]! pp=%d, os=%d, err=%s\n", position, parsedNum.data(), parsePosition.getIndex(),oldStart,u_errorName(err)); -#endif - } /* end SLOW parse */ - if(parsePosition.getIndex() == oldStart) - { -#ifdef FMT_DEBUG - printf(" PP didnt move, err\n"); -#endif - parsePosition.setErrorIndex(position); - return FALSE; - } -#if UCONFIG_HAVE_PARSEALLINPUT - else if (fParseAllInput==UNUM_YES&&parsePosition.getIndex()!=textLength) - { -#ifdef FMT_DEBUG - printf(" PP didnt consume all (UNUM_YES), err\n"); -#endif - parsePosition.setErrorIndex(position); - return FALSE; - } -#endif - // uint32_t bits = (fastParseOk?kFastpathOk:0) | - // (fastParseHadDecimal?0:kNoDecimal); - //printf("FPOK=%d, FPHD=%d, bits=%08X\n", fastParseOk, fastParseHadDecimal, bits); - digits.set(parsedNum.toStringPiece(), - err, - 0//bits - ); - - if (U_FAILURE(err)) { -#ifdef FMT_DEBUG - printf(" err setting %s\n", u_errorName(err)); -#endif - parsePosition.setErrorIndex(position); - return FALSE; - } - - // check if we missed a required decimal point - if(fastParseOk && isDecimalPatternMatchRequired()) - { - if(formatPattern.indexOf(kPatternDecimalSeparator) != -1) - { - parsePosition.setIndex(oldStart); - parsePosition.setErrorIndex(position); - debug("decimal point match required fail!"); - return FALSE; - } + ErrorCode status; + ParsedNumber result; + // Note: if this is a currency instance, currencies will be matched despite the fact that we are not in the + // parseCurrency method (backwards compatibility) + int32_t startIndex = parsePosition.getIndex(); + const NumberParserImpl* parser = getParser(status); + if (U_FAILURE(status)) { return; } + parser->parse(text, startIndex, true, result, status); + // TODO: Do we need to check for fImpl->properties->parseAllInput (UCONFIG_HAVE_PARSEALLINPUT) here? + if (result.success()) { + parsePosition.setIndex(result.charEnd); + result.populateFormattable(output, parser->getParseFlags()); + } else { + parsePosition.setErrorIndex(startIndex + result.charEnd); } +} - - return TRUE; -} - -/** - * Starting at position, advance past a run of pad characters, if any. - * Return the index of the first character after position that is not a pad - * character. Result is >= position. - */ -int32_t DecimalFormat::skipPadding(const UnicodeString& text, int32_t position) const { - int32_t padLen = U16_LENGTH(fImpl->fAffixes.fPadChar); - while (position < text.length() && - text.char32At(position) == fImpl->fAffixes.fPadChar) { - position += padLen; - } - return position; -} - -/** - * Return the length matched by the given affix, or -1 if none. - * Runs of white space in the affix, match runs of white space in - * the input. Pattern white space and input white space are - * determined differently; see code. - * @param text input text - * @param pos offset into input at which to begin matching - * @param isNegative - * @param isPrefix - * @param affixPat affix pattern used for currency affix comparison. - * @param complexCurrencyParsing whether it is currency parsing or not - * @param type the currency type to parse against, LONG_NAME only or not. - * @param currency return value for parsed currency, for generic - * currency parsing mode, or null for normal parsing. In generic - * currency parsing mode, any currency is parsed, not just the - * currency that this formatter is set to. - * @return length of input that matches, or -1 if match failure - */ -int32_t DecimalFormat::compareAffix(const UnicodeString& text, - int32_t pos, - UBool isNegative, - UBool isPrefix, - const UnicodeString* affixPat, - UBool complexCurrencyParsing, - int8_t type, - UChar* currency) const -{ - const UnicodeString *patternToCompare; - if (currency != NULL || - (fImpl->fMonetary && complexCurrencyParsing)) { - - if (affixPat != NULL) { - return compareComplexAffix(*affixPat, text, pos, type, currency); - } +CurrencyAmount* DecimalFormat::parseCurrency(const UnicodeString& text, ParsePosition& parsePosition) const { + if (parsePosition.getIndex() < 0 || parsePosition.getIndex() >= text.length()) { + return nullptr; } - if (isNegative) { - if (isPrefix) { - patternToCompare = &fImpl->fAffixes.fNegativePrefix.getOtherVariant().toString(); - } - else { - patternToCompare = &fImpl->fAffixes.fNegativeSuffix.getOtherVariant().toString(); - } - } - else { - if (isPrefix) { - patternToCompare = &fImpl->fAffixes.fPositivePrefix.getOtherVariant().toString(); - } - else { - patternToCompare = &fImpl->fAffixes.fPositiveSuffix.getOtherVariant().toString(); - } + ErrorCode status; + ParsedNumber result; + // Note: if this is a currency instance, currencies will be matched despite the fact that we are not in the + // parseCurrency method (backwards compatibility) + int32_t startIndex = parsePosition.getIndex(); + const NumberParserImpl* parser = getCurrencyParser(status); + if (U_FAILURE(status)) { return nullptr; } + parser->parse(text, startIndex, true, result, status); + // TODO: Do we need to check for fImpl->properties->parseAllInput (UCONFIG_HAVE_PARSEALLINPUT) here? + if (result.success()) { + parsePosition.setIndex(result.charEnd); + Formattable formattable; + result.populateFormattable(formattable, parser->getParseFlags()); + return new CurrencyAmount(formattable, result.currencyCode, status); + } else { + parsePosition.setErrorIndex(startIndex + result.charEnd); + return nullptr; } - return compareSimpleAffix(*patternToCompare, text, pos, isLenient()); } -UBool DecimalFormat::equalWithSignCompatibility(UChar32 lhs, UChar32 rhs) const { - if (lhs == rhs) { - return TRUE; - } - U_ASSERT(fStaticSets != NULL); // should already be loaded - const UnicodeSet *minusSigns = fStaticSets->fMinusSigns; - const UnicodeSet *plusSigns = fStaticSets->fPlusSigns; - return (minusSigns->contains(lhs) && minusSigns->contains(rhs)) || - (plusSigns->contains(lhs) && plusSigns->contains(rhs)); +const DecimalFormatSymbols* DecimalFormat::getDecimalFormatSymbols(void) const { + return fields->symbols.getAlias(); } -// check for LRM 0x200E, RLM 0x200F, ALM 0x061C -#define IS_BIDI_MARK(c) (c==0x200E || c==0x200F || c==0x061C) +void DecimalFormat::adoptDecimalFormatSymbols(DecimalFormatSymbols* symbolsToAdopt) { + if (symbolsToAdopt == nullptr) { + return; // do not allow caller to set fields->symbols to NULL + } + fields->symbols.adoptInstead(symbolsToAdopt); + touchNoError(); +} -#define TRIM_BUFLEN 32 -UnicodeString& DecimalFormat::trimMarksFromAffix(const UnicodeString& affix, UnicodeString& trimmedAffix) { - UChar trimBuf[TRIM_BUFLEN]; - int32_t affixLen = affix.length(); - int32_t affixPos, trimLen = 0; +void DecimalFormat::setDecimalFormatSymbols(const DecimalFormatSymbols& symbols) { + fields->symbols.adoptInstead(new DecimalFormatSymbols(symbols)); + touchNoError(); +} - for (affixPos = 0; affixPos < affixLen; affixPos++) { - UChar c = affix.charAt(affixPos); - if (!IS_BIDI_MARK(c)) { - if (trimLen < TRIM_BUFLEN) { - trimBuf[trimLen++] = c; - } else { - trimLen = 0; - break; - } - } - } - return (trimLen > 0)? trimmedAffix.setTo(trimBuf, trimLen): trimmedAffix.setTo(affix); -} - -/** - * Return the length matched by the given affix, or -1 if none. - * Runs of white space in the affix, match runs of white space in - * the input. Pattern white space and input white space are - * determined differently; see code. - * @param affix pattern string, taken as a literal - * @param input input text - * @param pos offset into input at which to begin matching - * @return length of input that matches, or -1 if match failure - */ -int32_t DecimalFormat::compareSimpleAffix(const UnicodeString& affix, - const UnicodeString& input, - int32_t pos, - UBool lenient) const { - int32_t start = pos; - UnicodeString trimmedAffix; - // For more efficiency we should keep lazily-created trimmed affixes around in - // instance variables instead of trimming each time they are used (the next step) - trimMarksFromAffix(affix, trimmedAffix); - UChar32 affixChar = trimmedAffix.char32At(0); - int32_t affixLength = trimmedAffix.length(); - int32_t inputLength = input.length(); - int32_t affixCharLength = U16_LENGTH(affixChar); - UnicodeSet *affixSet; - UErrorCode status = U_ZERO_ERROR; - - U_ASSERT(fStaticSets != NULL); // should already be loaded +const CurrencyPluralInfo* DecimalFormat::getCurrencyPluralInfo(void) const { + return fields->properties->currencyPluralInfo.fPtr.getAlias(); +} - if (U_FAILURE(status)) { - return -1; - } - if (!lenient) { - affixSet = fStaticSets->fStrictDashEquivalents; - - // If the trimmedAffix is exactly one character long and that character - // is in the dash set and the very next input character is also - // in the dash set, return a match. - if (affixCharLength == affixLength && affixSet->contains(affixChar)) { - UChar32 ic = input.char32At(pos); - if (affixSet->contains(ic)) { - pos += U16_LENGTH(ic); - pos = skipBidiMarks(input, pos); // skip any trailing bidi marks - return pos - start; - } - } +void DecimalFormat::adoptCurrencyPluralInfo(CurrencyPluralInfo* toAdopt) { + fields->properties->currencyPluralInfo.fPtr.adoptInstead(toAdopt); + touchNoError(); +} - for (int32_t i = 0; i < affixLength; ) { - UChar32 c = trimmedAffix.char32At(i); - int32_t len = U16_LENGTH(c); - if (PatternProps::isWhiteSpace(c)) { - // We may have a pattern like: \u200F \u0020 - // and input text like: \u200F \u0020 - // Note that U+200F and U+0020 are Pattern_White_Space but only - // U+0020 is UWhiteSpace. So we have to first do a direct - // match of the run of Pattern_White_Space in the pattern, - // then match any extra characters. - UBool literalMatch = FALSE; - while (pos < inputLength) { - UChar32 ic = input.char32At(pos); - if (ic == c) { - literalMatch = TRUE; - i += len; - pos += len; - if (i == affixLength) { - break; - } - c = trimmedAffix.char32At(i); - len = U16_LENGTH(c); - if (!PatternProps::isWhiteSpace(c)) { - break; - } - } else if (IS_BIDI_MARK(ic)) { - pos ++; // just skip over this input text - } else { - break; - } - } - - // Advance over run in pattern - i = skipPatternWhiteSpace(trimmedAffix, i); - - // Advance over run in input text - // Must see at least one white space char in input, - // unless we've already matched some characters literally. - int32_t s = pos; - pos = skipUWhiteSpace(input, pos); - if (pos == s && !literalMatch) { - return -1; - } - - // If we skip UWhiteSpace in the input text, we need to skip it in the pattern. - // Otherwise, the previous lines may have skipped over text (such as U+00A0) that - // is also in the trimmedAffix. - i = skipUWhiteSpace(trimmedAffix, i); - } else { - UBool match = FALSE; - while (pos < inputLength) { - UChar32 ic = input.char32At(pos); - if (!match && ic == c) { - i += len; - pos += len; - match = TRUE; - } else if (IS_BIDI_MARK(ic)) { - pos++; // just skip over this input text - } else { - break; - } - } - if (!match) { - return -1; - } - } - } +void DecimalFormat::setCurrencyPluralInfo(const CurrencyPluralInfo& info) { + if (fields->properties->currencyPluralInfo.fPtr.isNull()) { + fields->properties->currencyPluralInfo.fPtr.adoptInstead(info.clone()); } else { - UBool match = FALSE; + *fields->properties->currencyPluralInfo.fPtr = info; // copy-assignment operator + } + touchNoError(); +} - affixSet = fStaticSets->fDashEquivalents; +UnicodeString& DecimalFormat::getPositivePrefix(UnicodeString& result) const { + ErrorCode localStatus; + fields->formatter->getAffixImpl(true, false, result, localStatus); + return result; +} - if (affixCharLength == affixLength && affixSet->contains(affixChar)) { - pos = skipUWhiteSpaceAndMarks(input, pos); - UChar32 ic = input.char32At(pos); +void DecimalFormat::setPositivePrefix(const UnicodeString& newValue) { + if (newValue == fields->properties->positivePrefix) { return; } + fields->properties->positivePrefix = newValue; + touchNoError(); +} - if (affixSet->contains(ic)) { - pos += U16_LENGTH(ic); - pos = skipBidiMarks(input, pos); - return pos - start; - } - } +UnicodeString& DecimalFormat::getNegativePrefix(UnicodeString& result) const { + ErrorCode localStatus; + fields->formatter->getAffixImpl(true, true, result, localStatus); + return result; +} - for (int32_t i = 0; i < affixLength; ) - { - //i = skipRuleWhiteSpace(trimmedAffix, i); - i = skipUWhiteSpace(trimmedAffix, i); - pos = skipUWhiteSpaceAndMarks(input, pos); +void DecimalFormat::setNegativePrefix(const UnicodeString& newValue) { + if (newValue == fields->properties->negativePrefix) { return; } + fields->properties->negativePrefix = newValue; + touchNoError(); +} - if (i >= affixLength || pos >= inputLength) { - break; - } +UnicodeString& DecimalFormat::getPositiveSuffix(UnicodeString& result) const { + ErrorCode localStatus; + fields->formatter->getAffixImpl(false, false, result, localStatus); + return result; +} - UChar32 c = trimmedAffix.char32At(i); - UChar32 ic = input.char32At(pos); +void DecimalFormat::setPositiveSuffix(const UnicodeString& newValue) { + if (newValue == fields->properties->positiveSuffix) { return; } + fields->properties->positiveSuffix = newValue; + touchNoError(); +} - if (!equalWithSignCompatibility(ic, c)) { - return -1; - } +UnicodeString& DecimalFormat::getNegativeSuffix(UnicodeString& result) const { + ErrorCode localStatus; + fields->formatter->getAffixImpl(false, true, result, localStatus); + return result; +} - match = TRUE; - i += U16_LENGTH(c); - pos += U16_LENGTH(ic); - pos = skipBidiMarks(input, pos); - } +void DecimalFormat::setNegativeSuffix(const UnicodeString& newValue) { + if (newValue == fields->properties->negativeSuffix) { return; } + fields->properties->negativeSuffix = newValue; + touchNoError(); +} - if (affixLength > 0 && ! match) { - return -1; - } - } - return pos - start; +UBool DecimalFormat::isSignAlwaysShown() const { + return fields->properties->signAlwaysShown; } -/** - * Skip over a run of zero or more Pattern_White_Space characters at - * pos in text. - */ -int32_t DecimalFormat::skipPatternWhiteSpace(const UnicodeString& text, int32_t pos) { - const UChar* s = text.getBuffer(); - return (int32_t)(PatternProps::skipWhiteSpace(s + pos, text.length() - pos) - s); +void DecimalFormat::setSignAlwaysShown(UBool value) { + if (UBOOL_TO_BOOL(value) == fields->properties->signAlwaysShown) { return; } + fields->properties->signAlwaysShown = value; + touchNoError(); } -/** - * Skip over a run of zero or more isUWhiteSpace() characters at pos - * in text. - */ -int32_t DecimalFormat::skipUWhiteSpace(const UnicodeString& text, int32_t pos) { - while (pos < text.length()) { - UChar32 c = text.char32At(pos); - if (!u_isUWhiteSpace(c)) { - break; - } - pos += U16_LENGTH(c); +int32_t DecimalFormat::getMultiplier(void) const { + if (fields->properties->multiplier != 1) { + return fields->properties->multiplier; + } else if (fields->properties->magnitudeMultiplier != 0) { + return static_cast(uprv_pow10(fields->properties->magnitudeMultiplier)); + } else { + return 1; } - return pos; } -/** - * Skip over a run of zero or more isUWhiteSpace() characters or bidi marks at pos - * in text. - */ -int32_t DecimalFormat::skipUWhiteSpaceAndMarks(const UnicodeString& text, int32_t pos) { - while (pos < text.length()) { - UChar32 c = text.char32At(pos); - if (!u_isUWhiteSpace(c) && !IS_BIDI_MARK(c)) { // u_isUWhiteSpace doesn't include LRM,RLM,ALM - break; - } - pos += U16_LENGTH(c); +void DecimalFormat::setMultiplier(int32_t multiplier) { + if (multiplier == 0) { + multiplier = 1; // one being the benign default value for a multiplier. } - return pos; -} -/** - * Skip over a run of zero or more bidi marks at pos in text. - */ -int32_t DecimalFormat::skipBidiMarks(const UnicodeString& text, int32_t pos) { - while (pos < text.length()) { - UChar c = text.charAt(pos); - if (!IS_BIDI_MARK(c)) { + // Try to convert to a magnitude multiplier first + int delta = 0; + int value = multiplier; + while (value != 1) { + delta++; + int temp = value / 10; + if (temp * 10 != value) { + delta = -1; break; } - pos++; - } - return pos; -} - -/** - * Return the length matched by the given affix, or -1 if none. - * @param affixPat pattern string - * @param input input text - * @param pos offset into input at which to begin matching - * @param type the currency type to parse against, LONG_NAME only or not. - * @param currency return value for parsed currency, for generic - * currency parsing mode, or null for normal parsing. In generic - * currency parsing mode, any currency is parsed, not just the - * currency that this formatter is set to. - * @return length of input that matches, or -1 if match failure - */ -int32_t DecimalFormat::compareComplexAffix(const UnicodeString& affixPat, - const UnicodeString& text, - int32_t pos, - int8_t type, - UChar* currency) const -{ - int32_t start = pos; - U_ASSERT(currency != NULL || fImpl->fMonetary); - - for (int32_t i=0; - i= 0; ) { - UChar32 c = affixPat.char32At(i); - i += U16_LENGTH(c); - - if (c == kQuote) { - U_ASSERT(i <= affixPat.length()); - c = affixPat.char32At(i); - i += U16_LENGTH(c); - - const UnicodeString* affix = NULL; - - switch (c) { - case kCurrencySign: { - // since the currency names in choice format is saved - // the same way as other currency names, - // do not need to do currency choice parsing here. - // the general currency parsing parse against all names, - // including names in choice format. - UBool intl = igetLocale().getName(); - ParsePosition ppos(pos); - UChar curr[4]; - UErrorCode ec = U_ZERO_ERROR; - // Delegate parse of display name => ISO code to Currency - uprv_parseCurrency(loc, text, ppos, type, curr, ec); - - // If parse succeeds, populate currency[0] - if (U_SUCCESS(ec) && ppos.getIndex() != pos) { - if (currency) { - u_strcpy(currency, curr); - } else { - // The formatter is currency-style but the client has not requested - // the value of the parsed currency. In this case, if that value does - // not match the formatter's current value, then the parse fails. - UChar effectiveCurr[4]; - getEffectiveCurrency(effectiveCurr, ec); - if ( U_FAILURE(ec) || u_strncmp(curr,effectiveCurr,4) != 0 ) { - pos = -1; - continue; - } - } - pos = ppos.getIndex(); - } else if (!isLenient()){ - pos = -1; - } - continue; - } - case kPatternPercent: - affix = &fImpl->getConstSymbol(DecimalFormatSymbols::kPercentSymbol); - break; - case kPatternPerMill: - affix = &fImpl->getConstSymbol(DecimalFormatSymbols::kPerMillSymbol); - break; - case kPatternPlus: - affix = &fImpl->getConstSymbol(DecimalFormatSymbols::kPlusSignSymbol); - break; - case kPatternMinus: - affix = &fImpl->getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol); - break; - default: - // fall through to affix!=0 test, which will fail - break; - } - - if (affix != NULL) { - pos = match(text, pos, *affix); - continue; - } - } - - pos = match(text, pos, c); - if (PatternProps::isWhiteSpace(c)) { - i = skipPatternWhiteSpace(affixPat, i); - } + value = temp; } - return pos - start; -} - -/** - * Match a single character at text[pos] and return the index of the - * next character upon success. Return -1 on failure. If - * ch is a Pattern_White_Space then match a run of white space in text. - */ -int32_t DecimalFormat::match(const UnicodeString& text, int32_t pos, UChar32 ch) { - if (PatternProps::isWhiteSpace(ch)) { - // Advance over run of white space in input text - // Must see at least one white space char in input - int32_t s = pos; - pos = skipPatternWhiteSpace(text, pos); - if (pos == s) { - return -1; - } - return pos; - } - return (pos >= 0 && text.char32At(pos) == ch) ? - (pos + U16_LENGTH(ch)) : -1; -} - -/** - * Match a string at text[pos] and return the index of the next - * character upon success. Return -1 on failure. Match a run of - * white space in str with a run of white space in text. - */ -int32_t DecimalFormat::match(const UnicodeString& text, int32_t pos, const UnicodeString& str) { - for (int32_t i=0; i= 0; ) { - UChar32 ch = str.char32At(i); - i += U16_LENGTH(ch); - if (PatternProps::isWhiteSpace(ch)) { - i = skipPatternWhiteSpace(str, i); - } - pos = match(text, pos, ch); - } - return pos; -} - -UBool DecimalFormat::matchSymbol(const UnicodeString &text, int32_t position, int32_t length, const UnicodeString &symbol, - UnicodeSet *sset, UChar32 schar) -{ - if (sset != NULL) { - return sset->contains(schar); - } - - return text.compare(position, length, symbol) == 0; -} - -UBool DecimalFormat::matchDecimal(UChar32 symbolChar, - UBool sawDecimal, UChar32 sawDecimalChar, - const UnicodeSet *sset, UChar32 schar) { - if(sawDecimal) { - return schar==sawDecimalChar; - } else if(schar==symbolChar) { - return TRUE; - } else if(sset!=NULL) { - return sset->contains(schar); - } else { - return FALSE; - } -} - -UBool DecimalFormat::matchGrouping(UChar32 groupingChar, - UBool sawGrouping, UChar32 sawGroupingChar, - const UnicodeSet *sset, - UChar32 /*decimalChar*/, const UnicodeSet *decimalSet, - UChar32 schar) { - if(sawGrouping) { - return schar==sawGroupingChar; // previously found - } else if(schar==groupingChar) { - return TRUE; // char from symbols - } else if(sset!=NULL) { - return sset->contains(schar) && // in groupingSet but... - ((decimalSet==NULL) || !decimalSet->contains(schar)); // Exclude decimalSet from groupingSet + if (delta != -1) { + fields->properties->magnitudeMultiplier = delta; + fields->properties->multiplier = 1; } else { - return FALSE; + fields->properties->magnitudeMultiplier = 0; + fields->properties->multiplier = multiplier; } + touchNoError(); } +int32_t DecimalFormat::getMultiplierScale() const { + return fields->properties->multiplierScale; +} +void DecimalFormat::setMultiplierScale(int32_t newValue) { + if (newValue == fields->properties->multiplierScale) { return; } + fields->properties->multiplierScale = newValue; + touchNoError(); +} -//------------------------------------------------------------------------------ -// Gets the pointer to the localized decimal format symbols +double DecimalFormat::getRoundingIncrement(void) const { + return fields->exportedProperties->roundingIncrement; +} -const DecimalFormatSymbols* -DecimalFormat::getDecimalFormatSymbols() const -{ - return &fImpl->getDecimalFormatSymbols(); +void DecimalFormat::setRoundingIncrement(double newValue) { + if (newValue == fields->properties->roundingIncrement) { return; } + fields->properties->roundingIncrement = newValue; + touchNoError(); } -//------------------------------------------------------------------------------ -// De-owning the current localized symbols and adopt the new symbols. +ERoundingMode DecimalFormat::getRoundingMode(void) const { + // UNumberFormatRoundingMode and ERoundingMode have the same values. + return static_cast(fields->exportedProperties->roundingMode.getNoError()); +} -void -DecimalFormat::adoptDecimalFormatSymbols(DecimalFormatSymbols* symbolsToAdopt) -{ - if (symbolsToAdopt == NULL) { - return; // do not allow caller to set fSymbols to NULL +void DecimalFormat::setRoundingMode(ERoundingMode roundingMode) { + auto uRoundingMode = static_cast(roundingMode); + if (!fields->properties->roundingMode.isNull() && uRoundingMode == fields->properties->roundingMode.getNoError()) { + return; } - fImpl->adoptDecimalFormatSymbols(symbolsToAdopt); + NumberFormat::setMaximumIntegerDigits(roundingMode); // to set field for compatibility + fields->properties->roundingMode = uRoundingMode; + touchNoError(); } -//------------------------------------------------------------------------------ -// Setting the symbols is equlivalent to adopting a newly created localized -// symbols. -void -DecimalFormat::setDecimalFormatSymbols(const DecimalFormatSymbols& symbols) -{ - adoptDecimalFormatSymbols(new DecimalFormatSymbols(symbols)); +int32_t DecimalFormat::getFormatWidth(void) const { + return fields->properties->formatWidth; } - -const CurrencyPluralInfo* -DecimalFormat::getCurrencyPluralInfo(void) const -{ - return fCurrencyPluralInfo; +void DecimalFormat::setFormatWidth(int32_t width) { + if (width == fields->properties->formatWidth) { return; } + fields->properties->formatWidth = width; + touchNoError(); } - -void -DecimalFormat::adoptCurrencyPluralInfo(CurrencyPluralInfo* toAdopt) -{ - if (toAdopt != NULL) { - delete fCurrencyPluralInfo; - fCurrencyPluralInfo = toAdopt; - // re-set currency affix patterns and currency affixes. - if (fImpl->fMonetary) { - UErrorCode status = U_ZERO_ERROR; - if (fAffixPatternsForCurrency) { - deleteHashForAffixPattern(); - } - setupCurrencyAffixPatterns(status); - } +UnicodeString DecimalFormat::getPadCharacterString() const { + if (fields->properties->padString.isBogus()) { + // Readonly-alias the static string kFallbackPaddingString + return {TRUE, kFallbackPaddingString, -1}; + } else { + return fields->properties->padString; } } -void -DecimalFormat::setCurrencyPluralInfo(const CurrencyPluralInfo& info) -{ - adoptCurrencyPluralInfo(info.clone()); +void DecimalFormat::setPadCharacter(const UnicodeString& padChar) { + if (padChar == fields->properties->padString) { return; } + if (padChar.length() > 0) { + fields->properties->padString = UnicodeString(padChar.char32At(0)); + } else { + fields->properties->padString.setToBogus(); + } + touchNoError(); } - -//------------------------------------------------------------------------------ -// Gets the positive prefix of the number pattern. - -UnicodeString& -DecimalFormat::getPositivePrefix(UnicodeString& result) const -{ - return fImpl->getPositivePrefix(result); +EPadPosition DecimalFormat::getPadPosition(void) const { + if (fields->properties->padPosition.isNull()) { + return EPadPosition::kPadBeforePrefix; + } else { + // UNumberFormatPadPosition and EPadPosition have the same values. + return static_cast(fields->properties->padPosition.getNoError()); + } } -//------------------------------------------------------------------------------ -// Sets the positive prefix of the number pattern. - -void -DecimalFormat::setPositivePrefix(const UnicodeString& newValue) -{ - fImpl->setPositivePrefix(newValue); +void DecimalFormat::setPadPosition(EPadPosition padPos) { + auto uPadPos = static_cast(padPos); + if (!fields->properties->padPosition.isNull() && uPadPos == fields->properties->padPosition.getNoError()) { + return; + } + fields->properties->padPosition = uPadPos; + touchNoError(); } -//------------------------------------------------------------------------------ -// Gets the negative prefix of the number pattern. - -UnicodeString& -DecimalFormat::getNegativePrefix(UnicodeString& result) const -{ - return fImpl->getNegativePrefix(result); +UBool DecimalFormat::isScientificNotation(void) const { + return fields->properties->minimumExponentDigits != -1; } -//------------------------------------------------------------------------------ -// Gets the negative prefix of the number pattern. - -void -DecimalFormat::setNegativePrefix(const UnicodeString& newValue) -{ - fImpl->setNegativePrefix(newValue); +void DecimalFormat::setScientificNotation(UBool useScientific) { + int32_t minExp = useScientific ? 1 : -1; + if (fields->properties->minimumExponentDigits == minExp) { return; } + if (useScientific) { + fields->properties->minimumExponentDigits = 1; + } else { + fields->properties->minimumExponentDigits = -1; + } + touchNoError(); } -//------------------------------------------------------------------------------ -// Gets the positive suffix of the number pattern. - -UnicodeString& -DecimalFormat::getPositiveSuffix(UnicodeString& result) const -{ - return fImpl->getPositiveSuffix(result); +int8_t DecimalFormat::getMinimumExponentDigits(void) const { + return static_cast(fields->properties->minimumExponentDigits); } -//------------------------------------------------------------------------------ -// Sets the positive suffix of the number pattern. - -void -DecimalFormat::setPositiveSuffix(const UnicodeString& newValue) -{ - fImpl->setPositiveSuffix(newValue); +void DecimalFormat::setMinimumExponentDigits(int8_t minExpDig) { + if (minExpDig == fields->properties->minimumExponentDigits) { return; } + fields->properties->minimumExponentDigits = minExpDig; + touchNoError(); } -//------------------------------------------------------------------------------ -// Gets the negative suffix of the number pattern. - -UnicodeString& -DecimalFormat::getNegativeSuffix(UnicodeString& result) const -{ - return fImpl->getNegativeSuffix(result); +UBool DecimalFormat::isExponentSignAlwaysShown(void) const { + return fields->properties->exponentSignAlwaysShown; } -//------------------------------------------------------------------------------ -// Sets the negative suffix of the number pattern. - -void -DecimalFormat::setNegativeSuffix(const UnicodeString& newValue) -{ - fImpl->setNegativeSuffix(newValue); +void DecimalFormat::setExponentSignAlwaysShown(UBool expSignAlways) { + if (UBOOL_TO_BOOL(expSignAlways) == fields->properties->exponentSignAlwaysShown) { return; } + fields->properties->exponentSignAlwaysShown = expSignAlways; + touchNoError(); } -//------------------------------------------------------------------------------ -// Gets the multiplier of the number pattern. -// Multipliers are stored as decimal numbers (DigitLists) because that -// is the most convenient for muliplying or dividing the numbers to be formatted. -// A NULL multiplier implies one, and the scaling operations are skipped. - -int32_t -DecimalFormat::getMultiplier() const -{ - return fImpl->getMultiplier(); +int32_t DecimalFormat::getGroupingSize(void) const { + if (fields->properties->groupingSize < 0) { + return 0; + } + return fields->properties->groupingSize; } -//------------------------------------------------------------------------------ -// Sets the multiplier of the number pattern. -void -DecimalFormat::setMultiplier(int32_t newValue) -{ - fImpl->setMultiplier(newValue); -} - -/** - * Get the rounding increment. - * @return A positive rounding increment, or 0.0 if rounding - * is not in effect. - * @see #setRoundingIncrement - * @see #getRoundingMode - * @see #setRoundingMode - */ -double DecimalFormat::getRoundingIncrement() const { - return fImpl->getRoundingIncrement(); -} - -/** - * Set the rounding increment. This method also controls whether - * rounding is enabled. - * @param newValue A positive rounding increment, or 0.0 to disable rounding. - * Negative increments are equivalent to 0.0. - * @see #getRoundingIncrement - * @see #getRoundingMode - * @see #setRoundingMode - */ -void DecimalFormat::setRoundingIncrement(double newValue) { - fImpl->setRoundingIncrement(newValue); -} - -/** - * Get the rounding mode. - * @return A rounding mode - * @see #setRoundingIncrement - * @see #getRoundingIncrement - * @see #setRoundingMode - */ -DecimalFormat::ERoundingMode DecimalFormat::getRoundingMode() const { - return fImpl->getRoundingMode(); -} - -/** - * Set the rounding mode. This has no effect unless the rounding - * increment is greater than zero. - * @param roundingMode A rounding mode - * @see #setRoundingIncrement - * @see #getRoundingIncrement - * @see #getRoundingMode - */ -void DecimalFormat::setRoundingMode(ERoundingMode roundingMode) { - fImpl->setRoundingMode(roundingMode); -} - -/** - * Get the width to which the output of format() is padded. - * @return the format width, or zero if no padding is in effect - * @see #setFormatWidth - * @see #getPadCharacter - * @see #setPadCharacter - * @see #getPadPosition - * @see #setPadPosition - */ -int32_t DecimalFormat::getFormatWidth() const { - return fImpl->getFormatWidth(); -} - -/** - * Set the width to which the output of format() is padded. - * This method also controls whether padding is enabled. - * @param width the width to which to pad the result of - * format(), or zero to disable padding. A negative - * width is equivalent to 0. - * @see #getFormatWidth - * @see #getPadCharacter - * @see #setPadCharacter - * @see #getPadPosition - * @see #setPadPosition - */ -void DecimalFormat::setFormatWidth(int32_t width) { - int32_t formatWidth = (width > 0) ? width : 0; - fImpl->setFormatWidth(formatWidth); +void DecimalFormat::setGroupingSize(int32_t newValue) { + if (newValue == fields->properties->groupingSize) { return; } + fields->properties->groupingSize = newValue; + touchNoError(); } -UnicodeString DecimalFormat::getPadCharacterString() const { - return UnicodeString(fImpl->getPadCharacter()); +int32_t DecimalFormat::getSecondaryGroupingSize(void) const { + int grouping2 = fields->properties->secondaryGroupingSize; + if (grouping2 < 0) { + return 0; + } + return grouping2; } -void DecimalFormat::setPadCharacter(const UnicodeString &padChar) { - UChar32 pad; - if (padChar.length() > 0) { - pad = padChar.char32At(0); - } - else { - pad = kDefaultPad; - } - fImpl->setPadCharacter(pad); -} - -static DecimalFormat::EPadPosition fromPadPosition(DigitAffixesAndPadding::EPadPosition padPos) { - switch (padPos) { - case DigitAffixesAndPadding::kPadBeforePrefix: - return DecimalFormat::kPadBeforePrefix; - case DigitAffixesAndPadding::kPadAfterPrefix: - return DecimalFormat::kPadAfterPrefix; - case DigitAffixesAndPadding::kPadBeforeSuffix: - return DecimalFormat::kPadBeforeSuffix; - case DigitAffixesAndPadding::kPadAfterSuffix: - return DecimalFormat::kPadAfterSuffix; - default: - U_ASSERT(FALSE); - break; - } - return DecimalFormat::kPadBeforePrefix; -} - -/** - * Get the position at which padding will take place. This is the location - * at which padding will be inserted if the result of format() - * is shorter than the format width. - * @return the pad position, one of kPadBeforePrefix, - * kPadAfterPrefix, kPadBeforeSuffix, or - * kPadAfterSuffix. - * @see #setFormatWidth - * @see #getFormatWidth - * @see #setPadCharacter - * @see #getPadCharacter - * @see #setPadPosition - * @see #kPadBeforePrefix - * @see #kPadAfterPrefix - * @see #kPadBeforeSuffix - * @see #kPadAfterSuffix - */ -DecimalFormat::EPadPosition DecimalFormat::getPadPosition() const { - return fromPadPosition(fImpl->getPadPosition()); -} - -static DigitAffixesAndPadding::EPadPosition toPadPosition(DecimalFormat::EPadPosition padPos) { - switch (padPos) { - case DecimalFormat::kPadBeforePrefix: - return DigitAffixesAndPadding::kPadBeforePrefix; - case DecimalFormat::kPadAfterPrefix: - return DigitAffixesAndPadding::kPadAfterPrefix; - case DecimalFormat::kPadBeforeSuffix: - return DigitAffixesAndPadding::kPadBeforeSuffix; - case DecimalFormat::kPadAfterSuffix: - return DigitAffixesAndPadding::kPadAfterSuffix; - default: - U_ASSERT(FALSE); - break; - } - return DigitAffixesAndPadding::kPadBeforePrefix; -} - -/** - * NEW - * Set the position at which padding will take place. This is the location - * at which padding will be inserted if the result of format() - * is shorter than the format width. This has no effect unless padding is - * enabled. - * @param padPos the pad position, one of kPadBeforePrefix, - * kPadAfterPrefix, kPadBeforeSuffix, or - * kPadAfterSuffix. - * @see #setFormatWidth - * @see #getFormatWidth - * @see #setPadCharacter - * @see #getPadCharacter - * @see #getPadPosition - * @see #kPadBeforePrefix - * @see #kPadAfterPrefix - * @see #kPadBeforeSuffix - * @see #kPadAfterSuffix - */ -void DecimalFormat::setPadPosition(EPadPosition padPos) { - fImpl->setPadPosition(toPadPosition(padPos)); -} - -/** - * Return whether or not scientific notation is used. - * @return TRUE if this object formats and parses scientific notation - * @see #setScientificNotation - * @see #getMinimumExponentDigits - * @see #setMinimumExponentDigits - * @see #isExponentSignAlwaysShown - * @see #setExponentSignAlwaysShown - */ -UBool DecimalFormat::isScientificNotation() const { - return fImpl->isScientificNotation(); -} - -/** - * Set whether or not scientific notation is used. - * @param useScientific TRUE if this object formats and parses scientific - * notation - * @see #isScientificNotation - * @see #getMinimumExponentDigits - * @see #setMinimumExponentDigits - * @see #isExponentSignAlwaysShown - * @see #setExponentSignAlwaysShown - */ -void DecimalFormat::setScientificNotation(UBool useScientific) { - fImpl->setScientificNotation(useScientific); -} - -/** - * Return the minimum exponent digits that will be shown. - * @return the minimum exponent digits that will be shown - * @see #setScientificNotation - * @see #isScientificNotation - * @see #setMinimumExponentDigits - * @see #isExponentSignAlwaysShown - * @see #setExponentSignAlwaysShown - */ -int8_t DecimalFormat::getMinimumExponentDigits() const { - return fImpl->getMinimumExponentDigits(); -} - -/** - * Set the minimum exponent digits that will be shown. This has no - * effect unless scientific notation is in use. - * @param minExpDig a value >= 1 indicating the fewest exponent digits - * that will be shown. Values less than 1 will be treated as 1. - * @see #setScientificNotation - * @see #isScientificNotation - * @see #getMinimumExponentDigits - * @see #isExponentSignAlwaysShown - * @see #setExponentSignAlwaysShown - */ -void DecimalFormat::setMinimumExponentDigits(int8_t minExpDig) { - int32_t minExponentDigits = (int8_t)((minExpDig > 0) ? minExpDig : 1); - fImpl->setMinimumExponentDigits(minExponentDigits); -} - -/** - * Return whether the exponent sign is always shown. - * @return TRUE if the exponent is always prefixed with either the - * localized minus sign or the localized plus sign, false if only negative - * exponents are prefixed with the localized minus sign. - * @see #setScientificNotation - * @see #isScientificNotation - * @see #setMinimumExponentDigits - * @see #getMinimumExponentDigits - * @see #setExponentSignAlwaysShown - */ -UBool DecimalFormat::isExponentSignAlwaysShown() const { - return fImpl->isExponentSignAlwaysShown(); -} - -/** - * Set whether the exponent sign is always shown. This has no effect - * unless scientific notation is in use. - * @param expSignAlways TRUE if the exponent is always prefixed with either - * the localized minus sign or the localized plus sign, false if only - * negative exponents are prefixed with the localized minus sign. - * @see #setScientificNotation - * @see #isScientificNotation - * @see #setMinimumExponentDigits - * @see #getMinimumExponentDigits - * @see #isExponentSignAlwaysShown - */ -void DecimalFormat::setExponentSignAlwaysShown(UBool expSignAlways) { - fImpl->setExponentSignAlwaysShown(expSignAlways); +void DecimalFormat::setSecondaryGroupingSize(int32_t newValue) { + if (newValue == fields->properties->secondaryGroupingSize) { return; } + fields->properties->secondaryGroupingSize = newValue; + touchNoError(); } -//------------------------------------------------------------------------------ -// Gets the grouping size of the number pattern. For example, thousand or 10 -// thousand groupings. - -int32_t -DecimalFormat::getGroupingSize() const -{ - return fImpl->getGroupingSize(); +int32_t DecimalFormat::getMinimumGroupingDigits() const { + return fields->properties->minimumGroupingDigits; } -//------------------------------------------------------------------------------ -// Gets the grouping size of the number pattern. - -void -DecimalFormat::setGroupingSize(int32_t newValue) -{ - fImpl->setGroupingSize(newValue); +void DecimalFormat::setMinimumGroupingDigits(int32_t newValue) { + if (newValue == fields->properties->minimumGroupingDigits) { return; } + fields->properties->minimumGroupingDigits = newValue; + touchNoError(); } -//------------------------------------------------------------------------------ - -int32_t -DecimalFormat::getSecondaryGroupingSize() const -{ - return fImpl->getSecondaryGroupingSize(); +UBool DecimalFormat::isDecimalSeparatorAlwaysShown(void) const { + return fields->properties->decimalSeparatorAlwaysShown; } -//------------------------------------------------------------------------------ - -void -DecimalFormat::setSecondaryGroupingSize(int32_t newValue) -{ - fImpl->setSecondaryGroupingSize(newValue); +void DecimalFormat::setDecimalSeparatorAlwaysShown(UBool newValue) { + if (UBOOL_TO_BOOL(newValue) == fields->properties->decimalSeparatorAlwaysShown) { return; } + fields->properties->decimalSeparatorAlwaysShown = newValue; + touchNoError(); } -//------------------------------------------------------------------------------ - -int32_t -DecimalFormat::getMinimumGroupingDigits() const -{ - return fImpl->getMinimumGroupingDigits(); +UBool DecimalFormat::isDecimalPatternMatchRequired(void) const { + return fields->properties->decimalPatternMatchRequired; } -//------------------------------------------------------------------------------ - -void -DecimalFormat::setMinimumGroupingDigits(int32_t newValue) -{ - fImpl->setMinimumGroupingDigits(newValue); +void DecimalFormat::setDecimalPatternMatchRequired(UBool newValue) { + if (UBOOL_TO_BOOL(newValue) == fields->properties->decimalPatternMatchRequired) { return; } + fields->properties->decimalPatternMatchRequired = newValue; + touchNoError(); } -//------------------------------------------------------------------------------ -// Checks if to show the decimal separator. - -UBool -DecimalFormat::isDecimalSeparatorAlwaysShown() const -{ - return fImpl->isDecimalSeparatorAlwaysShown(); +UBool DecimalFormat::isParseNoExponent() const { + return fields->properties->parseNoExponent; } -//------------------------------------------------------------------------------ -// Sets to always show the decimal separator. - -void -DecimalFormat::setDecimalSeparatorAlwaysShown(UBool newValue) -{ - fImpl->setDecimalSeparatorAlwaysShown(newValue); +void DecimalFormat::setParseNoExponent(UBool value) { + if (UBOOL_TO_BOOL(value) == fields->properties->parseNoExponent) { return; } + fields->properties->parseNoExponent = value; + touchNoError(); } -//------------------------------------------------------------------------------ -// Checks if decimal point pattern match is required -UBool -DecimalFormat::isDecimalPatternMatchRequired(void) const -{ - return static_cast(fBoolFlags.contains(UNUM_PARSE_DECIMAL_MARK_REQUIRED)); +UBool DecimalFormat::isParseCaseSensitive() const { + return fields->properties->parseCaseSensitive; } -//------------------------------------------------------------------------------ -// Checks if decimal point pattern match is required - -void -DecimalFormat::setDecimalPatternMatchRequired(UBool newValue) -{ - fBoolFlags.set(UNUM_PARSE_DECIMAL_MARK_REQUIRED, newValue); +void DecimalFormat::setParseCaseSensitive(UBool value) { + if (UBOOL_TO_BOOL(value) == fields->properties->parseCaseSensitive) { return; } + fields->properties->parseCaseSensitive = value; + touchNoError(); } - -//------------------------------------------------------------------------------ -// Emits the pattern of this DecimalFormat instance. - -UnicodeString& -DecimalFormat::toPattern(UnicodeString& result) const -{ - return fImpl->toPattern(result); +UBool DecimalFormat::isFormatFailIfMoreThanMaxDigits() const { + return fields->properties->formatFailIfMoreThanMaxDigits; } -//------------------------------------------------------------------------------ -// Emits the localized pattern this DecimalFormat instance. - -UnicodeString& -DecimalFormat::toLocalizedPattern(UnicodeString& result) const -{ - // toLocalizedPattern is deprecated, so we just make it the same as - // toPattern. - return fImpl->toPattern(result); +void DecimalFormat::setFormatFailIfMoreThanMaxDigits(UBool value) { + if (UBOOL_TO_BOOL(value) == fields->properties->formatFailIfMoreThanMaxDigits) { return; } + fields->properties->formatFailIfMoreThanMaxDigits = value; + touchNoError(); } -//------------------------------------------------------------------------------ - -void -DecimalFormat::applyPattern(const UnicodeString& pattern, UErrorCode& status) -{ - if (pattern.indexOf(kCurrencySign) != -1) { - handleCurrencySignInPattern(status); +UnicodeString& DecimalFormat::toPattern(UnicodeString& result) const { + // Pull some properties from exportedProperties and others from properties + // to keep affix patterns intact. In particular, pull rounding properties + // so that CurrencyUsage is reflected properly. + // TODO: Consider putting this logic in number_patternstring.cpp instead. + ErrorCode localStatus; + DecimalFormatProperties tprops(*fields->properties); + bool useCurrency = ((!tprops.currency.isNull()) || !tprops.currencyPluralInfo.fPtr.isNull() || + !tprops.currencyUsage.isNull() || AffixUtils::hasCurrencySymbols( + tprops.positivePrefixPattern, localStatus) || AffixUtils::hasCurrencySymbols( + tprops.positiveSuffixPattern, localStatus) || AffixUtils::hasCurrencySymbols( + tprops.negativePrefixPattern, localStatus) || AffixUtils::hasCurrencySymbols( + tprops.negativeSuffixPattern, localStatus)); + if (useCurrency) { + tprops.minimumFractionDigits = fields->exportedProperties->minimumFractionDigits; + tprops.maximumFractionDigits = fields->exportedProperties->maximumFractionDigits; + tprops.roundingIncrement = fields->exportedProperties->roundingIncrement; } - fImpl->applyPattern(pattern, status); + result = PatternStringUtils::propertiesToPatternString(tprops, localStatus); + return result; } -//------------------------------------------------------------------------------ +UnicodeString& DecimalFormat::toLocalizedPattern(UnicodeString& result) const { + ErrorCode localStatus; + result = toPattern(result); + result = PatternStringUtils::convertLocalized(result, *fields->symbols, true, localStatus); + return result; +} -void -DecimalFormat::applyPattern(const UnicodeString& pattern, - UParseError& parseError, - UErrorCode& status) -{ - if (pattern.indexOf(kCurrencySign) != -1) { - handleCurrencySignInPattern(status); - } - fImpl->applyPattern(pattern, parseError, status); +void DecimalFormat::applyPattern(const UnicodeString& pattern, UParseError&, UErrorCode& status) { + // TODO: What is parseError for? + applyPattern(pattern, status); } -//------------------------------------------------------------------------------ -void -DecimalFormat::applyLocalizedPattern(const UnicodeString& pattern, UErrorCode& status) -{ - if (pattern.indexOf(kCurrencySign) != -1) { - handleCurrencySignInPattern(status); - } - fImpl->applyLocalizedPattern(pattern, status); +void DecimalFormat::applyPattern(const UnicodeString& pattern, UErrorCode& status) { + setPropertiesFromPattern(pattern, IGNORE_ROUNDING_NEVER, status); + touch(status); } -//------------------------------------------------------------------------------ +void DecimalFormat::applyLocalizedPattern(const UnicodeString& localizedPattern, UParseError&, + UErrorCode& status) { + // TODO: What is parseError for? + applyLocalizedPattern(localizedPattern, status); +} -void -DecimalFormat::applyLocalizedPattern(const UnicodeString& pattern, - UParseError& parseError, - UErrorCode& status) -{ - if (pattern.indexOf(kCurrencySign) != -1) { - handleCurrencySignInPattern(status); +void DecimalFormat::applyLocalizedPattern(const UnicodeString& localizedPattern, UErrorCode& status) { + if (U_SUCCESS(status)) { + UnicodeString pattern = PatternStringUtils::convertLocalized( + localizedPattern, *fields->symbols, false, status); + applyPattern(pattern, status); } - fImpl->applyLocalizedPattern(pattern, parseError, status); } -//------------------------------------------------------------------------------ - -/** - * Sets the maximum number of digits allowed in the integer portion of a - * number. - * @see NumberFormat#setMaximumIntegerDigits - */ void DecimalFormat::setMaximumIntegerDigits(int32_t newValue) { - newValue = _min(newValue, gDefaultMaxIntegerDigits); - NumberFormat::setMaximumIntegerDigits(newValue); - fImpl->updatePrecision(); + if (newValue == fields->properties->maximumIntegerDigits) { return; } + // For backwards compatibility, conflicting min/max need to keep the most recent setting. + int32_t min = fields->properties->minimumIntegerDigits; + if (min >= 0 && min > newValue) { + fields->properties->minimumIntegerDigits = newValue; + } + fields->properties->maximumIntegerDigits = newValue; + touchNoError(); } -/** - * Sets the minimum number of digits allowed in the integer portion of a - * number. This override limits the integer digit count to 309. - * @see NumberFormat#setMinimumIntegerDigits - */ void DecimalFormat::setMinimumIntegerDigits(int32_t newValue) { - newValue = _min(newValue, kDoubleIntegerDigits); - NumberFormat::setMinimumIntegerDigits(newValue); - fImpl->updatePrecision(); + if (newValue == fields->properties->minimumIntegerDigits) { return; } + // For backwards compatibility, conflicting min/max need to keep the most recent setting. + int32_t max = fields->properties->maximumIntegerDigits; + if (max >= 0 && max < newValue) { + fields->properties->maximumIntegerDigits = newValue; + } + fields->properties->minimumIntegerDigits = newValue; + touchNoError(); } -/** - * Sets the maximum number of digits allowed in the fraction portion of a - * number. This override limits the fraction digit count to 340. - * @see NumberFormat#setMaximumFractionDigits - */ void DecimalFormat::setMaximumFractionDigits(int32_t newValue) { - newValue = _min(newValue, kDoubleFractionDigits); - NumberFormat::setMaximumFractionDigits(newValue); - fImpl->updatePrecision(); + if (newValue == fields->properties->maximumFractionDigits) { return; } + // For backwards compatibility, conflicting min/max need to keep the most recent setting. + int32_t min = fields->properties->minimumFractionDigits; + if (min >= 0 && min > newValue) { + fields->properties->minimumFractionDigits = newValue; + } + fields->properties->maximumFractionDigits = newValue; + touchNoError(); } -/** - * Sets the minimum number of digits allowed in the fraction portion of a - * number. This override limits the fraction digit count to 340. - * @see NumberFormat#setMinimumFractionDigits - */ void DecimalFormat::setMinimumFractionDigits(int32_t newValue) { - newValue = _min(newValue, kDoubleFractionDigits); - NumberFormat::setMinimumFractionDigits(newValue); - fImpl->updatePrecision(); + if (newValue == fields->properties->minimumFractionDigits) { return; } + // For backwards compatibility, conflicting min/max need to keep the most recent setting. + int32_t max = fields->properties->maximumFractionDigits; + if (max >= 0 && max < newValue) { + fields->properties->maximumFractionDigits = newValue; + } + fields->properties->minimumFractionDigits = newValue; + touchNoError(); } int32_t DecimalFormat::getMinimumSignificantDigits() const { - return fImpl->getMinimumSignificantDigits(); + return fields->exportedProperties->minimumSignificantDigits; } int32_t DecimalFormat::getMaximumSignificantDigits() const { - return fImpl->getMaximumSignificantDigits(); + return fields->exportedProperties->maximumSignificantDigits; } -void DecimalFormat::setMinimumSignificantDigits(int32_t min) { - if (min < 1) { - min = 1; +void DecimalFormat::setMinimumSignificantDigits(int32_t value) { + if (value == fields->properties->minimumSignificantDigits) { return; } + int32_t max = fields->properties->maximumSignificantDigits; + if (max >= 0 && max < value) { + fields->properties->maximumSignificantDigits = value; } - // pin max sig dig to >= min - int32_t max = _max(fImpl->fMaxSigDigits, min); - fImpl->setMinMaxSignificantDigits(min, max); + fields->properties->minimumSignificantDigits = value; + touchNoError(); } -void DecimalFormat::setMaximumSignificantDigits(int32_t max) { - if (max < 1) { - max = 1; +void DecimalFormat::setMaximumSignificantDigits(int32_t value) { + if (value == fields->properties->maximumSignificantDigits) { return; } + int32_t min = fields->properties->minimumSignificantDigits; + if (min >= 0 && min > value) { + fields->properties->minimumSignificantDigits = value; } - // pin min sig dig to 1..max - U_ASSERT(fImpl->fMinSigDigits >= 1); - int32_t min = _min(fImpl->fMinSigDigits, max); - fImpl->setMinMaxSignificantDigits(min, max); + fields->properties->maximumSignificantDigits = value; + touchNoError(); } UBool DecimalFormat::areSignificantDigitsUsed() const { - return fImpl->areSignificantDigitsUsed(); + return fields->properties->minimumSignificantDigits != -1 || fields->properties->maximumSignificantDigits != -1; } void DecimalFormat::setSignificantDigitsUsed(UBool useSignificantDigits) { - fImpl->setSignificantDigitsUsed(useSignificantDigits); -} - -void DecimalFormat::setCurrency(const UChar* theCurrency, UErrorCode& ec) { - // set the currency before compute affixes to get the right currency names - NumberFormat::setCurrency(theCurrency, ec); - fImpl->updateCurrency(ec); -} - -void DecimalFormat::setCurrencyUsage(UCurrencyUsage newContext, UErrorCode* ec){ - fImpl->setCurrencyUsage(newContext, *ec); -} - -UCurrencyUsage DecimalFormat::getCurrencyUsage() const { - return fImpl->getCurrencyUsage(); -} - -// Deprecated variant with no UErrorCode parameter -void DecimalFormat::setCurrency(const UChar* theCurrency) { - UErrorCode ec = U_ZERO_ERROR; - setCurrency(theCurrency, ec); + // These are the default values from the old implementation. + int32_t minSig = useSignificantDigits ? 1 : -1; + int32_t maxSig = useSignificantDigits ? 6 : -1; + if (fields->properties->minimumSignificantDigits == minSig && + fields->properties->maximumSignificantDigits == maxSig) { + return; + } + fields->properties->minimumSignificantDigits = minSig; + fields->properties->maximumSignificantDigits = maxSig; + touchNoError(); } -void DecimalFormat::getEffectiveCurrency(UChar* result, UErrorCode& ec) const { - if (fImpl->fSymbols == NULL) { - ec = U_MEMORY_ALLOCATION_ERROR; +void DecimalFormat::setCurrency(const char16_t* theCurrency, UErrorCode& ec) { + CurrencyUnit currencyUnit(theCurrency, ec); + if (U_FAILURE(ec)) { return; } + if (!fields->properties->currency.isNull() && fields->properties->currency.getNoError() == currencyUnit) { return; } - ec = U_ZERO_ERROR; - const UChar* c = getCurrency(); - if (*c == 0) { - const UnicodeString &intl = - fImpl->getConstSymbol(DecimalFormatSymbols::kIntlCurrencySymbol); - c = intl.getBuffer(); // ok for intl to go out of scope - } - u_strncpy(result, c, 3); - result[3] = 0; + NumberFormat::setCurrency(theCurrency, ec); // to set field for compatibility + fields->properties->currency = currencyUnit; + // TODO: Set values in fields->symbols, too? + touchNoError(); } -Hashtable* -DecimalFormat::initHashForAffixPattern(UErrorCode& status) { - if ( U_FAILURE(status) ) { - return NULL; - } - Hashtable* hTable; - if ( (hTable = new Hashtable(TRUE, status)) == NULL ) { - status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - if ( U_FAILURE(status) ) { - delete hTable; - return NULL; - } - hTable->setValueComparator(decimfmtAffixPatternValueComparator); - return hTable; +void DecimalFormat::setCurrency(const char16_t* theCurrency) { + ErrorCode localStatus; + setCurrency(theCurrency, localStatus); } -void -DecimalFormat::deleteHashForAffixPattern() -{ - if ( fAffixPatternsForCurrency == NULL ) { +void DecimalFormat::setCurrencyUsage(UCurrencyUsage newUsage, UErrorCode* ec) { + if (U_FAILURE(*ec)) { return; } - int32_t pos = UHASH_FIRST; - const UHashElement* element = NULL; - while ( (element = fAffixPatternsForCurrency->nextElement(pos)) != NULL ) { - const UHashTok valueTok = element->value; - const AffixPatternsForCurrency* value = (AffixPatternsForCurrency*)valueTok.pointer; - delete value; + if (!fields->properties->currencyUsage.isNull() && newUsage == fields->properties->currencyUsage.getNoError()) { + return; } - delete fAffixPatternsForCurrency; - fAffixPatternsForCurrency = NULL; + fields->properties->currencyUsage = newUsage; + touch(*ec); } - -void -DecimalFormat::copyHashForAffixPattern(const Hashtable* source, - Hashtable* target, - UErrorCode& status) { - if ( U_FAILURE(status) ) { - return; - } - int32_t pos = UHASH_FIRST; - const UHashElement* element = NULL; - if ( source ) { - while ( (element = source->nextElement(pos)) != NULL ) { - const UHashTok keyTok = element->key; - const UnicodeString* key = (UnicodeString*)keyTok.pointer; - const UHashTok valueTok = element->value; - const AffixPatternsForCurrency* value = (AffixPatternsForCurrency*)valueTok.pointer; - AffixPatternsForCurrency* copy = new AffixPatternsForCurrency( - value->negPrefixPatternForCurrency, - value->negSuffixPatternForCurrency, - value->posPrefixPatternForCurrency, - value->posSuffixPatternForCurrency, - value->patternType); - target->put(UnicodeString(*key), copy, status); - if ( U_FAILURE(status) ) { - return; - } - } +UCurrencyUsage DecimalFormat::getCurrencyUsage() const { + // CurrencyUsage is not exported, so we have to get it from the input property bag. + // TODO: Should we export CurrencyUsage instead? + if (fields->properties->currencyUsage.isNull()) { + return UCURR_USAGE_STANDARD; } + return fields->properties->currencyUsage.getNoError(); } void -DecimalFormat::setGroupingUsed(UBool newValue) { - NumberFormat::setGroupingUsed(newValue); - fImpl->updateGrouping(); +DecimalFormat::formatToDecimalQuantity(double number, DecimalQuantity& output, UErrorCode& status) const { + fields->formatter->formatDouble(number, status).getDecimalQuantity(output, status); } -void -DecimalFormat::setParseIntegerOnly(UBool newValue) { - NumberFormat::setParseIntegerOnly(newValue); +void DecimalFormat::formatToDecimalQuantity(const Formattable& number, DecimalQuantity& output, + UErrorCode& status) const { + UFormattedNumberData obj; + number.populateDecimalQuantity(obj.quantity, status); + fields->formatter->formatImpl(&obj, status); + output = std::move(obj.quantity); } -void -DecimalFormat::setContext(UDisplayContext value, UErrorCode& status) { - NumberFormat::setContext(value, status); +const number::LocalizedNumberFormatter& DecimalFormat::toNumberFormatter() const { + return *fields->formatter; } -DecimalFormat& DecimalFormat::setAttribute( UNumberFormatAttribute attr, - int32_t newValue, - UErrorCode &status) { - if(U_FAILURE(status)) return *this; - - switch(attr) { - case UNUM_LENIENT_PARSE: - setLenient(newValue!=0); - break; - - case UNUM_PARSE_INT_ONLY: - setParseIntegerOnly(newValue!=0); - break; - - case UNUM_GROUPING_USED: - setGroupingUsed(newValue!=0); - break; - - case UNUM_DECIMAL_ALWAYS_SHOWN: - setDecimalSeparatorAlwaysShown(newValue!=0); - break; - - case UNUM_MAX_INTEGER_DIGITS: - setMaximumIntegerDigits(newValue); - break; - - case UNUM_MIN_INTEGER_DIGITS: - setMinimumIntegerDigits(newValue); - break; - - case UNUM_INTEGER_DIGITS: - setMinimumIntegerDigits(newValue); - setMaximumIntegerDigits(newValue); - break; - - case UNUM_MAX_FRACTION_DIGITS: - setMaximumFractionDigits(newValue); - break; - - case UNUM_MIN_FRACTION_DIGITS: - setMinimumFractionDigits(newValue); - break; - - case UNUM_FRACTION_DIGITS: - setMinimumFractionDigits(newValue); - setMaximumFractionDigits(newValue); - break; - - case UNUM_SIGNIFICANT_DIGITS_USED: - setSignificantDigitsUsed(newValue!=0); - break; - - case UNUM_MAX_SIGNIFICANT_DIGITS: - setMaximumSignificantDigits(newValue); - break; - - case UNUM_MIN_SIGNIFICANT_DIGITS: - setMinimumSignificantDigits(newValue); - break; - - case UNUM_MULTIPLIER: - setMultiplier(newValue); - break; - - case UNUM_GROUPING_SIZE: - setGroupingSize(newValue); - break; - - case UNUM_ROUNDING_MODE: - setRoundingMode((DecimalFormat::ERoundingMode)newValue); - break; - - case UNUM_FORMAT_WIDTH: - setFormatWidth(newValue); - break; - - case UNUM_PADDING_POSITION: - /** The position at which padding will take place. */ - setPadPosition((DecimalFormat::EPadPosition)newValue); - break; - - case UNUM_SECONDARY_GROUPING_SIZE: - setSecondaryGroupingSize(newValue); - break; - -#if UCONFIG_HAVE_PARSEALLINPUT - case UNUM_PARSE_ALL_INPUT: - setParseAllInput((UNumberFormatAttributeValue)newValue); - break; -#endif +/** Rebuilds the formatter object from the property bag. */ +void DecimalFormat::touch(UErrorCode& status) { + if (fields->exportedProperties == nullptr) { + // fields->exportedProperties is null only when the formatter is not ready yet. + // The only time when this happens is during legacy deserialization. + return; + } - /* These are stored in fBoolFlags */ - case UNUM_PARSE_NO_EXPONENT: - case UNUM_FORMAT_FAIL_IF_MORE_THAN_MAX_DIGITS: - case UNUM_PARSE_DECIMAL_MARK_REQUIRED: - if(!fBoolFlags.isValidValue(newValue)) { - status = U_ILLEGAL_ARGUMENT_ERROR; - } else { - if (attr == UNUM_FORMAT_FAIL_IF_MORE_THAN_MAX_DIGITS) { - fImpl->setFailIfMoreThanMaxDigits((UBool) newValue); - } - fBoolFlags.set(attr, newValue); - } - break; + // In C++, fields->symbols is the source of truth for the locale. + Locale locale = fields->symbols->getLocale(); - case UNUM_SCALE: - fImpl->setScale(newValue); - break; + // Note: The formatter is relatively cheap to create, and we need it to populate fields->exportedProperties, + // so automatically compute it here. The parser is a bit more expensive and is not needed until the + // parse method is called, so defer that until needed. + // TODO: Only update the pieces that changed instead of re-computing the whole formatter? + fields->formatter.adoptInstead( + new LocalizedNumberFormatter( + NumberPropertyMapper::create( + *fields->properties, *fields->symbols, fields->warehouse, *fields->exportedProperties, status).locale( + locale))); - case UNUM_CURRENCY_USAGE: - setCurrencyUsage((UCurrencyUsage)newValue, &status); - break; + // Do this after fields->exportedProperties are set up + setupFastFormat(); - case UNUM_MINIMUM_GROUPING_DIGITS: - setMinimumGroupingDigits(newValue); - break; + // Delete the parsers if they were made previously + delete fields->atomicParser.exchange(nullptr); + delete fields->atomicCurrencyParser.exchange(nullptr); - default: - status = U_UNSUPPORTED_ERROR; - break; - } - return *this; + // In order for the getters to work, we need to populate some fields in NumberFormat. + NumberFormat::setCurrency(fields->exportedProperties->currency.get(status).getISOCurrency(), status); + NumberFormat::setMaximumIntegerDigits(fields->exportedProperties->maximumIntegerDigits); + NumberFormat::setMinimumIntegerDigits(fields->exportedProperties->minimumIntegerDigits); + NumberFormat::setMaximumFractionDigits(fields->exportedProperties->maximumFractionDigits); + NumberFormat::setMinimumFractionDigits(fields->exportedProperties->minimumFractionDigits); + // fImpl->properties, not fields->exportedProperties, since this information comes from the pattern: + NumberFormat::setGroupingUsed(fields->properties->groupingUsed); } -int32_t DecimalFormat::getAttribute( UNumberFormatAttribute attr, - UErrorCode &status ) const { - if(U_FAILURE(status)) return -1; - switch(attr) { - case UNUM_LENIENT_PARSE: - return isLenient(); - - case UNUM_PARSE_INT_ONLY: - return isParseIntegerOnly(); - - case UNUM_GROUPING_USED: - return isGroupingUsed(); - - case UNUM_DECIMAL_ALWAYS_SHOWN: - return isDecimalSeparatorAlwaysShown(); +void DecimalFormat::touchNoError() { + UErrorCode localStatus = U_ZERO_ERROR; + touch(localStatus); +} - case UNUM_MAX_INTEGER_DIGITS: - return getMaximumIntegerDigits(); +void DecimalFormat::setPropertiesFromPattern(const UnicodeString& pattern, int32_t ignoreRounding, + UErrorCode& status) { + if (U_SUCCESS(status)) { + // Cast workaround to get around putting the enum in the public header file + auto actualIgnoreRounding = static_cast(ignoreRounding); + PatternParser::parseToExistingProperties(pattern, *fields->properties, actualIgnoreRounding, status); + } +} - case UNUM_MIN_INTEGER_DIGITS: - return getMinimumIntegerDigits(); +const numparse::impl::NumberParserImpl* DecimalFormat::getParser(UErrorCode& status) const { + if (U_FAILURE(status)) { return nullptr; } - case UNUM_INTEGER_DIGITS: - // TBD: what should this return? - return getMinimumIntegerDigits(); + // First try to get the pre-computed parser + auto* ptr = fields->atomicParser.load(); + if (ptr != nullptr) { + return ptr; + } - case UNUM_MAX_FRACTION_DIGITS: - return getMaximumFractionDigits(); + // Try computing the parser on our own + auto* temp = NumberParserImpl::createParserFromProperties(*fields->properties, *fields->symbols, false, status); + if (temp == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + // although we may still dereference, call sites should be guarded + } - case UNUM_MIN_FRACTION_DIGITS: - return getMinimumFractionDigits(); + // Note: ptr starts as nullptr; during compare_exchange, it is set to what is actually stored in the + // atomic if another thread beat us to computing the parser object. + auto* nonConstThis = const_cast(this); + if (!nonConstThis->fields->atomicParser.compare_exchange_strong(ptr, temp)) { + // Another thread beat us to computing the parser + delete temp; + return ptr; + } else { + // Our copy of the parser got stored in the atomic + return temp; + } +} - case UNUM_FRACTION_DIGITS: - // TBD: what should this return? - return getMinimumFractionDigits(); +const numparse::impl::NumberParserImpl* DecimalFormat::getCurrencyParser(UErrorCode& status) const { + if (U_FAILURE(status)) { return nullptr; } - case UNUM_SIGNIFICANT_DIGITS_USED: - return areSignificantDigitsUsed(); + // First try to get the pre-computed parser + auto* ptr = fields->atomicCurrencyParser.load(); + if (ptr != nullptr) { + return ptr; + } - case UNUM_MAX_SIGNIFICANT_DIGITS: - return getMaximumSignificantDigits(); + // Try computing the parser on our own + auto* temp = NumberParserImpl::createParserFromProperties(*fields->properties, *fields->symbols, true, status); + if (temp == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + // although we may still dereference, call sites should be guarded + } - case UNUM_MIN_SIGNIFICANT_DIGITS: - return getMinimumSignificantDigits(); + // Note: ptr starts as nullptr; during compare_exchange, it is set to what is actually stored in the + // atomic if another thread beat us to computing the parser object. + auto* nonConstThis = const_cast(this); + if (!nonConstThis->fields->atomicCurrencyParser.compare_exchange_strong(ptr, temp)) { + // Another thread beat us to computing the parser + delete temp; + return ptr; + } else { + // Our copy of the parser got stored in the atomic + return temp; + } +} - case UNUM_MULTIPLIER: - return getMultiplier(); +void +DecimalFormat::fieldPositionHelper(const number::FormattedNumber& formatted, FieldPosition& fieldPosition, + int32_t offset, UErrorCode& status) { + // always return first occurrence: + fieldPosition.setBeginIndex(0); + fieldPosition.setEndIndex(0); + bool found = formatted.nextFieldPosition(fieldPosition, status); + if (found && offset != 0) { + FieldPositionOnlyHandler fpoh(fieldPosition); + fpoh.shiftLast(offset); + } +} - case UNUM_GROUPING_SIZE: - return getGroupingSize(); +void +DecimalFormat::fieldPositionIteratorHelper(const number::FormattedNumber& formatted, FieldPositionIterator* fpi, + int32_t offset, UErrorCode& status) { + if (fpi != nullptr) { + FieldPositionIteratorHandler fpih(fpi, status); + fpih.setShift(offset); + formatted.getAllFieldPositionsImpl(fpih, status); + } +} - case UNUM_ROUNDING_MODE: - return getRoundingMode(); +// To debug fast-format, change void(x) to printf(x) +#define trace(x) void(x) - case UNUM_FORMAT_WIDTH: - return getFormatWidth(); +void DecimalFormat::setupFastFormat() { + // Check the majority of properties: + if (!fields->properties->equalsDefaultExceptFastFormat()) { + trace("no fast format: equality\n"); + fields->canUseFastFormat = false; + return; + } - case UNUM_PADDING_POSITION: - return getPadPosition(); + // Now check the remaining properties. + // Nontrivial affixes: + UBool trivialPP = fields->properties->positivePrefixPattern.isEmpty(); + UBool trivialPS = fields->properties->positiveSuffixPattern.isEmpty(); + UBool trivialNP = fields->properties->negativePrefixPattern.isBogus() || ( + fields->properties->negativePrefixPattern.length() == 1 && + fields->properties->negativePrefixPattern.charAt(0) == u'-'); + UBool trivialNS = fields->properties->negativeSuffixPattern.isEmpty(); + if (!trivialPP || !trivialPS || !trivialNP || !trivialNS) { + trace("no fast format: affixes\n"); + fields->canUseFastFormat = false; + return; + } - case UNUM_SECONDARY_GROUPING_SIZE: - return getSecondaryGroupingSize(); + // Grouping (secondary grouping is forbidden in equalsDefaultExceptFastFormat): + bool groupingUsed = fields->properties->groupingUsed; + int32_t groupingSize = fields->properties->groupingSize; + bool unusualGroupingSize = groupingSize > 0 && groupingSize != 3; + const UnicodeString& groupingString = fields->symbols->getConstSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol); + if (groupingUsed && (unusualGroupingSize || groupingString.length() != 1)) { + trace("no fast format: grouping\n"); + fields->canUseFastFormat = false; + return; + } - /* These are stored in fBoolFlags */ - case UNUM_PARSE_NO_EXPONENT: - case UNUM_FORMAT_FAIL_IF_MORE_THAN_MAX_DIGITS: - case UNUM_PARSE_DECIMAL_MARK_REQUIRED: - return fBoolFlags.get(attr); + // Integer length: + int32_t minInt = fields->exportedProperties->minimumIntegerDigits; + int32_t maxInt = fields->exportedProperties->maximumIntegerDigits; + // Fastpath supports up to only 10 digits (length of INT32_MIN) + if (minInt > 10) { + trace("no fast format: integer\n"); + fields->canUseFastFormat = false; + return; + } - case UNUM_SCALE: - return fImpl->fScale; + // Fraction length (no fraction part allowed in fast path): + int32_t minFrac = fields->exportedProperties->minimumFractionDigits; + if (minFrac > 0) { + trace("no fast format: fraction\n"); + fields->canUseFastFormat = false; + return; + } - case UNUM_CURRENCY_USAGE: - return fImpl->getCurrencyUsage(); + // Other symbols: + const UnicodeString& minusSignString = fields->symbols->getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol); + UChar32 codePointZero = fields->symbols->getCodePointZero(); + if (minusSignString.length() != 1 || U16_LENGTH(codePointZero) != 1) { + trace("no fast format: symbols\n"); + fields->canUseFastFormat = false; + return; + } - case UNUM_MINIMUM_GROUPING_DIGITS: - return getMinimumGroupingDigits(); + // Good to go! + trace("can use fast format!\n"); + fields->canUseFastFormat = true; + fields->fastData.cpZero = static_cast(codePointZero); + fields->fastData.cpGroupingSeparator = groupingUsed && groupingSize == 3 ? groupingString.charAt(0) : 0; + fields->fastData.cpMinusSign = minusSignString.charAt(0); + fields->fastData.minInt = (minInt < 0 || minInt > 127) ? 0 : static_cast(minInt); + fields->fastData.maxInt = (maxInt < 0 || maxInt > 127) ? 127 : static_cast(maxInt); +} - default: - status = U_UNSUPPORTED_ERROR; - break; - } +bool DecimalFormat::fastFormatDouble(double input, UnicodeString& output) const { + if (!fields->canUseFastFormat) { + return false; + } + if (std::isnan(input) + || std::trunc(input) != input + || input <= INT32_MIN + || input > INT32_MAX) { + return false; + } + doFastFormatInt32(static_cast(input), std::signbit(input), output); + return true; +} - return -1; /* undefined */ +bool DecimalFormat::fastFormatInt64(int64_t input, UnicodeString& output) const { + if (!fields->canUseFastFormat) { + return false; + } + if (input <= INT32_MIN || input > INT32_MAX) { + return false; + } + doFastFormatInt32(static_cast(input), input < 0, output); + return true; } -#if UCONFIG_HAVE_PARSEALLINPUT -void DecimalFormat::setParseAllInput(UNumberFormatAttributeValue value) { - fParseAllInput = value; +void DecimalFormat::doFastFormatInt32(int32_t input, bool isNegative, UnicodeString& output) const { + U_ASSERT(fields->canUseFastFormat); + if (isNegative) { + output.append(fields->fastData.cpMinusSign); + U_ASSERT(input != INT32_MIN); // handled by callers + input = -input; + } + // Cap at int32_t to make the buffer small and operations fast. + // Longest string: "2,147,483,648" (13 chars in length) + static constexpr int32_t localCapacity = 13; + char16_t localBuffer[localCapacity]; + char16_t* ptr = localBuffer + localCapacity; + int8_t group = 0; + for (int8_t i = 0; i < fields->fastData.maxInt && (input != 0 || i < fields->fastData.minInt); i++) { + if (group++ == 3 && fields->fastData.cpGroupingSeparator != 0) { + *(--ptr) = fields->fastData.cpGroupingSeparator; + group = 1; + } + std::div_t res = std::div(input, 10); + *(--ptr) = static_cast(fields->fastData.cpZero + res.rem); + input = res.quot; + } + int32_t len = localCapacity - static_cast(ptr - localBuffer); + output.append(ptr, len); } -#endif -U_NAMESPACE_END #endif /* #if !UCONFIG_NO_FORMATTING */ - -//eof diff --git a/deps/icu-small/source/i18n/decimfmtimpl.cpp b/deps/icu-small/source/i18n/decimfmtimpl.cpp deleted file mode 100644 index ef44eab0101453..00000000000000 --- a/deps/icu-small/source/i18n/decimfmtimpl.cpp +++ /dev/null @@ -1,1596 +0,0 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - * Copyright (C) 2015, International Business Machines - * Corporation and others. All Rights Reserved. - * - * file name: decimfmtimpl.cpp - */ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include -#include "unicode/numfmt.h" -#include "unicode/plurrule.h" -#include "unicode/ustring.h" -#include "decimalformatpattern.h" -#include "decimalformatpatternimpl.h" -#include "decimfmtimpl.h" -#include "fphdlimp.h" -#include "plurrule_impl.h" -#include "valueformatter.h" -#include "visibledigits.h" - -U_NAMESPACE_BEGIN - -static const int32_t kMaxScientificIntegerDigits = 8; - -static const int32_t kFormattingPosPrefix = (1 << 0); -static const int32_t kFormattingNegPrefix = (1 << 1); -static const int32_t kFormattingPosSuffix = (1 << 2); -static const int32_t kFormattingNegSuffix = (1 << 3); -static const int32_t kFormattingSymbols = (1 << 4); -static const int32_t kFormattingCurrency = (1 << 5); -static const int32_t kFormattingUsesCurrency = (1 << 6); -static const int32_t kFormattingPluralRules = (1 << 7); -static const int32_t kFormattingAffixParser = (1 << 8); -static const int32_t kFormattingCurrencyAffixInfo = (1 << 9); -static const int32_t kFormattingAll = (1 << 10) - 1; -static const int32_t kFormattingAffixes = - kFormattingPosPrefix | kFormattingPosSuffix | - kFormattingNegPrefix | kFormattingNegSuffix; -static const int32_t kFormattingAffixParserWithCurrency = - kFormattingAffixParser | kFormattingCurrencyAffixInfo; - -DecimalFormatImpl::DecimalFormatImpl( - NumberFormat *super, - const Locale &locale, - const UnicodeString &pattern, - UErrorCode &status) - : fSuper(super), - fScale(0), - fRoundingMode(DecimalFormat::kRoundHalfEven), - fSymbols(NULL), - fCurrencyUsage(UCURR_USAGE_STANDARD), - fRules(NULL), - fMonetary(FALSE) { - if (U_FAILURE(status)) { - return; - } - fSymbols = new DecimalFormatSymbols( - locale, status); - if (fSymbols == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - UParseError parseError; - applyPattern(pattern, FALSE, parseError, status); - updateAll(status); -} - -DecimalFormatImpl::DecimalFormatImpl( - NumberFormat *super, - const UnicodeString &pattern, - DecimalFormatSymbols *symbolsToAdopt, - UParseError &parseError, - UErrorCode &status) - : fSuper(super), - fScale(0), - fRoundingMode(DecimalFormat::kRoundHalfEven), - fSymbols(symbolsToAdopt), - fCurrencyUsage(UCURR_USAGE_STANDARD), - fRules(NULL), - fMonetary(FALSE) { - applyPattern(pattern, FALSE, parseError, status); - updateAll(status); -} - -DecimalFormatImpl::DecimalFormatImpl( - NumberFormat *super, const DecimalFormatImpl &other, UErrorCode &status) : - fSuper(super), - fMultiplier(other.fMultiplier), - fScale(other.fScale), - fRoundingMode(other.fRoundingMode), - fMinSigDigits(other.fMinSigDigits), - fMaxSigDigits(other.fMaxSigDigits), - fUseScientific(other.fUseScientific), - fUseSigDigits(other.fUseSigDigits), - fGrouping(other.fGrouping), - fPositivePrefixPattern(other.fPositivePrefixPattern), - fNegativePrefixPattern(other.fNegativePrefixPattern), - fPositiveSuffixPattern(other.fPositiveSuffixPattern), - fNegativeSuffixPattern(other.fNegativeSuffixPattern), - fSymbols(other.fSymbols), - fCurrencyUsage(other.fCurrencyUsage), - fRules(NULL), - fMonetary(other.fMonetary), - fAffixParser(other.fAffixParser), - fCurrencyAffixInfo(other.fCurrencyAffixInfo), - fEffPrecision(other.fEffPrecision), - fEffGrouping(other.fEffGrouping), - fOptions(other.fOptions), - fFormatter(other.fFormatter), - fAffixes(other.fAffixes) { - fSymbols = new DecimalFormatSymbols(*fSymbols); - if (fSymbols == NULL && U_SUCCESS(status)) { - status = U_MEMORY_ALLOCATION_ERROR; - } - if (other.fRules != NULL) { - fRules = new PluralRules(*other.fRules); - if (fRules == NULL && U_SUCCESS(status)) { - status = U_MEMORY_ALLOCATION_ERROR; - } - } -} - - -DecimalFormatImpl & -DecimalFormatImpl::assign(const DecimalFormatImpl &other, UErrorCode &status) { - if (U_FAILURE(status) || this == &other) { - return (*this); - } - UObject::operator=(other); - fMultiplier = other.fMultiplier; - fScale = other.fScale; - fRoundingMode = other.fRoundingMode; - fMinSigDigits = other.fMinSigDigits; - fMaxSigDigits = other.fMaxSigDigits; - fUseScientific = other.fUseScientific; - fUseSigDigits = other.fUseSigDigits; - fGrouping = other.fGrouping; - fPositivePrefixPattern = other.fPositivePrefixPattern; - fNegativePrefixPattern = other.fNegativePrefixPattern; - fPositiveSuffixPattern = other.fPositiveSuffixPattern; - fNegativeSuffixPattern = other.fNegativeSuffixPattern; - fCurrencyUsage = other.fCurrencyUsage; - fMonetary = other.fMonetary; - fAffixParser = other.fAffixParser; - fCurrencyAffixInfo = other.fCurrencyAffixInfo; - fEffPrecision = other.fEffPrecision; - fEffGrouping = other.fEffGrouping; - fOptions = other.fOptions; - fFormatter = other.fFormatter; - fAffixes = other.fAffixes; - *fSymbols = *other.fSymbols; - if (fRules != NULL && other.fRules != NULL) { - *fRules = *other.fRules; - } else { - delete fRules; - fRules = other.fRules; - if (fRules != NULL) { - fRules = new PluralRules(*fRules); - if (fRules == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return *this; - } - } - } - return *this; -} - -UBool -DecimalFormatImpl::operator==(const DecimalFormatImpl &other) const { - if (this == &other) { - return TRUE; - } - return (fMultiplier == other.fMultiplier) - && (fScale == other.fScale) - && (fRoundingMode == other.fRoundingMode) - && (fMinSigDigits == other.fMinSigDigits) - && (fMaxSigDigits == other.fMaxSigDigits) - && (fUseScientific == other.fUseScientific) - && (fUseSigDigits == other.fUseSigDigits) - && fGrouping.equals(other.fGrouping) - && fPositivePrefixPattern.equals(other.fPositivePrefixPattern) - && fNegativePrefixPattern.equals(other.fNegativePrefixPattern) - && fPositiveSuffixPattern.equals(other.fPositiveSuffixPattern) - && fNegativeSuffixPattern.equals(other.fNegativeSuffixPattern) - && fCurrencyUsage == other.fCurrencyUsage - && fAffixParser.equals(other.fAffixParser) - && fCurrencyAffixInfo.equals(other.fCurrencyAffixInfo) - && fEffPrecision.equals(other.fEffPrecision) - && fEffGrouping.equals(other.fEffGrouping) - && fOptions.equals(other.fOptions) - && fFormatter.equals(other.fFormatter) - && fAffixes.equals(other.fAffixes) - && (*fSymbols == *other.fSymbols) - && ((fRules == other.fRules) || ( - (fRules != NULL) && (other.fRules != NULL) - && (*fRules == *other.fRules))) - && (fMonetary == other.fMonetary); -} - -DecimalFormatImpl::~DecimalFormatImpl() { - delete fSymbols; - delete fRules; -} - -ValueFormatter & -DecimalFormatImpl::prepareValueFormatter(ValueFormatter &vf) const { - if (fUseScientific) { - vf.prepareScientificFormatting( - fFormatter, fEffPrecision, fOptions); - return vf; - } - vf.prepareFixedDecimalFormatting( - fFormatter, fEffGrouping, fEffPrecision.fMantissa, fOptions.fMantissa); - return vf; -} - -int32_t -DecimalFormatImpl::getPatternScale() const { - UBool usesPercent = fPositivePrefixPattern.usesPercent() || - fPositiveSuffixPattern.usesPercent() || - fNegativePrefixPattern.usesPercent() || - fNegativeSuffixPattern.usesPercent(); - if (usesPercent) { - return 2; - } - UBool usesPermill = fPositivePrefixPattern.usesPermill() || - fPositiveSuffixPattern.usesPermill() || - fNegativePrefixPattern.usesPermill() || - fNegativeSuffixPattern.usesPermill(); - if (usesPermill) { - return 3; - } - return 0; -} - -void -DecimalFormatImpl::setMultiplierScale(int32_t scale) { - if (scale == 0) { - // Needed to preserve equality. fMultiplier == 0 means - // multiplier is 1. - fMultiplier.set((int32_t)0); - } else { - fMultiplier.set((int32_t)1); - fMultiplier.shiftDecimalRight(scale); - } -} - -UnicodeString & -DecimalFormatImpl::format( - int32_t number, - UnicodeString &appendTo, - FieldPosition &pos, - UErrorCode &status) const { - FieldPositionOnlyHandler handler(pos); - return formatInt32(number, appendTo, handler, status); -} - -UnicodeString & -DecimalFormatImpl::format( - int32_t number, - UnicodeString &appendTo, - FieldPositionIterator *posIter, - UErrorCode &status) const { - FieldPositionIteratorHandler handler(posIter, status); - return formatInt32(number, appendTo, handler, status); -} - -template -UBool DecimalFormatImpl::maybeFormatWithDigitList( - T number, - UnicodeString &appendTo, - FieldPositionHandler &handler, - UErrorCode &status) const { - if (!fMultiplier.isZero()) { - DigitList digits; - digits.set(number); - digits.mult(fMultiplier, status); - digits.shiftDecimalRight(fScale); - formatAdjustedDigitList(digits, appendTo, handler, status); - return TRUE; - } - if (fScale != 0) { - DigitList digits; - digits.set(number); - digits.shiftDecimalRight(fScale); - formatAdjustedDigitList(digits, appendTo, handler, status); - return TRUE; - } - return FALSE; -} - -template -UBool DecimalFormatImpl::maybeInitVisibleDigitsFromDigitList( - T number, - VisibleDigitsWithExponent &visibleDigits, - UErrorCode &status) const { - if (!fMultiplier.isZero()) { - DigitList digits; - digits.set(number); - digits.mult(fMultiplier, status); - digits.shiftDecimalRight(fScale); - initVisibleDigitsFromAdjusted(digits, visibleDigits, status); - return TRUE; - } - if (fScale != 0) { - DigitList digits; - digits.set(number); - digits.shiftDecimalRight(fScale); - initVisibleDigitsFromAdjusted(digits, visibleDigits, status); - return TRUE; - } - return FALSE; -} - -UnicodeString & -DecimalFormatImpl::formatInt32( - int32_t number, - UnicodeString &appendTo, - FieldPositionHandler &handler, - UErrorCode &status) const { - if (maybeFormatWithDigitList(number, appendTo, handler, status)) { - return appendTo; - } - ValueFormatter vf; - return fAffixes.formatInt32( - number, - prepareValueFormatter(vf), - handler, - fRules, - appendTo, - status); -} - -UnicodeString & -DecimalFormatImpl::formatInt64( - int64_t number, - UnicodeString &appendTo, - FieldPositionHandler &handler, - UErrorCode &status) const { - if (number >= INT32_MIN && number <= INT32_MAX) { - return formatInt32((int32_t) number, appendTo, handler, status); - } - VisibleDigitsWithExponent digits; - initVisibleDigitsWithExponent(number, digits, status); - return formatVisibleDigitsWithExponent( - digits, appendTo, handler, status); -} - -UnicodeString & -DecimalFormatImpl::formatDouble( - double number, - UnicodeString &appendTo, - FieldPositionHandler &handler, - UErrorCode &status) const { - VisibleDigitsWithExponent digits; - initVisibleDigitsWithExponent(number, digits, status); - return formatVisibleDigitsWithExponent( - digits, appendTo, handler, status); -} - -UnicodeString & -DecimalFormatImpl::format( - double number, - UnicodeString &appendTo, - FieldPosition &pos, - UErrorCode &status) const { - FieldPositionOnlyHandler handler(pos); - return formatDouble(number, appendTo, handler, status); -} - -UnicodeString & -DecimalFormatImpl::format( - const DigitList &number, - UnicodeString &appendTo, - FieldPosition &pos, - UErrorCode &status) const { - DigitList dl(number); - FieldPositionOnlyHandler handler(pos); - return formatDigitList(dl, appendTo, handler, status); -} - -UnicodeString & -DecimalFormatImpl::format( - int64_t number, - UnicodeString &appendTo, - FieldPosition &pos, - UErrorCode &status) const { - FieldPositionOnlyHandler handler(pos); - return formatInt64(number, appendTo, handler, status); -} - -UnicodeString & -DecimalFormatImpl::format( - int64_t number, - UnicodeString &appendTo, - FieldPositionIterator *posIter, - UErrorCode &status) const { - FieldPositionIteratorHandler handler(posIter, status); - return formatInt64(number, appendTo, handler, status); -} - -UnicodeString & -DecimalFormatImpl::format( - double number, - UnicodeString &appendTo, - FieldPositionIterator *posIter, - UErrorCode &status) const { - FieldPositionIteratorHandler handler(posIter, status); - return formatDouble(number, appendTo, handler, status); -} - -UnicodeString & -DecimalFormatImpl::format( - const DigitList &number, - UnicodeString &appendTo, - FieldPositionIterator *posIter, - UErrorCode &status) const { - DigitList dl(number); - FieldPositionIteratorHandler handler(posIter, status); - return formatDigitList(dl, appendTo, handler, status); -} - -UnicodeString & -DecimalFormatImpl::format( - StringPiece number, - UnicodeString &appendTo, - FieldPositionIterator *posIter, - UErrorCode &status) const { - DigitList dl; - dl.set(number, status); - FieldPositionIteratorHandler handler(posIter, status); - return formatDigitList(dl, appendTo, handler, status); -} - -UnicodeString & -DecimalFormatImpl::format( - const VisibleDigitsWithExponent &digits, - UnicodeString &appendTo, - FieldPosition &pos, - UErrorCode &status) const { - FieldPositionOnlyHandler handler(pos); - return formatVisibleDigitsWithExponent( - digits, appendTo, handler, status); -} - -UnicodeString & -DecimalFormatImpl::format( - const VisibleDigitsWithExponent &digits, - UnicodeString &appendTo, - FieldPositionIterator *posIter, - UErrorCode &status) const { - FieldPositionIteratorHandler handler(posIter, status); - return formatVisibleDigitsWithExponent( - digits, appendTo, handler, status); -} - -DigitList & -DecimalFormatImpl::adjustDigitList( - DigitList &number, UErrorCode &status) const { - number.setRoundingMode(fRoundingMode); - if (!fMultiplier.isZero()) { - number.mult(fMultiplier, status); - } - if (fScale != 0) { - number.shiftDecimalRight(fScale); - } - number.reduce(); - return number; -} - -UnicodeString & -DecimalFormatImpl::formatDigitList( - DigitList &number, - UnicodeString &appendTo, - FieldPositionHandler &handler, - UErrorCode &status) const { - VisibleDigitsWithExponent digits; - initVisibleDigitsWithExponent(number, digits, status); - return formatVisibleDigitsWithExponent( - digits, appendTo, handler, status); -} - -UnicodeString & -DecimalFormatImpl::formatAdjustedDigitList( - DigitList &number, - UnicodeString &appendTo, - FieldPositionHandler &handler, - UErrorCode &status) const { - ValueFormatter vf; - return fAffixes.format( - number, - prepareValueFormatter(vf), - handler, - fRules, - appendTo, - status); -} - -UnicodeString & -DecimalFormatImpl::formatVisibleDigitsWithExponent( - const VisibleDigitsWithExponent &digits, - UnicodeString &appendTo, - FieldPositionHandler &handler, - UErrorCode &status) const { - ValueFormatter vf; - return fAffixes.format( - digits, - prepareValueFormatter(vf), - handler, - fRules, - appendTo, - status); -} - -static FixedDecimal &initFixedDecimal( - const VisibleDigits &digits, FixedDecimal &result) { - result.source = 0.0; - result.isNegative = digits.isNegative(); - result._isNaN = digits.isNaN(); - result._isInfinite = digits.isInfinite(); - digits.getFixedDecimal( - result.source, result.intValue, result.decimalDigits, - result.decimalDigitsWithoutTrailingZeros, - result.visibleDecimalDigitCount, result.hasIntegerValue); - return result; -} - -FixedDecimal & -DecimalFormatImpl::getFixedDecimal(double number, FixedDecimal &result, UErrorCode &status) const { - if (U_FAILURE(status)) { - return result; - } - VisibleDigits digits; - fEffPrecision.fMantissa.initVisibleDigits(number, digits, status); - return initFixedDecimal(digits, result); -} - -FixedDecimal & -DecimalFormatImpl::getFixedDecimal( - DigitList &number, FixedDecimal &result, UErrorCode &status) const { - if (U_FAILURE(status)) { - return result; - } - VisibleDigits digits; - fEffPrecision.fMantissa.initVisibleDigits(number, digits, status); - return initFixedDecimal(digits, result); -} - -VisibleDigitsWithExponent & -DecimalFormatImpl::initVisibleDigitsWithExponent( - int64_t number, - VisibleDigitsWithExponent &digits, - UErrorCode &status) const { - if (maybeInitVisibleDigitsFromDigitList( - number, digits, status)) { - return digits; - } - if (fUseScientific) { - fEffPrecision.initVisibleDigitsWithExponent( - number, digits, status); - } else { - fEffPrecision.fMantissa.initVisibleDigitsWithExponent( - number, digits, status); - } - return digits; -} - -VisibleDigitsWithExponent & -DecimalFormatImpl::initVisibleDigitsWithExponent( - double number, - VisibleDigitsWithExponent &digits, - UErrorCode &status) const { - if (maybeInitVisibleDigitsFromDigitList( - number, digits, status)) { - return digits; - } - if (fUseScientific) { - fEffPrecision.initVisibleDigitsWithExponent( - number, digits, status); - } else { - fEffPrecision.fMantissa.initVisibleDigitsWithExponent( - number, digits, status); - } - return digits; -} - -VisibleDigitsWithExponent & -DecimalFormatImpl::initVisibleDigitsWithExponent( - DigitList &number, - VisibleDigitsWithExponent &digits, - UErrorCode &status) const { - adjustDigitList(number, status); - return initVisibleDigitsFromAdjusted(number, digits, status); -} - -VisibleDigitsWithExponent & -DecimalFormatImpl::initVisibleDigitsFromAdjusted( - DigitList &number, - VisibleDigitsWithExponent &digits, - UErrorCode &status) const { - if (fUseScientific) { - fEffPrecision.initVisibleDigitsWithExponent( - number, digits, status); - } else { - fEffPrecision.fMantissa.initVisibleDigitsWithExponent( - number, digits, status); - } - return digits; -} - -DigitList & -DecimalFormatImpl::round( - DigitList &number, UErrorCode &status) const { - if (number.isNaN() || number.isInfinite()) { - return number; - } - adjustDigitList(number, status); - ValueFormatter vf; - prepareValueFormatter(vf); - return vf.round(number, status); -} - -void -DecimalFormatImpl::setMinimumSignificantDigits(int32_t newValue) { - fMinSigDigits = newValue; - fUseSigDigits = TRUE; // ticket 9936 - updatePrecision(); -} - -void -DecimalFormatImpl::setMaximumSignificantDigits(int32_t newValue) { - fMaxSigDigits = newValue; - fUseSigDigits = TRUE; // ticket 9936 - updatePrecision(); -} - -void -DecimalFormatImpl::setMinMaxSignificantDigits(int32_t min, int32_t max) { - fMinSigDigits = min; - fMaxSigDigits = max; - fUseSigDigits = TRUE; // ticket 9936 - updatePrecision(); -} - -void -DecimalFormatImpl::setScientificNotation(UBool newValue) { - fUseScientific = newValue; - updatePrecision(); -} - -void -DecimalFormatImpl::setSignificantDigitsUsed(UBool newValue) { - fUseSigDigits = newValue; - updatePrecision(); -} - -void -DecimalFormatImpl::setGroupingSize(int32_t newValue) { - fGrouping.fGrouping = newValue; - updateGrouping(); -} - -void -DecimalFormatImpl::setSecondaryGroupingSize(int32_t newValue) { - fGrouping.fGrouping2 = newValue; - updateGrouping(); -} - -void -DecimalFormatImpl::setMinimumGroupingDigits(int32_t newValue) { - fGrouping.fMinGrouping = newValue; - updateGrouping(); -} - -void -DecimalFormatImpl::setCurrencyUsage( - UCurrencyUsage currencyUsage, UErrorCode &status) { - fCurrencyUsage = currencyUsage; - updateFormatting(kFormattingCurrency, status); -} - -void -DecimalFormatImpl::setRoundingIncrement(double d) { - if (d > 0.0) { - fEffPrecision.fMantissa.fRoundingIncrement.set(d); - } else { - fEffPrecision.fMantissa.fRoundingIncrement.set(0.0); - } -} - -double -DecimalFormatImpl::getRoundingIncrement() const { - return fEffPrecision.fMantissa.fRoundingIncrement.getDouble(); -} - -int32_t -DecimalFormatImpl::getMultiplier() const { - if (fMultiplier.isZero()) { - return 1; - } - return (int32_t) fMultiplier.getDouble(); -} - -void -DecimalFormatImpl::setMultiplier(int32_t m) { - if (m == 0 || m == 1) { - fMultiplier.set((int32_t)0); - } else { - fMultiplier.set(m); - } -} - -void -DecimalFormatImpl::setPositivePrefix(const UnicodeString &str) { - fPositivePrefixPattern.remove(); - fPositivePrefixPattern.addLiteral(str.getBuffer(), 0, str.length()); - UErrorCode status = U_ZERO_ERROR; - updateFormatting(kFormattingPosPrefix, status); -} - -void -DecimalFormatImpl::setPositiveSuffix(const UnicodeString &str) { - fPositiveSuffixPattern.remove(); - fPositiveSuffixPattern.addLiteral(str.getBuffer(), 0, str.length()); - UErrorCode status = U_ZERO_ERROR; - updateFormatting(kFormattingPosSuffix, status); -} - -void -DecimalFormatImpl::setNegativePrefix(const UnicodeString &str) { - fNegativePrefixPattern.remove(); - fNegativePrefixPattern.addLiteral(str.getBuffer(), 0, str.length()); - UErrorCode status = U_ZERO_ERROR; - updateFormatting(kFormattingNegPrefix, status); -} - -void -DecimalFormatImpl::setNegativeSuffix(const UnicodeString &str) { - fNegativeSuffixPattern.remove(); - fNegativeSuffixPattern.addLiteral(str.getBuffer(), 0, str.length()); - UErrorCode status = U_ZERO_ERROR; - updateFormatting(kFormattingNegSuffix, status); -} - -UnicodeString & -DecimalFormatImpl::getPositivePrefix(UnicodeString &result) const { - result = fAffixes.fPositivePrefix.getOtherVariant().toString(); - return result; -} - -UnicodeString & -DecimalFormatImpl::getPositiveSuffix(UnicodeString &result) const { - result = fAffixes.fPositiveSuffix.getOtherVariant().toString(); - return result; -} - -UnicodeString & -DecimalFormatImpl::getNegativePrefix(UnicodeString &result) const { - result = fAffixes.fNegativePrefix.getOtherVariant().toString(); - return result; -} - -UnicodeString & -DecimalFormatImpl::getNegativeSuffix(UnicodeString &result) const { - result = fAffixes.fNegativeSuffix.getOtherVariant().toString(); - return result; -} - -void -DecimalFormatImpl::adoptDecimalFormatSymbols(DecimalFormatSymbols *symbolsToAdopt) { - if (symbolsToAdopt == NULL) { - return; - } - delete fSymbols; - fSymbols = symbolsToAdopt; - UErrorCode status = U_ZERO_ERROR; - updateFormatting(kFormattingSymbols, status); -} - -void -DecimalFormatImpl::applyPatternFavorCurrencyPrecision( - const UnicodeString &pattern, UErrorCode &status) { - UParseError perror; - applyPattern(pattern, FALSE, perror, status); - updateForApplyPatternFavorCurrencyPrecision(status); -} - -void -DecimalFormatImpl::applyPattern( - const UnicodeString &pattern, UErrorCode &status) { - UParseError perror; - applyPattern(pattern, FALSE, perror, status); - updateForApplyPattern(status); -} - -void -DecimalFormatImpl::applyPattern( - const UnicodeString &pattern, - UParseError &perror, UErrorCode &status) { - applyPattern(pattern, FALSE, perror, status); - updateForApplyPattern(status); -} - -void -DecimalFormatImpl::applyLocalizedPattern( - const UnicodeString &pattern, UErrorCode &status) { - UParseError perror; - applyPattern(pattern, TRUE, perror, status); - updateForApplyPattern(status); -} - -void -DecimalFormatImpl::applyLocalizedPattern( - const UnicodeString &pattern, - UParseError &perror, UErrorCode &status) { - applyPattern(pattern, TRUE, perror, status); - updateForApplyPattern(status); -} - -void -DecimalFormatImpl::applyPattern( - const UnicodeString &pattern, - UBool localized, UParseError &perror, UErrorCode &status) { - if (U_FAILURE(status)) { - return; - } - DecimalFormatPatternParser patternParser; - if (localized) { - patternParser.useSymbols(*fSymbols); - } - DecimalFormatPattern out; - patternParser.applyPatternWithoutExpandAffix( - pattern, out, perror, status); - if (U_FAILURE(status)) { - return; - } - fUseScientific = out.fUseExponentialNotation; - fUseSigDigits = out.fUseSignificantDigits; - fSuper->NumberFormat::setMinimumIntegerDigits(out.fMinimumIntegerDigits); - fSuper->NumberFormat::setMaximumIntegerDigits(out.fMaximumIntegerDigits); - fSuper->NumberFormat::setMinimumFractionDigits(out.fMinimumFractionDigits); - fSuper->NumberFormat::setMaximumFractionDigits(out.fMaximumFractionDigits); - fMinSigDigits = out.fMinimumSignificantDigits; - fMaxSigDigits = out.fMaximumSignificantDigits; - fEffPrecision.fMinExponentDigits = out.fMinExponentDigits; - fOptions.fExponent.fAlwaysShowSign = out.fExponentSignAlwaysShown; - fSuper->NumberFormat::setGroupingUsed(out.fGroupingUsed); - fGrouping.fGrouping = out.fGroupingSize; - fGrouping.fGrouping2 = out.fGroupingSize2; - fOptions.fMantissa.fAlwaysShowDecimal = out.fDecimalSeparatorAlwaysShown; - if (out.fRoundingIncrementUsed) { - fEffPrecision.fMantissa.fRoundingIncrement = out.fRoundingIncrement; - } else { - fEffPrecision.fMantissa.fRoundingIncrement.clear(); - } - fAffixes.fPadChar = out.fPad; - fNegativePrefixPattern = out.fNegPrefixAffix; - fNegativeSuffixPattern = out.fNegSuffixAffix; - fPositivePrefixPattern = out.fPosPrefixAffix; - fPositiveSuffixPattern = out.fPosSuffixAffix; - - // Work around. Pattern parsing code and DecimalFormat code don't agree - // on the definition of field width, so we have to translate from - // pattern field width to decimal format field width here. - fAffixes.fWidth = out.fFormatWidth == 0 ? 0 : - out.fFormatWidth + fPositivePrefixPattern.countChar32() - + fPositiveSuffixPattern.countChar32(); - switch (out.fPadPosition) { - case DecimalFormatPattern::kPadBeforePrefix: - fAffixes.fPadPosition = DigitAffixesAndPadding::kPadBeforePrefix; - break; - case DecimalFormatPattern::kPadAfterPrefix: - fAffixes.fPadPosition = DigitAffixesAndPadding::kPadAfterPrefix; - break; - case DecimalFormatPattern::kPadBeforeSuffix: - fAffixes.fPadPosition = DigitAffixesAndPadding::kPadBeforeSuffix; - break; - case DecimalFormatPattern::kPadAfterSuffix: - fAffixes.fPadPosition = DigitAffixesAndPadding::kPadAfterSuffix; - break; - default: - break; - } -} - -void -DecimalFormatImpl::updatePrecision() { - if (fUseScientific) { - updatePrecisionForScientific(); - } else { - updatePrecisionForFixed(); - } -} - -static void updatePrecisionForScientificMinMax( - const DigitInterval &min, - const DigitInterval &max, - DigitInterval &resultMin, - DigitInterval &resultMax, - SignificantDigitInterval &resultSignificant) { - resultMin.setIntDigitCount(0); - resultMin.setFracDigitCount(0); - resultSignificant.clear(); - resultMax.clear(); - - int32_t maxIntDigitCount = max.getIntDigitCount(); - int32_t minIntDigitCount = min.getIntDigitCount(); - int32_t maxFracDigitCount = max.getFracDigitCount(); - int32_t minFracDigitCount = min.getFracDigitCount(); - - - // Not in spec: maxIntDigitCount > 8 assume - // maxIntDigitCount = minIntDigitCount. Current DecimalFormat API has - // no provision for unsetting maxIntDigitCount which would be useful for - // scientific notation. The best we can do is assume that if - // maxIntDigitCount is the default of 2000000000 or is "big enough" then - // user did not intend to explicitly set it. The 8 was derived emperically - // by extensive testing of legacy code. - if (maxIntDigitCount > 8) { - maxIntDigitCount = minIntDigitCount; - } - - // Per the spec, exponent grouping happens if maxIntDigitCount is more - // than 1 and more than minIntDigitCount. - UBool bExponentGrouping = maxIntDigitCount > 1 && minIntDigitCount < maxIntDigitCount; - if (bExponentGrouping) { - resultMax.setIntDigitCount(maxIntDigitCount); - - // For exponent grouping minIntDigits is always treated as 1 even - // if it wasn't set to 1! - resultMin.setIntDigitCount(1); - } else { - // Fixed digit count left of decimal. minIntDigitCount doesn't have - // to equal maxIntDigitCount i.e minIntDigitCount == 0 while - // maxIntDigitCount == 1. - int32_t fixedIntDigitCount = maxIntDigitCount; - - // If fixedIntDigitCount is 0 but - // min or max fraction count is 0 too then use 1. This way we can get - // unlimited precision for X.XXXEX - if (fixedIntDigitCount == 0 && (minFracDigitCount == 0 || maxFracDigitCount == 0)) { - fixedIntDigitCount = 1; - } - resultMax.setIntDigitCount(fixedIntDigitCount); - resultMin.setIntDigitCount(fixedIntDigitCount); - } - // Spec says this is how we compute significant digits. 0 means - // unlimited significant digits. - int32_t maxSigDigits = minIntDigitCount + maxFracDigitCount; - if (maxSigDigits > 0) { - int32_t minSigDigits = minIntDigitCount + minFracDigitCount; - resultSignificant.setMin(minSigDigits); - resultSignificant.setMax(maxSigDigits); - } -} - -void -DecimalFormatImpl::updatePrecisionForScientific() { - FixedPrecision *result = &fEffPrecision.fMantissa; - if (fUseSigDigits) { - result->fMax.setFracDigitCount(-1); - result->fMax.setIntDigitCount(1); - result->fMin.setFracDigitCount(0); - result->fMin.setIntDigitCount(1); - result->fSignificant.clear(); - extractSigDigits(result->fSignificant); - return; - } - DigitInterval max; - DigitInterval min; - extractMinMaxDigits(min, max); - updatePrecisionForScientificMinMax( - min, max, - result->fMin, result->fMax, result->fSignificant); -} - -void -DecimalFormatImpl::updatePrecisionForFixed() { - FixedPrecision *result = &fEffPrecision.fMantissa; - if (!fUseSigDigits) { - extractMinMaxDigits(result->fMin, result->fMax); - result->fSignificant.clear(); - } else { - extractSigDigits(result->fSignificant); - result->fMin.setIntDigitCount(1); - result->fMin.setFracDigitCount(0); - result->fMax.clear(); - } -} - -void - DecimalFormatImpl::extractMinMaxDigits( - DigitInterval &min, DigitInterval &max) const { - min.setIntDigitCount(fSuper->getMinimumIntegerDigits()); - max.setIntDigitCount(fSuper->getMaximumIntegerDigits()); - min.setFracDigitCount(fSuper->getMinimumFractionDigits()); - max.setFracDigitCount(fSuper->getMaximumFractionDigits()); -} - -void - DecimalFormatImpl::extractSigDigits( - SignificantDigitInterval &sig) const { - sig.setMin(fMinSigDigits < 0 ? 0 : fMinSigDigits); - sig.setMax(fMaxSigDigits < 0 ? 0 : fMaxSigDigits); -} - -void -DecimalFormatImpl::updateGrouping() { - if (fSuper->isGroupingUsed()) { - fEffGrouping = fGrouping; - } else { - fEffGrouping.clear(); - } -} - -void -DecimalFormatImpl::updateCurrency(UErrorCode &status) { - updateFormatting(kFormattingCurrency, TRUE, status); -} - -void -DecimalFormatImpl::updateFormatting( - int32_t changedFormattingFields, - UErrorCode &status) { - updateFormatting(changedFormattingFields, TRUE, status); -} - -void -DecimalFormatImpl::updateFormatting( - int32_t changedFormattingFields, - UBool updatePrecisionBasedOnCurrency, - UErrorCode &status) { - if (U_FAILURE(status)) { - return; - } - // Each function updates one field. Order matters. For instance, - // updatePluralRules comes before updateCurrencyAffixInfo because the - // fRules field is needed to update the fCurrencyAffixInfo field. - updateFormattingUsesCurrency(changedFormattingFields); - updateFormattingFixedPointFormatter(changedFormattingFields); - updateFormattingAffixParser(changedFormattingFields); - updateFormattingPluralRules(changedFormattingFields, status); - updateFormattingCurrencyAffixInfo( - changedFormattingFields, - updatePrecisionBasedOnCurrency, - status); - updateFormattingLocalizedPositivePrefix( - changedFormattingFields, status); - updateFormattingLocalizedPositiveSuffix( - changedFormattingFields, status); - updateFormattingLocalizedNegativePrefix( - changedFormattingFields, status); - updateFormattingLocalizedNegativeSuffix( - changedFormattingFields, status); -} - -void -DecimalFormatImpl::updateFormattingUsesCurrency( - int32_t &changedFormattingFields) { - if ((changedFormattingFields & kFormattingAffixes) == 0) { - // If no affixes changed, don't need to do any work - return; - } - UBool newUsesCurrency = - fPositivePrefixPattern.usesCurrency() || - fPositiveSuffixPattern.usesCurrency() || - fNegativePrefixPattern.usesCurrency() || - fNegativeSuffixPattern.usesCurrency(); - if (fMonetary != newUsesCurrency) { - fMonetary = newUsesCurrency; - changedFormattingFields |= kFormattingUsesCurrency; - } -} - -void -DecimalFormatImpl::updateFormattingPluralRules( - int32_t &changedFormattingFields, UErrorCode &status) { - if ((changedFormattingFields & (kFormattingSymbols | kFormattingUsesCurrency)) == 0) { - // No work to do if both fSymbols and fMonetary - // fields are unchanged - return; - } - if (U_FAILURE(status)) { - return; - } - PluralRules *newRules = NULL; - if (fMonetary) { - newRules = PluralRules::forLocale(fSymbols->getLocale(), status); - if (U_FAILURE(status)) { - return; - } - } - // Its ok to say a field has changed when it really hasn't but not - // the other way around. Here we assume the field changed unless it - // was NULL before and is still NULL now - if (fRules != newRules) { - delete fRules; - fRules = newRules; - changedFormattingFields |= kFormattingPluralRules; - } -} - -void -DecimalFormatImpl::updateFormattingCurrencyAffixInfo( - int32_t &changedFormattingFields, - UBool updatePrecisionBasedOnCurrency, - UErrorCode &status) { - if ((changedFormattingFields & ( - kFormattingSymbols | kFormattingCurrency | - kFormattingUsesCurrency | kFormattingPluralRules)) == 0) { - // If all these fields are unchanged, no work to do. - return; - } - if (U_FAILURE(status)) { - return; - } - if (!fMonetary) { - if (fCurrencyAffixInfo.isDefault()) { - // In this case don't have to do any work - return; - } - fCurrencyAffixInfo.set(NULL, NULL, NULL, status); - if (U_FAILURE(status)) { - return; - } - changedFormattingFields |= kFormattingCurrencyAffixInfo; - } else { - const UChar *currency = fSuper->getCurrency(); - UChar localeCurr[4]; - if (currency[0] == 0) { - ucurr_forLocale(fSymbols->getLocale().getName(), localeCurr, UPRV_LENGTHOF(localeCurr), &status); - if (U_SUCCESS(status)) { - currency = localeCurr; - fSuper->NumberFormat::setCurrency(currency, status); - } else { - currency = NULL; - status = U_ZERO_ERROR; - } - } - fCurrencyAffixInfo.set( - fSymbols->getLocale().getName(), fRules, currency, status); - if (U_FAILURE(status)) { - return; - } - UBool customCurrencySymbol = FALSE; - // If DecimalFormatSymbols has custom currency symbol, prefer - // that over what we just read from the resource bundles - if (fSymbols->isCustomCurrencySymbol()) { - fCurrencyAffixInfo.setSymbol( - fSymbols->getConstSymbol(DecimalFormatSymbols::kCurrencySymbol)); - customCurrencySymbol = TRUE; - } - if (fSymbols->isCustomIntlCurrencySymbol()) { - fCurrencyAffixInfo.setISO( - fSymbols->getConstSymbol(DecimalFormatSymbols::kIntlCurrencySymbol)); - customCurrencySymbol = TRUE; - } - changedFormattingFields |= kFormattingCurrencyAffixInfo; - if (currency && !customCurrencySymbol && updatePrecisionBasedOnCurrency) { - FixedPrecision precision; - CurrencyAffixInfo::adjustPrecision( - currency, fCurrencyUsage, precision, status); - if (U_FAILURE(status)) { - return; - } - fSuper->NumberFormat::setMinimumFractionDigits( - precision.fMin.getFracDigitCount()); - fSuper->NumberFormat::setMaximumFractionDigits( - precision.fMax.getFracDigitCount()); - updatePrecision(); - fEffPrecision.fMantissa.fRoundingIncrement = - precision.fRoundingIncrement; - } - - } -} - -void -DecimalFormatImpl::updateFormattingFixedPointFormatter( - int32_t &changedFormattingFields) { - if ((changedFormattingFields & (kFormattingSymbols | kFormattingUsesCurrency)) == 0) { - // No work to do if fSymbols is unchanged - return; - } - if (fMonetary) { - fFormatter.setDecimalFormatSymbolsForMonetary(*fSymbols); - } else { - fFormatter.setDecimalFormatSymbols(*fSymbols); - } -} - -void -DecimalFormatImpl::updateFormattingAffixParser( - int32_t &changedFormattingFields) { - if ((changedFormattingFields & kFormattingSymbols) == 0) { - // No work to do if fSymbols is unchanged - return; - } - fAffixParser.setDecimalFormatSymbols(*fSymbols); - changedFormattingFields |= kFormattingAffixParser; -} - -void -DecimalFormatImpl::updateFormattingLocalizedPositivePrefix( - int32_t &changedFormattingFields, UErrorCode &status) { - if (U_FAILURE(status)) { - return; - } - if ((changedFormattingFields & ( - kFormattingPosPrefix | kFormattingAffixParserWithCurrency)) == 0) { - // No work to do - return; - } - fAffixes.fPositivePrefix.remove(); - fAffixParser.parse( - fPositivePrefixPattern, - fCurrencyAffixInfo, - fAffixes.fPositivePrefix, - status); -} - -void -DecimalFormatImpl::updateFormattingLocalizedPositiveSuffix( - int32_t &changedFormattingFields, UErrorCode &status) { - if (U_FAILURE(status)) { - return; - } - if ((changedFormattingFields & ( - kFormattingPosSuffix | kFormattingAffixParserWithCurrency)) == 0) { - // No work to do - return; - } - fAffixes.fPositiveSuffix.remove(); - fAffixParser.parse( - fPositiveSuffixPattern, - fCurrencyAffixInfo, - fAffixes.fPositiveSuffix, - status); -} - -void -DecimalFormatImpl::updateFormattingLocalizedNegativePrefix( - int32_t &changedFormattingFields, UErrorCode &status) { - if (U_FAILURE(status)) { - return; - } - if ((changedFormattingFields & ( - kFormattingNegPrefix | kFormattingAffixParserWithCurrency)) == 0) { - // No work to do - return; - } - fAffixes.fNegativePrefix.remove(); - fAffixParser.parse( - fNegativePrefixPattern, - fCurrencyAffixInfo, - fAffixes.fNegativePrefix, - status); -} - -void -DecimalFormatImpl::updateFormattingLocalizedNegativeSuffix( - int32_t &changedFormattingFields, UErrorCode &status) { - if (U_FAILURE(status)) { - return; - } - if ((changedFormattingFields & ( - kFormattingNegSuffix | kFormattingAffixParserWithCurrency)) == 0) { - // No work to do - return; - } - fAffixes.fNegativeSuffix.remove(); - fAffixParser.parse( - fNegativeSuffixPattern, - fCurrencyAffixInfo, - fAffixes.fNegativeSuffix, - status); -} - -void -DecimalFormatImpl::updateForApplyPatternFavorCurrencyPrecision( - UErrorCode &status) { - updateAll(kFormattingAll & ~kFormattingSymbols, TRUE, status); -} - -void -DecimalFormatImpl::updateForApplyPattern(UErrorCode &status) { - updateAll(kFormattingAll & ~kFormattingSymbols, FALSE, status); -} - -void -DecimalFormatImpl::updateAll(UErrorCode &status) { - updateAll(kFormattingAll, TRUE, status); -} - -void -DecimalFormatImpl::updateAll( - int32_t formattingFlags, - UBool updatePrecisionBasedOnCurrency, - UErrorCode &status) { - if (U_FAILURE(status)) { - return; - } - updatePrecision(); - updateGrouping(); - updateFormatting( - formattingFlags, updatePrecisionBasedOnCurrency, status); - setMultiplierScale(getPatternScale()); -} - - -static int32_t -getMinimumLengthToDescribeGrouping(const DigitGrouping &grouping) { - if (grouping.fGrouping <= 0) { - return 0; - } - if (grouping.fGrouping2 <= 0) { - return grouping.fGrouping + 1; - } - return grouping.fGrouping + grouping.fGrouping2 + 1; -} - -/** - * Given a grouping policy, calculates how many digits are needed left of - * the decimal point to achieve a desired length left of the - * decimal point. - * @param grouping the grouping policy - * @param desiredLength number of characters needed left of decimal point - * @param minLeftDigits at least this many digits is returned - * @param leftDigits the number of digits needed stored here - * which is >= minLeftDigits. - * @return true if a perfect fit or false if having leftDigits would exceed - * desiredLength - */ -static UBool -getLeftDigitsForLeftLength( - const DigitGrouping &grouping, - int32_t desiredLength, - int32_t minLeftDigits, - int32_t &leftDigits) { - leftDigits = minLeftDigits; - int32_t lengthSoFar = leftDigits + grouping.getSeparatorCount(leftDigits); - while (lengthSoFar < desiredLength) { - lengthSoFar += grouping.isSeparatorAt(leftDigits + 1, leftDigits) ? 2 : 1; - ++leftDigits; - } - return (lengthSoFar == desiredLength); -} - -int32_t -DecimalFormatImpl::computeExponentPatternLength() const { - if (fUseScientific) { - return 1 + (fOptions.fExponent.fAlwaysShowSign ? 1 : 0) + fEffPrecision.fMinExponentDigits; - } - return 0; -} - -int32_t -DecimalFormatImpl::countFractionDigitAndDecimalPatternLength( - int32_t fracDigitCount) const { - if (!fOptions.fMantissa.fAlwaysShowDecimal && fracDigitCount == 0) { - return 0; - } - return fracDigitCount + 1; -} - -UnicodeString& -DecimalFormatImpl::toNumberPattern( - UBool hasPadding, int32_t minimumLength, UnicodeString& result) const { - // Get a grouping policy like the one in this object that does not - // have minimum grouping since toPattern doesn't support it. - DigitGrouping grouping(fEffGrouping); - grouping.fMinGrouping = 0; - - // Only for fixed digits, these are the digits that get 0's. - DigitInterval minInterval; - - // Only for fixed digits, these are the digits that get #'s. - DigitInterval maxInterval; - - // Only for significant digits - int32_t sigMin = 0; /* initialize to avoid compiler warning */ - int32_t sigMax = 0; /* initialize to avoid compiler warning */ - - // These are all the digits to be displayed. For significant digits, - // this interval always starts at the 1's place an extends left. - DigitInterval fullInterval; - - // Digit range of rounding increment. If rounding increment is .025. - // then roundingIncrementLowerExp = -3 and roundingIncrementUpperExp = -1 - int32_t roundingIncrementLowerExp = 0; - int32_t roundingIncrementUpperExp = 0; - - if (fUseSigDigits) { - SignificantDigitInterval sigInterval; - extractSigDigits(sigInterval); - sigMax = sigInterval.getMax(); - sigMin = sigInterval.getMin(); - fullInterval.setFracDigitCount(0); - fullInterval.setIntDigitCount(sigMax); - } else { - extractMinMaxDigits(minInterval, maxInterval); - if (fUseScientific) { - if (maxInterval.getIntDigitCount() > kMaxScientificIntegerDigits) { - maxInterval.setIntDigitCount(1); - minInterval.shrinkToFitWithin(maxInterval); - } - } else if (hasPadding) { - // Make max int digits match min int digits for now, we - // compute necessary padding later. - maxInterval.setIntDigitCount(minInterval.getIntDigitCount()); - } else { - // For some reason toPattern adds at least one leading '#' - maxInterval.setIntDigitCount(minInterval.getIntDigitCount() + 1); - } - if (!fEffPrecision.fMantissa.fRoundingIncrement.isZero()) { - roundingIncrementLowerExp = - fEffPrecision.fMantissa.fRoundingIncrement.getLowerExponent(); - roundingIncrementUpperExp = - fEffPrecision.fMantissa.fRoundingIncrement.getUpperExponent(); - // We have to include the rounding increment in what we display - maxInterval.expandToContainDigit(roundingIncrementLowerExp); - maxInterval.expandToContainDigit(roundingIncrementUpperExp - 1); - } - fullInterval = maxInterval; - } - // We have to include enough digits to show grouping strategy - int32_t minLengthToDescribeGrouping = - getMinimumLengthToDescribeGrouping(grouping); - if (minLengthToDescribeGrouping > 0) { - fullInterval.expandToContainDigit( - getMinimumLengthToDescribeGrouping(grouping) - 1); - } - - // If we have a minimum length, we have to add digits to the left to - // depict padding. - if (hasPadding) { - // For non scientific notation, - // minimumLengthForMantissa = minimumLength - int32_t minimumLengthForMantissa = - minimumLength - computeExponentPatternLength(); - int32_t mininumLengthForMantissaIntPart = - minimumLengthForMantissa - - countFractionDigitAndDecimalPatternLength( - fullInterval.getFracDigitCount()); - // Because of grouping, we may need fewer than expected digits to - // achieve the length we need. - int32_t digitsNeeded; - if (getLeftDigitsForLeftLength( - grouping, - mininumLengthForMantissaIntPart, - fullInterval.getIntDigitCount(), - digitsNeeded)) { - - // In this case, we achieved the exact length that we want. - fullInterval.setIntDigitCount(digitsNeeded); - } else if (digitsNeeded > fullInterval.getIntDigitCount()) { - - // Having digitsNeeded digits goes over desired length which - // means that to have desired length would mean starting on a - // grouping sepearator e.g ,###,### so add a '#' and use one - // less digit. This trick gives ####,### but that is the best - // we can do. - result.append(kPatternDigit); - fullInterval.setIntDigitCount(digitsNeeded - 1); - } - } - int32_t maxDigitPos = fullInterval.getMostSignificantExclusive(); - int32_t minDigitPos = fullInterval.getLeastSignificantInclusive(); - for (int32_t i = maxDigitPos - 1; i >= minDigitPos; --i) { - if (!fOptions.fMantissa.fAlwaysShowDecimal && i == -1) { - result.append(kPatternDecimalSeparator); - } - if (fUseSigDigits) { - // Use digit symbol - if (i >= sigMax || i < sigMax - sigMin) { - result.append(kPatternDigit); - } else { - result.append(kPatternSignificantDigit); - } - } else { - if (i < roundingIncrementUpperExp && i >= roundingIncrementLowerExp) { - result.append((UChar)(fEffPrecision.fMantissa.fRoundingIncrement.getDigitByExponent(i) + kPatternZeroDigit)); - } else if (minInterval.contains(i)) { - result.append(kPatternZeroDigit); - } else { - result.append(kPatternDigit); - } - } - if (grouping.isSeparatorAt(i + 1, i)) { - result.append(kPatternGroupingSeparator); - } - if (fOptions.fMantissa.fAlwaysShowDecimal && i == 0) { - result.append(kPatternDecimalSeparator); - } - } - if (fUseScientific) { - result.append(kPatternExponent); - if (fOptions.fExponent.fAlwaysShowSign) { - result.append(kPatternPlus); - } - for (int32_t i = 0; i < 1 || i < fEffPrecision.fMinExponentDigits; ++i) { - result.append(kPatternZeroDigit); - } - } - return result; -} - -UnicodeString& -DecimalFormatImpl::toPattern(UnicodeString& result) const { - result.remove(); - UnicodeString padSpec; - if (fAffixes.fWidth > 0) { - padSpec.append(kPatternPadEscape); - padSpec.append(fAffixes.fPadChar); - } - if (fAffixes.fPadPosition == DigitAffixesAndPadding::kPadBeforePrefix) { - result.append(padSpec); - } - fPositivePrefixPattern.toUserString(result); - if (fAffixes.fPadPosition == DigitAffixesAndPadding::kPadAfterPrefix) { - result.append(padSpec); - } - toNumberPattern( - fAffixes.fWidth > 0, - fAffixes.fWidth - fPositivePrefixPattern.countChar32() - fPositiveSuffixPattern.countChar32(), - result); - if (fAffixes.fPadPosition == DigitAffixesAndPadding::kPadBeforeSuffix) { - result.append(padSpec); - } - fPositiveSuffixPattern.toUserString(result); - if (fAffixes.fPadPosition == DigitAffixesAndPadding::kPadAfterSuffix) { - result.append(padSpec); - } - AffixPattern withNegative; - withNegative.add(AffixPattern::kNegative); - withNegative.append(fPositivePrefixPattern); - if (!fPositiveSuffixPattern.equals(fNegativeSuffixPattern) || - !withNegative.equals(fNegativePrefixPattern)) { - result.append(kPatternSeparator); - if (fAffixes.fPadPosition == DigitAffixesAndPadding::kPadBeforePrefix) { - result.append(padSpec); - } - fNegativePrefixPattern.toUserString(result); - if (fAffixes.fPadPosition == DigitAffixesAndPadding::kPadAfterPrefix) { - result.append(padSpec); - } - toNumberPattern( - fAffixes.fWidth > 0, - fAffixes.fWidth - fNegativePrefixPattern.countChar32() - fNegativeSuffixPattern.countChar32(), - result); - if (fAffixes.fPadPosition == DigitAffixesAndPadding::kPadBeforeSuffix) { - result.append(padSpec); - } - fNegativeSuffixPattern.toUserString(result); - if (fAffixes.fPadPosition == DigitAffixesAndPadding::kPadAfterSuffix) { - result.append(padSpec); - } - } - return result; -} - -int32_t -DecimalFormatImpl::getOldFormatWidth() const { - if (fAffixes.fWidth == 0) { - return 0; - } - return fAffixes.fWidth - fPositiveSuffixPattern.countChar32() - fPositivePrefixPattern.countChar32(); -} - -const UnicodeString & -DecimalFormatImpl::getConstSymbol( - DecimalFormatSymbols::ENumberFormatSymbol symbol) const { - return fSymbols->getConstSymbol(symbol); -} - -UBool -DecimalFormatImpl::isParseFastpath() const { - AffixPattern negative; - negative.add(AffixPattern::kNegative); - - return fAffixes.fWidth == 0 && - fPositivePrefixPattern.countChar32() == 0 && - fNegativePrefixPattern.equals(negative) && - fPositiveSuffixPattern.countChar32() == 0 && - fNegativeSuffixPattern.countChar32() == 0; -} - - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/decimfmtimpl.h b/deps/icu-small/source/i18n/decimfmtimpl.h deleted file mode 100644 index b4438cba9e1ea8..00000000000000 --- a/deps/icu-small/source/i18n/decimfmtimpl.h +++ /dev/null @@ -1,549 +0,0 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************** -* Copyright (C) 2015, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************** -* -* File decimfmtimpl.h -******************************************************************************** -*/ - -#ifndef DECIMFMTIMPL_H -#define DECIMFMTIMPL_H - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/decimfmt.h" -#include "unicode/uobject.h" -#include "affixpatternparser.h" -#include "digitaffixesandpadding.h" -#include "digitformatter.h" -#include "digitgrouping.h" -#include "precision.h" - -U_NAMESPACE_BEGIN - -class UnicodeString; -class FieldPosition; -class ValueFormatter; -class FieldPositionHandler; -class FixedDecimal; - -/** - * DecimalFormatImpl is the glue code between the legacy DecimalFormat class - * and the new decimal formatting classes. DecimalFormat still handles - * parsing directly. However, DecimalFormat uses attributes of this class - * for parsing when possible. - * - * The public API of this class closely mirrors the legacy API of the - * legacy DecimalFormat deviating only when the legacy API does not make - * sense. For example, although DecimalFormat has a - * getPadCharacterString() method, DecimalFormatImpl has a getPadCharacter() - * method because formatting uses only a single pad character for padding. - * - * Each legacy DecimalFormat instance heap allocates its own instance of - * this class. Most DecimalFormat methods that deal with formatting simply - * delegate to the DecimalFormat's DecimalFormatImpl method. - * - * Because DecimalFormat extends NumberFormat, Each instance of this class - * "borrows" a pointer to the NumberFormat part of its enclosing DecimalFormat - * instance. This way each DecimalFormatImpl instance can read or even modify - * the NumberFormat portion of its enclosing DecimalFormat instance. - * - * Directed acyclic graph (DAG): - * - * This class can be represented as a directed acyclic graph (DAG) where each - * vertex is an attribute, and each directed edge indicates that the value - * of the destination attribute is calculated from the value of the source - * attribute. Attributes with setter methods reside at the bottom of the - * DAG. That is, no edges point to them. We call these independent attributes - * because their values can be set independently of one another. The rest of - * the attributes are derived attributes because their values depend on the - * independent attributes. DecimalFormatImpl often uses the derived - * attributes, not the independent attributes, when formatting numbers. - * - * The independent attributes at the bottom of the DAG correspond to the legacy - * attributes of DecimalFormat while the attributes at the top of the DAG - * correspond to the attributes of the new code. The edges of the DAG - * correspond to the code that handles the complex interaction among all the - * legacy attributes of the DecimalFormat API. - * - * We use a DAG for three reasons. - * - * First, the DAG preserves backward compatibility. Clients of the legacy - * DecimalFormat expect existing getters and setters of each attribute to be - * consistent. That means if a client sets a particular attribute to a new - * value, the attribute should retain that value until the client sets it to - * a new value. The DAG allows these attributes to remain consistent even - * though the new code may not use them when formatting. - * - * Second, the DAG obviates the need to recalculate derived attributes with - * each format. Instead, the DAG "remembers" the values of all derived - * attributes. Only setting an independent attribute requires a recalculation. - * Moreover, setting an independent attribute recalculates only the affected - * dependent attributes rather than all dependent attributes. - * - * Third, the DAG abstracts away the complex interaction among the legacy - * attributes of the DecimalFormat API. - * - * Only the independent attributes of the DAG have setters and getters. - * Derived attributes have no setters (and often no getters either). - * - * Copy and assign: - * - * For copy and assign, DecimalFormatImpl copies and assigns every attribute - * regardless of whether or not it is independent. We do this for simplicity. - * - * Implementation of the DAG: - * - * The DAG consists of three smaller DAGs: - * 1. Grouping attributes - * 2. Precision attributes - * 3. Formatting attributes. - * - * The first two DAGs are simple in that setting any independent attribute - * in the DAG recalculates all the dependent attributes in that DAG. - * The updateGrouping() and updatePrecision() perform the respective - * recalculations. - * - * Because some of the derived formatting attributes are expensive to - * calculate, the formatting attributes DAG is more complex. The - * updateFormatting() method is composed of many updateFormattingXXX() - * methods, each of which recalculates a single derived attribute. The - * updateFormatting() method accepts a bitfield of recently changed - * attributes and passes this bitfield by reference to each of the - * updateFormattingXXX() methods. Each updateFormattingXXX() method checks - * the bitfield to see if any of the attributes it uses to compute the XXX - * attribute changed. If none of them changed, it exists immediately. However, - * if at least one of them changed, it recalculates the XXX attribute and - * sets the corresponding bit in the bitfield. In this way, each - * updateFormattingXXX() method encodes the directed edges in the formatting - * DAG that point to the attribute its calculating. - * - * Maintenance of the updateFormatting() method. - * - * Use care when changing the updateFormatting() method. - * The updateFormatting() method must call each updateFormattingXXX() in the - * same partial order that the formatting DAG prescribes. That is, the - * attributes near the bottom of the DAG must be calculated before attributes - * further up. As we mentioned in the prvious paragraph, the directed edges of - * the formatting DAG are encoded within each updateFormattingXXX() method. - * Finally, adding new attributes may involve adding to the bitmap that the - * updateFormatting() method uses. The top most attributes in the DAG, - * those that do not point to any attributes but only have attributes - * pointing to it, need not have a slot in the bitmap. - * - * Keep in mind that most of the code that makes the legacy DecimalFormat API - * work the way it always has before can be found in these various updateXXX() - * methods. For example the updatePrecisionForScientific() method - * handles the complex interactions amoung the various precision attributes - * when formatting in scientific notation. Changing the way attributes - * interract, often means changing one of these updateXXX() methods. - * - * Conclusion: - * - * The DecimFmtImpl class is the glue code between the legacy and new - * number formatting code. It uses a direct acyclic graph (DAG) to - * maintain backward compatibility, to make the code efficient, and to - * abstract away the complex interraction among legacy attributs. - */ - - -class DecimalFormatImpl : public UObject { -public: - -DecimalFormatImpl( - NumberFormat *super, - const Locale &locale, - const UnicodeString &pattern, - UErrorCode &status); -DecimalFormatImpl( - NumberFormat *super, - const UnicodeString &pattern, - DecimalFormatSymbols *symbolsToAdopt, - UParseError &parseError, - UErrorCode &status); -DecimalFormatImpl( - NumberFormat *super, - const DecimalFormatImpl &other, - UErrorCode &status); -DecimalFormatImpl &assign( - const DecimalFormatImpl &other, UErrorCode &status); -virtual ~DecimalFormatImpl(); -void adoptDecimalFormatSymbols(DecimalFormatSymbols *symbolsToAdopt); -const DecimalFormatSymbols &getDecimalFormatSymbols() const { - return *fSymbols; -} -UnicodeString &format( - int32_t number, - UnicodeString &appendTo, - FieldPosition &pos, - UErrorCode &status) const; -UnicodeString &format( - int32_t number, - UnicodeString &appendTo, - FieldPositionIterator *posIter, - UErrorCode &status) const; -UnicodeString &format( - int64_t number, - UnicodeString &appendTo, - FieldPosition &pos, - UErrorCode &status) const; -UnicodeString &format( - double number, - UnicodeString &appendTo, - FieldPosition &pos, - UErrorCode &status) const; -UnicodeString &format( - const DigitList &number, - UnicodeString &appendTo, - FieldPosition &pos, - UErrorCode &status) const; -UnicodeString &format( - int64_t number, - UnicodeString &appendTo, - FieldPositionIterator *posIter, - UErrorCode &status) const; -UnicodeString &format( - double number, - UnicodeString &appendTo, - FieldPositionIterator *posIter, - UErrorCode &status) const; -UnicodeString &format( - const DigitList &number, - UnicodeString &appendTo, - FieldPositionIterator *posIter, - UErrorCode &status) const; -UnicodeString &format( - StringPiece number, - UnicodeString &appendTo, - FieldPositionIterator *posIter, - UErrorCode &status) const; -UnicodeString &format( - const VisibleDigitsWithExponent &digits, - UnicodeString &appendTo, - FieldPosition &pos, - UErrorCode &status) const; -UnicodeString &format( - const VisibleDigitsWithExponent &digits, - UnicodeString &appendTo, - FieldPositionIterator *posIter, - UErrorCode &status) const; - -UBool operator==(const DecimalFormatImpl &) const; - -UBool operator!=(const DecimalFormatImpl &other) const { - return !(*this == other); -} - -void setRoundingMode(DecimalFormat::ERoundingMode mode) { - fRoundingMode = mode; - fEffPrecision.fMantissa.fExactOnly = (fRoundingMode == DecimalFormat::kRoundUnnecessary); - fEffPrecision.fMantissa.fRoundingMode = mode; -} -DecimalFormat::ERoundingMode getRoundingMode() const { - return fRoundingMode; -} -void setFailIfMoreThanMaxDigits(UBool b) { - fEffPrecision.fMantissa.fFailIfOverMax = b; -} -UBool isFailIfMoreThanMaxDigits() const { return fEffPrecision.fMantissa.fFailIfOverMax; } -void setMinimumSignificantDigits(int32_t newValue); -void setMaximumSignificantDigits(int32_t newValue); -void setMinMaxSignificantDigits(int32_t min, int32_t max); -void setScientificNotation(UBool newValue); -void setSignificantDigitsUsed(UBool newValue); - -int32_t getMinimumSignificantDigits() const { - return fMinSigDigits; } -int32_t getMaximumSignificantDigits() const { - return fMaxSigDigits; } -UBool isScientificNotation() const { return fUseScientific; } -UBool areSignificantDigitsUsed() const { return fUseSigDigits; } -void setGroupingSize(int32_t newValue); -void setSecondaryGroupingSize(int32_t newValue); -void setMinimumGroupingDigits(int32_t newValue); -int32_t getGroupingSize() const { return fGrouping.fGrouping; } -int32_t getSecondaryGroupingSize() const { return fGrouping.fGrouping2; } -int32_t getMinimumGroupingDigits() const { return fGrouping.fMinGrouping; } -void applyPattern(const UnicodeString &pattern, UErrorCode &status); -void applyPatternFavorCurrencyPrecision( - const UnicodeString &pattern, UErrorCode &status); -void applyPattern( - const UnicodeString &pattern, UParseError &perror, UErrorCode &status); -void applyLocalizedPattern(const UnicodeString &pattern, UErrorCode &status); -void applyLocalizedPattern( - const UnicodeString &pattern, UParseError &perror, UErrorCode &status); -void setCurrencyUsage(UCurrencyUsage usage, UErrorCode &status); -UCurrencyUsage getCurrencyUsage() const { return fCurrencyUsage; } -void setRoundingIncrement(double d); -double getRoundingIncrement() const; -int32_t getMultiplier() const; -void setMultiplier(int32_t m); -UChar32 getPadCharacter() const { return fAffixes.fPadChar; } -void setPadCharacter(UChar32 c) { fAffixes.fPadChar = c; } -int32_t getFormatWidth() const { return fAffixes.fWidth; } -void setFormatWidth(int32_t x) { fAffixes.fWidth = x; } -DigitAffixesAndPadding::EPadPosition getPadPosition() const { - return fAffixes.fPadPosition; -} -void setPadPosition(DigitAffixesAndPadding::EPadPosition x) { - fAffixes.fPadPosition = x; -} -int32_t getMinimumExponentDigits() const { - return fEffPrecision.fMinExponentDigits; -} -void setMinimumExponentDigits(int32_t x) { - fEffPrecision.fMinExponentDigits = x; -} -UBool isExponentSignAlwaysShown() const { - return fOptions.fExponent.fAlwaysShowSign; -} -void setExponentSignAlwaysShown(UBool x) { - fOptions.fExponent.fAlwaysShowSign = x; -} -UBool isDecimalSeparatorAlwaysShown() const { - return fOptions.fMantissa.fAlwaysShowDecimal; -} -void setDecimalSeparatorAlwaysShown(UBool x) { - fOptions.fMantissa.fAlwaysShowDecimal = x; -} -UnicodeString &getPositivePrefix(UnicodeString &result) const; -UnicodeString &getPositiveSuffix(UnicodeString &result) const; -UnicodeString &getNegativePrefix(UnicodeString &result) const; -UnicodeString &getNegativeSuffix(UnicodeString &result) const; -void setPositivePrefix(const UnicodeString &str); -void setPositiveSuffix(const UnicodeString &str); -void setNegativePrefix(const UnicodeString &str); -void setNegativeSuffix(const UnicodeString &str); -UnicodeString &toPattern(UnicodeString& result) const; -FixedDecimal &getFixedDecimal(double value, FixedDecimal &result, UErrorCode &status) const; -FixedDecimal &getFixedDecimal(DigitList &number, FixedDecimal &result, UErrorCode &status) const; -DigitList &round(DigitList &number, UErrorCode &status) const; - -VisibleDigitsWithExponent & -initVisibleDigitsWithExponent( - int64_t number, - VisibleDigitsWithExponent &digits, - UErrorCode &status) const; -VisibleDigitsWithExponent & -initVisibleDigitsWithExponent( - double number, - VisibleDigitsWithExponent &digits, - UErrorCode &status) const; -VisibleDigitsWithExponent & -initVisibleDigitsWithExponent( - DigitList &number, - VisibleDigitsWithExponent &digits, - UErrorCode &status) const; - -void updatePrecision(); -void updateGrouping(); -void updateCurrency(UErrorCode &status); - - -private: -// Disallow copy and assign -DecimalFormatImpl(const DecimalFormatImpl &other); -DecimalFormatImpl &operator=(const DecimalFormatImpl &other); -NumberFormat *fSuper; -DigitList fMultiplier; -int32_t fScale; - -DecimalFormat::ERoundingMode fRoundingMode; - -// These fields include what the user can see and set. -// When the user updates these fields, it triggers automatic updates of -// other fields that may be invisible to user - -// Updating any of the following fields triggers an update to -// fEffPrecision.fMantissa.fMin, -// fEffPrecision.fMantissa.fMax, -// fEffPrecision.fMantissa.fSignificant fields -// We have this two phase update because of backward compatibility. -// DecimalFormat has to remember all settings even if those settings are -// invalid or disabled. -int32_t fMinSigDigits; -int32_t fMaxSigDigits; -UBool fUseScientific; -UBool fUseSigDigits; -// In addition to these listed above, changes to min/max int digits and -// min/max frac digits from fSuper also trigger an update. - -// Updating any of the following fields triggers an update to -// fEffGrouping field Again we do it this way because original -// grouping settings have to be retained if grouping is turned off. -DigitGrouping fGrouping; -// In addition to these listed above, changes to isGroupingUsed in -// fSuper also triggers an update to fEffGrouping. - -// Updating any of the following fields triggers updates on the following: -// fMonetary, fRules, fAffixParser, fCurrencyAffixInfo, -// fFormatter, fAffixes.fPositivePrefiix, fAffixes.fPositiveSuffix, -// fAffixes.fNegativePrefiix, fAffixes.fNegativeSuffix -// We do this two phase update because localizing the affix patterns -// and formatters can be expensive. Better to do it once with the setters -// than each time within format. -AffixPattern fPositivePrefixPattern; -AffixPattern fNegativePrefixPattern; -AffixPattern fPositiveSuffixPattern; -AffixPattern fNegativeSuffixPattern; -DecimalFormatSymbols *fSymbols; -UCurrencyUsage fCurrencyUsage; -// In addition to these listed above, changes to getCurrency() in -// fSuper also triggers an update. - -// Optional may be NULL -PluralRules *fRules; - -// These fields are totally hidden from user and are used to derive the affixes -// in fAffixes below from the four affix patterns above. -UBool fMonetary; -AffixPatternParser fAffixParser; -CurrencyAffixInfo fCurrencyAffixInfo; - -// The actual precision used when formatting -ScientificPrecision fEffPrecision; - -// The actual grouping used when formatting -DigitGrouping fEffGrouping; -SciFormatterOptions fOptions; // Encapsulates fixed precision options -DigitFormatter fFormatter; -DigitAffixesAndPadding fAffixes; - -UnicodeString &formatInt32( - int32_t number, - UnicodeString &appendTo, - FieldPositionHandler &handler, - UErrorCode &status) const; - -UnicodeString &formatInt64( - int64_t number, - UnicodeString &appendTo, - FieldPositionHandler &handler, - UErrorCode &status) const; - -UnicodeString &formatDouble( - double number, - UnicodeString &appendTo, - FieldPositionHandler &handler, - UErrorCode &status) const; - -// Scales for precent or permille symbols -UnicodeString &formatDigitList( - DigitList &number, - UnicodeString &appendTo, - FieldPositionHandler &handler, - UErrorCode &status) const; - -// Does not scale for precent or permille symbols -UnicodeString &formatAdjustedDigitList( - DigitList &number, - UnicodeString &appendTo, - FieldPositionHandler &handler, - UErrorCode &status) const; - -UnicodeString &formatVisibleDigitsWithExponent( - const VisibleDigitsWithExponent &number, - UnicodeString &appendTo, - FieldPositionHandler &handler, - UErrorCode &status) const; - -VisibleDigitsWithExponent & -initVisibleDigitsFromAdjusted( - DigitList &number, - VisibleDigitsWithExponent &digits, - UErrorCode &status) const; - -template -UBool maybeFormatWithDigitList( - T number, - UnicodeString &appendTo, - FieldPositionHandler &handler, - UErrorCode &status) const; - -template -UBool maybeInitVisibleDigitsFromDigitList( - T number, - VisibleDigitsWithExponent &digits, - UErrorCode &status) const; - -DigitList &adjustDigitList(DigitList &number, UErrorCode &status) const; - -void applyPattern( - const UnicodeString &pattern, - UBool localized, UParseError &perror, UErrorCode &status); - -ValueFormatter &prepareValueFormatter(ValueFormatter &vf) const; -void setMultiplierScale(int32_t s); -int32_t getPatternScale() const; -void setScale(int32_t s) { fScale = s; } -int32_t getScale() const { return fScale; } - -// Updates everything -void updateAll(UErrorCode &status); -void updateAll( - int32_t formattingFlags, - UBool updatePrecisionBasedOnCurrency, - UErrorCode &status); - -// Updates from formatting pattern changes -void updateForApplyPattern(UErrorCode &status); -void updateForApplyPatternFavorCurrencyPrecision(UErrorCode &status); - -// Updates from changes to third group of attributes -void updateFormatting(int32_t changedFormattingFields, UErrorCode &status); -void updateFormatting( - int32_t changedFormattingFields, - UBool updatePrecisionBasedOnCurrency, - UErrorCode &status); - -// Helper functions for updatePrecision -void updatePrecisionForScientific(); -void updatePrecisionForFixed(); -void extractMinMaxDigits(DigitInterval &min, DigitInterval &max) const; -void extractSigDigits(SignificantDigitInterval &sig) const; - -// Helper functions for updateFormatting -void updateFormattingUsesCurrency(int32_t &changedFormattingFields); -void updateFormattingPluralRules( - int32_t &changedFormattingFields, UErrorCode &status); -void updateFormattingAffixParser(int32_t &changedFormattingFields); -void updateFormattingCurrencyAffixInfo( - int32_t &changedFormattingFields, - UBool updatePrecisionBasedOnCurrency, - UErrorCode &status); -void updateFormattingFixedPointFormatter( - int32_t &changedFormattingFields); -void updateFormattingLocalizedPositivePrefix( - int32_t &changedFormattingFields, UErrorCode &status); -void updateFormattingLocalizedPositiveSuffix( - int32_t &changedFormattingFields, UErrorCode &status); -void updateFormattingLocalizedNegativePrefix( - int32_t &changedFormattingFields, UErrorCode &status); -void updateFormattingLocalizedNegativeSuffix( - int32_t &changedFormattingFields, UErrorCode &status); - -int32_t computeExponentPatternLength() const; -int32_t countFractionDigitAndDecimalPatternLength(int32_t fracDigitCount) const; -UnicodeString &toNumberPattern( - UBool hasPadding, int32_t minimumLength, UnicodeString& result) const; - -int32_t getOldFormatWidth() const; -const UnicodeString &getConstSymbol( - DecimalFormatSymbols::ENumberFormatSymbol symbol) const; -UBool isParseFastpath() const; - -friend class DecimalFormat; - -}; - - -U_NAMESPACE_END -#endif /* #if !UCONFIG_NO_FORMATTING */ -#endif // DECIMFMTIMPL_H -//eof diff --git a/deps/icu-small/source/i18n/digitaffix.cpp b/deps/icu-small/source/i18n/digitaffix.cpp deleted file mode 100644 index 396df2cf1d75eb..00000000000000 --- a/deps/icu-small/source/i18n/digitaffix.cpp +++ /dev/null @@ -1,109 +0,0 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - * Copyright (C) 2015, International Business Machines - * Corporation and others. All Rights Reserved. - * - * file name: digitaffix.cpp - */ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "digitaffix.h" -#include "fphdlimp.h" -#include "uassert.h" -#include "unistrappender.h" - -U_NAMESPACE_BEGIN - -DigitAffix::DigitAffix() : fAffix(), fAnnotations() { -} - -DigitAffix::DigitAffix( - const UChar *value, int32_t charCount, int32_t fieldId) - : fAffix(value, charCount), - fAnnotations(charCount, (UChar) fieldId, charCount) { -} - -void -DigitAffix::remove() { - fAffix.remove(); - fAnnotations.remove(); -} - -void -DigitAffix::appendUChar(UChar value, int32_t fieldId) { - fAffix.append(value); - fAnnotations.append((UChar) fieldId); -} - -void -DigitAffix::append(const UnicodeString &value, int32_t fieldId) { - fAffix.append(value); - { - UnicodeStringAppender appender(fAnnotations); - int32_t len = value.length(); - for (int32_t i = 0; i < len; ++i) { - appender.append((UChar) fieldId); - } - } -} - -void -DigitAffix::setTo(const UnicodeString &value, int32_t fieldId) { - fAffix = value; - fAnnotations.remove(); - { - UnicodeStringAppender appender(fAnnotations); - int32_t len = value.length(); - for (int32_t i = 0; i < len; ++i) { - appender.append((UChar) fieldId); - } - } -} - -void -DigitAffix::append(const UChar *value, int32_t charCount, int32_t fieldId) { - fAffix.append(value, charCount); - { - UnicodeStringAppender appender(fAnnotations); - for (int32_t i = 0; i < charCount; ++i) { - appender.append((UChar) fieldId); - } - } -} - -UnicodeString & -DigitAffix::format(FieldPositionHandler &handler, UnicodeString &appendTo) const { - int32_t len = fAffix.length(); - if (len == 0) { - return appendTo; - } - if (!handler.isRecording()) { - return appendTo.append(fAffix); - } - U_ASSERT(fAffix.length() == fAnnotations.length()); - int32_t appendToStart = appendTo.length(); - int32_t lastId = (int32_t) fAnnotations.charAt(0); - int32_t lastIdStart = 0; - for (int32_t i = 1; i < len; ++i) { - int32_t id = (int32_t) fAnnotations.charAt(i); - if (id != lastId) { - if (lastId != UNUM_FIELD_COUNT) { - handler.addAttribute(lastId, appendToStart + lastIdStart, appendToStart + i); - } - lastId = id; - lastIdStart = i; - } - } - if (lastId != UNUM_FIELD_COUNT) { - handler.addAttribute(lastId, appendToStart + lastIdStart, appendToStart + len); - } - return appendTo.append(fAffix); -} - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/digitaffix.h b/deps/icu-small/source/i18n/digitaffix.h deleted file mode 100644 index 005c36f8488d8f..00000000000000 --- a/deps/icu-small/source/i18n/digitaffix.h +++ /dev/null @@ -1,104 +0,0 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2015, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* digitaffix.h -* -* created on: 2015jan06 -* created by: Travis Keep -*/ - -#ifndef __DIGITAFFIX_H__ -#define __DIGITAFFIX_H__ - -#include "unicode/uobject.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/unistr.h" -#include "unicode/unum.h" -#include "unicode/utypes.h" - -U_NAMESPACE_BEGIN - -class FieldPositionHandler; - -/** - * A prefix or suffix of a formatted number. - */ -class U_I18N_API DigitAffix : public UMemory { -public: - - /** - * Creates an empty DigitAffix. - */ - DigitAffix(); - - /** - * Creates a DigitAffix containing given UChars where all of it has - * a field type of fieldId. - */ - DigitAffix( - const UChar *value, - int32_t charCount, - int32_t fieldId=UNUM_FIELD_COUNT); - - /** - * Makes this affix be the empty string. - */ - void remove(); - - /** - * Append value to this affix. If fieldId is present, the appended - * string is considered to be the type fieldId. - */ - void appendUChar(UChar value, int32_t fieldId=UNUM_FIELD_COUNT); - - /** - * Append value to this affix. If fieldId is present, the appended - * string is considered to be the type fieldId. - */ - void append(const UnicodeString &value, int32_t fieldId=UNUM_FIELD_COUNT); - - /** - * Sets this affix to given string. The entire string - * is considered to be the type fieldId. - */ - void setTo(const UnicodeString &value, int32_t fieldId=UNUM_FIELD_COUNT); - - /** - * Append value to this affix. If fieldId is present, the appended - * string is considered to be the type fieldId. - */ - void append(const UChar *value, int32_t charCount, int32_t fieldId=UNUM_FIELD_COUNT); - - /** - * Formats this affix. - */ - UnicodeString &format( - FieldPositionHandler &handler, UnicodeString &appendTo) const; - int32_t countChar32() const { return fAffix.countChar32(); } - - /** - * Returns this affix as a unicode string. - */ - const UnicodeString & toString() const { return fAffix; } - - /** - * Returns TRUE if this object equals rhs. - */ - UBool equals(const DigitAffix &rhs) const { - return ((fAffix == rhs.fAffix) && (fAnnotations == rhs.fAnnotations)); - } -private: - UnicodeString fAffix; - UnicodeString fAnnotations; -}; - - -U_NAMESPACE_END -#endif // #if !UCONFIG_NO_FORMATTING -#endif // __DIGITAFFIX_H__ diff --git a/deps/icu-small/source/i18n/digitaffixesandpadding.cpp b/deps/icu-small/source/i18n/digitaffixesandpadding.cpp deleted file mode 100644 index 487d9a345d3e21..00000000000000 --- a/deps/icu-small/source/i18n/digitaffixesandpadding.cpp +++ /dev/null @@ -1,175 +0,0 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - * Copyright (C) 2015, International Business Machines - * Corporation and others. All Rights Reserved. - * - * file name: digitaffixesandpadding.cpp - */ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/plurrule.h" -#include "charstr.h" -#include "digitaffix.h" -#include "digitaffixesandpadding.h" -#include "digitlst.h" -#include "uassert.h" -#include "valueformatter.h" -#include "visibledigits.h" - -U_NAMESPACE_BEGIN - -UBool -DigitAffixesAndPadding::needsPluralRules() const { - return ( - fPositivePrefix.hasMultipleVariants() || - fPositiveSuffix.hasMultipleVariants() || - fNegativePrefix.hasMultipleVariants() || - fNegativeSuffix.hasMultipleVariants()); -} - -UnicodeString & -DigitAffixesAndPadding::formatInt32( - int32_t value, - const ValueFormatter &formatter, - FieldPositionHandler &handler, - const PluralRules *optPluralRules, - UnicodeString &appendTo, - UErrorCode &status) const { - if (U_FAILURE(status)) { - return appendTo; - } - if (optPluralRules != NULL || fWidth > 0 || !formatter.isFastFormattable(value)) { - VisibleDigitsWithExponent digits; - formatter.toVisibleDigitsWithExponent( - (int64_t) value, digits, status); - return format( - digits, - formatter, - handler, - optPluralRules, - appendTo, - status); - } - UBool bPositive = value >= 0; - const DigitAffix *prefix = bPositive ? &fPositivePrefix.getOtherVariant() : &fNegativePrefix.getOtherVariant(); - const DigitAffix *suffix = bPositive ? &fPositiveSuffix.getOtherVariant() : &fNegativeSuffix.getOtherVariant(); - if (value < 0) { - value = -value; - } - prefix->format(handler, appendTo); - formatter.formatInt32(value, handler, appendTo); - return suffix->format(handler, appendTo); -} - -static UnicodeString & -formatAffix( - const DigitAffix *affix, - FieldPositionHandler &handler, - UnicodeString &appendTo) { - if (affix) { - affix->format(handler, appendTo); - } - return appendTo; -} - -static int32_t -countAffixChar32(const DigitAffix *affix) { - if (affix) { - return affix->countChar32(); - } - return 0; -} - -UnicodeString & -DigitAffixesAndPadding::format( - const VisibleDigitsWithExponent &digits, - const ValueFormatter &formatter, - FieldPositionHandler &handler, - const PluralRules *optPluralRules, - UnicodeString &appendTo, - UErrorCode &status) const { - if (U_FAILURE(status)) { - return appendTo; - } - const DigitAffix *prefix = NULL; - const DigitAffix *suffix = NULL; - if (!digits.isNaN()) { - UBool bPositive = !digits.isNegative(); - const PluralAffix *pluralPrefix = bPositive ? &fPositivePrefix : &fNegativePrefix; - const PluralAffix *pluralSuffix = bPositive ? &fPositiveSuffix : &fNegativeSuffix; - if (optPluralRules == NULL || digits.isInfinite()) { - prefix = &pluralPrefix->getOtherVariant(); - suffix = &pluralSuffix->getOtherVariant(); - } else { - UnicodeString count(optPluralRules->select(digits)); - prefix = &pluralPrefix->getByCategory(count); - suffix = &pluralSuffix->getByCategory(count); - } - } - if (fWidth <= 0) { - formatAffix(prefix, handler, appendTo); - formatter.format(digits, handler, appendTo); - return formatAffix(suffix, handler, appendTo); - } - int32_t codePointCount = countAffixChar32(prefix) + formatter.countChar32(digits) + countAffixChar32(suffix); - int32_t paddingCount = fWidth - codePointCount; - switch (fPadPosition) { - case kPadBeforePrefix: - appendPadding(paddingCount, appendTo); - formatAffix(prefix, handler, appendTo); - formatter.format(digits, handler, appendTo); - return formatAffix(suffix, handler, appendTo); - case kPadAfterPrefix: - formatAffix(prefix, handler, appendTo); - appendPadding(paddingCount, appendTo); - formatter.format(digits, handler, appendTo); - return formatAffix(suffix, handler, appendTo); - case kPadBeforeSuffix: - formatAffix(prefix, handler, appendTo); - formatter.format(digits, handler, appendTo); - appendPadding(paddingCount, appendTo); - return formatAffix(suffix, handler, appendTo); - case kPadAfterSuffix: - formatAffix(prefix, handler, appendTo); - formatter.format(digits, handler, appendTo); - formatAffix(suffix, handler, appendTo); - return appendPadding(paddingCount, appendTo); - default: - U_ASSERT(FALSE); - return appendTo; - } -} - -UnicodeString & -DigitAffixesAndPadding::format( - DigitList &value, - const ValueFormatter &formatter, - FieldPositionHandler &handler, - const PluralRules *optPluralRules, - UnicodeString &appendTo, - UErrorCode &status) const { - VisibleDigitsWithExponent digits; - formatter.toVisibleDigitsWithExponent( - value, digits, status); - if (U_FAILURE(status)) { - return appendTo; - } - return format( - digits, formatter, handler, optPluralRules, appendTo, status); -} - -UnicodeString & -DigitAffixesAndPadding::appendPadding(int32_t paddingCount, UnicodeString &appendTo) const { - for (int32_t i = 0; i < paddingCount; ++i) { - appendTo.append(fPadChar); - } - return appendTo; -} - - -U_NAMESPACE_END -#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/digitaffixesandpadding.h b/deps/icu-small/source/i18n/digitaffixesandpadding.h deleted file mode 100644 index d570599d180e77..00000000000000 --- a/deps/icu-small/source/i18n/digitaffixesandpadding.h +++ /dev/null @@ -1,179 +0,0 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2015, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* digitaffixesandpadding.h -* -* created on: 2015jan06 -* created by: Travis Keep -*/ - -#ifndef __DIGITAFFIXESANDPADDING_H__ -#define __DIGITAFFIXESANDPADDING_H__ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/uobject.h" -#include "pluralaffix.h" - -U_NAMESPACE_BEGIN - -class DigitList; -class ValueFormatter; -class UnicodeString; -class FieldPositionHandler; -class PluralRules; -class VisibleDigitsWithExponent; - -/** - * A formatter of numbers. This class can format any numerical value - * except for not a number (NaN), positive infinity, and negative infinity. - * This class manages prefixes, suffixes, and padding but delegates the - * formatting of actual positive values to a ValueFormatter. - */ -class U_I18N_API DigitAffixesAndPadding : public UMemory { -public: - -/** - * Equivalent to DecimalFormat EPadPosition, but redeclared here to prevent - * depending on DecimalFormat which would cause a circular dependency. - */ -enum EPadPosition { - kPadBeforePrefix, - kPadAfterPrefix, - kPadBeforeSuffix, - kPadAfterSuffix -}; - -/** - * The positive prefix - */ -PluralAffix fPositivePrefix; - -/** - * The positive suffix - */ -PluralAffix fPositiveSuffix; - -/** - * The negative suffix - */ -PluralAffix fNegativePrefix; - -/** - * The negative suffix - */ -PluralAffix fNegativeSuffix; - -/** - * The padding position - */ -EPadPosition fPadPosition; - -/** - * The padding character. - */ -UChar32 fPadChar; - -/** - * The field width in code points. The format method inserts instances of - * the padding character as needed in the desired padding position so that - * the entire formatted string contains this many code points. If the - * formatted string already exceeds this many code points, the format method - * inserts no padding. - */ -int32_t fWidth; - -/** - * Pad position is before prefix; padding character is '*' field width is 0. - * The affixes are all the empty string with no annotated fields with just - * the 'other' plural variation. - */ -DigitAffixesAndPadding() - : fPadPosition(kPadBeforePrefix), fPadChar(0x2a), fWidth(0) { } - -/** - * Returns TRUE if this object is equal to rhs. - */ -UBool equals(const DigitAffixesAndPadding &rhs) const { - return (fPositivePrefix.equals(rhs.fPositivePrefix) && - fPositiveSuffix.equals(rhs.fPositiveSuffix) && - fNegativePrefix.equals(rhs.fNegativePrefix) && - fNegativeSuffix.equals(rhs.fNegativeSuffix) && - fPadPosition == rhs.fPadPosition && - fWidth == rhs.fWidth && - fPadChar == rhs.fPadChar); -} - -/** - * Returns TRUE if a plural rules instance is needed to complete the - * formatting by detecting if any of the affixes have multiple plural - * variations. - */ -UBool needsPluralRules() const; - -/** - * Formats value and appends to appendTo. - * - * @param value the value to format. May be NaN or ininite. - * @param formatter handles the details of formatting the actual value. - * @param handler records field positions - * @param optPluralRules the plural rules, but may be NULL if - * needsPluralRules returns FALSE. - * @appendTo formatted string appended here. - * @status any error returned here. - */ -UnicodeString &format( - const VisibleDigitsWithExponent &value, - const ValueFormatter &formatter, - FieldPositionHandler &handler, - const PluralRules *optPluralRules, - UnicodeString &appendTo, - UErrorCode &status) const; - -/** - * For testing only. - */ -UnicodeString &format( - DigitList &value, - const ValueFormatter &formatter, - FieldPositionHandler &handler, - const PluralRules *optPluralRules, - UnicodeString &appendTo, - UErrorCode &status) const; - -/** - * Formats a 32-bit integer and appends to appendTo. When formatting an - * integer, this method is preferred to plain format as it can run - * several times faster under certain conditions. - * - * @param value the value to format. - * @param formatter handles the details of formatting the actual value. - * @param handler records field positions - * @param optPluralRules the plural rules, but may be NULL if - * needsPluralRules returns FALSE. - * @appendTo formatted string appended here. - * @status any error returned here. - */ -UnicodeString &formatInt32( - int32_t value, - const ValueFormatter &formatter, - FieldPositionHandler &handler, - const PluralRules *optPluralRules, - UnicodeString &appendTo, - UErrorCode &status) const; - -private: -UnicodeString &appendPadding(int32_t paddingCount, UnicodeString &appendTo) const; - -}; - - -U_NAMESPACE_END -#endif /* #if !UCONFIG_NO_FORMATTING */ -#endif // __DIGITAFFIXANDPADDING_H__ diff --git a/deps/icu-small/source/i18n/digitformatter.cpp b/deps/icu-small/source/i18n/digitformatter.cpp deleted file mode 100644 index 0d857f8f6873c6..00000000000000 --- a/deps/icu-small/source/i18n/digitformatter.cpp +++ /dev/null @@ -1,417 +0,0 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - * Copyright (C) 2015, International Business Machines - * Corporation and others. All Rights Reserved. - * - * file name: digitformatter.cpp - */ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/dcfmtsym.h" -#include "unicode/unum.h" - -#include "digitformatter.h" -#include "digitgrouping.h" -#include "digitinterval.h" -#include "digitlst.h" -#include "fphdlimp.h" -#include "smallintformatter.h" -#include "unistrappender.h" -#include "visibledigits.h" - -U_NAMESPACE_BEGIN - -DigitFormatter::DigitFormatter() - : fGroupingSeparator(",", -1, US_INV), fDecimal(".", -1, US_INV), - fNegativeSign("-", -1, US_INV), fPositiveSign("+", -1, US_INV), - fIsStandardDigits(TRUE), fExponent("E", -1, US_INV) { - for (int32_t i = 0; i < 10; ++i) { - fLocalizedDigits[i] = (UChar32) (0x30 + i); - } - fInfinity.setTo(UnicodeString("Inf", -1, US_INV), UNUM_INTEGER_FIELD); - fNan.setTo(UnicodeString("Nan", -1, US_INV), UNUM_INTEGER_FIELD); -} - -DigitFormatter::DigitFormatter(const DecimalFormatSymbols &symbols) { - setDecimalFormatSymbols(symbols); -} - -void -DigitFormatter::setOtherDecimalFormatSymbols( - const DecimalFormatSymbols &symbols) { - fLocalizedDigits[0] = symbols.getConstSymbol(DecimalFormatSymbols::kZeroDigitSymbol).char32At(0); - fLocalizedDigits[1] = symbols.getConstSymbol(DecimalFormatSymbols::kOneDigitSymbol).char32At(0); - fLocalizedDigits[2] = symbols.getConstSymbol(DecimalFormatSymbols::kTwoDigitSymbol).char32At(0); - fLocalizedDigits[3] = symbols.getConstSymbol(DecimalFormatSymbols::kThreeDigitSymbol).char32At(0); - fLocalizedDigits[4] = symbols.getConstSymbol(DecimalFormatSymbols::kFourDigitSymbol).char32At(0); - fLocalizedDigits[5] = symbols.getConstSymbol(DecimalFormatSymbols::kFiveDigitSymbol).char32At(0); - fLocalizedDigits[6] = symbols.getConstSymbol(DecimalFormatSymbols::kSixDigitSymbol).char32At(0); - fLocalizedDigits[7] = symbols.getConstSymbol(DecimalFormatSymbols::kSevenDigitSymbol).char32At(0); - fLocalizedDigits[8] = symbols.getConstSymbol(DecimalFormatSymbols::kEightDigitSymbol).char32At(0); - fLocalizedDigits[9] = symbols.getConstSymbol(DecimalFormatSymbols::kNineDigitSymbol).char32At(0); - fIsStandardDigits = isStandardDigits(); - fNegativeSign = symbols.getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol); - fPositiveSign = symbols.getConstSymbol(DecimalFormatSymbols::kPlusSignSymbol); - fInfinity.setTo(symbols.getConstSymbol(DecimalFormatSymbols::kInfinitySymbol), UNUM_INTEGER_FIELD); - fNan.setTo(symbols.getConstSymbol(DecimalFormatSymbols::kNaNSymbol), UNUM_INTEGER_FIELD); - fExponent = symbols.getConstSymbol(DecimalFormatSymbols::kExponentialSymbol); -} - -void -DigitFormatter::setDecimalFormatSymbolsForMonetary( - const DecimalFormatSymbols &symbols) { - setOtherDecimalFormatSymbols(symbols); - fGroupingSeparator = symbols.getConstSymbol(DecimalFormatSymbols::kMonetaryGroupingSeparatorSymbol); - fDecimal = symbols.getConstSymbol(DecimalFormatSymbols::kMonetarySeparatorSymbol); -} - -void -DigitFormatter::setDecimalFormatSymbols( - const DecimalFormatSymbols &symbols) { - setOtherDecimalFormatSymbols(symbols); - fGroupingSeparator = symbols.getConstSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol); - fDecimal = symbols.getConstSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol); -} - -static void appendField( - int32_t fieldId, - const UnicodeString &value, - FieldPositionHandler &handler, - UnicodeString &appendTo) { - int32_t currentLength = appendTo.length(); - appendTo.append(value); - handler.addAttribute( - fieldId, - currentLength, - appendTo.length()); -} - -int32_t DigitFormatter::countChar32( - const DigitGrouping &grouping, - const DigitInterval &interval, - const DigitFormatterOptions &options) const { - int32_t result = interval.length(); - - // We always emit '0' in lieu of no digits. - if (result == 0) { - result = 1; - } - if (options.fAlwaysShowDecimal || interval.getLeastSignificantInclusive() < 0) { - result += fDecimal.countChar32(); - } - result += grouping.getSeparatorCount(interval.getIntDigitCount()) * fGroupingSeparator.countChar32(); - return result; -} - -int32_t -DigitFormatter::countChar32( - const VisibleDigits &digits, - const DigitGrouping &grouping, - const DigitFormatterOptions &options) const { - if (digits.isNaN()) { - return countChar32ForNaN(); - } - if (digits.isInfinite()) { - return countChar32ForInfinity(); - } - return countChar32( - grouping, - digits.getInterval(), - options); -} - -int32_t -DigitFormatter::countChar32( - const VisibleDigitsWithExponent &digits, - const SciFormatterOptions &options) const { - if (digits.isNaN()) { - return countChar32ForNaN(); - } - if (digits.isInfinite()) { - return countChar32ForInfinity(); - } - const VisibleDigits *exponent = digits.getExponent(); - if (exponent == NULL) { - DigitGrouping grouping; - return countChar32( - grouping, - digits.getMantissa().getInterval(), - options.fMantissa); - } - return countChar32( - *exponent, digits.getMantissa().getInterval(), options); -} - -int32_t -DigitFormatter::countChar32( - const VisibleDigits &exponent, - const DigitInterval &mantissaInterval, - const SciFormatterOptions &options) const { - DigitGrouping grouping; - int32_t count = countChar32( - grouping, mantissaInterval, options.fMantissa); - count += fExponent.countChar32(); - count += countChar32ForExponent( - exponent, options.fExponent); - return count; -} - -UnicodeString &DigitFormatter::format( - const VisibleDigits &digits, - const DigitGrouping &grouping, - const DigitFormatterOptions &options, - FieldPositionHandler &handler, - UnicodeString &appendTo) const { - if (digits.isNaN()) { - return formatNaN(handler, appendTo); - } - if (digits.isInfinite()) { - return formatInfinity(handler, appendTo); - } - - const DigitInterval &interval = digits.getInterval(); - int32_t digitsLeftOfDecimal = interval.getMostSignificantExclusive(); - int32_t lastDigitPos = interval.getLeastSignificantInclusive(); - int32_t intBegin = appendTo.length(); - int32_t fracBegin = 0; /* initialize to avoid compiler warning */ - - // Emit "0" instead of empty string. - if (digitsLeftOfDecimal == 0 && lastDigitPos == 0) { - appendTo.append(fLocalizedDigits[0]); - handler.addAttribute(UNUM_INTEGER_FIELD, intBegin, appendTo.length()); - if (options.fAlwaysShowDecimal) { - appendField( - UNUM_DECIMAL_SEPARATOR_FIELD, - fDecimal, - handler, - appendTo); - } - return appendTo; - } - { - UnicodeStringAppender appender(appendTo); - for (int32_t i = interval.getMostSignificantExclusive() - 1; - i >= interval.getLeastSignificantInclusive(); --i) { - if (i == -1) { - appender.flush(); - appendField( - UNUM_DECIMAL_SEPARATOR_FIELD, - fDecimal, - handler, - appendTo); - fracBegin = appendTo.length(); - } - appender.append(fLocalizedDigits[digits.getDigitByExponent(i)]); - if (grouping.isSeparatorAt(digitsLeftOfDecimal, i)) { - appender.flush(); - appendField( - UNUM_GROUPING_SEPARATOR_FIELD, - fGroupingSeparator, - handler, - appendTo); - } - if (i == 0) { - appender.flush(); - if (digitsLeftOfDecimal > 0) { - handler.addAttribute(UNUM_INTEGER_FIELD, intBegin, appendTo.length()); - } - } - } - if (options.fAlwaysShowDecimal && lastDigitPos == 0) { - appender.flush(); - appendField( - UNUM_DECIMAL_SEPARATOR_FIELD, - fDecimal, - handler, - appendTo); - } - } - // lastDigitPos is never > 0 so we are guaranteed that kIntegerField - // is already added. - if (lastDigitPos < 0) { - handler.addAttribute(UNUM_FRACTION_FIELD, fracBegin, appendTo.length()); - } - return appendTo; -} - -UnicodeString & -DigitFormatter::format( - const VisibleDigitsWithExponent &digits, - const SciFormatterOptions &options, - FieldPositionHandler &handler, - UnicodeString &appendTo) const { - DigitGrouping grouping; - format( - digits.getMantissa(), - grouping, - options.fMantissa, - handler, - appendTo); - const VisibleDigits *exponent = digits.getExponent(); - if (exponent == NULL) { - return appendTo; - } - int32_t expBegin = appendTo.length(); - appendTo.append(fExponent); - handler.addAttribute( - UNUM_EXPONENT_SYMBOL_FIELD, expBegin, appendTo.length()); - return formatExponent( - *exponent, - options.fExponent, - UNUM_EXPONENT_SIGN_FIELD, - UNUM_EXPONENT_FIELD, - handler, - appendTo); -} - -static int32_t formatInt( - int32_t value, uint8_t *digits) { - int32_t idx = 0; - while (value > 0) { - digits[idx++] = (uint8_t) (value % 10); - value /= 10; - } - return idx; -} - -UnicodeString & -DigitFormatter::formatDigits( - const uint8_t *digits, - int32_t count, - const IntDigitCountRange &range, - int32_t intField, - FieldPositionHandler &handler, - UnicodeString &appendTo) const { - int32_t i = range.pin(count) - 1; - int32_t begin = appendTo.length(); - - // Always emit '0' as placeholder for empty string. - if (i == -1) { - appendTo.append(fLocalizedDigits[0]); - handler.addAttribute(intField, begin, appendTo.length()); - return appendTo; - } - { - UnicodeStringAppender appender(appendTo); - for (; i >= count; --i) { - appender.append(fLocalizedDigits[0]); - } - for (; i >= 0; --i) { - appender.append(fLocalizedDigits[digits[i]]); - } - } - handler.addAttribute(intField, begin, appendTo.length()); - return appendTo; -} - -UnicodeString & -DigitFormatter::formatExponent( - const VisibleDigits &digits, - const DigitFormatterIntOptions &options, - int32_t signField, - int32_t intField, - FieldPositionHandler &handler, - UnicodeString &appendTo) const { - UBool neg = digits.isNegative(); - if (neg || options.fAlwaysShowSign) { - appendField( - signField, - neg ? fNegativeSign : fPositiveSign, - handler, - appendTo); - } - int32_t begin = appendTo.length(); - DigitGrouping grouping; - DigitFormatterOptions expOptions; - FieldPosition fpos(FieldPosition::DONT_CARE); - FieldPositionOnlyHandler noHandler(fpos); - format( - digits, - grouping, - expOptions, - noHandler, - appendTo); - handler.addAttribute(intField, begin, appendTo.length()); - return appendTo; -} - -int32_t -DigitFormatter::countChar32ForExponent( - const VisibleDigits &exponent, - const DigitFormatterIntOptions &options) const { - int32_t result = 0; - UBool neg = exponent.isNegative(); - if (neg || options.fAlwaysShowSign) { - result += neg ? fNegativeSign.countChar32() : fPositiveSign.countChar32(); - } - DigitGrouping grouping; - DigitFormatterOptions expOptions; - result += countChar32(grouping, exponent.getInterval(), expOptions); - return result; -} - -UnicodeString & -DigitFormatter::formatPositiveInt32( - int32_t positiveValue, - const IntDigitCountRange &range, - FieldPositionHandler &handler, - UnicodeString &appendTo) const { - // super fast path - if (fIsStandardDigits && SmallIntFormatter::canFormat(positiveValue, range)) { - int32_t begin = appendTo.length(); - SmallIntFormatter::format(positiveValue, range, appendTo); - handler.addAttribute(UNUM_INTEGER_FIELD, begin, appendTo.length()); - return appendTo; - } - uint8_t digits[10]; - int32_t count = formatInt(positiveValue, digits); - return formatDigits( - digits, - count, - range, - UNUM_INTEGER_FIELD, - handler, - appendTo); -} - -UBool DigitFormatter::isStandardDigits() const { - UChar32 cdigit = 0x30; - for (int32_t i = 0; i < UPRV_LENGTHOF(fLocalizedDigits); ++i) { - if (fLocalizedDigits[i] != cdigit) { - return FALSE; - } - ++cdigit; - } - return TRUE; -} - -UBool -DigitFormatter::equals(const DigitFormatter &rhs) const { - UBool result = (fGroupingSeparator == rhs.fGroupingSeparator) && - (fDecimal == rhs.fDecimal) && - (fNegativeSign == rhs.fNegativeSign) && - (fPositiveSign == rhs.fPositiveSign) && - (fInfinity.equals(rhs.fInfinity)) && - (fNan.equals(rhs.fNan)) && - (fIsStandardDigits == rhs.fIsStandardDigits) && - (fExponent == rhs.fExponent); - - if (!result) { - return FALSE; - } - for (int32_t i = 0; i < UPRV_LENGTHOF(fLocalizedDigits); ++i) { - if (fLocalizedDigits[i] != rhs.fLocalizedDigits[i]) { - return FALSE; - } - } - return TRUE; -} - - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/digitformatter.h b/deps/icu-small/source/i18n/digitformatter.h deleted file mode 100644 index 54a54c3639a629..00000000000000 --- a/deps/icu-small/source/i18n/digitformatter.h +++ /dev/null @@ -1,288 +0,0 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2015, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* digitformatter.h -* -* created on: 2015jan06 -* created by: Travis Keep -*/ - -#ifndef __DIGITFORMATTER_H__ -#define __DIGITFORMATTER_H__ - -#include "unicode/uobject.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/utypes.h" -#include "unicode/unistr.h" -#include "digitaffix.h" - -U_NAMESPACE_BEGIN - -class DecimalFormatSymbols; -class DigitList; -class DigitGrouping; -class DigitInterval; -class UnicodeString; -class FieldPositionHandler; -class IntDigitCountRange; -class VisibleDigits; -class VisibleDigitsWithExponent; - -/** - * Various options for formatting in fixed point. - */ -class U_I18N_API DigitFormatterOptions : public UMemory { - public: - DigitFormatterOptions() : fAlwaysShowDecimal(FALSE) { } - - /** - * Returns TRUE if this object equals rhs. - */ - UBool equals(const DigitFormatterOptions &rhs) const { - return ( - fAlwaysShowDecimal == rhs.fAlwaysShowDecimal); - } - - /** - * Returns TRUE if these options allow for fast formatting of - * integers. - */ - UBool isFastFormattable() const { - return (fAlwaysShowDecimal == FALSE); - } - - /** - * If TRUE, show the decimal separator even when there are no fraction - * digits. default is FALSE. - */ - UBool fAlwaysShowDecimal; -}; - -/** - * Various options for formatting an integer. - */ -class U_I18N_API DigitFormatterIntOptions : public UMemory { - public: - DigitFormatterIntOptions() : fAlwaysShowSign(FALSE) { } - - /** - * Returns TRUE if this object equals rhs. - */ - UBool equals(const DigitFormatterIntOptions &rhs) const { - return (fAlwaysShowSign == rhs.fAlwaysShowSign); - } - - /** - * If TRUE, always prefix the integer with its sign even if the number is - * positive. Default is FALSE. - */ - UBool fAlwaysShowSign; -}; - -/** - * Options for formatting in scientific notation. - */ -class U_I18N_API SciFormatterOptions : public UMemory { - public: - - /** - * Returns TRUE if this object equals rhs. - */ - UBool equals(const SciFormatterOptions &rhs) const { - return (fMantissa.equals(rhs.fMantissa) && - fExponent.equals(rhs.fExponent)); - } - - /** - * Options for formatting the mantissa. - */ - DigitFormatterOptions fMantissa; - - /** - * Options for formatting the exponent. - */ - DigitFormatterIntOptions fExponent; -}; - - -/** - * Does fixed point formatting. - * - * This class only does fixed point formatting. It does no rounding before - * formatting. - */ -class U_I18N_API DigitFormatter : public UMemory { -public: - -/** - * Decimal separator is period (.), Plus sign is plus (+), - * minus sign is minus (-), grouping separator is comma (,), digits are 0-9. - */ -DigitFormatter(); - -/** - * Let symbols determine the digits, decimal separator, - * plus and mius sign, grouping separator, and possibly other settings. - */ -DigitFormatter(const DecimalFormatSymbols &symbols); - -/** - * Change what this instance uses for digits, decimal separator, - * plus and mius sign, grouping separator, and possibly other settings - * according to symbols. - */ -void setDecimalFormatSymbols(const DecimalFormatSymbols &symbols); - -/** - * Change what this instance uses for digits, decimal separator, - * plus and mius sign, grouping separator, and possibly other settings - * according to symbols in the context of monetary amounts. - */ -void setDecimalFormatSymbolsForMonetary(const DecimalFormatSymbols &symbols); - -/** - * Fixed point formatting. - * - * @param positiveDigits the value to format - * Negative sign can be present, but it won't show. - * @param grouping controls how digit grouping is done - * @param options formatting options - * @param handler records field positions - * @param appendTo formatted value appended here. - * @return appendTo - */ -UnicodeString &format( - const VisibleDigits &positiveDigits, - const DigitGrouping &grouping, - const DigitFormatterOptions &options, - FieldPositionHandler &handler, - UnicodeString &appendTo) const; - -/** - * formats in scientifc notation. - * @param positiveDigits the value to format. - * Negative sign can be present, but it won't show. - * @param options formatting options - * @param handler records field positions. - * @param appendTo formatted value appended here. - */ -UnicodeString &format( - const VisibleDigitsWithExponent &positiveDigits, - const SciFormatterOptions &options, - FieldPositionHandler &handler, - UnicodeString &appendTo) const; - -/** - * Fixed point formatting of integers. - * Always performed with no grouping and no decimal point. - * - * @param positiveValue the value to format must be positive. - * @param range specifies minimum and maximum number of digits. - * @param handler records field positions - * @param appendTo formatted value appended here. - * @return appendTo - */ -UnicodeString &formatPositiveInt32( - int32_t positiveValue, - const IntDigitCountRange &range, - FieldPositionHandler &handler, - UnicodeString &appendTo) const; - -/** - * Counts how many code points are needed for fixed formatting. - * If digits is negative, the negative sign is not included in the count. - */ -int32_t countChar32( - const VisibleDigits &digits, - const DigitGrouping &grouping, - const DigitFormatterOptions &options) const; - -/** - * Counts how many code points are needed for scientific formatting. - * If digits is negative, the negative sign is not included in the count. - */ -int32_t countChar32( - const VisibleDigitsWithExponent &digits, - const SciFormatterOptions &options) const; - -/** - * Returns TRUE if this object equals rhs. - */ -UBool equals(const DigitFormatter &rhs) const; - -private: -UChar32 fLocalizedDigits[10]; -UnicodeString fGroupingSeparator; -UnicodeString fDecimal; -UnicodeString fNegativeSign; -UnicodeString fPositiveSign; -DigitAffix fInfinity; -DigitAffix fNan; -UBool fIsStandardDigits; -UnicodeString fExponent; -UBool isStandardDigits() const; - -UnicodeString &formatDigits( - const uint8_t *digits, - int32_t count, - const IntDigitCountRange &range, - int32_t intField, - FieldPositionHandler &handler, - UnicodeString &appendTo) const; - -void setOtherDecimalFormatSymbols(const DecimalFormatSymbols &symbols); - -int32_t countChar32( - const VisibleDigits &exponent, - const DigitInterval &mantissaInterval, - const SciFormatterOptions &options) const; - -UnicodeString &formatNaN( - FieldPositionHandler &handler, - UnicodeString &appendTo) const { - return fNan.format(handler, appendTo); -} - -int32_t countChar32ForNaN() const { - return fNan.toString().countChar32(); -} - -UnicodeString &formatInfinity( - FieldPositionHandler &handler, - UnicodeString &appendTo) const { - return fInfinity.format(handler, appendTo); -} - -int32_t countChar32ForInfinity() const { - return fInfinity.toString().countChar32(); -} - -UnicodeString &formatExponent( - const VisibleDigits &digits, - const DigitFormatterIntOptions &options, - int32_t signField, - int32_t intField, - FieldPositionHandler &handler, - UnicodeString &appendTo) const; - -int32_t countChar32( - const DigitGrouping &grouping, - const DigitInterval &interval, - const DigitFormatterOptions &options) const; - -int32_t countChar32ForExponent( - const VisibleDigits &exponent, - const DigitFormatterIntOptions &options) const; - -}; - - -U_NAMESPACE_END -#endif /* #if !UCONFIG_NO_FORMATTING */ -#endif // __DIGITFORMATTER_H__ diff --git a/deps/icu-small/source/i18n/digitgrouping.cpp b/deps/icu-small/source/i18n/digitgrouping.cpp deleted file mode 100644 index cffa122b6ceaf6..00000000000000 --- a/deps/icu-small/source/i18n/digitgrouping.cpp +++ /dev/null @@ -1,58 +0,0 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - * Copyright (C) 2015, International Business Machines - * Corporation and others. All Rights Reserved. - * - * file name: digitgrouping.cpp - */ - -#include "unicode/utypes.h" - -#include "digitgrouping.h" -#include "smallintformatter.h" - -U_NAMESPACE_BEGIN - -UBool DigitGrouping::isSeparatorAt( - int32_t digitsLeftOfDecimal, int32_t digitPos) const { - if (!isGroupingEnabled(digitsLeftOfDecimal) || digitPos < fGrouping) { - return FALSE; - } - return ((digitPos - fGrouping) % getGrouping2() == 0); -} - -int32_t DigitGrouping::getSeparatorCount(int32_t digitsLeftOfDecimal) const { - if (!isGroupingEnabled(digitsLeftOfDecimal)) { - return 0; - } - return (digitsLeftOfDecimal - 1 - fGrouping) / getGrouping2() + 1; -} - -UBool DigitGrouping::isGroupingEnabled(int32_t digitsLeftOfDecimal) const { - return (isGroupingUsed() - && digitsLeftOfDecimal >= fGrouping + getMinGrouping()); -} - -UBool DigitGrouping::isNoGrouping( - int32_t positiveValue, const IntDigitCountRange &range) const { - return getSeparatorCount( - SmallIntFormatter::estimateDigitCount(positiveValue, range)) == 0; -} - -int32_t DigitGrouping::getGrouping2() const { - return (fGrouping2 > 0 ? fGrouping2 : fGrouping); -} - -int32_t DigitGrouping::getMinGrouping() const { - return (fMinGrouping > 0 ? fMinGrouping : 1); -} - -void -DigitGrouping::clear() { - fMinGrouping = 0; - fGrouping = 0; - fGrouping2 = 0; -} - -U_NAMESPACE_END diff --git a/deps/icu-small/source/i18n/digitgrouping.h b/deps/icu-small/source/i18n/digitgrouping.h deleted file mode 100644 index f3f8679b879e95..00000000000000 --- a/deps/icu-small/source/i18n/digitgrouping.h +++ /dev/null @@ -1,112 +0,0 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2015, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* digitgrouping.h -* -* created on: 2015jan6 -* created by: Travis Keep -*/ - -#ifndef __DIGITGROUPING_H__ -#define __DIGITGROUPING_H__ - -#include "unicode/uobject.h" -#include "unicode/utypes.h" - -U_NAMESPACE_BEGIN - -class IntDigitCountRange; - -/** - * The digit grouping policy. - */ -class U_I18N_API DigitGrouping : public UMemory { -public: - /** - * Default is no digit grouping. - */ - DigitGrouping() : fGrouping(0), fGrouping2(0), fMinGrouping(0) { } - - /** - * Returns TRUE if this object is equal to rhs. - */ - UBool equals(const DigitGrouping &rhs) const { - return ((fGrouping == rhs.fGrouping) && - (fGrouping2 == rhs.fGrouping2) && - (fMinGrouping == rhs.fMinGrouping)); - } - - /** - * Returns true if a separator is needed after a particular digit. - * @param digitsLeftOfDecimal the total count of digits left of the - * decimal. - * @param digitPos 0 is the one's place; 1 is the 10's place; -1 is the - * 1/10's place etc. - */ - UBool isSeparatorAt(int32_t digitsLeftOfDecimal, int32_t digitPos) const; - - /** - * Returns the total number of separators to be used to format a particular - * number. - * @param digitsLeftOfDecimal the total number of digits to the left of - * the decimal. - */ - int32_t getSeparatorCount(int32_t digitsLeftOfDecimal) const; - - /** - * Returns true if grouping is used FALSE otherwise. When - * isGroupingUsed() returns FALSE; isSeparatorAt always returns FALSE - * and getSeparatorCount always returns 0. - */ - UBool isGroupingUsed() const { return fGrouping > 0; } - - /** - * Returns TRUE if this instance would not add grouping separators - * when formatting value using the given constraint on digit count. - * - * @param value the value to format. - * @param range the minimum and maximum digits for formatting value. - */ - UBool isNoGrouping( - int32_t positiveValue, const IntDigitCountRange &range) const; - - /** - * Clears this instance so that digit grouping is not in effect. - */ - void clear(); - -public: - - /** - * Primary grouping size. A value of 0, the default, or a negative - * number causes isGroupingUsed() to return FALSE. - */ - int32_t fGrouping; - - /** - * Secondary grouping size. If > 0, this size is used instead of - * 'fGrouping' for all but the group just to the left of the decimal - * point. The default value of 0, or a negative value indicates that - * there is no secondary grouping size. - */ - int32_t fGrouping2; - - /** - * If set (that is > 0), uses no grouping separators if fewer than - * (fGrouping + fMinGrouping) digits appear left of the decimal place. - * The default value for this field is 0. - */ - int32_t fMinGrouping; -private: - UBool isGroupingEnabled(int32_t digitsLeftOfDecimal) const; - int32_t getGrouping2() const; - int32_t getMinGrouping() const; -}; - -U_NAMESPACE_END - -#endif // __DIGITGROUPING_H__ diff --git a/deps/icu-small/source/i18n/digitinterval.cpp b/deps/icu-small/source/i18n/digitinterval.cpp deleted file mode 100644 index 32d952e0267950..00000000000000 --- a/deps/icu-small/source/i18n/digitinterval.cpp +++ /dev/null @@ -1,55 +0,0 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - * Copyright (C) 2015, International Business Machines - * Corporation and others. All Rights Reserved. - * - * file name: digitinterval.cpp - */ - -#include "unicode/utypes.h" - -#include "digitinterval.h" - -U_NAMESPACE_BEGIN - -void DigitInterval::expandToContain(const DigitInterval &rhs) { - if (fSmallestInclusive > rhs.fSmallestInclusive) { - fSmallestInclusive = rhs.fSmallestInclusive; - } - if (fLargestExclusive < rhs.fLargestExclusive) { - fLargestExclusive = rhs.fLargestExclusive; - } -} - -void DigitInterval::shrinkToFitWithin(const DigitInterval &rhs) { - if (fSmallestInclusive < rhs.fSmallestInclusive) { - fSmallestInclusive = rhs.fSmallestInclusive; - } - if (fLargestExclusive > rhs.fLargestExclusive) { - fLargestExclusive = rhs.fLargestExclusive; - } -} - -void DigitInterval::setIntDigitCount(int32_t count) { - fLargestExclusive = count < 0 ? INT32_MAX : count; -} - -void DigitInterval::setFracDigitCount(int32_t count) { - fSmallestInclusive = count < 0 ? INT32_MIN : -count; -} - -void DigitInterval::expandToContainDigit(int32_t digitExponent) { - if (fLargestExclusive <= digitExponent) { - fLargestExclusive = digitExponent + 1; - } else if (fSmallestInclusive > digitExponent) { - fSmallestInclusive = digitExponent; - } -} - -UBool DigitInterval::contains(int32_t x) const { - return (x < fLargestExclusive && x >= fSmallestInclusive); -} - - -U_NAMESPACE_END diff --git a/deps/icu-small/source/i18n/digitinterval.h b/deps/icu-small/source/i18n/digitinterval.h deleted file mode 100644 index 95d406da206f4c..00000000000000 --- a/deps/icu-small/source/i18n/digitinterval.h +++ /dev/null @@ -1,159 +0,0 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2015, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* digitinterval.h -* -* created on: 2015jan6 -* created by: Travis Keep -*/ - -#ifndef __DIGITINTERVAL_H__ -#define __DIGITINTERVAL_H__ - -#include "unicode/uobject.h" -#include "unicode/utypes.h" - -U_NAMESPACE_BEGIN - -/** - * An interval of digits. - * DigitIntervals are for fixed point formatting. A DigitInterval specifies - * zero or more integer digits and zero or more fractional digits. This class - * specifies particular digits in a number by their power of 10. For example, - * the digit position just to the left of the decimal is 0, and the digit - * position just left of that is 1. The digit position just to the right of - * the decimal is -1. The digit position just to the right of that is -2. - */ -class U_I18N_API DigitInterval : public UMemory { -public: - - /** - * Spans all integer and fraction digits - */ - DigitInterval() - : fLargestExclusive(INT32_MAX), fSmallestInclusive(INT32_MIN) { } - - - /** - * Makes this instance span all digits. - */ - void clear() { - fLargestExclusive = INT32_MAX; - fSmallestInclusive = INT32_MIN; - } - - /** - * Returns TRUE if this interval contains this digit position. - */ - UBool contains(int32_t digitPosition) const; - - /** - * Returns true if this object is the same as rhs. - */ - UBool equals(const DigitInterval &rhs) const { - return ((fLargestExclusive == rhs.fLargestExclusive) && - (fSmallestInclusive == rhs.fSmallestInclusive)); - } - - /** - * Expand this interval so that it contains all of rhs. - */ - void expandToContain(const DigitInterval &rhs); - - /** - * Shrink this interval so that it contains no more than rhs. - */ - void shrinkToFitWithin(const DigitInterval &rhs); - - /** - * Expand this interval as necessary to contain digit with given exponent - * After this method returns, this interval is guaranteed to contain - * digitExponent. - */ - void expandToContainDigit(int32_t digitExponent); - - /** - * Changes the number of digits to the left of the decimal point that - * this interval spans. If count is negative, it means span all digits - * to the left of the decimal point. - */ - void setIntDigitCount(int32_t count); - - /** - * Changes the number of digits to the right of the decimal point that - * this interval spans. If count is negative, it means span all digits - * to the right of the decimal point. - */ - void setFracDigitCount(int32_t count); - - /** - * Sets the least significant inclusive value to smallest. If smallest >= 0 - * then least significant inclusive value becomes 0. - */ - void setLeastSignificantInclusive(int32_t smallest) { - fSmallestInclusive = smallest < 0 ? smallest : 0; - } - - /** - * Sets the most significant exclusive value to largest. - * If largest <= 0 then most significant exclusive value becomes 0. - */ - void setMostSignificantExclusive(int32_t largest) { - fLargestExclusive = largest > 0 ? largest : 0; - } - - /** - * If returns 8, the most significant digit in interval is the 10^7 digit. - * Returns INT32_MAX if this interval spans all digits to left of - * decimal point. - */ - int32_t getMostSignificantExclusive() const { - return fLargestExclusive; - } - - /** - * Returns number of digits to the left of the decimal that this - * interval includes. This is a synonym for getMostSignificantExclusive(). - */ - int32_t getIntDigitCount() const { - return fLargestExclusive; - } - - /** - * Returns number of digits to the right of the decimal that this - * interval includes. - */ - int32_t getFracDigitCount() const { - return fSmallestInclusive == INT32_MIN ? INT32_MAX : -fSmallestInclusive; - } - - /** - * Returns the total number of digits that this interval spans. - * Caution: If this interval spans all digits to the left or right of - * decimal point instead of some fixed number, then what length() - * returns is undefined. - */ - int32_t length() const { - return fLargestExclusive - fSmallestInclusive; - } - - /** - * If returns -3, the least significant digit in interval is the 10^-3 - * digit. Returns INT32_MIN if this interval spans all digits to right of - * decimal point. - */ - int32_t getLeastSignificantInclusive() const { - return fSmallestInclusive; - } -private: - int32_t fLargestExclusive; - int32_t fSmallestInclusive; -}; - -U_NAMESPACE_END - -#endif // __DIGITINTERVAL_H__ diff --git a/deps/icu-small/source/i18n/digitlst.cpp b/deps/icu-small/source/i18n/digitlst.cpp deleted file mode 100644 index 37760defd708bc..00000000000000 --- a/deps/icu-small/source/i18n/digitlst.cpp +++ /dev/null @@ -1,1143 +0,0 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 1997-2015, International Business Machines -* Corporation and others. All Rights Reserved. -********************************************************************** -* -* File DIGITLST.CPP -* -* Modification History: -* -* Date Name Description -* 03/21/97 clhuang Converted from java. -* 03/21/97 clhuang Implemented with new APIs. -* 03/27/97 helena Updated to pass the simple test after code review. -* 03/31/97 aliu Moved isLONG_MIN to here, and fixed it. -* 04/15/97 aliu Changed MAX_COUNT to DBL_DIG. Changed Digit to char. -* Reworked representation by replacing fDecimalAt -* with fExponent. -* 04/16/97 aliu Rewrote set() and getDouble() to use sprintf/atof -* to do digit conversion. -* 09/09/97 aliu Modified for exponential notation support. -* 08/02/98 stephen Added nearest/even rounding -* Fixed bug in fitsIntoLong -****************************************************************************** -*/ - -#if defined(__CYGWIN__) && !defined(_GNU_SOURCE) -#define _GNU_SOURCE -#endif - -#include "digitlst.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/putil.h" -#include "charstr.h" -#include "cmemory.h" -#include "cstring.h" -#include "mutex.h" -#include "putilimp.h" -#include "uassert.h" -#include "digitinterval.h" -#include "ucln_in.h" -#include "umutex.h" -#include "double-conversion.h" -#include -#include -#include -#include -#include - -using icu::double_conversion::DoubleToStringConverter; - -#if !defined(U_USE_STRTOD_L) -# if U_PLATFORM_USES_ONLY_WIN32_API -# define U_USE_STRTOD_L 1 -# define U_HAVE_XLOCALE_H 0 -# elif defined(U_HAVE_STRTOD_L) -# define U_USE_STRTOD_L U_HAVE_STRTOD_L -# else -# define U_USE_STRTOD_L 0 -# endif -#endif - -#if U_USE_STRTOD_L -# if U_HAVE_XLOCALE_H -# include -# else -# include -# endif -#endif - -// *************************************************************************** -// class DigitList -// A wrapper onto decNumber. -// Used to be standalone. -// *************************************************************************** - -/** - * This is the zero digit. The base for the digits returned by getDigit() - * Note that it is the platform invariant digit, and is not Unicode. - */ -#define kZero '0' - - -/* Only for 32 bit numbers. Ignore the negative sign. */ -//static const char LONG_MIN_REP[] = "2147483648"; -//static const char I64_MIN_REP[] = "9223372036854775808"; - - -U_NAMESPACE_BEGIN - -// ------------------------------------- -// default constructor - -DigitList::DigitList() -{ - uprv_decContextDefault(&fContext, DEC_INIT_BASE); - fContext.traps = 0; - uprv_decContextSetRounding(&fContext, DEC_ROUND_HALF_EVEN); - fContext.digits = fStorage.getCapacity(); - - fDecNumber = fStorage.getAlias(); - uprv_decNumberZero(fDecNumber); - - internalSetDouble(0.0); -} - -// ------------------------------------- - -DigitList::~DigitList() -{ -} - -// ------------------------------------- -// copy constructor - -DigitList::DigitList(const DigitList &other) -{ - fDecNumber = fStorage.getAlias(); - *this = other; -} - - -// ------------------------------------- -// assignment operator - -DigitList& -DigitList::operator=(const DigitList& other) -{ - if (this != &other) - { - uprv_memcpy(&fContext, &other.fContext, sizeof(decContext)); - - if (other.fStorage.getCapacity() > fStorage.getCapacity()) { - fDecNumber = fStorage.resize(other.fStorage.getCapacity()); - } - // Always reset the fContext.digits, even if fDecNumber was not reallocated, - // because above we copied fContext from other.fContext. - fContext.digits = fStorage.getCapacity(); - uprv_decNumberCopy(fDecNumber, other.fDecNumber); - - { - // fDouble is lazily created and cached. - // Avoid potential races with that happening with other.fDouble - // while we are doing the assignment. - Mutex mutex; - - if(other.fHave==kDouble) { - fUnion.fDouble = other.fUnion.fDouble; - } - fHave = other.fHave; - } - } - return *this; -} - -// ------------------------------------- -// operator == (does not exactly match the old DigitList function) - -UBool -DigitList::operator==(const DigitList& that) const -{ - if (this == &that) { - return TRUE; - } - decNumber n; // Has space for only a none digit value. - decContext c; - uprv_decContextDefault(&c, DEC_INIT_BASE); - c.digits = 1; - c.traps = 0; - - uprv_decNumberCompare(&n, this->fDecNumber, that.fDecNumber, &c); - UBool result = decNumberIsZero(&n); - return result; -} - -// ------------------------------------- -// comparison function. Returns -// Not Comparable : -2 -// < : -1 -// == : 0 -// > : +1 -int32_t DigitList::compare(const DigitList &other) { - decNumber result; - int32_t savedDigits = fContext.digits; - fContext.digits = 1; - uprv_decNumberCompare(&result, this->fDecNumber, other.fDecNumber, &fContext); - fContext.digits = savedDigits; - if (decNumberIsZero(&result)) { - return 0; - } else if (decNumberIsSpecial(&result)) { - return -2; - } else if (result.bits & DECNEG) { - return -1; - } else { - return 1; - } -} - - -// ------------------------------------- -// Reduce - remove trailing zero digits. -void -DigitList::reduce() { - uprv_decNumberReduce(fDecNumber, fDecNumber, &fContext); -} - - -// ------------------------------------- -// trim - remove trailing fraction zero digits. -void -DigitList::trim() { - uprv_decNumberTrim(fDecNumber); -} - -// ------------------------------------- -// Resets the digit list; sets all the digits to zero. - -void -DigitList::clear() -{ - uprv_decNumberZero(fDecNumber); - uprv_decContextSetRounding(&fContext, DEC_ROUND_HALF_EVEN); - internalSetDouble(0.0); -} - - -/** - * Formats a int64_t number into a base 10 string representation, and NULL terminates it. - * @param number The number to format - * @param outputStr The string to output to. Must be at least MAX_DIGITS+2 in length (21), - * to hold the longest int64_t value. - * @return the number of digits written, not including the sign. - */ -static int32_t -formatBase10(int64_t number, char *outputStr) { - // The number is output backwards, starting with the LSD. - // Fill the buffer from the far end. After the number is complete, - // slide the string contents to the front. - - const int32_t MAX_IDX = MAX_DIGITS+2; - int32_t destIdx = MAX_IDX; - outputStr[--destIdx] = 0; - - int64_t n = number; - if (number < 0) { // Negative numbers are slightly larger than a postive - outputStr[--destIdx] = (char)(-(n % 10) + kZero); - n /= -10; - } - do { - outputStr[--destIdx] = (char)(n % 10 + kZero); - n /= 10; - } while (n > 0); - - if (number < 0) { - outputStr[--destIdx] = '-'; - } - - // Slide the number to the start of the output str - U_ASSERT(destIdx >= 0); - int32_t length = MAX_IDX - destIdx; - uprv_memmove(outputStr, outputStr+MAX_IDX-length, length); - - return length; -} - - -// ------------------------------------- -// -// setRoundingMode() -// For most modes, the meaning and names are the same between the decNumber library -// (which DigitList follows) and the ICU Formatting Rounding Mode values. -// The flag constants are different, however. -// -// Note that ICU's kRoundingUnnecessary is not implemented directly by DigitList. -// This mode, inherited from Java, means that numbers that would not format exactly -// will return an error when formatting is attempted. - -void -DigitList::setRoundingMode(DecimalFormat::ERoundingMode m) { - enum rounding r; - - switch (m) { - case DecimalFormat::kRoundCeiling: r = DEC_ROUND_CEILING; break; - case DecimalFormat::kRoundFloor: r = DEC_ROUND_FLOOR; break; - case DecimalFormat::kRoundDown: r = DEC_ROUND_DOWN; break; - case DecimalFormat::kRoundUp: r = DEC_ROUND_UP; break; - case DecimalFormat::kRoundHalfEven: r = DEC_ROUND_HALF_EVEN; break; - case DecimalFormat::kRoundHalfDown: r = DEC_ROUND_HALF_DOWN; break; - case DecimalFormat::kRoundHalfUp: r = DEC_ROUND_HALF_UP; break; - case DecimalFormat::kRoundUnnecessary: r = DEC_ROUND_HALF_EVEN; break; - default: - // TODO: how to report the problem? - // Leave existing mode unchanged. - r = uprv_decContextGetRounding(&fContext); - } - uprv_decContextSetRounding(&fContext, r); - -} - - -// ------------------------------------- - -void -DigitList::setPositive(UBool s) { - if (s) { - fDecNumber->bits &= ~DECNEG; - } else { - fDecNumber->bits |= DECNEG; - } - internalClear(); -} -// ------------------------------------- - -void -DigitList::setDecimalAt(int32_t d) { - U_ASSERT((fDecNumber->bits & DECSPECIAL) == 0); // Not Infinity or NaN - U_ASSERT(d-1>-999999999); - U_ASSERT(d-1< 999999999); - int32_t adjustedDigits = fDecNumber->digits; - if (decNumberIsZero(fDecNumber)) { - // Account for difference in how zero is represented between DigitList & decNumber. - adjustedDigits = 0; - } - fDecNumber->exponent = d - adjustedDigits; - internalClear(); -} - -int32_t -DigitList::getDecimalAt() { - U_ASSERT((fDecNumber->bits & DECSPECIAL) == 0); // Not Infinity or NaN - if (decNumberIsZero(fDecNumber) || ((fDecNumber->bits & DECSPECIAL) != 0)) { - return fDecNumber->exponent; // Exponent should be zero for these cases. - } - return fDecNumber->exponent + fDecNumber->digits; -} - -void -DigitList::setCount(int32_t c) { - U_ASSERT(c <= fContext.digits); - if (c == 0) { - // For a value of zero, DigitList sets all fields to zero, while - // decNumber keeps one digit (with that digit being a zero) - c = 1; - fDecNumber->lsu[0] = 0; - } - fDecNumber->digits = c; - internalClear(); -} - -int32_t -DigitList::getCount() const { - if (decNumberIsZero(fDecNumber) && fDecNumber->exponent==0) { - // The extra test for exponent==0 is needed because parsing sometimes appends - // zero digits. It's bogus, decimalFormatter parsing needs to be cleaned up. - return 0; - } else { - return fDecNumber->digits; - } -} - -void -DigitList::setDigit(int32_t i, char v) { - int32_t count = fDecNumber->digits; - U_ASSERT(i='0' && v<='9'); - v &= 0x0f; - fDecNumber->lsu[count-i-1] = v; - internalClear(); -} - -char -DigitList::getDigit(int32_t i) { - int32_t count = fDecNumber->digits; - U_ASSERT(ilsu[count-i-1] + '0'; -} - -// copied from DigitList::getDigit() -uint8_t -DigitList::getDigitValue(int32_t i) { - int32_t count = fDecNumber->digits; - U_ASSERT(ilsu[count-i-1]; -} - -// ------------------------------------- -// Appends the digit to the digit list if it's not out of scope. -// Ignores the digit, otherwise. -// -// This function is horribly inefficient to implement with decNumber because -// the digits are stored least significant first, which requires moving all -// existing digits down one to make space for the new one to be appended. -// -void -DigitList::append(char digit) -{ - U_ASSERT(digit>='0' && digit<='9'); - // Ignore digits which exceed the precision we can represent - // And don't fix for larger precision. Fix callers instead. - if (decNumberIsZero(fDecNumber)) { - // Zero needs to be special cased because of the difference in the way - // that the old DigitList and decNumber represent it. - // digit cout was zero for digitList, is one for decNumber - fDecNumber->lsu[0] = digit & 0x0f; - fDecNumber->digits = 1; - fDecNumber->exponent--; // To match the old digit list implementation. - } else { - int32_t nDigits = fDecNumber->digits; - if (nDigits < fContext.digits) { - int i; - for (i=nDigits; i>0; i--) { - fDecNumber->lsu[i] = fDecNumber->lsu[i-1]; - } - fDecNumber->lsu[0] = digit & 0x0f; - fDecNumber->digits++; - // DigitList emulation - appending doesn't change the magnitude of existing - // digits. With decNumber's decimal being after the - // least signficant digit, we need to adjust the exponent. - fDecNumber->exponent--; - } - } - internalClear(); -} - -// ------------------------------------- - -/** - * Currently, getDouble() depends on strtod() to do its conversion. - * - * WARNING!! - * This is an extremely costly function. ~1/2 of the conversion time - * can be linked to this function. - */ -double -DigitList::getDouble() const -{ - { - Mutex mutex; - if (fHave == kDouble) { - return fUnion.fDouble; - } - } - - double tDouble = 0.0; - if (isZero()) { - tDouble = 0.0; - if (decNumberIsNegative(fDecNumber)) { - tDouble /= -1; - } - } else if (isInfinite()) { - if (std::numeric_limits::has_infinity) { - tDouble = std::numeric_limits::infinity(); - } else { - tDouble = std::numeric_limits::max(); - } - if (!isPositive()) { - tDouble = -tDouble; //this was incorrectly "-fDouble" originally. - } - } else { - MaybeStackArray s; - // Note: 14 is a magic constant from the decNumber library documentation, - // the max number of extra characters beyond the number of digits - // needed to represent the number in string form. Add a few more - // for the additional digits we retain. - - // Round down to appx. double precision, if the number is longer than that. - // Copy the number first, so that we don't modify the original. - if (getCount() > MAX_DBL_DIGITS + 3) { - DigitList numToConvert(*this); - numToConvert.reduce(); // Removes any trailing zeros, so that digit count is good. - numToConvert.round(MAX_DBL_DIGITS+3); - uprv_decNumberToString(numToConvert.fDecNumber, s.getAlias()); - // TODO: how many extra digits should be included for an accurate conversion? - } else { - uprv_decNumberToString(this->fDecNumber, s.getAlias()); - } - U_ASSERT(uprv_strlen(&s[0]) < MAX_DBL_DIGITS+18); - - char *end = NULL; - tDouble = decimalStrToDouble(s.getAlias(), &end); - } - { - Mutex mutex; - DigitList *nonConstThis = const_cast(this); - nonConstThis->internalSetDouble(tDouble); - } - return tDouble; -} - -#if U_USE_STRTOD_L && U_PLATFORM_USES_ONLY_WIN32_API -# define locale_t _locale_t -# define freelocale _free_locale -# define strtod_l _strtod_l -#endif - -#if U_USE_STRTOD_L -static locale_t gCLocale = (locale_t)0; -#endif -static icu::UInitOnce gCLocaleInitOnce = U_INITONCE_INITIALIZER; - -U_CDECL_BEGIN -// Cleanup callback func -static UBool U_CALLCONV digitList_cleanup(void) -{ -#if U_USE_STRTOD_L - if (gCLocale != (locale_t)0) { - freelocale(gCLocale); - } -#endif - return TRUE; -} -// C Locale initialization func -static void U_CALLCONV initCLocale(void) { - ucln_i18n_registerCleanup(UCLN_I18N_DIGITLIST, digitList_cleanup); -#if U_USE_STRTOD_L -# if U_PLATFORM_USES_ONLY_WIN32_API - gCLocale = _create_locale(LC_ALL, "C"); -# else - gCLocale = newlocale(LC_ALL_MASK, "C", (locale_t)0); -# endif -#endif -} -U_CDECL_END - -double -DigitList::decimalStrToDouble(char *decstr, char **end) { - umtx_initOnce(gCLocaleInitOnce, &initCLocale); -#if U_USE_STRTOD_L - return strtod_l(decstr, end, gCLocale); -#else - char *decimalPt = strchr(decstr, '.'); - if (decimalPt) { - // We need to know the decimal separator character that will be used with strtod(). - // Depends on the C runtime global locale. - // Most commonly is '.' - char rep[MAX_DIGITS]; - sprintf(rep, "%+1.1f", 1.0); - *decimalPt = rep[2]; - } - return uprv_strtod(decstr, end); -#endif -} - -// ------------------------------------- - -/** - * convert this number to an int32_t. Round if there is a fractional part. - * Return zero if the number cannot be represented. - */ -int32_t DigitList::getLong() /*const*/ -{ - int32_t result = 0; - if (getUpperExponent() > 10) { - // Overflow, absolute value too big. - return result; - } - if (fDecNumber->exponent != 0) { - // Force to an integer, with zero exponent, rounding if necessary. - // (decNumberToInt32 will only work if the exponent is exactly zero.) - DigitList copy(*this); - DigitList zero; - uprv_decNumberQuantize(copy.fDecNumber, copy.fDecNumber, zero.fDecNumber, &fContext); - result = uprv_decNumberToInt32(copy.fDecNumber, &fContext); - } else { - result = uprv_decNumberToInt32(fDecNumber, &fContext); - } - return result; -} - - -/** - * convert this number to an int64_t. Truncate if there is a fractional part. - * Return zero if the number cannot be represented. - */ -int64_t DigitList::getInt64() /*const*/ { - // TODO: fast conversion if fHave == fDouble - - // Truncate if non-integer. - // Return 0 if out of range. - // Range of in64_t is -9223372036854775808 to 9223372036854775807 (19 digits) - // - if (getUpperExponent() > 19) { - // Overflow, absolute value too big. - return 0; - } - - // The number of integer digits may differ from the number of digits stored - // in the decimal number. - // for 12.345 numIntDigits = 2, number->digits = 5 - // for 12E4 numIntDigits = 6, number->digits = 2 - // The conversion ignores the fraction digits in the first case, - // and fakes up extra zero digits in the second. - // TODO: It would be faster to store a table of powers of ten to multiply by - // instead of looping over zero digits, multiplying each time. - - int32_t numIntDigits = getUpperExponent(); - uint64_t value = 0; - for (int32_t i = 0; i < numIntDigits; i++) { - // Loop is iterating over digits starting with the most significant. - // Numbers are stored with the least significant digit at index zero. - int32_t digitIndex = fDecNumber->digits - i - 1; - int32_t v = (digitIndex >= 0) ? fDecNumber->lsu[digitIndex] : 0; - value = value * (uint64_t)10 + (uint64_t)v; - } - - if (decNumberIsNegative(fDecNumber)) { - value = ~value; - value += 1; - } - int64_t svalue = (int64_t)value; - - // Check overflow. It's convenient that the MSD is 9 only on overflow, the amount of - // overflow can't wrap too far. The test will also fail -0, but - // that does no harm; the right answer is 0. - if (numIntDigits == 19) { - if (( decNumberIsNegative(fDecNumber) && svalue>0) || - (!decNumberIsNegative(fDecNumber) && svalue<0)) { - svalue = 0; - } - } - - return svalue; -} - - -/** - * Return a string form of this number. - * Format is as defined by the decNumber library, for interchange of - * decimal numbers. - */ -void DigitList::getDecimal(CharString &str, UErrorCode &status) { - if (U_FAILURE(status)) { - return; - } - - // A decimal number in string form can, worst case, be 14 characters longer - // than the number of digits. So says the decNumber library doc. - int32_t maxLength = fDecNumber->digits + 14; - int32_t capacity = 0; - char *buffer = str.clear().getAppendBuffer(maxLength, 0, capacity, status); - if (U_FAILURE(status)) { - return; // Memory allocation error on growing the string. - } - U_ASSERT(capacity >= maxLength); - uprv_decNumberToString(this->fDecNumber, buffer); - U_ASSERT((int32_t)uprv_strlen(buffer) <= maxLength); - str.append(buffer, -1, status); -} - -/** - * Return true if this is an integer value that can be held - * by an int32_t type. - */ -UBool -DigitList::fitsIntoLong(UBool ignoreNegativeZero) /*const*/ -{ - if (decNumberIsSpecial(this->fDecNumber)) { - // NaN or Infinity. Does not fit in int32. - return FALSE; - } - uprv_decNumberTrim(this->fDecNumber); - if (fDecNumber->exponent < 0) { - // Number contains fraction digits. - return FALSE; - } - if (decNumberIsZero(this->fDecNumber) && !ignoreNegativeZero && - (fDecNumber->bits & DECNEG) != 0) { - // Negative Zero, not ingored. Cannot represent as a long. - return FALSE; - } - if (getUpperExponent() < 10) { - // The number is 9 or fewer digits. - // The max and min int32 are 10 digts, so this number fits. - // This is the common case. - return TRUE; - } - - // TODO: Should cache these constants; construction is relatively costly. - // But not of huge consequence; they're only needed for 10 digit ints. - UErrorCode status = U_ZERO_ERROR; - DigitList min32; min32.set("-2147483648", status); - if (this->compare(min32) < 0) { - return FALSE; - } - DigitList max32; max32.set("2147483647", status); - if (this->compare(max32) > 0) { - return FALSE; - } - if (U_FAILURE(status)) { - return FALSE; - } - return true; -} - - - -/** - * Return true if the number represented by this object can fit into - * a long. - */ -UBool -DigitList::fitsIntoInt64(UBool ignoreNegativeZero) /*const*/ -{ - if (decNumberIsSpecial(this->fDecNumber)) { - // NaN or Infinity. Does not fit in int32. - return FALSE; - } - uprv_decNumberTrim(this->fDecNumber); - if (fDecNumber->exponent < 0) { - // Number contains fraction digits. - return FALSE; - } - if (decNumberIsZero(this->fDecNumber) && !ignoreNegativeZero && - (fDecNumber->bits & DECNEG) != 0) { - // Negative Zero, not ingored. Cannot represent as a long. - return FALSE; - } - if (getUpperExponent() < 19) { - // The number is 18 or fewer digits. - // The max and min int64 are 19 digts, so this number fits. - // This is the common case. - return TRUE; - } - - // TODO: Should cache these constants; construction is relatively costly. - // But not of huge consequence; they're only needed for 19 digit ints. - UErrorCode status = U_ZERO_ERROR; - DigitList min64; min64.set("-9223372036854775808", status); - if (this->compare(min64) < 0) { - return FALSE; - } - DigitList max64; max64.set("9223372036854775807", status); - if (this->compare(max64) > 0) { - return FALSE; - } - if (U_FAILURE(status)) { - return FALSE; - } - return true; -} - - -// ------------------------------------- - -void -DigitList::set(int32_t source) -{ - set((int64_t)source); - internalSetDouble(source); -} - -// ------------------------------------- -/** - * Set an int64, via decnumber - */ -void -DigitList::set(int64_t source) -{ - char str[MAX_DIGITS+2]; // Leave room for sign and trailing nul. - formatBase10(source, str); - U_ASSERT(uprv_strlen(str) < sizeof(str)); - - uprv_decNumberFromString(fDecNumber, str, &fContext); - internalSetDouble(static_cast(source)); -} - -// ------------------------------------- -/** - * Set the DigitList from a decimal number string. - * - * The incoming string _must_ be nul terminated, even though it is arriving - * as a StringPiece because that is what the decNumber library wants. - * We can get away with this for an internal function; it would not - * be acceptable for a public API. - */ -void -DigitList::set(StringPiece source, UErrorCode &status, uint32_t /*fastpathBits*/) { - if (U_FAILURE(status)) { - return; - } - -#if 0 - if(fastpathBits==(kFastpathOk|kNoDecimal)) { - int32_t size = source.size(); - const char *data = source.data(); - int64_t r = 0; - int64_t m = 1; - // fast parse - while(size>0) { - char ch = data[--size]; - if(ch=='+') { - break; - } else if(ch=='-') { - r = -r; - break; - } else { - int64_t d = ch-'0'; - //printf("CH[%d]=%c, %d, *=%d\n", size,ch, (int)d, (int)m); - r+=(d)*m; - m *= 10; - } - } - //printf("R=%d\n", r); - set(r); - } else -#endif - { - // Figure out a max number of digits to use during the conversion, and - // resize the number up if necessary. - int32_t numDigits = source.length(); - if (numDigits > fContext.digits) { - // fContext.digits == fStorage.getCapacity() - decNumber *t = fStorage.resize(numDigits, fStorage.getCapacity()); - if (t == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - fDecNumber = t; - fContext.digits = numDigits; - } - - fContext.status = 0; - uprv_decNumberFromString(fDecNumber, source.data(), &fContext); - if ((fContext.status & DEC_Conversion_syntax) != 0) { - status = U_DECIMAL_NUMBER_SYNTAX_ERROR; - } - } - internalClear(); -} - -/** - * Set the digit list to a representation of the given double value. - * This method supports both fixed-point and exponential notation. - * @param source Value to be converted. - */ -void -DigitList::set(double source) -{ - // for now, simple implementation; later, do proper IEEE stuff - char rep[MAX_DIGITS + 8]; // Extra space for '+', '.', e+NNN, and '\0' (actually +8 is enough) - - // Generate a representation of the form /[+-][0-9].[0-9]+e[+-][0-9]+/ - // Can also generate /[+-]nan/ or /[+-]inf/ - // TODO: Use something other than sprintf() here, since it's behavior is somewhat platform specific. - // That is why infinity is special cased here. - if (uprv_isInfinite(source)) { - if (uprv_isNegativeInfinity(source)) { - uprv_strcpy(rep,"-inf"); // Handle negative infinity - } else { - uprv_strcpy(rep,"inf"); - } - } else if (uprv_isNaN(source)) { - uprv_strcpy(rep, "NaN"); - } else { - bool sign; - int32_t length; - int32_t point; - DoubleToStringConverter::DoubleToAscii( - source, - DoubleToStringConverter::DtoaMode::SHORTEST, - 0, - rep + 1, - sizeof(rep), - &sign, - &length, - &point - ); - - // Convert the raw buffer into a string for decNumber - int32_t power = point - length; - if (sign) { - rep[0] = '-'; - } else { - rep[0] = '0'; - } - length++; - rep[length++] = 'E'; - if (power < 0) { - rep[length++] = '-'; - power = -power; - } else { - rep[length++] = '+'; - } - if (power < 10) { - rep[length++] = power + '0'; - } else if (power < 100) { - rep[length++] = (power / 10) + '0'; - rep[length++] = (power % 10) + '0'; - } else { - U_ASSERT(power < 1000); - rep[length + 2] = (power % 10) + '0'; - power /= 10; - rep[length + 1] = (power % 10) + '0'; - power /= 10; - rep[length] = power + '0'; - length += 3; - } - rep[length++] = 0; - } - U_ASSERT(uprv_strlen(rep) < sizeof(rep)); - - // uprv_decNumberFromString() will parse the string expecting '.' as a - // decimal separator, however sprintf() can use ',' in certain locales. - // Overwrite a ',' with '.' here before proceeding. - char *decimalSeparator = strchr(rep, ','); - if (decimalSeparator != NULL) { - *decimalSeparator = '.'; - } - - // Create a decNumber from the string. - uprv_decNumberFromString(fDecNumber, rep, &fContext); - uprv_decNumberTrim(fDecNumber); - internalSetDouble(source); -} - -// ------------------------------------- - -/* - * Multiply - * The number will be expanded if need be to retain full precision. - * In practice, for formatting, multiply is by 10, 100 or 1000, so more digits - * will not be required for this use. - */ -void -DigitList::mult(const DigitList &other, UErrorCode &status) { - if (U_FAILURE(status)) { - return; - } - fContext.status = 0; - int32_t requiredDigits = this->digits() + other.digits(); - if (requiredDigits > fContext.digits) { - reduce(); // Remove any trailing zeros - int32_t requiredDigits = this->digits() + other.digits(); - ensureCapacity(requiredDigits, status); - } - uprv_decNumberMultiply(fDecNumber, fDecNumber, other.fDecNumber, &fContext); - internalClear(); -} - -// ------------------------------------- - -/* - * Divide - * The number will _not_ be expanded for inexact results. - * TODO: probably should expand some, for rounding increments that - * could add a few digits, e.g. .25, but not expand arbitrarily. - */ -void -DigitList::div(const DigitList &other, UErrorCode &status) { - if (U_FAILURE(status)) { - return; - } - uprv_decNumberDivide(fDecNumber, fDecNumber, other.fDecNumber, &fContext); - internalClear(); -} - -// ------------------------------------- - -/* - * ensureCapacity. Grow the digit storage for the number if it's less than the requested - * amount. Never reduce it. Available size is kept in fContext.digits. - */ -void -DigitList::ensureCapacity(int32_t requestedCapacity, UErrorCode &status) { - if (U_FAILURE(status)) { - return; - } - if (requestedCapacity <= 0) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return; - } - if (requestedCapacity > DEC_MAX_DIGITS) { - // Don't report an error for requesting too much. - // Arithemetic Results will be rounded to what can be supported. - // At 999,999,999 max digits, exceeding the limit is not too likely! - requestedCapacity = DEC_MAX_DIGITS; - } - if (requestedCapacity > fContext.digits) { - decNumber *newBuffer = fStorage.resize(requestedCapacity, fStorage.getCapacity()); - if (newBuffer == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - fContext.digits = requestedCapacity; - fDecNumber = newBuffer; - } -} - -// ------------------------------------- - -/** - * Round the representation to the given number of digits. - * @param maximumDigits The maximum number of digits to be shown. - * Upon return, count will be less than or equal to maximumDigits. - */ -void -DigitList::round(int32_t maximumDigits) -{ - reduce(); - if (maximumDigits >= fDecNumber->digits) { - return; - } - int32_t savedDigits = fContext.digits; - fContext.digits = maximumDigits; - uprv_decNumberPlus(fDecNumber, fDecNumber, &fContext); - fContext.digits = savedDigits; - uprv_decNumberTrim(fDecNumber); - reduce(); - internalClear(); -} - - -void -DigitList::roundFixedPoint(int32_t maximumFractionDigits) { - reduce(); // Remove trailing zeros. - if (fDecNumber->exponent >= -maximumFractionDigits) { - return; - } - decNumber scale; // Dummy decimal number, but with the desired number of - uprv_decNumberZero(&scale); // fraction digits. - scale.exponent = -maximumFractionDigits; - scale.lsu[0] = 1; - - uprv_decNumberQuantize(fDecNumber, fDecNumber, &scale, &fContext); - reduce(); - internalClear(); -} - -// ------------------------------------- - -void -DigitList::toIntegralValue() { - uprv_decNumberToIntegralValue(fDecNumber, fDecNumber, &fContext); -} - - -// ------------------------------------- -UBool -DigitList::isZero() const -{ - return decNumberIsZero(fDecNumber); -} - -// ------------------------------------- -int32_t -DigitList::getUpperExponent() const { - return fDecNumber->digits + fDecNumber->exponent; -} - -DigitInterval & -DigitList::getSmallestInterval(DigitInterval &result) const { - result.setLeastSignificantInclusive(fDecNumber->exponent); - result.setMostSignificantExclusive(getUpperExponent()); - return result; -} - -uint8_t -DigitList::getDigitByExponent(int32_t exponent) const { - int32_t idx = exponent - fDecNumber->exponent; - if (idx < 0 || idx >= fDecNumber->digits) { - return 0; - } - return fDecNumber->lsu[idx]; -} - -void -DigitList::appendDigitsTo(CharString &str, UErrorCode &status) const { - str.append((const char *) fDecNumber->lsu, fDecNumber->digits, status); -} - -void -DigitList::roundAtExponent(int32_t exponent, int32_t maxSigDigits) { - reduce(); - if (maxSigDigits < fDecNumber->digits) { - int32_t minExponent = getUpperExponent() - maxSigDigits; - if (exponent < minExponent) { - exponent = minExponent; - } - } - if (exponent <= fDecNumber->exponent) { - return; - } - int32_t digits = getUpperExponent() - exponent; - if (digits > 0) { - round(digits); - } else { - roundFixedPoint(-exponent); - } -} - -void -DigitList::quantize(const DigitList &quantity, UErrorCode &status) { - if (U_FAILURE(status)) { - return; - } - div(quantity, status); - roundAtExponent(0); - mult(quantity, status); - reduce(); -} - -int32_t -DigitList::getScientificExponent( - int32_t minIntDigitCount, int32_t exponentMultiplier) const { - // The exponent for zero is always zero. - if (isZero()) { - return 0; - } - int32_t intDigitCount = getUpperExponent(); - int32_t exponent; - if (intDigitCount >= minIntDigitCount) { - int32_t maxAdjustment = intDigitCount - minIntDigitCount; - exponent = (maxAdjustment / exponentMultiplier) * exponentMultiplier; - } else { - int32_t minAdjustment = minIntDigitCount - intDigitCount; - exponent = ((minAdjustment + exponentMultiplier - 1) / exponentMultiplier) * -exponentMultiplier; - } - return exponent; -} - -int32_t -DigitList::toScientific( - int32_t minIntDigitCount, int32_t exponentMultiplier) { - int32_t exponent = getScientificExponent( - minIntDigitCount, exponentMultiplier); - shiftDecimalRight(-exponent); - return exponent; -} - -void -DigitList::shiftDecimalRight(int32_t n) { - fDecNumber->exponent += n; - internalClear(); -} - -U_NAMESPACE_END -#endif // #if !UCONFIG_NO_FORMATTING - -//eof diff --git a/deps/icu-small/source/i18n/digitlst.h b/deps/icu-small/source/i18n/digitlst.h deleted file mode 100644 index 6befaf32e6f340..00000000000000 --- a/deps/icu-small/source/i18n/digitlst.h +++ /dev/null @@ -1,529 +0,0 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -****************************************************************************** -* -* Copyright (C) 1997-2015, International Business Machines -* Corporation and others. All Rights Reserved. -* -****************************************************************************** -* -* File DIGITLST.H -* -* Modification History: -* -* Date Name Description -* 02/25/97 aliu Converted from java. -* 03/21/97 clhuang Updated per C++ implementation. -* 04/15/97 aliu Changed MAX_COUNT to DBL_DIG. Changed Digit to char. -* 09/09/97 aliu Adapted for exponential notation support. -* 08/02/98 stephen Added nearest/even rounding -* 06/29/99 stephen Made LONG_DIGITS a macro to satisfy SUN compiler -* 07/09/99 stephen Removed kMaxCount (unused, for HP compiler) -****************************************************************************** -*/ - -#ifndef DIGITLST_H -#define DIGITLST_H - -#include "unicode/uobject.h" - -#if !UCONFIG_NO_FORMATTING -#include "unicode/decimfmt.h" -#include -#include "decContext.h" -#include "decNumber.h" -#include "cmemory.h" - -// Decimal digits in a 64-bit int -#define INT64_DIGITS 19 - -typedef enum EDigitListValues { - MAX_DBL_DIGITS = DBL_DIG, - MAX_I64_DIGITS = INT64_DIGITS, - MAX_DIGITS = MAX_I64_DIGITS, - MAX_EXPONENT = DBL_DIG, - DIGIT_PADDING = 3, - DEFAULT_DIGITS = 40, // Initial storage size, will grow as needed. - - // "+." + fDigits + "e" + fDecimalAt - MAX_DEC_DIGITS = MAX_DIGITS + DIGIT_PADDING + MAX_EXPONENT -} EDigitListValues; - -U_NAMESPACE_BEGIN - -class CharString; -class DigitInterval; - -// Export an explicit template instantiation of the MaybeStackHeaderAndArray that -// is used as a data member of DigitList. -// -// MSVC requires this, even though it should not be necessary. -// No direct access to the MaybeStackHeaderAndArray leaks out of the i18n library. -// -// Macintosh produces duplicate definition linker errors with the explicit template -// instantiation. -// -#if !U_PLATFORM_IS_DARWIN_BASED -template class U_I18N_API MaybeStackHeaderAndArray; -#endif - - -enum EStackMode { kOnStack }; - -enum EFastpathBits { kFastpathOk = 1, kNoDecimal = 2 }; - -/** - * Digit List is actually a Decimal Floating Point number. - * The original implementation has been replaced by a thin wrapper onto a - * decimal number from the decNumber library. - * - * The original DigitList API has been retained, to minimize the impact of - * the change on the rest of the ICU formatting code. - * - * The change to decNumber enables support for big decimal numbers, and - * allows rounding computations to be done directly in decimal, avoiding - * extra, and inaccurate, conversions to and from doubles. - * - * Original DigitList comments: - * - * Digit List utility class. Private to DecimalFormat. Handles the transcoding - * between numeric values and strings of characters. Only handles - * non-negative numbers. The division of labor between DigitList and - * DecimalFormat is that DigitList handles the radix 10 representation - * issues; DecimalFormat handles the locale-specific issues such as - * positive/negative, grouping, decimal point, currency, and so on. - *

- * A DigitList is really a representation of a floating point value. - * It may be an integer value; we assume that a double has sufficient - * precision to represent all digits of a long. - *

- * The DigitList representation consists of a string of characters, - * which are the digits radix 10, from '0' to '9'. It also has a radix - * 10 exponent associated with it. The value represented by a DigitList - * object can be computed by mulitplying the fraction f, where 0 <= f < 1, - * derived by placing all the digits of the list to the right of the - * decimal point, by 10^exponent. - * - * -------- - * - * DigitList vs. decimalNumber: - * - * DigitList stores digits with the most significant first. - * decNumber stores digits with the least significant first. - * - * DigitList, decimal point is before the most significant. - * decNumber, decimal point is after the least signficant digit. - * - * digitList: 0.ddddd * 10 ^ exp - * decNumber: ddddd. * 10 ^ exp - * - * digitList exponent = decNumber exponent + digit count - * - * digitList, digits are platform invariant chars, '0' - '9' - * decNumber, digits are binary, one per byte, 0 - 9. - * - * (decNumber library is configurable in how digits are stored, ICU has configured - * it this way for convenience in replacing the old DigitList implementation.) - */ -class U_I18N_API DigitList : public UMemory { // Declare external to make compiler happy -public: - - DigitList(); - ~DigitList(); - - /* copy constructor - * @param DigitList The object to be copied. - * @return the newly created object. - */ - DigitList(const DigitList&); // copy constructor - - /* assignment operator - * @param DigitList The object to be copied. - * @return the newly created object. - */ - DigitList& operator=(const DigitList&); // assignment operator - - /** - * Return true if another object is semantically equal to this one. - * @param other The DigitList to be compared for equality - * @return true if another object is semantically equal to this one. - * return false otherwise. - */ - UBool operator==(const DigitList& other) const; - - int32_t compare(const DigitList& other); - - - inline UBool operator!=(const DigitList& other) const { return !operator==(other); } - - /** - * Clears out the digits. - * Use before appending them. - * Typically, you set a series of digits with append, then at the point - * you hit the decimal point, you set myDigitList.fDecimalAt = myDigitList.fCount; - * then go on appending digits. - */ - void clear(void); - - /** - * Remove, by rounding, any fractional part of the decimal number, - * leaving an integer value. - */ - void toIntegralValue(); - - /** - * Appends digits to the list. - * CAUTION: this function is not recommended for new code. - * In the original DigitList implementation, decimal numbers were - * parsed by appending them to a digit list as they were encountered. - * With the revamped DigitList based on decNumber, append is very - * inefficient, and the interaction with the exponent value is confusing. - * Best avoided. - * TODO: remove this function once all use has been replaced. - * TODO: describe alternative to append() - * @param digit The digit to be appended. - */ - void append(char digit); - - /** - * Utility routine to get the value of the digit list - * Returns 0.0 if zero length. - * @return the value of the digit list. - */ - double getDouble(void) const; - - /** - * Utility routine to get the value of the digit list - * Make sure that fitsIntoLong() is called before calling this function. - * Returns 0 if zero length. - * @return the value of the digit list, return 0 if it is zero length - */ - int32_t getLong(void) /*const*/; - - /** - * Utility routine to get the value of the digit list - * Make sure that fitsIntoInt64() is called before calling this function. - * Returns 0 if zero length. - * @return the value of the digit list, return 0 if it is zero length - */ - int64_t getInt64(void) /*const*/; - - /** - * Utility routine to get the value of the digit list as a decimal string. - */ - void getDecimal(CharString &str, UErrorCode &status); - - /** - * Return true if the number represented by this object can fit into - * a long. - * @param ignoreNegativeZero True if negative zero is ignored. - * @return true if the number represented by this object can fit into - * a long, return false otherwise. - */ - UBool fitsIntoLong(UBool ignoreNegativeZero) /*const*/; - - /** - * Return true if the number represented by this object can fit into - * an int64_t. - * @param ignoreNegativeZero True if negative zero is ignored. - * @return true if the number represented by this object can fit into - * a long, return false otherwise. - */ - UBool fitsIntoInt64(UBool ignoreNegativeZero) /*const*/; - - /** - * Utility routine to set the value of the digit list from a double. - * @param source The value to be set - */ - void set(double source); - - /** - * Utility routine to set the value of the digit list from a long. - * If a non-zero maximumDigits is specified, no more than that number of - * significant digits will be produced. - * @param source The value to be set - */ - void set(int32_t source); - - /** - * Utility routine to set the value of the digit list from an int64. - * If a non-zero maximumDigits is specified, no more than that number of - * significant digits will be produced. - * @param source The value to be set - */ - void set(int64_t source); - - /** - * Utility routine to set the value of the digit list from an int64. - * Does not set the decnumber unless requested later - * If a non-zero maximumDigits is specified, no more than that number of - * significant digits will be produced. - * @param source The value to be set - */ - void setInteger(int64_t source); - - /** - * Utility routine to set the value of the digit list from a decimal number - * string. - * @param source The value to be set. The string must be nul-terminated. - * @param fastpathBits special flags for fast parsing - */ - void set(StringPiece source, UErrorCode &status, uint32_t fastpathBits = 0); - - /** - * Multiply this = this * arg - * This digitlist will be expanded if necessary to accomodate the result. - * @param arg the number to multiply by. - */ - void mult(const DigitList &arg, UErrorCode &status); - - /** - * Divide this = this / arg - */ - void div(const DigitList &arg, UErrorCode &status); - - // The following functions replace direct access to the original DigitList implmentation - // data structures. - - void setRoundingMode(DecimalFormat::ERoundingMode m); - - /** Test a number for zero. - * @return TRUE if the number is zero - */ - UBool isZero(void) const; - - /** Test for a Nan - * @return TRUE if the number is a NaN - */ - UBool isNaN(void) const {return decNumberIsNaN(fDecNumber);} - - UBool isInfinite() const {return decNumberIsInfinite(fDecNumber);} - - /** Reduce, or normalize. Removes trailing zeroes, adjusts exponent appropriately. */ - void reduce(); - - /** Remove trailing fraction zeros, adjust exponent accordingly. */ - void trim(); - - /** Set to zero */ - void setToZero() {uprv_decNumberZero(fDecNumber);} - - /** get the number of digits in the decimal number */ - int32_t digits() const {return fDecNumber->digits;} - - /** - * Round the number to the given number of digits. - * @param maximumDigits The maximum number of digits to be shown. - * Upon return, count will be less than or equal to maximumDigits. - * result is guaranteed to be trimmed. - */ - void round(int32_t maximumDigits); - - void roundFixedPoint(int32_t maximumFractionDigits); - - /** Ensure capacity for digits. Grow the storage if it is currently less than - * the requested size. Capacity is not reduced if it is already greater - * than requested. - */ - void ensureCapacity(int32_t requestedSize, UErrorCode &status); - - UBool isPositive(void) const { return decNumberIsNegative(fDecNumber) == 0;} - void setPositive(UBool s); - - void setDecimalAt(int32_t d); - int32_t getDecimalAt(); - - void setCount(int32_t c); - int32_t getCount() const; - - /** - * Set the digit in platform (invariant) format, from '0'..'9' - * @param i index of digit - * @param v digit value, from '0' to '9' in platform invariant format - */ - void setDigit(int32_t i, char v); - - /** - * Get the digit in platform (invariant) format, from '0'..'9' inclusive - * @param i index of digit - * @return invariant format of the digit - */ - char getDigit(int32_t i); - - - /** - * Get the digit's value, as an integer from 0..9 inclusive. - * Note that internally this value is a decNumberUnit, but ICU configures it to be a uint8_t. - * @param i index of digit - * @return value of that digit - */ - uint8_t getDigitValue(int32_t i); - - /** - * Gets the upper bound exponent for this value. For 987, returns 3 - * because 10^3 is the smallest power of 10 that is just greater than - * 987. - */ - int32_t getUpperExponent() const; - - /** - * Gets the lower bound exponent for this value. For 98.7, returns -1 - * because the right most digit, is the 10^-1 place. - */ - int32_t getLowerExponent() const { return fDecNumber->exponent; } - - /** - * Sets result to the smallest DigitInterval needed to display this - * DigitList in fixed point form and returns result. - */ - DigitInterval& getSmallestInterval(DigitInterval &result) const; - - /** - * Like getDigitValue, but the digit is identified by exponent. - * For example, getDigitByExponent(7) returns the 10^7 place of this - * DigitList. Unlike getDigitValue, there are no upper or lower bounds - * for passed parameter. Instead, getDigitByExponent returns 0 if - * the exponent falls outside the interval for this DigitList. - */ - uint8_t getDigitByExponent(int32_t exponent) const; - - /** - * Appends the digits in this object to a CharString. - * 3 is appended as (char) 3, not '3' - */ - void appendDigitsTo(CharString &str, UErrorCode &status) const; - - /** - * Equivalent to roundFixedPoint(-digitExponent) except unlike - * roundFixedPoint, this works for any digitExponent value. - * If maxSigDigits is set then this instance is rounded to have no more - * than maxSigDigits. The end result is guaranteed to be trimmed. - */ - void roundAtExponent(int32_t digitExponent, int32_t maxSigDigits=INT32_MAX); - - /** - * Quantizes according to some amount and rounds according to the - * context of this instance. Quantizing 3.233 with 0.05 gives 3.25. - */ - void quantize(const DigitList &amount, UErrorCode &status); - - /** - * Like toScientific but only returns the exponent - * leaving this instance unchanged. - */ - int32_t getScientificExponent( - int32_t minIntDigitCount, int32_t exponentMultiplier) const; - - /** - * Converts this instance to scientific notation. This instance - * becomes the mantissa and the exponent is returned. - * @param minIntDigitCount minimum integer digits in mantissa - * Exponent is set so that the actual number of integer digits - * in mantissa is as close to the minimum as possible. - * @param exponentMultiplier The exponent is always a multiple of - * This number. Usually 1, but set to 3 for engineering notation. - * @return exponent - */ - int32_t toScientific( - int32_t minIntDigitCount, int32_t exponentMultiplier); - - /** - * Shifts decimal to the right. - */ - void shiftDecimalRight(int32_t numPlaces); - -private: - /* - * These data members are intentionally public and can be set directly. - *

- * The value represented is given by placing the decimal point before - * fDigits[fDecimalAt]. If fDecimalAt is < 0, then leading zeros between - * the decimal point and the first nonzero digit are implied. If fDecimalAt - * is > fCount, then trailing zeros between the fDigits[fCount-1] and the - * decimal point are implied. - *

- * Equivalently, the represented value is given by f * 10^fDecimalAt. Here - * f is a value 0.1 <= f < 1 arrived at by placing the digits in fDigits to - * the right of the decimal. - *

- * DigitList is normalized, so if it is non-zero, fDigits[0] is non-zero. We - * don't allow denormalized numbers because our exponent is effectively of - * unlimited magnitude. The fCount value contains the number of significant - * digits present in fDigits[]. - *

- * Zero is represented by any DigitList with fCount == 0 or with each fDigits[i] - * for all i <= fCount == '0'. - * - * int32_t fDecimalAt; - * int32_t fCount; - * UBool fIsPositive; - * char *fDigits; - * DecimalFormat::ERoundingMode fRoundingMode; - */ - -public: - decContext fContext; // public access to status flags. - -private: - decNumber *fDecNumber; - MaybeStackHeaderAndArray fStorage; - - /* Cached double value corresponding to this decimal number. - * This is an optimization for the formatting implementation, which may - * ask for the double value multiple times. - */ - union DoubleOrInt64 { - double fDouble; - int64_t fInt64; - } fUnion; - enum EHave { - kNone=0, - kDouble - } fHave; - - - - UBool shouldRoundUp(int32_t maximumDigits) const; - - public: - -#if U_OVERRIDE_CXX_ALLOCATION - using UMemory::operator new; - using UMemory::operator delete; -#else - static inline void * U_EXPORT2 operator new(size_t size) U_NO_THROW { return ::operator new(size); }; - static inline void U_EXPORT2 operator delete(void *ptr ) U_NO_THROW { ::operator delete(ptr); }; -#endif - - static double U_EXPORT2 decimalStrToDouble(char *decstr, char **end); - - /** - * Placement new for stack usage - * @internal - */ - static inline void * U_EXPORT2 operator new(size_t /*size*/, void * onStack, EStackMode /*mode*/) U_NO_THROW { return onStack; } - - /** - * Placement delete for stack usage - * @internal - */ - static inline void U_EXPORT2 operator delete(void * /*ptr*/, void * /*onStack*/, EStackMode /*mode*/) U_NO_THROW {} - - private: - inline void internalSetDouble(double d) { - fHave = kDouble; - fUnion.fDouble=d; - } - inline void internalClear() { - fHave = kNone; - } -}; - - -U_NAMESPACE_END - -#endif // #if !UCONFIG_NO_FORMATTING -#endif // _DIGITLST - -//eof diff --git a/deps/icu-small/source/i18n/double-conversion-strtod.cpp b/deps/icu-small/source/i18n/double-conversion-strtod.cpp new file mode 100644 index 00000000000000..be9b0b3bce0e76 --- /dev/null +++ b/deps/icu-small/source/i18n/double-conversion-strtod.cpp @@ -0,0 +1,574 @@ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +// +// From the double-conversion library. Original license: +// +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// ICU PATCH: ifdef around UCONFIG_NO_FORMATTING +#include "unicode/utypes.h" +#if !UCONFIG_NO_FORMATTING + +#include +#include + +// ICU PATCH: Customize header file paths for ICU. +// The file fixed-dtoa.h is not needed. + +#include "double-conversion-strtod.h" +#include "double-conversion-bignum.h" +#include "double-conversion-cached-powers.h" +#include "double-conversion-ieee.h" + +// ICU PATCH: Wrap in ICU namespace +U_NAMESPACE_BEGIN + +namespace double_conversion { + +// 2^53 = 9007199254740992. +// Any integer with at most 15 decimal digits will hence fit into a double +// (which has a 53bit significand) without loss of precision. +static const int kMaxExactDoubleIntegerDecimalDigits = 15; +// 2^64 = 18446744073709551616 > 10^19 +static const int kMaxUint64DecimalDigits = 19; + +// Max double: 1.7976931348623157 x 10^308 +// Min non-zero double: 4.9406564584124654 x 10^-324 +// Any x >= 10^309 is interpreted as +infinity. +// Any x <= 10^-324 is interpreted as 0. +// Note that 2.5e-324 (despite being smaller than the min double) will be read +// as non-zero (equal to the min non-zero double). +static const int kMaxDecimalPower = 309; +static const int kMinDecimalPower = -324; + +// 2^64 = 18446744073709551616 +static const uint64_t kMaxUint64 = UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF); + + +static const double exact_powers_of_ten[] = { + 1.0, // 10^0 + 10.0, + 100.0, + 1000.0, + 10000.0, + 100000.0, + 1000000.0, + 10000000.0, + 100000000.0, + 1000000000.0, + 10000000000.0, // 10^10 + 100000000000.0, + 1000000000000.0, + 10000000000000.0, + 100000000000000.0, + 1000000000000000.0, + 10000000000000000.0, + 100000000000000000.0, + 1000000000000000000.0, + 10000000000000000000.0, + 100000000000000000000.0, // 10^20 + 1000000000000000000000.0, + // 10^22 = 0x21e19e0c9bab2400000 = 0x878678326eac9 * 2^22 + 10000000000000000000000.0 +}; +static const int kExactPowersOfTenSize = ARRAY_SIZE(exact_powers_of_ten); + +// Maximum number of significant digits in the decimal representation. +// In fact the value is 772 (see conversions.cc), but to give us some margin +// we round up to 780. +static const int kMaxSignificantDecimalDigits = 780; + +static Vector TrimLeadingZeros(Vector buffer) { + for (int i = 0; i < buffer.length(); i++) { + if (buffer[i] != '0') { + return buffer.SubVector(i, buffer.length()); + } + } + return Vector(buffer.start(), 0); +} + + +static Vector TrimTrailingZeros(Vector buffer) { + for (int i = buffer.length() - 1; i >= 0; --i) { + if (buffer[i] != '0') { + return buffer.SubVector(0, i + 1); + } + } + return Vector(buffer.start(), 0); +} + + +static void CutToMaxSignificantDigits(Vector buffer, + int exponent, + char* significant_buffer, + int* significant_exponent) { + for (int i = 0; i < kMaxSignificantDecimalDigits - 1; ++i) { + significant_buffer[i] = buffer[i]; + } + // The input buffer has been trimmed. Therefore the last digit must be + // different from '0'. + ASSERT(buffer[buffer.length() - 1] != '0'); + // Set the last digit to be non-zero. This is sufficient to guarantee + // correct rounding. + significant_buffer[kMaxSignificantDecimalDigits - 1] = '1'; + *significant_exponent = + exponent + (buffer.length() - kMaxSignificantDecimalDigits); +} + + +// Trims the buffer and cuts it to at most kMaxSignificantDecimalDigits. +// If possible the input-buffer is reused, but if the buffer needs to be +// modified (due to cutting), then the input needs to be copied into the +// buffer_copy_space. +static void TrimAndCut(Vector buffer, int exponent, + char* buffer_copy_space, int space_size, + Vector* trimmed, int* updated_exponent) { + Vector left_trimmed = TrimLeadingZeros(buffer); + Vector right_trimmed = TrimTrailingZeros(left_trimmed); + exponent += left_trimmed.length() - right_trimmed.length(); + if (right_trimmed.length() > kMaxSignificantDecimalDigits) { + (void) space_size; // Mark variable as used. + ASSERT(space_size >= kMaxSignificantDecimalDigits); + CutToMaxSignificantDigits(right_trimmed, exponent, + buffer_copy_space, updated_exponent); + *trimmed = Vector(buffer_copy_space, + kMaxSignificantDecimalDigits); + } else { + *trimmed = right_trimmed; + *updated_exponent = exponent; + } +} + + +// Reads digits from the buffer and converts them to a uint64. +// Reads in as many digits as fit into a uint64. +// When the string starts with "1844674407370955161" no further digit is read. +// Since 2^64 = 18446744073709551616 it would still be possible read another +// digit if it was less or equal than 6, but this would complicate the code. +static uint64_t ReadUint64(Vector buffer, + int* number_of_read_digits) { + uint64_t result = 0; + int i = 0; + while (i < buffer.length() && result <= (kMaxUint64 / 10 - 1)) { + int digit = buffer[i++] - '0'; + ASSERT(0 <= digit && digit <= 9); + result = 10 * result + digit; + } + *number_of_read_digits = i; + return result; +} + + +// Reads a DiyFp from the buffer. +// The returned DiyFp is not necessarily normalized. +// If remaining_decimals is zero then the returned DiyFp is accurate. +// Otherwise it has been rounded and has error of at most 1/2 ulp. +static void ReadDiyFp(Vector buffer, + DiyFp* result, + int* remaining_decimals) { + int read_digits; + uint64_t significand = ReadUint64(buffer, &read_digits); + if (buffer.length() == read_digits) { + *result = DiyFp(significand, 0); + *remaining_decimals = 0; + } else { + // Round the significand. + if (buffer[read_digits] >= '5') { + significand++; + } + // Compute the binary exponent. + int exponent = 0; + *result = DiyFp(significand, exponent); + *remaining_decimals = buffer.length() - read_digits; + } +} + + +static bool DoubleStrtod(Vector trimmed, + int exponent, + double* result) { +#if !defined(DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS) + // On x86 the floating-point stack can be 64 or 80 bits wide. If it is + // 80 bits wide (as is the case on Linux) then double-rounding occurs and the + // result is not accurate. + // We know that Windows32 uses 64 bits and is therefore accurate. + // Note that the ARM simulator is compiled for 32bits. It therefore exhibits + // the same problem. + return false; +#endif + if (trimmed.length() <= kMaxExactDoubleIntegerDecimalDigits) { + int read_digits; + // The trimmed input fits into a double. + // If the 10^exponent (resp. 10^-exponent) fits into a double too then we + // can compute the result-double simply by multiplying (resp. dividing) the + // two numbers. + // This is possible because IEEE guarantees that floating-point operations + // return the best possible approximation. + if (exponent < 0 && -exponent < kExactPowersOfTenSize) { + // 10^-exponent fits into a double. + *result = static_cast(ReadUint64(trimmed, &read_digits)); + ASSERT(read_digits == trimmed.length()); + *result /= exact_powers_of_ten[-exponent]; + return true; + } + if (0 <= exponent && exponent < kExactPowersOfTenSize) { + // 10^exponent fits into a double. + *result = static_cast(ReadUint64(trimmed, &read_digits)); + ASSERT(read_digits == trimmed.length()); + *result *= exact_powers_of_ten[exponent]; + return true; + } + int remaining_digits = + kMaxExactDoubleIntegerDecimalDigits - trimmed.length(); + if ((0 <= exponent) && + (exponent - remaining_digits < kExactPowersOfTenSize)) { + // The trimmed string was short and we can multiply it with + // 10^remaining_digits. As a result the remaining exponent now fits + // into a double too. + *result = static_cast(ReadUint64(trimmed, &read_digits)); + ASSERT(read_digits == trimmed.length()); + *result *= exact_powers_of_ten[remaining_digits]; + *result *= exact_powers_of_ten[exponent - remaining_digits]; + return true; + } + } + return false; +} + + +// Returns 10^exponent as an exact DiyFp. +// The given exponent must be in the range [1; kDecimalExponentDistance[. +static DiyFp AdjustmentPowerOfTen(int exponent) { + ASSERT(0 < exponent); + ASSERT(exponent < PowersOfTenCache::kDecimalExponentDistance); + // Simply hardcode the remaining powers for the given decimal exponent + // distance. + ASSERT(PowersOfTenCache::kDecimalExponentDistance == 8); + switch (exponent) { + case 1: return DiyFp(UINT64_2PART_C(0xa0000000, 00000000), -60); + case 2: return DiyFp(UINT64_2PART_C(0xc8000000, 00000000), -57); + case 3: return DiyFp(UINT64_2PART_C(0xfa000000, 00000000), -54); + case 4: return DiyFp(UINT64_2PART_C(0x9c400000, 00000000), -50); + case 5: return DiyFp(UINT64_2PART_C(0xc3500000, 00000000), -47); + case 6: return DiyFp(UINT64_2PART_C(0xf4240000, 00000000), -44); + case 7: return DiyFp(UINT64_2PART_C(0x98968000, 00000000), -40); + default: + UNREACHABLE(); + } +} + + +// If the function returns true then the result is the correct double. +// Otherwise it is either the correct double or the double that is just below +// the correct double. +static bool DiyFpStrtod(Vector buffer, + int exponent, + double* result) { + DiyFp input; + int remaining_decimals; + ReadDiyFp(buffer, &input, &remaining_decimals); + // Since we may have dropped some digits the input is not accurate. + // If remaining_decimals is different than 0 than the error is at most + // .5 ulp (unit in the last place). + // We don't want to deal with fractions and therefore keep a common + // denominator. + const int kDenominatorLog = 3; + const int kDenominator = 1 << kDenominatorLog; + // Move the remaining decimals into the exponent. + exponent += remaining_decimals; + uint64_t error = (remaining_decimals == 0 ? 0 : kDenominator / 2); + + int old_e = input.e(); + input.Normalize(); + error <<= old_e - input.e(); + + ASSERT(exponent <= PowersOfTenCache::kMaxDecimalExponent); + if (exponent < PowersOfTenCache::kMinDecimalExponent) { + *result = 0.0; + return true; + } + DiyFp cached_power; + int cached_decimal_exponent; + PowersOfTenCache::GetCachedPowerForDecimalExponent(exponent, + &cached_power, + &cached_decimal_exponent); + + if (cached_decimal_exponent != exponent) { + int adjustment_exponent = exponent - cached_decimal_exponent; + DiyFp adjustment_power = AdjustmentPowerOfTen(adjustment_exponent); + input.Multiply(adjustment_power); + if (kMaxUint64DecimalDigits - buffer.length() >= adjustment_exponent) { + // The product of input with the adjustment power fits into a 64 bit + // integer. + ASSERT(DiyFp::kSignificandSize == 64); + } else { + // The adjustment power is exact. There is hence only an error of 0.5. + error += kDenominator / 2; + } + } + + input.Multiply(cached_power); + // The error introduced by a multiplication of a*b equals + // error_a + error_b + error_a*error_b/2^64 + 0.5 + // Substituting a with 'input' and b with 'cached_power' we have + // error_b = 0.5 (all cached powers have an error of less than 0.5 ulp), + // error_ab = 0 or 1 / kDenominator > error_a*error_b/ 2^64 + int error_b = kDenominator / 2; + int error_ab = (error == 0 ? 0 : 1); // We round up to 1. + int fixed_error = kDenominator / 2; + error += error_b + error_ab + fixed_error; + + old_e = input.e(); + input.Normalize(); + error <<= old_e - input.e(); + + // See if the double's significand changes if we add/subtract the error. + int order_of_magnitude = DiyFp::kSignificandSize + input.e(); + int effective_significand_size = + Double::SignificandSizeForOrderOfMagnitude(order_of_magnitude); + int precision_digits_count = + DiyFp::kSignificandSize - effective_significand_size; + if (precision_digits_count + kDenominatorLog >= DiyFp::kSignificandSize) { + // This can only happen for very small denormals. In this case the + // half-way multiplied by the denominator exceeds the range of an uint64. + // Simply shift everything to the right. + int shift_amount = (precision_digits_count + kDenominatorLog) - + DiyFp::kSignificandSize + 1; + input.set_f(input.f() >> shift_amount); + input.set_e(input.e() + shift_amount); + // We add 1 for the lost precision of error, and kDenominator for + // the lost precision of input.f(). + error = (error >> shift_amount) + 1 + kDenominator; + precision_digits_count -= shift_amount; + } + // We use uint64_ts now. This only works if the DiyFp uses uint64_ts too. + ASSERT(DiyFp::kSignificandSize == 64); + ASSERT(precision_digits_count < 64); + uint64_t one64 = 1; + uint64_t precision_bits_mask = (one64 << precision_digits_count) - 1; + uint64_t precision_bits = input.f() & precision_bits_mask; + uint64_t half_way = one64 << (precision_digits_count - 1); + precision_bits *= kDenominator; + half_way *= kDenominator; + DiyFp rounded_input(input.f() >> precision_digits_count, + input.e() + precision_digits_count); + if (precision_bits >= half_way + error) { + rounded_input.set_f(rounded_input.f() + 1); + } + // If the last_bits are too close to the half-way case than we are too + // inaccurate and round down. In this case we return false so that we can + // fall back to a more precise algorithm. + + *result = Double(rounded_input).value(); + if (half_way - error < precision_bits && precision_bits < half_way + error) { + // Too imprecise. The caller will have to fall back to a slower version. + // However the returned number is guaranteed to be either the correct + // double, or the next-lower double. + return false; + } else { + return true; + } +} + + +// Returns +// - -1 if buffer*10^exponent < diy_fp. +// - 0 if buffer*10^exponent == diy_fp. +// - +1 if buffer*10^exponent > diy_fp. +// Preconditions: +// buffer.length() + exponent <= kMaxDecimalPower + 1 +// buffer.length() + exponent > kMinDecimalPower +// buffer.length() <= kMaxDecimalSignificantDigits +static int CompareBufferWithDiyFp(Vector buffer, + int exponent, + DiyFp diy_fp) { + ASSERT(buffer.length() + exponent <= kMaxDecimalPower + 1); + ASSERT(buffer.length() + exponent > kMinDecimalPower); + ASSERT(buffer.length() <= kMaxSignificantDecimalDigits); + // Make sure that the Bignum will be able to hold all our numbers. + // Our Bignum implementation has a separate field for exponents. Shifts will + // consume at most one bigit (< 64 bits). + // ln(10) == 3.3219... + ASSERT(((kMaxDecimalPower + 1) * 333 / 100) < Bignum::kMaxSignificantBits); + Bignum buffer_bignum; + Bignum diy_fp_bignum; + buffer_bignum.AssignDecimalString(buffer); + diy_fp_bignum.AssignUInt64(diy_fp.f()); + if (exponent >= 0) { + buffer_bignum.MultiplyByPowerOfTen(exponent); + } else { + diy_fp_bignum.MultiplyByPowerOfTen(-exponent); + } + if (diy_fp.e() > 0) { + diy_fp_bignum.ShiftLeft(diy_fp.e()); + } else { + buffer_bignum.ShiftLeft(-diy_fp.e()); + } + return Bignum::Compare(buffer_bignum, diy_fp_bignum); +} + + +// Returns true if the guess is the correct double. +// Returns false, when guess is either correct or the next-lower double. +static bool ComputeGuess(Vector trimmed, int exponent, + double* guess) { + if (trimmed.length() == 0) { + *guess = 0.0; + return true; + } + if (exponent + trimmed.length() - 1 >= kMaxDecimalPower) { + *guess = Double::Infinity(); + return true; + } + if (exponent + trimmed.length() <= kMinDecimalPower) { + *guess = 0.0; + return true; + } + + if (DoubleStrtod(trimmed, exponent, guess) || + DiyFpStrtod(trimmed, exponent, guess)) { + return true; + } + if (*guess == Double::Infinity()) { + return true; + } + return false; +} + +double Strtod(Vector buffer, int exponent) { + char copy_buffer[kMaxSignificantDecimalDigits]; + Vector trimmed; + int updated_exponent; + TrimAndCut(buffer, exponent, copy_buffer, kMaxSignificantDecimalDigits, + &trimmed, &updated_exponent); + exponent = updated_exponent; + + double guess; + bool is_correct = ComputeGuess(trimmed, exponent, &guess); + if (is_correct) return guess; + + DiyFp upper_boundary = Double(guess).UpperBoundary(); + int comparison = CompareBufferWithDiyFp(trimmed, exponent, upper_boundary); + if (comparison < 0) { + return guess; + } else if (comparison > 0) { + return Double(guess).NextDouble(); + } else if ((Double(guess).Significand() & 1) == 0) { + // Round towards even. + return guess; + } else { + return Double(guess).NextDouble(); + } +} + +float Strtof(Vector buffer, int exponent) { + char copy_buffer[kMaxSignificantDecimalDigits]; + Vector trimmed; + int updated_exponent; + TrimAndCut(buffer, exponent, copy_buffer, kMaxSignificantDecimalDigits, + &trimmed, &updated_exponent); + exponent = updated_exponent; + + double double_guess; + bool is_correct = ComputeGuess(trimmed, exponent, &double_guess); + + float float_guess = static_cast(double_guess); + if (float_guess == double_guess) { + // This shortcut triggers for integer values. + return float_guess; + } + + // We must catch double-rounding. Say the double has been rounded up, and is + // now a boundary of a float, and rounds up again. This is why we have to + // look at previous too. + // Example (in decimal numbers): + // input: 12349 + // high-precision (4 digits): 1235 + // low-precision (3 digits): + // when read from input: 123 + // when rounded from high precision: 124. + // To do this we simply look at the neigbors of the correct result and see + // if they would round to the same float. If the guess is not correct we have + // to look at four values (since two different doubles could be the correct + // double). + + double double_next = Double(double_guess).NextDouble(); + double double_previous = Double(double_guess).PreviousDouble(); + + float f1 = static_cast(double_previous); + float f2 = float_guess; + float f3 = static_cast(double_next); + float f4; + if (is_correct) { + f4 = f3; + } else { + double double_next2 = Double(double_next).NextDouble(); + f4 = static_cast(double_next2); + } + (void) f2; // Mark variable as used. + ASSERT(f1 <= f2 && f2 <= f3 && f3 <= f4); + + // If the guess doesn't lie near a single-precision boundary we can simply + // return its float-value. + if (f1 == f4) { + return float_guess; + } + + ASSERT((f1 != f2 && f2 == f3 && f3 == f4) || + (f1 == f2 && f2 != f3 && f3 == f4) || + (f1 == f2 && f2 == f3 && f3 != f4)); + + // guess and next are the two possible canditates (in the same way that + // double_guess was the lower candidate for a double-precision guess). + float guess = f1; + float next = f4; + DiyFp upper_boundary; + if (guess == 0.0f) { + float min_float = 1e-45f; + upper_boundary = Double(static_cast(min_float) / 2).AsDiyFp(); + } else { + upper_boundary = Single(guess).UpperBoundary(); + } + int comparison = CompareBufferWithDiyFp(trimmed, exponent, upper_boundary); + if (comparison < 0) { + return guess; + } else if (comparison > 0) { + return next; + } else if ((Single(guess).Significand() & 1) == 0) { + // Round towards even. + return guess; + } else { + return next; + } +} + +} // namespace double_conversion + +// ICU PATCH: Close ICU namespace +U_NAMESPACE_END +#endif // ICU PATCH: close #if !UCONFIG_NO_FORMATTING diff --git a/deps/icu-small/source/i18n/double-conversion-strtod.h b/deps/icu-small/source/i18n/double-conversion-strtod.h new file mode 100644 index 00000000000000..e2d6d3c2fe5d7d --- /dev/null +++ b/deps/icu-small/source/i18n/double-conversion-strtod.h @@ -0,0 +1,63 @@ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +// +// From the double-conversion library. Original license: +// +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// ICU PATCH: ifdef around UCONFIG_NO_FORMATTING +#include "unicode/utypes.h" +#if !UCONFIG_NO_FORMATTING + +#ifndef DOUBLE_CONVERSION_STRTOD_H_ +#define DOUBLE_CONVERSION_STRTOD_H_ + +// ICU PATCH: Customize header file paths for ICU. + +#include "double-conversion-utils.h" + +// ICU PATCH: Wrap in ICU namespace +U_NAMESPACE_BEGIN + +namespace double_conversion { + +// The buffer must only contain digits in the range [0-9]. It must not +// contain a dot or a sign. It must not start with '0', and must not be empty. +double Strtod(Vector buffer, int exponent); + +// The buffer must only contain digits in the range [0-9]. It must not +// contain a dot or a sign. It must not start with '0', and must not be empty. +float Strtof(Vector buffer, int exponent); + +} // namespace double_conversion + +// ICU PATCH: Close ICU namespace +U_NAMESPACE_END + +#endif // DOUBLE_CONVERSION_STRTOD_H_ +#endif // ICU PATCH: close #if !UCONFIG_NO_FORMATTING diff --git a/deps/icu-small/source/i18n/double-conversion-utils.h b/deps/icu-small/source/i18n/double-conversion-utils.h index 02795b4bc565ae..57fc49b231a3a0 100644 --- a/deps/icu-small/source/i18n/double-conversion-utils.h +++ b/deps/icu-small/source/i18n/double-conversion-utils.h @@ -75,9 +75,9 @@ inline void abort_noreturn() { abort(); } // the output of the division with the expected result. (Inlining must be // disabled.) // On Linux,x86 89255e-22 != Div_double(89255.0/1e22) -// ICU PATCH: Enable ARM builds for Windows with 'defined(_M_ARM)'. +// ICU PATCH: Enable ARM32 & ARM64 builds for Windows with 'defined(_M_ARM) || defined(_M_ARM64)'. #if defined(_M_X64) || defined(__x86_64__) || \ - defined(__ARMEL__) || defined(__avr32__) || defined(_M_ARM) || \ + defined(__ARMEL__) || defined(__avr32__) || defined(_M_ARM) || defined(_M_ARM64) || \ defined(__hppa__) || defined(__ia64__) || \ defined(__mips__) || \ defined(__powerpc__) || defined(__ppc__) || defined(__ppc64__) || \ diff --git a/deps/icu-small/source/i18n/double-conversion.cpp b/deps/icu-small/source/i18n/double-conversion.cpp index 8629284aa0e0f5..570a05bc42946c 100644 --- a/deps/icu-small/source/i18n/double-conversion.cpp +++ b/deps/icu-small/source/i18n/double-conversion.cpp @@ -38,13 +38,14 @@ #include // ICU PATCH: Customize header file paths for ICU. -// The files fixed-dtoa.h and strtod.h are not needed. +// The file fixed-dtoa.h is not needed. #include "double-conversion.h" #include "double-conversion-bignum-dtoa.h" #include "double-conversion-fast-dtoa.h" #include "double-conversion-ieee.h" +#include "double-conversion-strtod.h" #include "double-conversion-utils.h" // ICU PATCH: Wrap in ICU namespace @@ -431,7 +432,6 @@ void DoubleToStringConverter::DoubleToAscii(double v, } -#if 0 // not needed for ICU // Consumes the given substring from the iterator. // Returns false, if the substring does not match. template @@ -469,6 +469,7 @@ static const uc16 kWhitespaceTable16[] = { static const int kWhitespaceTable16Length = ARRAY_SIZE(kWhitespaceTable16); + static bool isWhitespace(int x) { if (x < 128) { for (int i = 0; i < kWhitespaceTable7Length; i++) { @@ -647,7 +648,6 @@ static double RadixStringToIeee(Iterator* current, return Double(DiyFp(number, exponent)).value(); } - template double StringToDoubleConverter::StringToIeee( Iterator input, @@ -996,7 +996,6 @@ float StringToDoubleConverter::StringToFloat( return static_cast(StringToIeee(buffer, length, false, processed_characters_count)); } -#endif // not needed for ICU } // namespace double_conversion diff --git a/deps/icu-small/source/i18n/double-conversion.h b/deps/icu-small/source/i18n/double-conversion.h index 0939412734a6bb..200537a360a7cc 100644 --- a/deps/icu-small/source/i18n/double-conversion.h +++ b/deps/icu-small/source/i18n/double-conversion.h @@ -391,6 +391,7 @@ class DoubleToStringConverter { const int decimal_in_shortest_high_; const int max_leading_padding_zeroes_in_precision_mode_; const int max_trailing_padding_zeroes_in_precision_mode_; +#endif // not needed for ICU DISALLOW_IMPLICIT_CONSTRUCTORS(DoubleToStringConverter); }; @@ -554,7 +555,6 @@ class StringToDoubleConverter { int* processed_characters_count) const; DISALLOW_IMPLICIT_CONSTRUCTORS(StringToDoubleConverter); -#endif // not needed for ICU }; } // namespace double_conversion diff --git a/deps/icu-small/source/i18n/fmtable.cpp b/deps/icu-small/source/i18n/fmtable.cpp index 73f9b66ab6f950..cb6134cb4b2423 100644 --- a/deps/icu-small/source/i18n/fmtable.cpp +++ b/deps/icu-small/source/i18n/fmtable.cpp @@ -19,6 +19,7 @@ #if !UCONFIG_NO_FORMATTING +#include #include #include "unicode/fmtable.h" #include "unicode/ustring.h" @@ -28,9 +29,8 @@ #include "charstr.h" #include "cmemory.h" #include "cstring.h" -#include "decNumber.h" -#include "digitlst.h" #include "fmtableimp.h" +#include "number_decimalquantity.h" // ***************************************************************************** // class Formattable @@ -40,6 +40,8 @@ U_NAMESPACE_BEGIN UOBJECT_DEFINE_RTTI_IMPLEMENTATION(Formattable) +using number::impl::DecimalQuantity; + //-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. @@ -103,7 +105,7 @@ void Formattable::init() { fValue.fInt64 = 0; fType = kLong; fDecimalStr = NULL; - fDecimalNum = NULL; + fDecimalQuantity = NULL; fBogus.setToBogus(); } @@ -257,8 +259,8 @@ Formattable::operator=(const Formattable& source) } UErrorCode status = U_ZERO_ERROR; - if (source.fDecimalNum != NULL) { - fDecimalNum = new DigitList(*source.fDecimalNum); // TODO: use internal digit list + if (source.fDecimalQuantity != NULL) { + fDecimalQuantity = new DecimalQuantity(*source.fDecimalQuantity); } if (source.fDecimalStr != NULL) { fDecimalStr = new CharString(*source.fDecimalStr, status); @@ -357,13 +359,8 @@ void Formattable::dispose() delete fDecimalStr; fDecimalStr = NULL; - FmtStackData *stackData = (FmtStackData*)fStackData; - if(fDecimalNum != &(stackData->stackDecimalNum)) { - delete fDecimalNum; - } else { - fDecimalNum->~DigitList(); // destruct, don't deallocate - } - fDecimalNum = NULL; + delete fDecimalQuantity; + fDecimalQuantity = NULL; } Formattable * @@ -465,13 +462,13 @@ Formattable::getInt64(UErrorCode& status) const } 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 && fDecimalNum != NULL) { - int64_t val = fDecimalNum->getInt64(); - if (val != 0) { - return val; + } 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 fValue.fDouble > 0 ? U_INT64_MAX : U_INT64_MIN; + return fDecimalQuantity->isNegative() ? U_INT64_MIN : U_INT64_MAX; } } else { return (int64_t)fValue.fDouble; @@ -714,84 +711,85 @@ StringPiece Formattable::getDecimalNumber(UErrorCode &status) { CharString *Formattable::internalGetCharString(UErrorCode &status) { if(fDecimalStr == NULL) { - if (fDecimalNum == 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. // - fDecimalNum = new DigitList; // TODO: use internal digit list - if (fDecimalNum == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - - switch (fType) { - case kDouble: - fDecimalNum->set(this->getDouble()); - break; - case kLong: - fDecimalNum->set(this->getLong()); - break; - case kInt64: - fDecimalNum->set(this->getInt64()); - break; - default: - // The formattable's value is not a numeric type. - status = U_INVALID_STATE_ERROR; - return NULL; - } + 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; + fDecimalStr = new CharString(); if (fDecimalStr == NULL) { status = U_MEMORY_ALLOCATION_ERROR; return NULL; } - fDecimalNum->getDecimal(*fDecimalStr, status); + // 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->isZero()) { + fDecimalStr->append("0", -1, status); + } else if (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; + } -DigitList * -Formattable::getInternalDigitList() { - FmtStackData *stackData = (FmtStackData*)fStackData; - if(fDecimalNum != &(stackData->stackDecimalNum)) { - delete fDecimalNum; - fDecimalNum = new (&(stackData->stackDecimalNum), kOnStack) DigitList(); - } else { - fDecimalNum->clear(); - } - return fDecimalNum; + 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::adoptDigitList(DigitList *dl) { - if(fDecimalNum==dl) { - fDecimalNum = NULL; // don't delete - } - dispose(); - - fDecimalNum = dl; - - if(dl==NULL) { // allow adoptDigitList(NULL) to clear - return; - } +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 (fDecimalNum->fitsIntoLong(FALSE)) { - fType = kLong; - fValue.fInt64 = fDecimalNum->getLong(); - } else if (fDecimalNum->fitsIntoInt64(FALSE)) { - fType = kInt64; - fValue.fInt64 = fDecimalNum->getInt64(); + // 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 = fDecimalNum->getDouble(); + fValue.fDouble = fDecimalQuantity->toDouble(); } } @@ -804,24 +802,12 @@ Formattable::setDecimalNumber(StringPiece numberString, UErrorCode &status) { } dispose(); - // Copy the input string and nul-terminate it. - // The decNumber library requires nul-terminated input. StringPiece input - // is not guaranteed nul-terminated. Too bad. - // CharString automatically adds the nul. - DigitList *dnum = new DigitList(); // TODO: use getInternalDigitList - if (dnum == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - dnum->set(CharString(numberString, status).toStringPiece(), status); - if (U_FAILURE(status)) { - delete dnum; - return; // String didn't contain a decimal number. - } - adoptDigitList(dnum); + 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 fDecimalNum. + // If we are asked for the string, we will regenerate one from fDecimalQuantity. } #if 0 diff --git a/deps/icu-small/source/i18n/fmtableimp.h b/deps/icu-small/source/i18n/fmtableimp.h index 0e6ccd24da7f02..78b7caff548e82 100644 --- a/deps/icu-small/source/i18n/fmtableimp.h +++ b/deps/icu-small/source/i18n/fmtableimp.h @@ -10,22 +10,12 @@ #ifndef FMTABLEIMP_H #define FMTABLEIMP_H -#include "digitlst.h" +#include "number_decimalquantity.h" #if !UCONFIG_NO_FORMATTING U_NAMESPACE_BEGIN -/** - * @internal - */ -struct FmtStackData { - DigitList stackDecimalNum; // 128 - //CharString stackDecimalStr; // 64 - // ----- - // 192 total -}; - /** * Maximum int64_t value that can be stored in a double without chancing losing precision. * IEEE doubles have 53 bits of mantissa, 10 bits exponent, 1 bit sign. diff --git a/deps/icu-small/source/i18n/fphdlimp.cpp b/deps/icu-small/source/i18n/fphdlimp.cpp index abcec97ee3171c..c4015fae1bbaff 100644 --- a/deps/icu-small/source/i18n/fphdlimp.cpp +++ b/deps/icu-small/source/i18n/fphdlimp.cpp @@ -22,17 +22,8 @@ U_NAMESPACE_BEGIN FieldPositionHandler::~FieldPositionHandler() { } -void -FieldPositionHandler::addAttribute(int32_t, int32_t, int32_t) { -} - -void -FieldPositionHandler::shiftLast(int32_t) { -} - -UBool -FieldPositionHandler::isRecording(void) const { - return FALSE; +void FieldPositionHandler::setShift(int32_t delta) { + fShift = delta; } @@ -48,8 +39,8 @@ FieldPositionOnlyHandler::~FieldPositionOnlyHandler() { void FieldPositionOnlyHandler::addAttribute(int32_t id, int32_t start, int32_t limit) { if (pos.getField() == id) { - pos.setBeginIndex(start); - pos.setEndIndex(limit); + pos.setBeginIndex(start + fShift); + pos.setEndIndex(limit + fShift); } } @@ -91,8 +82,8 @@ FieldPositionIteratorHandler::addAttribute(int32_t id, int32_t start, int32_t li if (iter && U_SUCCESS(status) && start < limit) { int32_t size = vec->size(); vec->addElement(id, status); - vec->addElement(start, status); - vec->addElement(limit, status); + vec->addElement(start + fShift, status); + vec->addElement(limit + fShift, status); if (!U_SUCCESS(status)) { vec->setSize(size); } diff --git a/deps/icu-small/source/i18n/fphdlimp.h b/deps/icu-small/source/i18n/fphdlimp.h index f3ac12c2bacb9a..2e9d5622b1b5b0 100644 --- a/deps/icu-small/source/i18n/fphdlimp.h +++ b/deps/icu-small/source/i18n/fphdlimp.h @@ -22,11 +22,16 @@ U_NAMESPACE_BEGIN // base class, null implementation class U_I18N_API FieldPositionHandler: public UMemory { + protected: + int32_t fShift = 0; + public: virtual ~FieldPositionHandler(); - virtual void addAttribute(int32_t id, int32_t start, int32_t limit); - virtual void shiftLast(int32_t delta); - virtual UBool isRecording(void) const; + virtual void addAttribute(int32_t id, int32_t start, int32_t limit) = 0; + virtual void shiftLast(int32_t delta) = 0; + virtual UBool isRecording(void) const = 0; + + void setShift(int32_t delta); }; @@ -39,9 +44,9 @@ class FieldPositionOnlyHandler : public FieldPositionHandler { FieldPositionOnlyHandler(FieldPosition& pos); virtual ~FieldPositionOnlyHandler(); - virtual void addAttribute(int32_t id, int32_t start, int32_t limit); - virtual void shiftLast(int32_t delta); - virtual UBool isRecording(void) const; + void addAttribute(int32_t id, int32_t start, int32_t limit) U_OVERRIDE; + void shiftLast(int32_t delta) U_OVERRIDE; + UBool isRecording(void) const U_OVERRIDE; }; @@ -63,9 +68,9 @@ class FieldPositionIteratorHandler : public FieldPositionHandler { FieldPositionIteratorHandler(FieldPositionIterator* posIter, UErrorCode& status); ~FieldPositionIteratorHandler(); - virtual void addAttribute(int32_t id, int32_t start, int32_t limit); - virtual void shiftLast(int32_t delta); - virtual UBool isRecording(void) const; + void addAttribute(int32_t id, int32_t start, int32_t limit) U_OVERRIDE; + void shiftLast(int32_t delta) U_OVERRIDE; + UBool isRecording(void) const U_OVERRIDE; }; U_NAMESPACE_END diff --git a/deps/icu-small/source/i18n/measunit.cpp b/deps/icu-small/source/i18n/measunit.cpp index e21afcba029e04..dc156aa720aae0 100644 --- a/deps/icu-small/source/i18n/measunit.cpp +++ b/deps/icu-small/source/i18n/measunit.cpp @@ -41,21 +41,21 @@ static const int32_t gOffsets[] = { 16, 20, 24, - 285, - 295, - 306, - 310, - 316, - 320, - 340, - 341, + 321, + 331, + 342, + 346, 352, - 355, - 361, - 366, - 370, - 374, - 399 + 356, + 376, + 377, + 388, + 391, + 397, + 402, + 406, + 410, + 435 }; static const int32_t gIndexes[] = { @@ -136,15 +136,18 @@ static const char * const gSubTypes[] = { "AED", "AFA", "AFN", + "ALK", "ALL", "AMD", "ANG", "AOA", + "AOK", "AON", "AOR", "ARA", "ARP", "ARS", + "ARY", "ATS", "AUD", "AWG", @@ -158,6 +161,8 @@ static const char * const gSubTypes[] = { "BEC", "BEF", "BEL", + "BGJ", + "BGK", "BGL", "BGN", "BHD", @@ -165,7 +170,9 @@ static const char * const gSubTypes[] = { "BMD", "BND", "BOB", + "BOP", "BOV", + "BRB", "BRC", "BRE", "BRL", @@ -173,6 +180,7 @@ static const char * const gSubTypes[] = { "BRR", "BSD", "BTN", + "BUK", "BWP", "BYB", "BYN", @@ -191,6 +199,7 @@ static const char * const gSubTypes[] = { "COU", "CRC", "CSD", + "CSJ", "CSK", "CUC", "CUP", @@ -225,10 +234,13 @@ static const char * const gSubTypes[] = { "GHS", "GIP", "GMD", + "GNE", "GNF", + "GNS", "GQE", "GRD", "GTQ", + "GWE", "GWP", "GYD", "HKD", @@ -239,10 +251,13 @@ static const char * const gSubTypes[] = { "HUF", "IDR", "IEP", + "ILP", + "ILR", "ILS", "INR", "IQD", "IRR", + "ISJ", "ISK", "ITL", "JMD", @@ -257,11 +272,13 @@ static const char * const gSubTypes[] = { "KWD", "KYD", "KZT", + "LAJ", "LAK", "LBP", "LKR", "LRD", "LSL", + "LSM", "LTL", "LTT", "LUC", @@ -280,17 +297,23 @@ static const char * const gSubTypes[] = { "MNT", "MOP", "MRO", + "MRU", "MTL", + "MTP", "MUR", + "MVQ", "MVR", "MWK", "MXN", + "MXP", "MXV", "MYR", + "MZE", "MZM", "MZN", "NAD", "NGN", + "NIC", "NIO", "NLG", "NOK", @@ -298,6 +321,7 @@ static const char * const gSubTypes[] = { "NZD", "OMR", "PAB", + "PEH", "PEI", "PEN", "PES", @@ -309,6 +333,8 @@ static const char * const gSubTypes[] = { "PTE", "PYG", "QAR", + "RHD", + "ROK", "ROL", "RON", "RSD", @@ -320,6 +346,7 @@ static const char * const gSubTypes[] = { "SCR", "SDD", "SDG", + "SDP", "SEK", "SGD", "SHP", @@ -331,6 +358,8 @@ static const char * const gSubTypes[] = { "SRG", "SSP", "STD", + "STN", + "SUR", "SVC", "SYP", "SZL", @@ -349,15 +378,20 @@ static const char * const gSubTypes[] = { "TZS", "UAH", "UAK", + "UGS", + "UGW", "UGX", "USD", "USN", "USS", "UYI", + "UYN", + "UYP", "UYU", "UZS", "VEB", "VEF", + "VNC", "VND", "VUV", "WST", @@ -381,6 +415,7 @@ static const char * const gSubTypes[] = { "XXX", "YDD", "YER", + "YUD", "YUM", "YUN", "ZAL", @@ -389,6 +424,7 @@ static const char * const gSubTypes[] = { "ZMW", "ZRN", "ZRZ", + "ZWC", "ZWD", "ZWL", "ZWN", @@ -511,16 +547,20 @@ static const char * const gSubTypes[] = { // Must be sorted by first value and then second value. static int32_t unitPerUnitToSingleUnit[][4] = { - {327, 297, 17, 0}, - {329, 303, 17, 2}, - {331, 297, 17, 3}, - {331, 388, 4, 2}, - {331, 389, 4, 3}, - {346, 386, 3, 1}, - {349, 11, 16, 4}, - {391, 327, 4, 1} + {363, 333, 17, 0}, + {365, 339, 17, 2}, + {367, 333, 17, 3}, + {367, 424, 4, 2}, + {367, 425, 4, 3}, + {382, 422, 3, 1}, + {385, 11, 16, 4}, + {427, 363, 4, 1} }; +// Shortcuts to the base unit in order to make the default constructor fast +static const int32_t kBaseTypeIdx = 14; +static const int32_t kBaseSubTypeIdx = 0; + MeasureUnit *MeasureUnit::createGForce(UErrorCode &status) { return MeasureUnit::create(0, 0, status); } @@ -1082,7 +1122,8 @@ static int32_t binarySearch( MeasureUnit::MeasureUnit() { fCurrency[0] = 0; - initNoUnit("base"); + fTypeId = kBaseTypeIdx; + fSubTypeId = kBaseSubTypeIdx; } MeasureUnit::MeasureUnit(const MeasureUnit &other) diff --git a/deps/icu-small/source/i18n/msgfmt.cpp b/deps/icu-small/source/i18n/msgfmt.cpp index 064585665ae5e6..8b3807e67148a4 100644 --- a/deps/icu-small/source/i18n/msgfmt.cpp +++ b/deps/icu-small/source/i18n/msgfmt.cpp @@ -31,6 +31,7 @@ #include "unicode/decimfmt.h" #include "unicode/localpointer.h" #include "unicode/msgfmt.h" +#include "unicode/numberformatter.h" #include "unicode/plurfmt.h" #include "unicode/rbnf.h" #include "unicode/selfmt.h" @@ -48,7 +49,7 @@ #include "ustrfmt.h" #include "util.h" #include "uvector.h" -#include "visibledigits.h" +#include "number_decimalquantity.h" // ***************************************************************************** // class MessageFormat @@ -1700,12 +1701,21 @@ Format* MessageFormat::createAppropriateFormat(UnicodeString& type, UnicodeStrin formattableType = Formattable::kLong; fmt = createIntegerFormat(fLocale, ec); break; - default: // pattern - fmt = NumberFormat::createInstance(fLocale, ec); - if (fmt) { - DecimalFormat* decfmt = dynamic_cast(fmt); - if (decfmt != NULL) { - decfmt->applyPattern(style,parseError,ec); + default: // pattern or skeleton + int32_t i = 0; + for (; PatternProps::isWhiteSpace(style.charAt(i)); i++); + if (style.compare(i, 2, u"::", 0, 2) == 0) { + // Skeleton + UnicodeString skeleton = style.tempSubString(i + 2); + fmt = number::NumberFormatter::forSkeleton(skeleton, ec).locale(fLocale).toFormat(ec); + } else { + // Pattern + fmt = NumberFormat::createInstance(fLocale, ec); + if (fmt) { + auto* decfmt = dynamic_cast(fmt); + if (decfmt != nullptr) { + decfmt->applyPattern(style, parseError, ec); + } } } break; @@ -1959,14 +1969,14 @@ UnicodeString MessageFormat::PluralSelectorProvider::select(void *ctx, double nu return UnicodeString(FALSE, OTHER_STRING, 5); } context.formatter->format(context.number, context.numberString, ec); - const DecimalFormat *decFmt = dynamic_cast(context.formatter); + auto* decFmt = dynamic_cast(context.formatter); if(decFmt != NULL) { - VisibleDigitsWithExponent digits; - decFmt->initVisibleDigitsWithExponent(context.number, digits, ec); + number::impl::DecimalQuantity dq; + decFmt->formatToDecimalQuantity(context.number, dq, ec); if (U_FAILURE(ec)) { return UnicodeString(FALSE, OTHER_STRING, 5); } - return rules->select(digits); + return rules->select(dq); } else { return rules->select(number); } diff --git a/deps/icu-small/source/i18n/nfsubs.cpp b/deps/icu-small/source/i18n/nfsubs.cpp index ea817453d87c18..3733f0ca74d3e3 100644 --- a/deps/icu-small/source/i18n/nfsubs.cpp +++ b/deps/icu-small/source/i18n/nfsubs.cpp @@ -19,8 +19,9 @@ #include "utypeinfo.h" // for 'typeid' to work #include "nfsubs.h" -#include "digitlst.h" #include "fmtableimp.h" +#include "putilimp.h" +#include "number_decimalquantity.h" #if U_HAVE_RBNF @@ -47,6 +48,8 @@ static const UChar gGreaterGreaterThan[] = U_NAMESPACE_BEGIN +using number::impl::DecimalQuantity; + class SameValueSubstitution : public NFSubstitution { public: SameValueSubstitution(int32_t pos, @@ -1069,13 +1072,12 @@ FractionalPartSubstitution::doSubstitution(double number, UnicodeString& toInser // numberToFormat /= 10; // } - DigitList dl; - dl.set(number); - dl.roundFixedPoint(20); // round to 20 fraction digits. - dl.reduce(); // Removes any trailing zeros. + DecimalQuantity dl; + dl.setToDouble(number); + dl.roundToMagnitude(-20, UNUM_ROUND_HALFEVEN, status); // round to 20 fraction digits. UBool pad = FALSE; - for (int32_t didx = dl.getCount()-1; didx>=dl.getDecimalAt(); didx--) { + for (int32_t didx = dl.getLowerDisplayMagnitude(); didx<0; didx++) { // Loop iterates over fraction digits, starting with the LSD. // include both real digits from the number, and zeros // to the left of the MSD but to the right of the decimal point. @@ -1084,7 +1086,7 @@ FractionalPartSubstitution::doSubstitution(double number, UnicodeString& toInser } else { pad = TRUE; } - int64_t digit = didx>=0 ? dl.getDigit(didx) - '0' : 0; + int64_t digit = dl.getDigit(didx); getRuleSet()->format(digit, toInsertInto, _pos + getPos(), recursionCount, status); } @@ -1142,7 +1144,8 @@ FractionalPartSubstitution::doParse(const UnicodeString& text, int32_t digit; // double p10 = 0.1; - DigitList dl; + DecimalQuantity dl; + int32_t totalDigits = 0; NumberFormat* fmt = NULL; while (workText.length() > 0 && workPos.getIndex() != 0) { workPos.setIndex(0); @@ -1170,7 +1173,8 @@ FractionalPartSubstitution::doParse(const UnicodeString& text, } if (workPos.getIndex() != 0) { - dl.append((char)('0' + digit)); + dl.appendDigit(static_cast(digit), 0, true); + totalDigits++; // result += digit * p10; // p10 /= 10; parsePosition.setIndex(parsePosition.getIndex() + workPos.getIndex()); @@ -1183,7 +1187,8 @@ FractionalPartSubstitution::doParse(const UnicodeString& text, } delete fmt; - result = dl.getCount() == 0 ? 0 : dl.getDouble(); + dl.adjustMagnitude(-totalDigits); + result = dl.toDouble(); result = composeRuleValue(result, baseValue); resVal.setDouble(result); return TRUE; diff --git a/deps/icu-small/source/i18n/number_affixutils.cpp b/deps/icu-small/source/i18n/number_affixutils.cpp index df4b267af5a004..8da29a03d52d56 100644 --- a/deps/icu-small/source/i18n/number_affixutils.cpp +++ b/deps/icu-small/source/i18n/number_affixutils.cpp @@ -3,21 +3,25 @@ #include "unicode/utypes.h" -#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT +#if !UCONFIG_NO_FORMATTING #include "number_affixutils.h" #include "unicode/utf16.h" +#include "unicode/uniset.h" using namespace icu; using namespace icu::number; using namespace icu::number::impl; -int32_t AffixUtils::estimateLength(const CharSequence &patternString, UErrorCode &status) { +TokenConsumer::~TokenConsumer() = default; +SymbolProvider::~SymbolProvider() = default; + +int32_t AffixUtils::estimateLength(const UnicodeString &patternString, UErrorCode &status) { AffixPatternState state = STATE_BASE; int32_t offset = 0; int32_t length = 0; for (; offset < patternString.length();) { - UChar32 cp = patternString.codePointAt(offset); + UChar32 cp = patternString.char32At(offset); switch (state) { case STATE_BASE: @@ -78,12 +82,12 @@ int32_t AffixUtils::estimateLength(const CharSequence &patternString, UErrorCode return length; } -UnicodeString AffixUtils::escape(const CharSequence &input) { +UnicodeString AffixUtils::escape(const UnicodeString &input) { AffixPatternState state = STATE_BASE; int32_t offset = 0; UnicodeString output; for (; offset < input.length();) { - UChar32 cp = input.codePointAt(offset); + UChar32 cp = input.char32At(offset); switch (cp) { case u'\'': @@ -153,7 +157,7 @@ Field AffixUtils::getFieldForType(AffixPatternType type) { } int32_t -AffixUtils::unescape(const CharSequence &affixPattern, NumberStringBuilder &output, int32_t position, +AffixUtils::unescape(const UnicodeString &affixPattern, NumberStringBuilder &output, int32_t position, const SymbolProvider &provider, UErrorCode &status) { int32_t length = 0; AffixTag tag; @@ -173,7 +177,7 @@ AffixUtils::unescape(const CharSequence &affixPattern, NumberStringBuilder &outp return length; } -int32_t AffixUtils::unescapedCodePointCount(const CharSequence &affixPattern, +int32_t AffixUtils::unescapedCodePointCount(const UnicodeString &affixPattern, const SymbolProvider &provider, UErrorCode &status) { int32_t length = 0; AffixTag tag; @@ -192,7 +196,7 @@ int32_t AffixUtils::unescapedCodePointCount(const CharSequence &affixPattern, } bool -AffixUtils::containsType(const CharSequence &affixPattern, AffixPatternType type, UErrorCode &status) { +AffixUtils::containsType(const UnicodeString &affixPattern, AffixPatternType type, UErrorCode &status) { if (affixPattern.length() == 0) { return false; } @@ -207,7 +211,7 @@ AffixUtils::containsType(const CharSequence &affixPattern, AffixPatternType type return false; } -bool AffixUtils::hasCurrencySymbols(const CharSequence &affixPattern, UErrorCode &status) { +bool AffixUtils::hasCurrencySymbols(const UnicodeString &affixPattern, UErrorCode &status) { if (affixPattern.length() == 0) { return false; } @@ -222,9 +226,9 @@ bool AffixUtils::hasCurrencySymbols(const CharSequence &affixPattern, UErrorCode return false; } -UnicodeString AffixUtils::replaceType(const CharSequence &affixPattern, AffixPatternType type, +UnicodeString AffixUtils::replaceType(const UnicodeString &affixPattern, AffixPatternType type, char16_t replacementChar, UErrorCode &status) { - UnicodeString output = affixPattern.toUnicodeString(); + UnicodeString output(affixPattern); // copy if (affixPattern.length() == 0) { return output; }; @@ -239,11 +243,41 @@ UnicodeString AffixUtils::replaceType(const CharSequence &affixPattern, AffixPat return output; } -AffixTag AffixUtils::nextToken(AffixTag tag, const CharSequence &patternString, UErrorCode &status) { +bool AffixUtils::containsOnlySymbolsAndIgnorables(const UnicodeString& affixPattern, + const UnicodeSet& ignorables, UErrorCode& status) { + if (affixPattern.length() == 0) { + return true; + }; + AffixTag tag; + while (hasNext(tag, affixPattern)) { + tag = nextToken(tag, affixPattern, status); + if (U_FAILURE(status)) { return false; } + if (tag.type == TYPE_CODEPOINT && !ignorables.contains(tag.codePoint)) { + return false; + } + } + return true; +} + +void AffixUtils::iterateWithConsumer(const UnicodeString& affixPattern, TokenConsumer& consumer, + UErrorCode& status) { + if (affixPattern.length() == 0) { + return; + }; + AffixTag tag; + while (hasNext(tag, affixPattern)) { + tag = nextToken(tag, affixPattern, status); + if (U_FAILURE(status)) { return; } + consumer.consumeToken(tag.type, tag.codePoint, status); + if (U_FAILURE(status)) { return; } + } +} + +AffixTag AffixUtils::nextToken(AffixTag tag, const UnicodeString &patternString, UErrorCode &status) { int32_t offset = tag.offset; int32_t state = tag.state; for (; offset < patternString.length();) { - UChar32 cp = patternString.codePointAt(offset); + UChar32 cp = patternString.char32At(offset); int32_t count = U16_LENGTH(cp); switch (state) { @@ -382,7 +416,7 @@ AffixTag AffixUtils::nextToken(AffixTag tag, const CharSequence &patternString, } } -bool AffixUtils::hasNext(const AffixTag &tag, const CharSequence &string) { +bool AffixUtils::hasNext(const AffixTag &tag, const UnicodeString &string) { // First check for the {-1} and default initializer syntax. if (tag.offset < 0) { return false; diff --git a/deps/icu-small/source/i18n/number_affixutils.h b/deps/icu-small/source/i18n/number_affixutils.h index fd76c99b975566..1d7e1a115e046a 100644 --- a/deps/icu-small/source/i18n/number_affixutils.h +++ b/deps/icu-small/source/i18n/number_affixutils.h @@ -3,7 +3,7 @@ #include "unicode/utypes.h" -#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT +#if !UCONFIG_NO_FORMATTING #ifndef __NUMBER_AFFIXUTILS_H__ #define __NUMBER_AFFIXUTILS_H__ @@ -12,6 +12,7 @@ #include "unicode/stringpiece.h" #include "unicode/unistr.h" #include "number_stringbuilder.h" +#include "unicode/uniset.h" U_NAMESPACE_BEGIN namespace number { namespace impl { @@ -37,19 +38,27 @@ struct AffixTag { AffixPatternState state; AffixPatternType type; - AffixTag() : offset(0), state(STATE_BASE) {} + AffixTag() + : offset(0), state(STATE_BASE) {} - AffixTag(int32_t offset) : offset(offset) {} + AffixTag(int32_t offset) + : offset(offset) {} AffixTag(int32_t offset, UChar32 codePoint, AffixPatternState state, AffixPatternType type) - : offset(offset), codePoint(codePoint), state(state), type(type) - {} + : offset(offset), codePoint(codePoint), state(state), type(type) {} +}; + +class TokenConsumer { + public: + virtual ~TokenConsumer(); + + virtual void consumeToken(AffixPatternType type, UChar32 cp, UErrorCode& status) = 0; }; // Exported as U_I18N_API because it is a base class for other exported types class U_I18N_API SymbolProvider { public: - virtual ~SymbolProvider() = default; + virtual ~SymbolProvider(); // TODO: Could this be more efficient if it returned by reference? virtual UnicodeString getSymbol(AffixPatternType type) const = 0; @@ -107,7 +116,7 @@ class U_I18N_API AffixUtils { * @param patternString The original string whose width will be estimated. * @return The length of the unescaped string. */ - static int32_t estimateLength(const CharSequence &patternString, UErrorCode &status); + static int32_t estimateLength(const UnicodeString& patternString, UErrorCode& status); /** * Takes a string and escapes (quotes) characters that have special meaning in the affix pattern @@ -118,7 +127,7 @@ class U_I18N_API AffixUtils { * @param input The string to be escaped. * @return The resulting UnicodeString. */ - static UnicodeString escape(const CharSequence &input); + static UnicodeString escape(const UnicodeString& input); static Field getFieldForType(AffixPatternType type); @@ -134,9 +143,8 @@ class U_I18N_API AffixUtils { * @param position The index into the NumberStringBuilder to insert the string. * @param provider An object to generate locale symbols. */ - static int32_t - unescape(const CharSequence &affixPattern, NumberStringBuilder &output, int32_t position, - const SymbolProvider &provider, UErrorCode &status); + static int32_t unescape(const UnicodeString& affixPattern, NumberStringBuilder& output, + int32_t position, const SymbolProvider& provider, UErrorCode& status); /** * Sames as {@link #unescape}, but only calculates the code point count. More efficient than {@link #unescape} @@ -146,8 +154,8 @@ class U_I18N_API AffixUtils { * @param provider An object to generate locale symbols. * @return The same return value as if you called {@link #unescape}. */ - static int32_t unescapedCodePointCount(const CharSequence &affixPattern, - const SymbolProvider &provider, UErrorCode &status); + static int32_t unescapedCodePointCount(const UnicodeString& affixPattern, + const SymbolProvider& provider, UErrorCode& status); /** * Checks whether the given affix pattern contains at least one token of the given type, which is @@ -157,8 +165,7 @@ class U_I18N_API AffixUtils { * @param type The token type. * @return true if the affix pattern contains the given token type; false otherwise. */ - static bool - containsType(const CharSequence &affixPattern, AffixPatternType type, UErrorCode &status); + static bool containsType(const UnicodeString& affixPattern, AffixPatternType type, UErrorCode& status); /** * Checks whether the specified affix pattern has any unquoted currency symbols ("¤"). @@ -166,7 +173,7 @@ class U_I18N_API AffixUtils { * @param affixPattern The string to check for currency symbols. * @return true if the literal has at least one unquoted currency symbol; false otherwise. */ - static bool hasCurrencySymbols(const CharSequence &affixPattern, UErrorCode &status); + static bool hasCurrencySymbols(const UnicodeString& affixPattern, UErrorCode& status); /** * Replaces all occurrences of tokens with the given type with the given replacement char. @@ -176,9 +183,21 @@ class U_I18N_API AffixUtils { * @param replacementChar The char to substitute in place of chars of the given token type. * @return A string containing the new affix pattern. */ - static UnicodeString - replaceType(const CharSequence &affixPattern, AffixPatternType type, char16_t replacementChar, - UErrorCode &status); + static UnicodeString replaceType(const UnicodeString& affixPattern, AffixPatternType type, + char16_t replacementChar, UErrorCode& status); + + /** + * Returns whether the given affix pattern contains only symbols and ignorables as defined by the + * given ignorables set. + */ + static bool containsOnlySymbolsAndIgnorables(const UnicodeString& affixPattern, + const UnicodeSet& ignorables, UErrorCode& status); + + /** + * Iterates over the affix pattern, calling the TokenConsumer for each token. + */ + static void iterateWithConsumer(const UnicodeString& affixPattern, TokenConsumer& consumer, + UErrorCode& status); /** * Returns the next token from the affix pattern. @@ -190,7 +209,7 @@ class U_I18N_API AffixUtils { * (never negative), or -1 if there were no more tokens in the affix pattern. * @see #hasNext */ - static AffixTag nextToken(AffixTag tag, const CharSequence &patternString, UErrorCode &status); + static AffixTag nextToken(AffixTag tag, const UnicodeString& patternString, UErrorCode& status); /** * Returns whether the affix pattern string has any more tokens to be retrieved from a call to @@ -200,7 +219,7 @@ class U_I18N_API AffixUtils { * @param string The affix pattern. * @return true if there are more tokens to consume; false otherwise. */ - static bool hasNext(const AffixTag &tag, const CharSequence &string); + static bool hasNext(const AffixTag& tag, const UnicodeString& string); private: /** @@ -208,8 +227,8 @@ class U_I18N_API AffixUtils { * The order of the arguments is consistent with Java, but the order of the stored * fields is not necessarily the same. */ - static inline AffixTag - makeTag(int32_t offset, AffixPatternType type, AffixPatternState state, UChar32 cp) { + static inline AffixTag makeTag(int32_t offset, AffixPatternType type, AffixPatternState state, + UChar32 cp) { return {offset, cp, state, type}; } }; diff --git a/deps/icu-small/source/i18n/number_asformat.cpp b/deps/icu-small/source/i18n/number_asformat.cpp new file mode 100644 index 00000000000000..c6bb538932cec8 --- /dev/null +++ b/deps/icu-small/source/i18n/number_asformat.cpp @@ -0,0 +1,105 @@ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +// Allow implicit conversion from char16_t* to UnicodeString for this file: +// Helpful in toString methods and elsewhere. +#define UNISTR_FROM_STRING_EXPLICIT + +#include +#include +#include "number_asformat.h" +#include "number_types.h" +#include "number_utils.h" +#include "fphdlimp.h" +#include "number_utypes.h" + +using namespace icu; +using namespace icu::number; +using namespace icu::number::impl; + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(LocalizedNumberFormatterAsFormat) + +LocalizedNumberFormatterAsFormat::LocalizedNumberFormatterAsFormat( + const LocalizedNumberFormatter& formatter, const Locale& locale) + : fFormatter(formatter), fLocale(locale) { + const char* localeName = locale.getName(); + setLocaleIDs(localeName, localeName); +} + +LocalizedNumberFormatterAsFormat::~LocalizedNumberFormatterAsFormat() = default; + +UBool LocalizedNumberFormatterAsFormat::operator==(const Format& other) const { + auto* _other = dynamic_cast(&other); + if (_other == nullptr) { + return false; + } + // TODO: Change this to use LocalizedNumberFormatter::operator== if it is ever proposed. + // This implementation is fine, but not particularly efficient. + UErrorCode localStatus = U_ZERO_ERROR; + return fFormatter.toSkeleton(localStatus) == _other->fFormatter.toSkeleton(localStatus); +} + +Format* LocalizedNumberFormatterAsFormat::clone() const { + return new LocalizedNumberFormatterAsFormat(*this); +} + +UnicodeString& LocalizedNumberFormatterAsFormat::format(const Formattable& obj, UnicodeString& appendTo, + FieldPosition& pos, UErrorCode& status) const { + if (U_FAILURE(status)) { return appendTo; } + UFormattedNumberData data; + obj.populateDecimalQuantity(data.quantity, status); + if (U_FAILURE(status)) { + return appendTo; + } + fFormatter.formatImpl(&data, status); + if (U_FAILURE(status)) { + return appendTo; + } + // always return first occurrence: + pos.setBeginIndex(0); + pos.setEndIndex(0); + bool found = data.string.nextFieldPosition(pos, status); + if (found && appendTo.length() != 0) { + pos.setBeginIndex(pos.getBeginIndex() + appendTo.length()); + pos.setEndIndex(pos.getEndIndex() + appendTo.length()); + } + appendTo.append(data.string.toTempUnicodeString()); + return appendTo; +} + +UnicodeString& LocalizedNumberFormatterAsFormat::format(const Formattable& obj, UnicodeString& appendTo, + FieldPositionIterator* posIter, + UErrorCode& status) const { + if (U_FAILURE(status)) { return appendTo; } + UFormattedNumberData data; + obj.populateDecimalQuantity(data.quantity, status); + if (U_FAILURE(status)) { + return appendTo; + } + fFormatter.formatImpl(&data, status); + if (U_FAILURE(status)) { + return appendTo; + } + appendTo.append(data.string.toTempUnicodeString()); + if (posIter != nullptr) { + FieldPositionIteratorHandler fpih(posIter, status); + data.string.getAllFieldPositions(fpih, status); + } + return appendTo; +} + +void LocalizedNumberFormatterAsFormat::parseObject(const UnicodeString&, Formattable&, + ParsePosition& parse_pos) const { + // Not supported. + parse_pos.setErrorIndex(0); +} + +const LocalizedNumberFormatter& LocalizedNumberFormatterAsFormat::getNumberFormatter() const { + return fFormatter; +} + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/number_asformat.h b/deps/icu-small/source/i18n/number_asformat.h new file mode 100644 index 00000000000000..bf82d72ae302a4 --- /dev/null +++ b/deps/icu-small/source/i18n/number_asformat.h @@ -0,0 +1,107 @@ +// © 2017 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING +#ifndef __NUMBER_ASFORMAT_H__ +#define __NUMBER_ASFORMAT_H__ + +#include "unicode/numberformatter.h" +#include "number_types.h" +#include "number_decimalquantity.h" +#include "number_scientific.h" +#include "number_patternstring.h" +#include "number_modifiers.h" +#include "number_multiplier.h" +#include "number_roundingutils.h" +#include "decNumber.h" +#include "charstr.h" + +U_NAMESPACE_BEGIN namespace number { +namespace impl { + +/** + * A wrapper around LocalizedNumberFormatter implementing the Format interface, enabling improved + * compatibility with other APIs. + * + * @draft ICU 62 + * @see NumberFormatter + */ +class U_I18N_API LocalizedNumberFormatterAsFormat : public Format { + public: + LocalizedNumberFormatterAsFormat(const LocalizedNumberFormatter& formatter, const Locale& locale); + + /** + * Destructor. + */ + ~LocalizedNumberFormatterAsFormat() U_OVERRIDE; + + /** + * Equals operator. + */ + UBool operator==(const Format& other) const U_OVERRIDE; + + /** + * Creates a copy of this object. + */ + Format* clone() const U_OVERRIDE; + + /** + * Formats a Number using the wrapped LocalizedNumberFormatter. The provided formattable must be a + * number type. + */ + UnicodeString& format(const Formattable& obj, UnicodeString& appendTo, FieldPosition& pos, + UErrorCode& status) const U_OVERRIDE; + + /** + * Formats a Number using the wrapped LocalizedNumberFormatter. The provided formattable must be a + * number type. + */ + UnicodeString& format(const Formattable& obj, UnicodeString& appendTo, FieldPositionIterator* posIter, + UErrorCode& status) const U_OVERRIDE; + + /** + * Not supported: sets an error index and returns. + */ + void parseObject(const UnicodeString& source, Formattable& result, + ParsePosition& parse_pos) const U_OVERRIDE; + + /** + * Gets the LocalizedNumberFormatter that this wrapper class uses to format numbers. + * + * For maximum efficiency, this function returns by const reference. You must copy the return value + * into a local variable if you want to use it beyond the lifetime of the current object: + * + *

+     * LocalizedNumberFormatter localFormatter = fmt->getNumberFormatter();
+     * 
+ * + * You can however use the return value directly when chaining: + * + *
+     * FormattedNumber result = fmt->getNumberFormatter().formatDouble(514.23, status);
+     * 
+ * + * @return The unwrapped LocalizedNumberFormatter. + */ + const LocalizedNumberFormatter& getNumberFormatter() const; + + UClassID getDynamicClassID() const U_OVERRIDE; + static UClassID U_EXPORT2 getStaticClassID(); + + private: + LocalizedNumberFormatter fFormatter; + + // Even though the locale is inside the LocalizedNumberFormatter, we have to keep it here, too, because + // LocalizedNumberFormatter doesn't have a getLocale() method, and ICU-TC didn't want to add one. + Locale fLocale; +}; + +} // namespace impl +} // namespace number +U_NAMESPACE_END + +#endif // __NUMBER_ASFORMAT_H__ + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/number_capi.cpp b/deps/icu-small/source/i18n/number_capi.cpp new file mode 100644 index 00000000000000..37ad8bd76fcd72 --- /dev/null +++ b/deps/icu-small/source/i18n/number_capi.cpp @@ -0,0 +1,213 @@ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +// Allow implicit conversion from char16_t* to UnicodeString for this file: +// Helpful in toString methods and elsewhere. +#define UNISTR_FROM_STRING_EXPLICIT + +#include "fphdlimp.h" +#include "number_utypes.h" +#include "numparse_types.h" +#include "unicode/numberformatter.h" +#include "unicode/unumberformatter.h" + +using namespace icu; +using namespace icu::number; +using namespace icu::number::impl; + + +////////////////////////////////// +/// C API CONVERSION FUNCTIONS /// +////////////////////////////////// + +UNumberFormatterData* UNumberFormatterData::validate(UNumberFormatter* input, UErrorCode& status) { + auto* constInput = static_cast(input); + auto* validated = validate(constInput, status); + return const_cast(validated); +} + +const UNumberFormatterData* +UNumberFormatterData::validate(const UNumberFormatter* input, UErrorCode& status) { + if (U_FAILURE(status)) { + return nullptr; + } + if (input == nullptr) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + auto* impl = reinterpret_cast(input); + if (impl->fMagic != UNumberFormatterData::kMagic) { + status = U_INVALID_FORMAT_ERROR; + return nullptr; + } + return impl; +} + +UNumberFormatter* UNumberFormatterData::exportForC() { + return reinterpret_cast(this); +} + +UFormattedNumberData* UFormattedNumberData::validate(UFormattedNumber* input, UErrorCode& status) { + auto* constInput = static_cast(input); + auto* validated = validate(constInput, status); + return const_cast(validated); +} + +const UFormattedNumberData* +UFormattedNumberData::validate(const UFormattedNumber* input, UErrorCode& status) { + if (U_FAILURE(status)) { + return nullptr; + } + if (input == nullptr) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + auto* impl = reinterpret_cast(input); + if (impl->fMagic != UFormattedNumberData::kMagic) { + status = U_INVALID_FORMAT_ERROR; + return nullptr; + } + return impl; +} + +UFormattedNumber* UFormattedNumberData::exportForC() { + return reinterpret_cast(this); +} + +///////////////////////////////////// +/// END CAPI CONVERSION FUNCTIONS /// +///////////////////////////////////// + + +U_CAPI UNumberFormatter* U_EXPORT2 +unumf_openForSkeletonAndLocale(const UChar* skeleton, int32_t skeletonLen, const char* locale, + UErrorCode* ec) { + auto* impl = new UNumberFormatterData(); + if (impl == nullptr) { + *ec = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + // Readonly-alias constructor (first argument is whether we are NUL-terminated) + UnicodeString skeletonString(skeletonLen == -1, skeleton, skeletonLen); + impl->fFormatter = NumberFormatter::forSkeleton(skeletonString, *ec).locale(locale); + return impl->exportForC(); +} + +U_CAPI UFormattedNumber* U_EXPORT2 +unumf_openResult(UErrorCode* ec) { + auto* impl = new UFormattedNumberData(); + if (impl == nullptr) { + *ec = U_MEMORY_ALLOCATION_ERROR; + return nullptr; + } + return impl->exportForC(); +} + +U_CAPI void U_EXPORT2 +unumf_formatInt(const UNumberFormatter* uformatter, int64_t value, UFormattedNumber* uresult, + UErrorCode* ec) { + const UNumberFormatterData* formatter = UNumberFormatterData::validate(uformatter, *ec); + UFormattedNumberData* result = UFormattedNumberData::validate(uresult, *ec); + if (U_FAILURE(*ec)) { return; } + + result->string.clear(); + result->quantity.setToLong(value); + formatter->fFormatter.formatImpl(result, *ec); +} + +U_CAPI void U_EXPORT2 +unumf_formatDouble(const UNumberFormatter* uformatter, double value, UFormattedNumber* uresult, + UErrorCode* ec) { + const UNumberFormatterData* formatter = UNumberFormatterData::validate(uformatter, *ec); + UFormattedNumberData* result = UFormattedNumberData::validate(uresult, *ec); + if (U_FAILURE(*ec)) { return; } + + result->string.clear(); + result->quantity.setToDouble(value); + formatter->fFormatter.formatImpl(result, *ec); +} + +U_CAPI void U_EXPORT2 +unumf_formatDecimal(const UNumberFormatter* uformatter, const char* value, int32_t valueLen, + UFormattedNumber* uresult, UErrorCode* ec) { + const UNumberFormatterData* formatter = UNumberFormatterData::validate(uformatter, *ec); + UFormattedNumberData* result = UFormattedNumberData::validate(uresult, *ec); + if (U_FAILURE(*ec)) { return; } + + result->string.clear(); + result->quantity.setToDecNumber({value, valueLen}, *ec); + if (U_FAILURE(*ec)) { return; } + formatter->fFormatter.formatImpl(result, *ec); +} + +U_CAPI int32_t U_EXPORT2 +unumf_resultToString(const UFormattedNumber* uresult, UChar* buffer, int32_t bufferCapacity, + UErrorCode* ec) { + const UFormattedNumberData* result = UFormattedNumberData::validate(uresult, *ec); + if (U_FAILURE(*ec)) { return 0; } + + if (buffer == nullptr ? bufferCapacity != 0 : bufferCapacity < 0) { + *ec = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + + return result->string.toTempUnicodeString().extract(buffer, bufferCapacity, *ec); +} + +U_CAPI UBool U_EXPORT2 +unumf_resultNextFieldPosition(const UFormattedNumber* uresult, UFieldPosition* ufpos, UErrorCode* ec) { + const UFormattedNumberData* result = UFormattedNumberData::validate(uresult, *ec); + if (U_FAILURE(*ec)) { return FALSE; } + + if (ufpos == nullptr) { + *ec = U_ILLEGAL_ARGUMENT_ERROR; + return FALSE; + } + + FieldPosition fp; + fp.setField(ufpos->field); + fp.setBeginIndex(ufpos->beginIndex); + fp.setEndIndex(ufpos->endIndex); + bool retval = result->string.nextFieldPosition(fp, *ec); + ufpos->beginIndex = fp.getBeginIndex(); + ufpos->endIndex = fp.getEndIndex(); + // NOTE: MSVC sometimes complains when implicitly converting between bool and UBool + return retval ? TRUE : FALSE; +} + +U_CAPI void U_EXPORT2 +unumf_resultGetAllFieldPositions(const UFormattedNumber* uresult, UFieldPositionIterator* ufpositer, + UErrorCode* ec) { + const UFormattedNumberData* result = UFormattedNumberData::validate(uresult, *ec); + if (U_FAILURE(*ec)) { return; } + + if (ufpositer == nullptr) { + *ec = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + + auto* fpi = reinterpret_cast(ufpositer); + FieldPositionIteratorHandler fpih(fpi, *ec); + result->string.getAllFieldPositions(fpih, *ec); +} + +U_CAPI void U_EXPORT2 +unumf_closeResult(UFormattedNumber* uresult) { + UErrorCode localStatus = U_ZERO_ERROR; + const UFormattedNumberData* impl = UFormattedNumberData::validate(uresult, localStatus); + delete impl; +} + +U_CAPI void U_EXPORT2 +unumf_close(UNumberFormatter* f) { + UErrorCode localStatus = U_ZERO_ERROR; + const UNumberFormatterData* impl = UNumberFormatterData::validate(f, localStatus); + delete impl; +} + + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/number_compact.cpp b/deps/icu-small/source/i18n/number_compact.cpp index cc0d8fd2a20cce..40278e1a012e54 100644 --- a/deps/icu-small/source/i18n/number_compact.cpp +++ b/deps/icu-small/source/i18n/number_compact.cpp @@ -3,14 +3,15 @@ #include "unicode/utypes.h" -#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT +#if !UCONFIG_NO_FORMATTING -#include "resource.h" -#include "number_compact.h" #include "unicode/ustring.h" #include "unicode/ures.h" #include "cstring.h" #include "charstr.h" +#include "resource.h" +#include "number_compact.h" +#include "number_microprops.h" #include "uresimp.h" using namespace icu; @@ -275,15 +276,15 @@ void CompactHandler::processQuantity(DecimalQuantity &quantity, MicroProps &micr int magnitude; if (quantity.isZero()) { magnitude = 0; - micros.rounding.apply(quantity, status); + micros.rounder.apply(quantity, status); } else { // TODO: Revisit chooseMultiplierAndApply - int multiplier = micros.rounding.chooseMultiplierAndApply(quantity, data, status); + int multiplier = micros.rounder.chooseMultiplierAndApply(quantity, data, status); magnitude = quantity.isZero() ? 0 : quantity.getMagnitude(); magnitude -= multiplier; } - StandardPlural::Form plural = quantity.getStandardPlural(rules); + StandardPlural::Form plural = utils::getStandardPlural(rules, quantity); const UChar *patternString = data.getPattern(magnitude, plural); if (patternString == nullptr) { // Use the default (non-compact) modifier. @@ -313,7 +314,7 @@ void CompactHandler::processQuantity(DecimalQuantity &quantity, MicroProps &micr } // We already performed rounding. Do not perform it again. - micros.rounding = Rounder::constructPassThrough(); + micros.rounder = RoundingImpl::passThrough(); } #endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/number_compact.h b/deps/icu-small/source/i18n/number_compact.h index f7adf36416e92f..dda5f9f9b2dc91 100644 --- a/deps/icu-small/source/i18n/number_compact.h +++ b/deps/icu-small/source/i18n/number_compact.h @@ -3,7 +3,7 @@ #include "unicode/utypes.h" -#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT +#if !UCONFIG_NO_FORMATTING #ifndef __NUMBER_COMPACT_H__ #define __NUMBER_COMPACT_H__ diff --git a/deps/icu-small/source/i18n/number_currencysymbols.cpp b/deps/icu-small/source/i18n/number_currencysymbols.cpp new file mode 100644 index 00000000000000..0b79d6596f18c0 --- /dev/null +++ b/deps/icu-small/source/i18n/number_currencysymbols.cpp @@ -0,0 +1,123 @@ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +// Allow implicit conversion from char16_t* to UnicodeString for this file: +// Helpful in toString methods and elsewhere. +#define UNISTR_FROM_STRING_EXPLICIT + +#include "numparse_types.h" +#include "number_currencysymbols.h" + +using namespace icu; +using namespace icu::number; +using namespace icu::number::impl; + + +CurrencySymbols::CurrencySymbols(CurrencyUnit currency, const Locale& locale, UErrorCode& status) + : fCurrency(currency), fLocaleName(locale.getName(), status) { + fCurrencySymbol.setToBogus(); + fIntlCurrencySymbol.setToBogus(); +} + +CurrencySymbols::CurrencySymbols(CurrencyUnit currency, const Locale& locale, + const DecimalFormatSymbols& symbols, UErrorCode& status) + : CurrencySymbols(currency, locale, status) { + // If either of the overrides is present, save it in the local UnicodeString. + if (symbols.isCustomCurrencySymbol()) { + fCurrencySymbol = symbols.getConstSymbol(DecimalFormatSymbols::kCurrencySymbol); + } + if (symbols.isCustomIntlCurrencySymbol()) { + fIntlCurrencySymbol = symbols.getConstSymbol(DecimalFormatSymbols::kIntlCurrencySymbol); + } +} + +const char16_t* CurrencySymbols::getIsoCode() const { + return fCurrency.getISOCurrency(); +} + +UnicodeString CurrencySymbols::getNarrowCurrencySymbol(UErrorCode& status) const { + // Note: currently no override is available for narrow currency symbol + return loadSymbol(UCURR_NARROW_SYMBOL_NAME, status); +} + +UnicodeString CurrencySymbols::getCurrencySymbol(UErrorCode& status) const { + if (!fCurrencySymbol.isBogus()) { + return fCurrencySymbol; + } + return loadSymbol(UCURR_SYMBOL_NAME, status); +} + +UnicodeString CurrencySymbols::loadSymbol(UCurrNameStyle selector, UErrorCode& status) const { + const char16_t* isoCode = fCurrency.getISOCurrency(); + UBool ignoredIsChoiceFormatFillIn = FALSE; + int32_t symbolLen = 0; + const char16_t* symbol = ucurr_getName( + isoCode, + fLocaleName.data(), + selector, + &ignoredIsChoiceFormatFillIn, + &symbolLen, + &status); + // If given an unknown currency, ucurr_getName returns the input string, which we can't alias safely! + // Otherwise, symbol points to a resource bundle, and we can use readonly-aliasing constructor. + if (symbol == isoCode) { + return UnicodeString(isoCode, 3); + } else { + return UnicodeString(TRUE, symbol, symbolLen); + } +} + +UnicodeString CurrencySymbols::getIntlCurrencySymbol(UErrorCode&) const { + if (!fIntlCurrencySymbol.isBogus()) { + return fIntlCurrencySymbol; + } + // Note: Not safe to use readonly-aliasing constructor here because the buffer belongs to this object, + // which could be destructed or moved during the lifetime of the return value. + return UnicodeString(fCurrency.getISOCurrency(), 3); +} + +UnicodeString CurrencySymbols::getPluralName(StandardPlural::Form plural, UErrorCode& status) const { + const char16_t* isoCode = fCurrency.getISOCurrency(); + UBool isChoiceFormat = FALSE; + int32_t symbolLen = 0; + const char16_t* symbol = ucurr_getPluralName( + isoCode, + fLocaleName.data(), + &isChoiceFormat, + StandardPlural::getKeyword(plural), + &symbolLen, + &status); + // If given an unknown currency, ucurr_getName returns the input string, which we can't alias safely! + // Otherwise, symbol points to a resource bundle, and we can use readonly-aliasing constructor. + if (symbol == isoCode) { + return UnicodeString(isoCode, 3); + } else { + return UnicodeString(TRUE, symbol, symbolLen); + } +} + + +CurrencyUnit +icu::number::impl::resolveCurrency(const DecimalFormatProperties& properties, const Locale& locale, + UErrorCode& status) { + if (!properties.currency.isNull()) { + return properties.currency.getNoError(); + } else { + UErrorCode localStatus = U_ZERO_ERROR; + char16_t buf[4] = {}; + ucurr_forLocale(locale.getName(), buf, 4, &localStatus); + if (U_SUCCESS(localStatus)) { + return CurrencyUnit(buf, status); + } else { + // Default currency (XXX) + return CurrencyUnit(); + } + } +} + + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/number_currencysymbols.h b/deps/icu-small/source/i18n/number_currencysymbols.h new file mode 100644 index 00000000000000..9996bf96ae08a1 --- /dev/null +++ b/deps/icu-small/source/i18n/number_currencysymbols.h @@ -0,0 +1,65 @@ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING +#ifndef __SOURCE_NUMBER_CURRENCYSYMBOLS_H__ +#define __SOURCE_NUMBER_CURRENCYSYMBOLS_H__ + +#include "numparse_types.h" +#include "charstr.h" +#include "number_decimfmtprops.h" + +U_NAMESPACE_BEGIN namespace number { +namespace impl { + + +// Exported as U_I18N_API for tests +class U_I18N_API CurrencySymbols : public UMemory { + public: + CurrencySymbols() = default; // default constructor: leaves class in valid but undefined state + + /** Creates an instance in which all symbols are loaded from data. */ + CurrencySymbols(CurrencyUnit currency, const Locale& locale, UErrorCode& status); + + /** Creates an instance in which some symbols might be pre-populated. */ + CurrencySymbols(CurrencyUnit currency, const Locale& locale, const DecimalFormatSymbols& symbols, + UErrorCode& status); + + const char16_t* getIsoCode() const; + + UnicodeString getNarrowCurrencySymbol(UErrorCode& status) const; + + UnicodeString getCurrencySymbol(UErrorCode& status) const; + + UnicodeString getIntlCurrencySymbol(UErrorCode& status) const; + + UnicodeString getPluralName(StandardPlural::Form plural, UErrorCode& status) const; + + protected: + // Required fields: + CurrencyUnit fCurrency; + CharString fLocaleName; + + // Optional fields: + UnicodeString fCurrencySymbol; + UnicodeString fIntlCurrencySymbol; + + UnicodeString loadSymbol(UCurrNameStyle selector, UErrorCode& status) const; +}; + + +/** + * Resolves the effective currency from the property bag. + */ +CurrencyUnit +resolveCurrency(const DecimalFormatProperties& properties, const Locale& locale, UErrorCode& status); + + +} // namespace impl +} // namespace numparse +U_NAMESPACE_END + +#endif //__SOURCE_NUMBER_CURRENCYSYMBOLS_H__ +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/number_decimalquantity.cpp b/deps/icu-small/source/i18n/number_decimalquantity.cpp index b68df26ba26167..9d80e3349cb8aa 100644 --- a/deps/icu-small/source/i18n/number_decimalquantity.cpp +++ b/deps/icu-small/source/i18n/number_decimalquantity.cpp @@ -3,25 +3,30 @@ #include "unicode/utypes.h" -#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT +#if !UCONFIG_NO_FORMATTING -#include "uassert.h" +#include #include -#include "cmemory.h" -#include "decNumber.h" #include +#include + +#include "unicode/plurrule.h" +#include "cmemory.h" +#include "number_decnum.h" +#include "putilimp.h" #include "number_decimalquantity.h" -#include "decContext.h" -#include "decNumber.h" #include "number_roundingutils.h" #include "double-conversion.h" -#include "unicode/plurrule.h" +#include "charstr.h" +#include "number_utils.h" +#include "uassert.h" using namespace icu; using namespace icu::number; using namespace icu::number::impl; using icu::double_conversion::DoubleToStringConverter; +using icu::double_conversion::StringToDoubleConverter; namespace { @@ -29,25 +34,6 @@ int8_t NEGATIVE_FLAG = 1; int8_t INFINITY_FLAG = 2; int8_t NAN_FLAG = 4; -static constexpr int32_t DEFAULT_DIGITS = 34; -typedef MaybeStackHeaderAndArray DecNumberWithStorage; - -/** Helper function to convert a decNumber-compatible string into a decNumber. */ -void stringToDecNumber(StringPiece n, DecNumberWithStorage &dn) { - decContext set; - uprv_decContextDefault(&set, DEC_INIT_BASE); - uprv_decContextSetRounding(&set, DEC_ROUND_HALF_EVEN); - set.traps = 0; // no traps, thank you - if (n.length() > DEFAULT_DIGITS) { - dn.resize(n.length(), 0); - set.digits = n.length(); - } else { - set.digits = DEFAULT_DIGITS; - } - uprv_decNumberFromString(dn.getAlias(), n.data(), &set); - U_ASSERT(DECDPUN == 1); -} - /** Helper function for safe subtraction (no overflow). */ inline int32_t safeSubtract(int32_t a, int32_t b) { // Note: In C++, signed integer subtraction is undefined behavior. @@ -83,6 +69,7 @@ static double DOUBLE_MULTIPLIERS[] = { } // namespace +icu::IFixedDecimal::~IFixedDecimal() = default; DecimalQuantity::DecimalQuantity() { setBcdToZero(); @@ -101,11 +88,30 @@ DecimalQuantity::DecimalQuantity(const DecimalQuantity &other) { *this = other; } +DecimalQuantity::DecimalQuantity(DecimalQuantity&& src) U_NOEXCEPT { + *this = std::move(src); +} + DecimalQuantity &DecimalQuantity::operator=(const DecimalQuantity &other) { if (this == &other) { return *this; } copyBcdFrom(other); + copyFieldsFrom(other); + return *this; +} + +DecimalQuantity& DecimalQuantity::operator=(DecimalQuantity&& src) U_NOEXCEPT { + if (this == &src) { + return *this; + } + moveBcdFrom(src); + copyFieldsFrom(src); + return *this; +} + +void DecimalQuantity::copyFieldsFrom(const DecimalQuantity& other) { + bogus = other.bogus; lOptPos = other.lOptPos; lReqPos = other.lReqPos; rReqPos = other.rReqPos; @@ -116,7 +122,6 @@ DecimalQuantity &DecimalQuantity::operator=(const DecimalQuantity &other) { origDouble = other.origDouble; origDelta = other.origDelta; isApproximate = other.isApproximate; - return *this; } void DecimalQuantity::clear() { @@ -129,10 +134,16 @@ void DecimalQuantity::clear() { } void DecimalQuantity::setIntegerLength(int32_t minInt, int32_t maxInt) { - // Validation should happen outside of DecimalQuantity, e.g., in the Rounder class. + // Validation should happen outside of DecimalQuantity, e.g., in the Precision class. U_ASSERT(minInt >= 0); U_ASSERT(maxInt >= minInt); + // Special behavior: do not set minInt to be less than what is already set. + // This is so significant digits rounding can set the integer length. + if (minInt < lReqPos) { + minInt = lReqPos; + } + // Save values into internal state // Negation is safe for minFrac/maxFrac because -Integer.MAX_VALUE > Integer.MIN_VALUE lOptPos = maxInt; @@ -140,7 +151,7 @@ void DecimalQuantity::setIntegerLength(int32_t minInt, int32_t maxInt) { } void DecimalQuantity::setFractionLength(int32_t minFrac, int32_t maxFrac) { - // Validation should happen outside of DecimalQuantity, e.g., in the Rounder class. + // Validation should happen outside of DecimalQuantity, e.g., in the Precision class. U_ASSERT(minFrac >= 0); U_ASSERT(maxFrac >= minFrac); @@ -160,29 +171,53 @@ uint64_t DecimalQuantity::getPositionFingerprint() const { } void DecimalQuantity::roundToIncrement(double roundingIncrement, RoundingMode roundingMode, - int32_t minMaxFrac, UErrorCode& status) { - // TODO: This is innefficient. Improve? - // TODO: Should we convert to decNumber instead? + int32_t maxFrac, UErrorCode& status) { + // TODO(13701): This is innefficient. Improve? + // TODO(13701): Should we convert to decNumber instead? + roundToInfinity(); double temp = toDouble(); temp /= roundingIncrement; - setToDouble(temp); - roundToMagnitude(0, roundingMode, status); - temp = toDouble(); + // Use another DecimalQuantity to perform the actual rounding... + DecimalQuantity dq; + dq.setToDouble(temp); + dq.roundToMagnitude(0, roundingMode, status); + temp = dq.toDouble(); temp *= roundingIncrement; setToDouble(temp); // Since we reset the value to a double, we need to specify the rounding boundary // in order to get the DecimalQuantity out of approximation mode. - roundToMagnitude(-minMaxFrac, roundingMode, status); + // NOTE: In Java, we have minMaxFrac, but in C++, the two are differentiated. + roundToMagnitude(-maxFrac, roundingMode, status); } -void DecimalQuantity::multiplyBy(int32_t multiplicand) { +void DecimalQuantity::multiplyBy(const DecNum& multiplicand, UErrorCode& status) { if (isInfinite() || isZero() || isNaN()) { return; } - // TODO: Should we convert to decNumber instead? - double temp = toDouble(); - temp *= multiplicand; - setToDouble(temp); + // Convert to DecNum, multiply, and convert back. + DecNum decnum; + toDecNum(decnum, status); + if (U_FAILURE(status)) { return; } + decnum.multiplyBy(multiplicand, status); + if (U_FAILURE(status)) { return; } + setToDecNum(decnum, status); +} + +void DecimalQuantity::divideBy(const DecNum& divisor, UErrorCode& status) { + if (isInfinite() || isZero() || isNaN()) { + return; + } + // Convert to DecNum, multiply, and convert back. + DecNum decnum; + toDecNum(decnum, status); + if (U_FAILURE(status)) { return; } + decnum.divideBy(divisor, status); + if (U_FAILURE(status)) { return; } + setToDecNum(decnum, status); +} + +void DecimalQuantity::negate() { + flags ^= NEGATIVE_FLAG; } int32_t DecimalQuantity::getMagnitude() const { @@ -190,21 +225,17 @@ int32_t DecimalQuantity::getMagnitude() const { return scale + precision - 1; } -void DecimalQuantity::adjustMagnitude(int32_t delta) { +bool DecimalQuantity::adjustMagnitude(int32_t delta) { if (precision != 0) { - scale += delta; - origDelta += delta; - } -} - -StandardPlural::Form DecimalQuantity::getStandardPlural(const PluralRules *rules) const { - if (rules == nullptr) { - // Fail gracefully if the user didn't provide a PluralRules - return StandardPlural::Form::OTHER; - } else { - UnicodeString ruleString = rules->select(*this); - return StandardPlural::orOtherFromString(ruleString); + // i.e., scale += delta; origDelta += delta + bool overflow = uprv_add32_overflow(scale, delta, &scale); + overflow = uprv_add32_overflow(origDelta, delta, &origDelta) || overflow; + // Make sure that precision + scale won't overflow, either + int32_t dummy; + overflow = overflow || uprv_add32_overflow(scale, precision, &dummy); + return overflow; } + return false; } double DecimalQuantity::getPluralOperand(PluralOperand operand) const { @@ -214,7 +245,8 @@ double DecimalQuantity::getPluralOperand(PluralOperand operand) const { switch (operand) { case PLURAL_OPERAND_I: - return static_cast(toLong()); + // Invert the negative sign if necessary + return static_cast(isNegative() ? -toLong(true) : toLong(true)); case PLURAL_OPERAND_F: return static_cast(toFractionLong(true)); case PLURAL_OPERAND_T: @@ -228,6 +260,10 @@ double DecimalQuantity::getPluralOperand(PluralOperand operand) const { } } +bool DecimalQuantity::hasIntegerValue() const { + return scale >= 0; +} + int32_t DecimalQuantity::getUpperDisplayMagnitude() const { // If this assertion fails, you need to call roundToInfinity() or some other rounding method. // See the comment in the header file explaining the "isApproximate" field. @@ -287,7 +323,10 @@ bool DecimalQuantity::isZero() const { DecimalQuantity &DecimalQuantity::setToInt(int32_t n) { setBcdToZero(); flags = 0; - if (n < 0) { + if (n == INT32_MIN) { + flags |= NEGATIVE_FLAG; + // leave as INT32_MIN; handled below in _setToInt() + } else if (n < 0) { flags |= NEGATIVE_FLAG; n = -n; } @@ -309,7 +348,7 @@ void DecimalQuantity::_setToInt(int32_t n) { DecimalQuantity &DecimalQuantity::setToLong(int64_t n) { setBcdToZero(); flags = 0; - if (n < 0) { + if (n < 0 && n > INT64_MIN) { flags |= NEGATIVE_FLAG; n = -n; } @@ -322,10 +361,12 @@ DecimalQuantity &DecimalQuantity::setToLong(int64_t n) { void DecimalQuantity::_setToLong(int64_t n) { if (n == INT64_MIN) { - static const char *int64minStr = "9.223372036854775808E+18"; - DecNumberWithStorage dn; - stringToDecNumber(int64minStr, dn); - readDecNumberToBcd(dn.getAlias()); + DecNum decnum; + UErrorCode localStatus = U_ZERO_ERROR; + decnum.setTo("9.223372036854775808E+18", localStatus); + if (U_FAILURE(localStatus)) { return; } // unexpected + flags |= NEGATIVE_FLAG; + readDecNumberToBcd(decnum); } else if (n <= INT32_MAX) { readIntToBcd(static_cast(n)); } else { @@ -337,7 +378,7 @@ DecimalQuantity &DecimalQuantity::setToDouble(double n) { setBcdToZero(); flags = 0; // signbit() from handles +0.0 vs -0.0 - if (std::signbit(n) != 0) { + if (std::signbit(n)) { flags |= NEGATIVE_FLAG; n = -n; } @@ -424,51 +465,107 @@ void DecimalQuantity::convertToAccurateDouble() { explicitExactDouble = true; } -DecimalQuantity &DecimalQuantity::setToDecNumber(StringPiece n) { +DecimalQuantity &DecimalQuantity::setToDecNumber(StringPiece n, UErrorCode& status) { setBcdToZero(); flags = 0; - DecNumberWithStorage dn; - stringToDecNumber(n, dn); + // Compute the decNumber representation + DecNum decnum; + decnum.setTo(n, status); - // The code path for decNumber is modeled after BigDecimal in Java. - if (decNumberIsNegative(dn.getAlias())) { - flags |= NEGATIVE_FLAG; - } - if (!decNumberIsZero(dn.getAlias())) { - _setToDecNumber(dn.getAlias()); - } + _setToDecNum(decnum, status); return *this; } -void DecimalQuantity::_setToDecNumber(decNumber *n) { - // Java fastpaths for ints here. In C++, just always read directly from the decNumber. - readDecNumberToBcd(n); - compact(); +DecimalQuantity& DecimalQuantity::setToDecNum(const DecNum& decnum, UErrorCode& status) { + setBcdToZero(); + flags = 0; + + _setToDecNum(decnum, status); + return *this; } -int64_t DecimalQuantity::toLong() const { - int64_t result = 0L; - for (int32_t magnitude = scale + precision - 1; magnitude >= 0; magnitude--) { +void DecimalQuantity::_setToDecNum(const DecNum& decnum, UErrorCode& status) { + if (U_FAILURE(status)) { return; } + if (decnum.isNegative()) { + flags |= NEGATIVE_FLAG; + } + if (!decnum.isZero()) { + readDecNumberToBcd(decnum); + compact(); + } +} + +int64_t DecimalQuantity::toLong(bool truncateIfOverflow) const { + // NOTE: Call sites should be guarded by fitsInLong(), like this: + // if (dq.fitsInLong()) { /* use dq.toLong() */ } else { /* use some fallback */ } + // Fallback behavior upon truncateIfOverflow is to truncate at 17 digits. + uint64_t result = 0L; + int32_t upperMagnitude = std::min(scale + precision, lOptPos) - 1; + if (truncateIfOverflow) { + upperMagnitude = std::min(upperMagnitude, 17); + } + for (int32_t magnitude = upperMagnitude; magnitude >= 0; magnitude--) { result = result * 10 + getDigitPos(magnitude - scale); } - return result; + if (isNegative()) { + return static_cast(0LL - result); // i.e., -result + } + return static_cast(result); } -int64_t DecimalQuantity::toFractionLong(bool includeTrailingZeros) const { - int64_t result = 0L; +uint64_t DecimalQuantity::toFractionLong(bool includeTrailingZeros) const { + uint64_t result = 0L; int32_t magnitude = -1; - for (; (magnitude >= scale || (includeTrailingZeros && magnitude >= rReqPos)) && - magnitude >= rOptPos; magnitude--) { + int32_t lowerMagnitude = std::max(scale, rOptPos); + if (includeTrailingZeros) { + lowerMagnitude = std::min(lowerMagnitude, rReqPos); + } + for (; magnitude >= lowerMagnitude && result <= 1e18L; magnitude--) { result = result * 10 + getDigitPos(magnitude - scale); } + // Remove trailing zeros; this can happen during integer overflow cases. + if (!includeTrailingZeros) { + while (result > 0 && (result % 10) == 0) { + result /= 10; + } + } return result; } -double DecimalQuantity::toDouble() const { - if (isApproximate) { - return toDoubleFromOriginal(); +bool DecimalQuantity::fitsInLong(bool ignoreFraction) const { + if (isZero()) { + return true; + } + if (scale < 0 && !ignoreFraction) { + return false; } + int magnitude = getMagnitude(); + if (magnitude < 18) { + return true; + } + if (magnitude > 18) { + return false; + } + // Hard case: the magnitude is 10^18. + // The largest int64 is: 9,223,372,036,854,775,807 + for (int p = 0; p < precision; p++) { + int8_t digit = getDigit(18 - p); + static int8_t INT64_BCD[] = { 9, 2, 2, 3, 3, 7, 2, 0, 3, 6, 8, 5, 4, 7, 7, 5, 8, 0, 8 }; + if (digit < INT64_BCD[p]) { + return true; + } else if (digit > INT64_BCD[p]) { + return false; + } + } + // Exactly equal to max long plus one. + return isNegative(); +} + +double DecimalQuantity::toDouble() const { + // If this assertion fails, you need to call roundToInfinity() or some other rounding method. + // See the comment in the header file explaining the "isApproximate" field. + U_ASSERT(!isApproximate); if (isNaN()) { return NAN; @@ -476,42 +573,37 @@ double DecimalQuantity::toDouble() const { return isNegative() ? -INFINITY : INFINITY; } - int64_t tempLong = 0L; - int32_t lostDigits = precision - (precision < 17 ? precision : 17); - for (int shift = precision - 1; shift >= lostDigits; shift--) { - tempLong = tempLong * 10 + getDigitPos(shift); + // We are processing well-formed input, so we don't need any special options to StringToDoubleConverter. + StringToDoubleConverter converter(0, 0, 0, "", ""); + UnicodeString numberString = this->toScientificString(); + int32_t count; + return converter.StringToDouble( + reinterpret_cast(numberString.getBuffer()), + numberString.length(), + &count); +} + +void DecimalQuantity::toDecNum(DecNum& output, UErrorCode& status) const { + // Special handling for zero + if (precision == 0) { + output.setTo("0", status); } - double result = static_cast(tempLong); - int32_t _scale = scale + lostDigits; - if (_scale >= 0) { - // 1e22 is the largest exact double. - int32_t i = _scale; - for (; i >= 22; i -= 22) result *= 1e22; - result *= DOUBLE_MULTIPLIERS[i]; - } else { - // 1e22 is the largest exact double. - int32_t i = _scale; - for (; i <= -22; i += 22) result /= 1e22; - result /= DOUBLE_MULTIPLIERS[-i]; + + // Use the BCD constructor. We need to do a little bit of work to convert, though. + // The decNumber constructor expects most-significant first, but we store least-significant first. + MaybeStackArray ubcd(precision); + for (int32_t m = 0; m < precision; m++) { + ubcd[precision - m - 1] = static_cast(getDigitPos(m)); } - if (isNegative()) { result = -result; } - return result; + output.setTo(ubcd.getAlias(), precision, scale, isNegative(), status); } -double DecimalQuantity::toDoubleFromOriginal() const { - double result = origDouble; - int32_t delta = origDelta; - if (delta >= 0) { - // 1e22 is the largest exact double. - for (; delta >= 22; delta -= 22) result *= 1e22; - result *= DOUBLE_MULTIPLIERS[delta]; - } else { - // 1e22 is the largest exact double. - for (; delta <= -22; delta += 22) result /= 1e22; - result /= DOUBLE_MULTIPLIERS[-delta]; +void DecimalQuantity::truncate() { + if (scale < 0) { + shiftRight(-scale); + scale = 0; + compact(); } - if (isNegative()) { result *= -1; } - return result; } void DecimalQuantity::roundToMagnitude(int32_t magnitude, RoundingMode roundingMode, UErrorCode& status) { @@ -689,17 +781,63 @@ void DecimalQuantity::appendDigit(int8_t value, int32_t leadingZeros, bool appen } UnicodeString DecimalQuantity::toPlainString() const { + U_ASSERT(!isApproximate); UnicodeString sb; if (isNegative()) { sb.append(u'-'); } + if (precision == 0 || getMagnitude() < 0) { + sb.append(u'0'); + } for (int m = getUpperDisplayMagnitude(); m >= getLowerDisplayMagnitude(); m--) { + if (m == -1) { sb.append(u'.'); } sb.append(getDigit(m) + u'0'); - if (m == 0) { sb.append(u'.'); } } return sb; } +UnicodeString DecimalQuantity::toScientificString() const { + U_ASSERT(!isApproximate); + UnicodeString result; + if (isNegative()) { + result.append(u'-'); + } + if (precision == 0) { + result.append(u"0E+0", -1); + return result; + } + // NOTE: It is not safe to add to lOptPos (aka maxInt) or subtract from + // rOptPos (aka -maxFrac) due to overflow. + int32_t upperPos = std::min(precision + scale, lOptPos) - scale - 1; + int32_t lowerPos = std::max(scale, rOptPos) - scale; + int32_t p = upperPos; + result.append(u'0' + getDigitPos(p)); + if ((--p) >= lowerPos) { + result.append(u'.'); + for (; p >= lowerPos; p--) { + result.append(u'0' + getDigitPos(p)); + } + } + result.append(u'E'); + int32_t _scale = upperPos + scale; + if (_scale < 0) { + _scale *= -1; + result.append(u'-'); + } else { + result.append(u'+'); + } + if (_scale == 0) { + result.append(u'0'); + } + int32_t insertIndex = result.length(); + while (_scale > 0) { + std::div_t res = std::div(_scale, 10); + result.insert(insertIndex, u'0' + res.rem); + _scale = res.quot; + } + return result; +} + //////////////////////////////////////////////////// /// End of DecimalQuantity_AbstractBCD.java /// /// Start of DecimalQuantity_DualStorageBCD.java /// @@ -707,7 +845,7 @@ UnicodeString DecimalQuantity::toPlainString() const { int8_t DecimalQuantity::getDigitPos(int32_t position) const { if (usingBytes) { - if (position < 0 || position > precision) { return 0; } + if (position < 0 || position >= precision) { return 0; } return fBCD.bcdBytes.ptr[position]; } else { if (position < 0 || position >= 16) { return 0; } @@ -819,7 +957,8 @@ void DecimalQuantity::readLongToBcd(int64_t n) { } } -void DecimalQuantity::readDecNumberToBcd(decNumber *dn) { +void DecimalQuantity::readDecNumberToBcd(const DecNum& decnum) { + const decNumber* dn = decnum.getRawDecNumber(); if (dn->digits > 16) { ensureCapacity(dn->digits); for (int32_t i = 0; i < dn->digits; i++) { @@ -919,7 +1058,7 @@ void DecimalQuantity::ensureCapacity(int32_t capacity) { auto bcd1 = static_cast(uprv_malloc(capacity * 2 * sizeof(int8_t))); uprv_memcpy(bcd1, fBCD.bcdBytes.ptr, oldCapacity * sizeof(int8_t)); // Initialize the rest of the byte array to zeros (this is done automatically in Java) - uprv_memset(fBCD.bcdBytes.ptr + oldCapacity, 0, (capacity - oldCapacity) * sizeof(int8_t)); + uprv_memset(bcd1 + oldCapacity, 0, (capacity - oldCapacity) * sizeof(int8_t)); uprv_free(fBCD.bcdBytes.ptr); fBCD.bcdBytes.ptr = bcd1; fBCD.bcdBytes.len = capacity * 2; @@ -962,6 +1101,20 @@ void DecimalQuantity::copyBcdFrom(const DecimalQuantity &other) { } } +void DecimalQuantity::moveBcdFrom(DecimalQuantity &other) { + setBcdToZero(); + if (other.usingBytes) { + usingBytes = true; + fBCD.bcdBytes.ptr = other.fBCD.bcdBytes.ptr; + fBCD.bcdBytes.len = other.fBCD.bcdBytes.len; + // Take ownership away from the old instance: + other.fBCD.bcdBytes.ptr = nullptr; + other.usingBytes = false; + } else { + fBCD.bcdLong = other.fBCD.bcdLong; + } +} + const char16_t* DecimalQuantity::checkHealth() const { if (usingBytes) { if (precision == 0) { return u"Zero precision but we are in byte mode"; } @@ -1000,6 +1153,11 @@ const char16_t* DecimalQuantity::checkHealth() const { return nullptr; } +bool DecimalQuantity::operator==(const DecimalQuantity& other) const { + // FIXME: Make a faster implementation. + return toString() == other.toString(); +} + UnicodeString DecimalQuantity::toString() const { MaybeStackArray digits(precision + 1); for (int32_t i = 0; i < precision; i++) { @@ -1010,25 +1168,17 @@ UnicodeString DecimalQuantity::toString() const { snprintf( buffer8, sizeof(buffer8), - "", + "", (lOptPos > 999 ? 999 : lOptPos), lReqPos, rReqPos, (rOptPos < -999 ? -999 : rOptPos), (usingBytes ? "bytes" : "long"), + (isNegative() ? "-" : ""), (precision == 0 ? "0" : digits.getAlias()), "E", scale); return UnicodeString(buffer8, -1, US_INV); } -UnicodeString DecimalQuantity::toNumberString() const { - MaybeStackArray digits(precision + 11); - for (int32_t i = 0; i < precision; i++) { - digits[i] = getDigitPos(precision - i - 1) + '0'; - } - snprintf(digits.getAlias() + precision, 11, "E%d", scale); - return UnicodeString(digits.getAlias(), -1, US_INV); -} - #endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/number_decimalquantity.h b/deps/icu-small/source/i18n/number_decimalquantity.h index 4309c3c6380ac4..8e04dea7eb5c43 100644 --- a/deps/icu-small/source/i18n/number_decimalquantity.h +++ b/deps/icu-small/source/i18n/number_decimalquantity.h @@ -3,13 +3,12 @@ #include "unicode/utypes.h" -#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT +#if !UCONFIG_NO_FORMATTING #ifndef __NUMBER_DECIMALQUANTITY_H__ #define __NUMBER_DECIMALQUANTITY_H__ #include #include "unicode/umachine.h" -#include "decNumber.h" #include "standardplural.h" #include "plurrule_impl.h" #include "number_types.h" @@ -17,6 +16,9 @@ U_NAMESPACE_BEGIN namespace number { namespace impl { +// Forward-declare (maybe don't want number_utils.h included here): +class DecNum; + /** * An class for representing a number to be processed by the decimal formatting pipeline. Includes * methods for rounding, plural rules, and decimal digit extraction. @@ -33,9 +35,12 @@ class U_I18N_API DecimalQuantity : public IFixedDecimal, public UMemory { /** Copy constructor. */ DecimalQuantity(const DecimalQuantity &other); + /** Move constructor. */ + DecimalQuantity(DecimalQuantity &&src) U_NOEXCEPT; + DecimalQuantity(); - ~DecimalQuantity(); + ~DecimalQuantity() override; /** * Sets this instance to be equal to another instance. @@ -44,6 +49,9 @@ class U_I18N_API DecimalQuantity : public IFixedDecimal, public UMemory { */ DecimalQuantity &operator=(const DecimalQuantity &other); + /** Move assignment */ + DecimalQuantity &operator=(DecimalQuantity&& src) U_NOEXCEPT; + /** * Sets the minimum and maximum integer digits that this {@link DecimalQuantity} should generate. * This method does not perform rounding. @@ -71,7 +79,10 @@ class U_I18N_API DecimalQuantity : public IFixedDecimal, public UMemory { * @param mathContext The {@link RoundingMode} to use if rounding is necessary. */ void roundToIncrement(double roundingIncrement, RoundingMode roundingMode, - int32_t minMaxFrac, UErrorCode& status); + int32_t maxFrac, UErrorCode& status); + + /** Removes all fraction digits. */ + void truncate(); /** * Rounds the number to a specified magnitude (power of ten). @@ -89,19 +100,30 @@ class U_I18N_API DecimalQuantity : public IFixedDecimal, public UMemory { void roundToInfinity(); /** - * Multiply the internal value. + * Multiply the internal value. Uses decNumber. + * + * @param multiplicand The value by which to multiply. + */ + void multiplyBy(const DecNum& multiplicand, UErrorCode& status); + + /** + * Divide the internal value. Uses decNumber. * * @param multiplicand The value by which to multiply. */ - void multiplyBy(int32_t multiplicand); + void divideBy(const DecNum& divisor, UErrorCode& status); + + /** Flips the sign from positive to negative and back. */ + void negate(); /** * Scales the number by a power of ten. For example, if the value is currently "1234.56", calling * this method with delta=-3 will change the value to "1.23456". * * @param delta The number of magnitudes of ten to change by. + * @return true if integer overflow occured; false otherwise. */ - void adjustMagnitude(int32_t delta); + bool adjustMagnitude(int32_t delta); /** * @return The power of ten corresponding to the most significant nonzero digit. @@ -124,13 +146,23 @@ class U_I18N_API DecimalQuantity : public IFixedDecimal, public UMemory { /** @return Whether the value represented by this {@link DecimalQuantity} is not a number. */ bool isNaN() const U_OVERRIDE; - int64_t toLong() const; + /** @param truncateIfOverflow if false and the number does NOT fit, fails with an assertion error. */ + int64_t toLong(bool truncateIfOverflow = false) const; - int64_t toFractionLong(bool includeTrailingZeros) const; + uint64_t toFractionLong(bool includeTrailingZeros) const; + + /** + * Returns whether or not a Long can fully represent the value stored in this DecimalQuantity. + * @param ignoreFraction if true, silently ignore digits after the decimal place. + */ + bool fitsInLong(bool ignoreFraction = false) const; /** @return The value contained in this {@link DecimalQuantity} approximated as a double. */ double toDouble() const; + /** Computes a DecNum representation of this DecimalQuantity, saving it to the output parameter. */ + void toDecNum(DecNum& output, UErrorCode& status) const; + DecimalQuantity &setToInt(int32_t n); DecimalQuantity &setToLong(int64_t n); @@ -138,8 +170,10 @@ class U_I18N_API DecimalQuantity : public IFixedDecimal, public UMemory { DecimalQuantity &setToDouble(double n); /** decNumber is similar to BigDecimal in Java. */ + DecimalQuantity &setToDecNumber(StringPiece n, UErrorCode& status); - DecimalQuantity &setToDecNumber(StringPiece n); + /** Internal method if the caller already has a DecNum. */ + DecimalQuantity &setToDecNum(const DecNum& n, UErrorCode& status); /** * Appends a digit, optionally with one or more leading zeros, to the end of the value represented @@ -160,17 +194,10 @@ class U_I18N_API DecimalQuantity : public IFixedDecimal, public UMemory { */ void appendDigit(int8_t value, int32_t leadingZeros, bool appendAsInteger); - /** - * Computes the plural form for this number based on the specified set of rules. - * - * @param rules A {@link PluralRules} object representing the set of rules. - * @return The {@link StandardPlural} according to the PluralRules. If the plural form is not in - * the set of standard plurals, {@link StandardPlural#OTHER} is returned instead. - */ - StandardPlural::Form getStandardPlural(const PluralRules *rules) const; - double getPluralOperand(PluralOperand operand) const U_OVERRIDE; + bool hasIntegerValue() const U_OVERRIDE; + /** * Gets the digit at the specified magnitude. For example, if the represented number is 12.3, * getDigit(-1) returns 3, since 3 is the digit corresponding to 10^-1. @@ -223,10 +250,10 @@ class U_I18N_API DecimalQuantity : public IFixedDecimal, public UMemory { UnicodeString toString() const; - /* Returns the string in exponential notation. */ - UnicodeString toNumberString() const; + /** Returns the string in standard exponential notation. */ + UnicodeString toScientificString() const; - /* Returns the string without exponential notation. Slightly slower than toNumberString(). */ + /** Returns the string without exponential notation. Slightly slower than toScientificString(). */ UnicodeString toPlainString() const; /** Visible for testing */ @@ -235,6 +262,17 @@ class U_I18N_API DecimalQuantity : public IFixedDecimal, public UMemory { /** Visible for testing */ inline bool isExplicitExactDouble() { return explicitExactDouble; }; + bool operator==(const DecimalQuantity& other) const; + + inline bool operator!=(const DecimalQuantity& other) const { + return !(*this == other); + } + + /** + * Bogus flag for when a DecimalQuantity is stored on the stack. + */ + bool bogus = false; + private: /** * The power of ten corresponding to the least significant digit in the BCD. For example, if this @@ -396,12 +434,16 @@ class U_I18N_API DecimalQuantity : public IFixedDecimal, public UMemory { */ void readLongToBcd(int64_t n); - void readDecNumberToBcd(decNumber *dn); + void readDecNumberToBcd(const DecNum& dn); void readDoubleConversionToBcd(const char* buffer, int32_t length, int32_t point); + void copyFieldsFrom(const DecimalQuantity& other); + void copyBcdFrom(const DecimalQuantity &other); + void moveBcdFrom(DecimalQuantity& src); + /** * Removes trailing zeros from the BCD (adjusting the scale as required) and then computes the * precision. The precision is the number of digits in the number up through the greatest nonzero @@ -418,12 +460,10 @@ class U_I18N_API DecimalQuantity : public IFixedDecimal, public UMemory { void _setToDoubleFast(double n); - void _setToDecNumber(decNumber *n); + void _setToDecNum(const DecNum& dn, UErrorCode& status); void convertToAccurateDouble(); - double toDoubleFromOriginal() const; - /** Ensure that a byte array of at least 40 digits is allocated. */ void ensureCapacity(); diff --git a/deps/icu-small/source/i18n/number_decimfmtprops.cpp b/deps/icu-small/source/i18n/number_decimfmtprops.cpp index cc57cfce6ac1fa..6754fe19eca56c 100644 --- a/deps/icu-small/source/i18n/number_decimfmtprops.cpp +++ b/deps/icu-small/source/i18n/number_decimfmtprops.cpp @@ -3,14 +3,29 @@ #include "unicode/utypes.h" -#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT +#if !UCONFIG_NO_FORMATTING #include "number_decimfmtprops.h" +#include "umutex.h" using namespace icu; using namespace icu::number; using namespace icu::number::impl; + +namespace { + +char kRawDefaultProperties[sizeof(DecimalFormatProperties)]; + +icu::UInitOnce gDefaultPropertiesInitOnce = U_INITONCE_INITIALIZER; + +void U_CALLCONV initDefaultProperties(UErrorCode&) { + new(kRawDefaultProperties) DecimalFormatProperties(); // set to the default instance +} + +} + + DecimalFormatProperties::DecimalFormatProperties() { clear(); } @@ -23,8 +38,10 @@ void DecimalFormatProperties::clear() { decimalPatternMatchRequired = false; decimalSeparatorAlwaysShown = false; exponentSignAlwaysShown = false; + formatFailIfMoreThanMaxDigits = false; formatWidth = -1; groupingSize = -1; + groupingUsed = true; magnitudeMultiplier = 0; maximumFractionDigits = -1; maximumIntegerDigits = -1; @@ -34,7 +51,8 @@ void DecimalFormatProperties::clear() { minimumGroupingDigits = -1; minimumIntegerDigits = -1; minimumSignificantDigits = -1; - multiplier = 0; + multiplier = 1; + multiplierScale = 0; negativePrefix.setToBogus(); negativePrefixPattern.setToBogus(); negativeSuffix.setToBogus(); @@ -43,9 +61,10 @@ void DecimalFormatProperties::clear() { padString.setToBogus(); parseCaseSensitive = false; parseIntegerOnly = false; - parseLenient = false; + parseMode.nullify(); parseNoExponent = false; parseToBigDecimal = false; + parseAllInput = UNUM_MAYBE; positivePrefix.setToBogus(); positivePrefixPattern.setToBogus(); positiveSuffix.setToBogus(); @@ -56,47 +75,70 @@ void DecimalFormatProperties::clear() { signAlwaysShown = false; } -bool DecimalFormatProperties::operator==(const DecimalFormatProperties &other) const { +bool +DecimalFormatProperties::_equals(const DecimalFormatProperties& other, bool ignoreForFastFormat) const { bool eq = true; + + // Properties that must be equal both normally and for fast-path formatting eq = eq && compactStyle == other.compactStyle; eq = eq && currency == other.currency; eq = eq && currencyPluralInfo.fPtr.getAlias() == other.currencyPluralInfo.fPtr.getAlias(); eq = eq && currencyUsage == other.currencyUsage; - eq = eq && decimalPatternMatchRequired == other.decimalPatternMatchRequired; eq = eq && decimalSeparatorAlwaysShown == other.decimalSeparatorAlwaysShown; eq = eq && exponentSignAlwaysShown == other.exponentSignAlwaysShown; + eq = eq && formatFailIfMoreThanMaxDigits == other.formatFailIfMoreThanMaxDigits; eq = eq && formatWidth == other.formatWidth; - eq = eq && groupingSize == other.groupingSize; eq = eq && magnitudeMultiplier == other.magnitudeMultiplier; - eq = eq && maximumFractionDigits == other.maximumFractionDigits; - eq = eq && maximumIntegerDigits == other.maximumIntegerDigits; eq = eq && maximumSignificantDigits == other.maximumSignificantDigits; eq = eq && minimumExponentDigits == other.minimumExponentDigits; - eq = eq && minimumFractionDigits == other.minimumFractionDigits; eq = eq && minimumGroupingDigits == other.minimumGroupingDigits; - eq = eq && minimumIntegerDigits == other.minimumIntegerDigits; eq = eq && minimumSignificantDigits == other.minimumSignificantDigits; eq = eq && multiplier == other.multiplier; + eq = eq && multiplierScale == other.multiplierScale; eq = eq && negativePrefix == other.negativePrefix; - eq = eq && negativePrefixPattern == other.negativePrefixPattern; eq = eq && negativeSuffix == other.negativeSuffix; - eq = eq && negativeSuffixPattern == other.negativeSuffixPattern; eq = eq && padPosition == other.padPosition; eq = eq && padString == other.padString; - eq = eq && parseCaseSensitive == other.parseCaseSensitive; - eq = eq && parseIntegerOnly == other.parseIntegerOnly; - eq = eq && parseLenient == other.parseLenient; - eq = eq && parseNoExponent == other.parseNoExponent; - eq = eq && parseToBigDecimal == other.parseToBigDecimal; eq = eq && positivePrefix == other.positivePrefix; - eq = eq && positivePrefixPattern == other.positivePrefixPattern; eq = eq && positiveSuffix == other.positiveSuffix; - eq = eq && positiveSuffixPattern == other.positiveSuffixPattern; eq = eq && roundingIncrement == other.roundingIncrement; eq = eq && roundingMode == other.roundingMode; eq = eq && secondaryGroupingSize == other.secondaryGroupingSize; eq = eq && signAlwaysShown == other.signAlwaysShown; + + if (ignoreForFastFormat) { + return eq; + } + + // Properties ignored by fast-path formatting + // Formatting (special handling required): + eq = eq && groupingSize == other.groupingSize; + eq = eq && groupingUsed == other.groupingUsed; + eq = eq && minimumFractionDigits == other.minimumFractionDigits; + eq = eq && maximumFractionDigits == other.maximumFractionDigits; + eq = eq && maximumIntegerDigits == other.maximumIntegerDigits; + eq = eq && minimumIntegerDigits == other.minimumIntegerDigits; + eq = eq && negativePrefixPattern == other.negativePrefixPattern; + eq = eq && negativeSuffixPattern == other.negativeSuffixPattern; + eq = eq && positivePrefixPattern == other.positivePrefixPattern; + eq = eq && positiveSuffixPattern == other.positiveSuffixPattern; + + // Parsing (always safe to ignore): + eq = eq && decimalPatternMatchRequired == other.decimalPatternMatchRequired; + eq = eq && parseCaseSensitive == other.parseCaseSensitive; + eq = eq && parseIntegerOnly == other.parseIntegerOnly; + eq = eq && parseMode == other.parseMode; + eq = eq && parseNoExponent == other.parseNoExponent; + eq = eq && parseToBigDecimal == other.parseToBigDecimal; + eq = eq && parseAllInput == other.parseAllInput; + return eq; } +bool DecimalFormatProperties::equalsDefaultExceptFastFormat() const { + UErrorCode localStatus = U_ZERO_ERROR; + umtx_initOnce(gDefaultPropertiesInitOnce, &initDefaultProperties, localStatus); + return _equals(*reinterpret_cast(kRawDefaultProperties), true); +} + #endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/number_decimfmtprops.h b/deps/icu-small/source/i18n/number_decimfmtprops.h index 96356cad45321d..f288b6e0d97f58 100644 --- a/deps/icu-small/source/i18n/number_decimfmtprops.h +++ b/deps/icu-small/source/i18n/number_decimfmtprops.h @@ -3,7 +3,7 @@ #include "unicode/utypes.h" -#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT +#if !UCONFIG_NO_FORMATTING #ifndef __NUMBER_DECIMFMTPROPS_H__ #define __NUMBER_DECIMFMTPROPS_H__ @@ -30,21 +30,65 @@ template class U_I18N_API LocalPointer; namespace number { namespace impl { -// TODO: Figure out a nicer way to deal with CurrencyPluralInfo. // Exported as U_I18N_API because it is a public member field of exported DecimalFormatProperties -struct U_I18N_API CurrencyPluralInfoWrapper { +// Using this wrapper is rather unfortunate, but is needed on Windows platforms in order to allow +// for DLL-exporting an fully specified template instantiation. +class U_I18N_API CurrencyPluralInfoWrapper { +public: LocalPointer fPtr; - CurrencyPluralInfoWrapper() {} + CurrencyPluralInfoWrapper() = default; + CurrencyPluralInfoWrapper(const CurrencyPluralInfoWrapper& other) { if (!other.fPtr.isNull()) { fPtr.adoptInstead(new CurrencyPluralInfo(*other.fPtr)); } } + + CurrencyPluralInfoWrapper& operator=(const CurrencyPluralInfoWrapper& other) { + if (!other.fPtr.isNull()) { + fPtr.adoptInstead(new CurrencyPluralInfo(*other.fPtr)); + } + return *this; + } +}; + +/** Controls the set of rules for parsing a string from the old DecimalFormat API. */ +enum ParseMode { + /** + * Lenient mode should be used if you want to accept malformed user input. It will use heuristics + * to attempt to parse through typographical errors in the string. + */ + PARSE_MODE_LENIENT, + + /** + * Strict mode should be used if you want to require that the input is well-formed. More + * specifically, it differs from lenient mode in the following ways: + * + *
    + *
  • Grouping widths must match the grouping settings. For example, "12,3,45" will fail if the + * grouping width is 3, as in the pattern "#,##0". + *
  • The string must contain a complete prefix and suffix. For example, if the pattern is + * "{#};(#)", then "{123}" or "(123)" would match, but "{123", "123}", and "123" would all fail. + * (The latter strings would be accepted in lenient mode.) + *
  • Whitespace may not appear at arbitrary places in the string. In lenient mode, whitespace + * is allowed to occur arbitrarily before and after prefixes and exponent separators. + *
  • Leading grouping separators are not allowed, as in ",123". + *
  • Minus and plus signs can only appear if specified in the pattern. In lenient mode, a plus + * or minus sign can always precede a number. + *
  • The set of characters that can be interpreted as a decimal or grouping separator is + * smaller. + *
  • If currency parsing is enabled, currencies must only appear where + * specified in either the current pattern string or in a valid pattern string for the current + * locale. For example, if the pattern is "¤0.00", then "$1.23" would match, but "1.23$" would + * fail to match. + *
+ */ + PARSE_MODE_STRICT, }; // Exported as U_I18N_API because it is needed for the unit test PatternStringTest -struct U_I18N_API DecimalFormatProperties { +struct U_I18N_API DecimalFormatProperties : public UMemory { public: NullableValue compactStyle; @@ -54,9 +98,11 @@ struct U_I18N_API DecimalFormatProperties { bool decimalPatternMatchRequired; bool decimalSeparatorAlwaysShown; bool exponentSignAlwaysShown; + bool formatFailIfMoreThanMaxDigits; // ICU4C-only int32_t formatWidth; int32_t groupingSize; - int32_t magnitudeMultiplier; + bool groupingUsed; + int32_t magnitudeMultiplier; // internal field like multiplierScale but separate to avoid conflict int32_t maximumFractionDigits; int32_t maximumIntegerDigits; int32_t maximumSignificantDigits; @@ -66,6 +112,7 @@ struct U_I18N_API DecimalFormatProperties { int32_t minimumIntegerDigits; int32_t minimumSignificantDigits; int32_t multiplier; + int32_t multiplierScale; // ICU4C-only UnicodeString negativePrefix; UnicodeString negativePrefixPattern; UnicodeString negativeSuffix; @@ -74,9 +121,10 @@ struct U_I18N_API DecimalFormatProperties { UnicodeString padString; bool parseCaseSensitive; bool parseIntegerOnly; - bool parseLenient; + NullableValue parseMode; bool parseNoExponent; - bool parseToBigDecimal; + bool parseToBigDecimal; // TODO: Not needed in ICU4C? + UNumberFormatAttributeValue parseAllInput; // ICU4C-only //PluralRules pluralRules; UnicodeString positivePrefix; UnicodeString positivePrefixPattern; @@ -89,13 +137,20 @@ struct U_I18N_API DecimalFormatProperties { DecimalFormatProperties(); - //DecimalFormatProperties(const DecimalFormatProperties &other) = default; + inline bool operator==(const DecimalFormatProperties& other) const { + return _equals(other, false); + } - DecimalFormatProperties &operator=(const DecimalFormatProperties &other) = default; + void clear(); - bool operator==(const DecimalFormatProperties &other) const; + /** + * Checks for equality to the default DecimalFormatProperties, but ignores the prescribed set of + * options for fast-path formatting. + */ + bool equalsDefaultExceptFastFormat() const; - void clear(); + private: + bool _equals(const DecimalFormatProperties& other, bool ignoreForFastFormat) const; }; } // namespace impl diff --git a/deps/icu-small/source/i18n/number_decnum.h b/deps/icu-small/source/i18n/number_decnum.h new file mode 100644 index 00000000000000..a7793470b55695 --- /dev/null +++ b/deps/icu-small/source/i18n/number_decnum.h @@ -0,0 +1,77 @@ +// © 2017 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING +#ifndef __NUMBER_DECNUM_H__ +#define __NUMBER_DECNUM_H__ + +#include "decNumber.h" +#include "charstr.h" + +U_NAMESPACE_BEGIN + +#define DECNUM_INITIAL_CAPACITY 34 + +// Export an explicit template instantiation of the MaybeStackHeaderAndArray that is used as a data member of DecNum. +// When building DLLs for Windows this is required even though no direct access to the MaybeStackHeaderAndArray leaks out of the i18n library. +// (See digitlst.h, pluralaffix.h, datefmt.h, and others for similar examples.) +#if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN +template class U_I18N_API MaybeStackHeaderAndArray; +#endif + +namespace number { +namespace impl { + +/** A very thin C++ wrapper around decNumber.h */ +// Exported as U_I18N_API for tests +class U_I18N_API DecNum : public UMemory { + public: + DecNum(); // leaves object in valid but undefined state + + // Copy-like constructor; use the default move operators. + DecNum(const DecNum& other, UErrorCode& status); + + /** Sets the decNumber to the StringPiece. */ + void setTo(StringPiece str, UErrorCode& status); + + /** Sets the decNumber to the NUL-terminated char string. */ + void setTo(const char* str, UErrorCode& status); + + /** Uses double_conversion to set this decNumber to the given double. */ + void setTo(double d, UErrorCode& status); + + /** Sets the decNumber to the BCD representation. */ + void setTo(const uint8_t* bcd, int32_t length, int32_t scale, bool isNegative, UErrorCode& status); + + void normalize(); + + void multiplyBy(const DecNum& rhs, UErrorCode& status); + + void divideBy(const DecNum& rhs, UErrorCode& status); + + bool isNegative() const; + + bool isZero() const; + + inline const decNumber* getRawDecNumber() const { + return fData.getAlias(); + } + + private: + static constexpr int32_t kDefaultDigits = DECNUM_INITIAL_CAPACITY; + MaybeStackHeaderAndArray fData; + decContext fContext; + + void _setTo(const char* str, int32_t maxDigits, UErrorCode& status); +}; + +} // namespace impl +} // namespace number + +U_NAMESPACE_END + +#endif // __NUMBER_DECNUM_H__ + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/number_fluent.cpp b/deps/icu-small/source/i18n/number_fluent.cpp index 27113106c50451..687adb6b5babd2 100644 --- a/deps/icu-small/source/i18n/number_fluent.cpp +++ b/deps/icu-small/source/i18n/number_fluent.cpp @@ -3,20 +3,26 @@ #include "unicode/utypes.h" -#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT +#if !UCONFIG_NO_FORMATTING #include "uassert.h" #include "unicode/numberformatter.h" #include "number_decimalquantity.h" #include "number_formatimpl.h" #include "umutex.h" +#include "number_asformat.h" +#include "number_skeletons.h" +#include "number_utils.h" +#include "number_utypes.h" +#include "util.h" +#include "fphdlimp.h" using namespace icu; using namespace icu::number; using namespace icu::number::impl; template -Derived NumberFormatterSettings::notation(const Notation ¬ation) const { +Derived NumberFormatterSettings::notation(const Notation& notation) const& { Derived copy(*this); // NOTE: Slicing is OK. copy.fMacros.notation = notation; @@ -24,7 +30,15 @@ Derived NumberFormatterSettings::notation(const Notation ¬ation) con } template -Derived NumberFormatterSettings::unit(const icu::MeasureUnit &unit) const { +Derived NumberFormatterSettings::notation(const Notation& notation)&& { + Derived move(std::move(*this)); + // NOTE: Slicing is OK. + move.fMacros.notation = notation; + return move; +} + +template +Derived NumberFormatterSettings::unit(const icu::MeasureUnit& unit) const& { Derived copy(*this); // NOTE: Slicing occurs here. However, CurrencyUnit can be restored from MeasureUnit. // TimeUnit may be affected, but TimeUnit is not as relevant to number formatting. @@ -33,21 +47,41 @@ Derived NumberFormatterSettings::unit(const icu::MeasureUnit &unit) con } template -Derived NumberFormatterSettings::adoptUnit(icu::MeasureUnit *unit) const { +Derived NumberFormatterSettings::unit(const icu::MeasureUnit& unit)&& { + Derived move(std::move(*this)); + // See comments above about slicing. + move.fMacros.unit = unit; + return move; +} + +template +Derived NumberFormatterSettings::adoptUnit(icu::MeasureUnit* unit) const& { Derived copy(*this); - // Just copy the unit into the MacroProps by value, and delete it since we have ownership. + // Just move the unit into the MacroProps by value, and delete it since we have ownership. // NOTE: Slicing occurs here. However, CurrencyUnit can be restored from MeasureUnit. // TimeUnit may be affected, but TimeUnit is not as relevant to number formatting. if (unit != nullptr) { - // TODO: On nullptr, reset to default value? - copy.fMacros.unit = *unit; + // TODO: On nullptr, reset to default value? + copy.fMacros.unit = std::move(*unit); delete unit; } return copy; } template -Derived NumberFormatterSettings::perUnit(const icu::MeasureUnit &perUnit) const { +Derived NumberFormatterSettings::adoptUnit(icu::MeasureUnit* unit)&& { + Derived move(std::move(*this)); + // See comments above about slicing and ownership. + if (unit != nullptr) { + // TODO: On nullptr, reset to default value? + move.fMacros.unit = std::move(*unit); + delete unit; + } + return move; +} + +template +Derived NumberFormatterSettings::perUnit(const icu::MeasureUnit& perUnit) const& { Derived copy(*this); // See comments above about slicing. copy.fMacros.perUnit = perUnit; @@ -55,27 +89,69 @@ Derived NumberFormatterSettings::perUnit(const icu::MeasureUnit &perUni } template -Derived NumberFormatterSettings::adoptPerUnit(icu::MeasureUnit *perUnit) const { +Derived NumberFormatterSettings::perUnit(const icu::MeasureUnit& perUnit)&& { + Derived move(std::move(*this)); + // See comments above about slicing. + move.fMacros.perUnit = perUnit; + return move; +} + +template +Derived NumberFormatterSettings::adoptPerUnit(icu::MeasureUnit* perUnit) const& { Derived copy(*this); // See comments above about slicing and ownership. if (perUnit != nullptr) { - // TODO: On nullptr, reset to default value? - copy.fMacros.perUnit = *perUnit; + // TODO: On nullptr, reset to default value? + copy.fMacros.perUnit = std::move(*perUnit); delete perUnit; } return copy; } template -Derived NumberFormatterSettings::rounding(const Rounder &rounder) const { +Derived NumberFormatterSettings::adoptPerUnit(icu::MeasureUnit* perUnit)&& { + Derived move(std::move(*this)); + // See comments above about slicing and ownership. + if (perUnit != nullptr) { + // TODO: On nullptr, reset to default value? + move.fMacros.perUnit = std::move(*perUnit); + delete perUnit; + } + return move; +} + +template +Derived NumberFormatterSettings::precision(const Precision& precision) const& { Derived copy(*this); // NOTE: Slicing is OK. - copy.fMacros.rounder = rounder; + copy.fMacros.precision = precision; return copy; } template -Derived NumberFormatterSettings::grouping(const UGroupingStrategy &strategy) const { +Derived NumberFormatterSettings::precision(const Precision& precision)&& { + Derived move(std::move(*this)); + // NOTE: Slicing is OK. + move.fMacros.precision = precision; + return move; +} + +template +Derived NumberFormatterSettings::roundingMode(UNumberFormatRoundingMode roundingMode) const& { + Derived copy(*this); + copy.fMacros.roundingMode = roundingMode; + return copy; +} + +template +Derived NumberFormatterSettings::roundingMode(UNumberFormatRoundingMode roundingMode)&& { + Derived move(std::move(*this)); + move.fMacros.roundingMode = roundingMode; + return move; +} + +template +Derived NumberFormatterSettings::grouping(UGroupingStrategy strategy) const& { Derived copy(*this); // NOTE: This is slightly different than how the setting is stored in Java // because we want to put it on the stack. @@ -84,61 +160,174 @@ Derived NumberFormatterSettings::grouping(const UGroupingStrategy &stra } template -Derived NumberFormatterSettings::integerWidth(const IntegerWidth &style) const { +Derived NumberFormatterSettings::grouping(UGroupingStrategy strategy)&& { + Derived move(std::move(*this)); + move.fMacros.grouper = Grouper::forStrategy(strategy); + return move; +} + +template +Derived NumberFormatterSettings::integerWidth(const IntegerWidth& style) const& { Derived copy(*this); copy.fMacros.integerWidth = style; return copy; } template -Derived NumberFormatterSettings::symbols(const DecimalFormatSymbols &symbols) const { +Derived NumberFormatterSettings::integerWidth(const IntegerWidth& style)&& { + Derived move(std::move(*this)); + move.fMacros.integerWidth = style; + return move; +} + +template +Derived NumberFormatterSettings::symbols(const DecimalFormatSymbols& symbols) const& { Derived copy(*this); copy.fMacros.symbols.setTo(symbols); return copy; } template -Derived NumberFormatterSettings::adoptSymbols(NumberingSystem *ns) const { +Derived NumberFormatterSettings::symbols(const DecimalFormatSymbols& symbols)&& { + Derived move(std::move(*this)); + move.fMacros.symbols.setTo(symbols); + return move; +} + +template +Derived NumberFormatterSettings::adoptSymbols(NumberingSystem* ns) const& { Derived copy(*this); copy.fMacros.symbols.setTo(ns); return copy; } template -Derived NumberFormatterSettings::unitWidth(const UNumberUnitWidth &width) const { +Derived NumberFormatterSettings::adoptSymbols(NumberingSystem* ns)&& { + Derived move(std::move(*this)); + move.fMacros.symbols.setTo(ns); + return move; +} + +template +Derived NumberFormatterSettings::unitWidth(UNumberUnitWidth width) const& { Derived copy(*this); copy.fMacros.unitWidth = width; return copy; } template -Derived NumberFormatterSettings::sign(const UNumberSignDisplay &style) const { +Derived NumberFormatterSettings::unitWidth(UNumberUnitWidth width)&& { + Derived move(std::move(*this)); + move.fMacros.unitWidth = width; + return move; +} + +template +Derived NumberFormatterSettings::sign(UNumberSignDisplay style) const& { Derived copy(*this); copy.fMacros.sign = style; return copy; } template -Derived NumberFormatterSettings::decimal(const UNumberDecimalSeparatorDisplay &style) const { +Derived NumberFormatterSettings::sign(UNumberSignDisplay style)&& { + Derived move(std::move(*this)); + move.fMacros.sign = style; + return move; +} + +template +Derived NumberFormatterSettings::decimal(UNumberDecimalSeparatorDisplay style) const& { Derived copy(*this); copy.fMacros.decimal = style; return copy; } template -Derived NumberFormatterSettings::padding(const Padder &padder) const { +Derived NumberFormatterSettings::decimal(UNumberDecimalSeparatorDisplay style)&& { + Derived move(std::move(*this)); + move.fMacros.decimal = style; + return move; +} + +template +Derived NumberFormatterSettings::scale(const Scale& scale) const& { + Derived copy(*this); + copy.fMacros.scale = scale; + return copy; +} + +template +Derived NumberFormatterSettings::scale(const Scale& scale)&& { + Derived move(std::move(*this)); + move.fMacros.scale = scale; + return move; +} + +template +Derived NumberFormatterSettings::padding(const Padder& padder) const& { Derived copy(*this); copy.fMacros.padder = padder; return copy; } template -Derived NumberFormatterSettings::threshold(int32_t threshold) const { +Derived NumberFormatterSettings::padding(const Padder& padder)&& { + Derived move(std::move(*this)); + move.fMacros.padder = padder; + return move; +} + +template +Derived NumberFormatterSettings::threshold(int32_t threshold) const& { Derived copy(*this); copy.fMacros.threshold = threshold; return copy; } +template +Derived NumberFormatterSettings::threshold(int32_t threshold)&& { + Derived move(std::move(*this)); + move.fMacros.threshold = threshold; + return move; +} + +template +Derived NumberFormatterSettings::macros(const impl::MacroProps& macros) const& { + Derived copy(*this); + copy.fMacros = macros; + return copy; +} + +template +Derived NumberFormatterSettings::macros(const impl::MacroProps& macros)&& { + Derived move(std::move(*this)); + move.fMacros = macros; + return move; +} + +template +Derived NumberFormatterSettings::macros(impl::MacroProps&& macros) const& { + Derived copy(*this); + copy.fMacros = std::move(macros); + return copy; +} + +template +Derived NumberFormatterSettings::macros(impl::MacroProps&& macros)&& { + Derived move(std::move(*this)); + move.fMacros = std::move(macros); + return move; +} + +template +UnicodeString NumberFormatterSettings::toSkeleton(UErrorCode& status) const { + if (fMacros.copyErrorTo(status)) { + return ICU_Utility::makeBogusString(); + } + return skeleton::generate(fMacros, status); +} + // Declare all classes that implement NumberFormatterSettings // See https://stackoverflow.com/a/495056/1407170 template @@ -152,38 +341,135 @@ UnlocalizedNumberFormatter NumberFormatter::with() { return result; } -LocalizedNumberFormatter NumberFormatter::withLocale(const Locale &locale) { +LocalizedNumberFormatter NumberFormatter::withLocale(const Locale& locale) { return with().locale(locale); } -// Make the child class constructor that takes the parent class call the parent class's copy constructor -UnlocalizedNumberFormatter::UnlocalizedNumberFormatter( - const NumberFormatterSettings &other) - : NumberFormatterSettings(other) { +UnlocalizedNumberFormatter +NumberFormatter::forSkeleton(const UnicodeString& skeleton, UErrorCode& status) { + return skeleton::create(skeleton, status); +} + + +template using NFS = NumberFormatterSettings; +using LNF = LocalizedNumberFormatter; +using UNF = UnlocalizedNumberFormatter; + +UnlocalizedNumberFormatter::UnlocalizedNumberFormatter(const UNF& other) + : UNF(static_cast&>(other)) {} + +UnlocalizedNumberFormatter::UnlocalizedNumberFormatter(const NFS& other) + : NFS(other) { + // No additional fields to assign +} + +UnlocalizedNumberFormatter::UnlocalizedNumberFormatter(UNF&& src) U_NOEXCEPT + : UNF(static_cast&&>(src)) {} + +UnlocalizedNumberFormatter::UnlocalizedNumberFormatter(NFS&& src) U_NOEXCEPT + : NFS(std::move(src)) { + // No additional fields to assign +} + +UnlocalizedNumberFormatter& UnlocalizedNumberFormatter::operator=(const UNF& other) { + NFS::operator=(static_cast&>(other)); + // No additional fields to assign + return *this; +} + +UnlocalizedNumberFormatter& UnlocalizedNumberFormatter::operator=(UNF&& src) U_NOEXCEPT { + NFS::operator=(static_cast&&>(src)); + // No additional fields to assign + return *this; +} + +LocalizedNumberFormatter::LocalizedNumberFormatter(const LNF& other) + : LNF(static_cast&>(other)) {} + +LocalizedNumberFormatter::LocalizedNumberFormatter(const NFS& other) + : NFS(other) { + // No additional fields to assign (let call count and compiled formatter reset to defaults) +} + +LocalizedNumberFormatter::LocalizedNumberFormatter(LocalizedNumberFormatter&& src) U_NOEXCEPT + : LNF(static_cast&&>(src)) {} + +LocalizedNumberFormatter::LocalizedNumberFormatter(NFS&& src) U_NOEXCEPT + : NFS(std::move(src)) { + // For the move operators, copy over the compiled formatter. + // Note: if the formatter is not compiled, call count information is lost. + if (static_cast(src).fCompiled != nullptr) { + lnfMoveHelper(static_cast(src)); + } +} + +LocalizedNumberFormatter& LocalizedNumberFormatter::operator=(const LNF& other) { + NFS::operator=(static_cast&>(other)); + // No additional fields to assign (let call count and compiled formatter reset to defaults) + return *this; +} + +LocalizedNumberFormatter& LocalizedNumberFormatter::operator=(LNF&& src) U_NOEXCEPT { + NFS::operator=(static_cast&&>(src)); + // For the move operators, copy over the compiled formatter. + // Note: if the formatter is not compiled, call count information is lost. + if (static_cast(src).fCompiled != nullptr) { + // Formatter is compiled + lnfMoveHelper(static_cast(src)); + } else { + // Reset to default values. + auto* callCount = reinterpret_cast(fUnsafeCallCount); + umtx_storeRelease(*callCount, 0); + fCompiled = nullptr; + } + return *this; +} + +void LocalizedNumberFormatter::lnfMoveHelper(LNF&& src) { + // Copy over the compiled formatter and set call count to INT32_MIN as in computeCompiled(). + // Don't copy the call count directly because doing so requires a loadAcquire/storeRelease. + // The bits themselves appear to be platform-dependent, so copying them might not be safe. + auto* callCount = reinterpret_cast(fUnsafeCallCount); + umtx_storeRelease(*callCount, INT32_MIN); + fCompiled = src.fCompiled; + // Reset the source object to leave it in a safe state. + auto* srcCallCount = reinterpret_cast(src.fUnsafeCallCount); + umtx_storeRelease(*srcCallCount, 0); + src.fCompiled = nullptr; } -// Make the child class constructor that takes the parent class call the parent class's copy constructor -// For LocalizedNumberFormatter, also copy over the extra fields -LocalizedNumberFormatter::LocalizedNumberFormatter( - const NumberFormatterSettings &other) - : NumberFormatterSettings(other) { - // No additional copies required + +LocalizedNumberFormatter::~LocalizedNumberFormatter() { + delete fCompiled; } -LocalizedNumberFormatter::LocalizedNumberFormatter(const MacroProps ¯os, const Locale &locale) { +LocalizedNumberFormatter::LocalizedNumberFormatter(const MacroProps& macros, const Locale& locale) { fMacros = macros; fMacros.locale = locale; } -LocalizedNumberFormatter UnlocalizedNumberFormatter::locale(const Locale &locale) const { +LocalizedNumberFormatter::LocalizedNumberFormatter(MacroProps&& macros, const Locale& locale) { + fMacros = std::move(macros); + fMacros.locale = locale; +} + +LocalizedNumberFormatter UnlocalizedNumberFormatter::locale(const Locale& locale) const& { return LocalizedNumberFormatter(fMacros, locale); } -SymbolsWrapper::SymbolsWrapper(const SymbolsWrapper &other) { +LocalizedNumberFormatter UnlocalizedNumberFormatter::locale(const Locale& locale)&& { + return LocalizedNumberFormatter(std::move(fMacros), locale); +} + +SymbolsWrapper::SymbolsWrapper(const SymbolsWrapper& other) { doCopyFrom(other); } -SymbolsWrapper &SymbolsWrapper::operator=(const SymbolsWrapper &other) { +SymbolsWrapper::SymbolsWrapper(SymbolsWrapper&& src) U_NOEXCEPT { + doMoveFrom(std::move(src)); +} + +SymbolsWrapper& SymbolsWrapper::operator=(const SymbolsWrapper& other) { if (this == &other) { return *this; } @@ -192,23 +478,32 @@ SymbolsWrapper &SymbolsWrapper::operator=(const SymbolsWrapper &other) { return *this; } +SymbolsWrapper& SymbolsWrapper::operator=(SymbolsWrapper&& src) U_NOEXCEPT { + if (this == &src) { + return *this; + } + doCleanup(); + doMoveFrom(std::move(src)); + return *this; +} + SymbolsWrapper::~SymbolsWrapper() { doCleanup(); } -void SymbolsWrapper::setTo(const DecimalFormatSymbols &dfs) { +void SymbolsWrapper::setTo(const DecimalFormatSymbols& dfs) { doCleanup(); fType = SYMPTR_DFS; fPtr.dfs = new DecimalFormatSymbols(dfs); } -void SymbolsWrapper::setTo(const NumberingSystem *ns) { +void SymbolsWrapper::setTo(const NumberingSystem* ns) { doCleanup(); fType = SYMPTR_NS; fPtr.ns = ns; } -void SymbolsWrapper::doCopyFrom(const SymbolsWrapper &other) { +void SymbolsWrapper::doCopyFrom(const SymbolsWrapper& other) { fType = other.fType; switch (fType) { case SYMPTR_NONE: @@ -233,6 +528,23 @@ void SymbolsWrapper::doCopyFrom(const SymbolsWrapper &other) { } } +void SymbolsWrapper::doMoveFrom(SymbolsWrapper&& src) { + fType = src.fType; + switch (fType) { + case SYMPTR_NONE: + // No action necessary + break; + case SYMPTR_DFS: + fPtr.dfs = src.fPtr.dfs; + src.fPtr.dfs = nullptr; + break; + case SYMPTR_NS: + fPtr.ns = src.fPtr.ns; + src.fPtr.ns = nullptr; + break; + } +} + void SymbolsWrapper::doCleanup() { switch (fType) { case SYMPTR_NONE: @@ -265,53 +577,122 @@ const NumberingSystem* SymbolsWrapper::getNumberingSystem() const { return fPtr.ns; } -LocalizedNumberFormatter::~LocalizedNumberFormatter() { - delete fCompiled; -} -FormattedNumber LocalizedNumberFormatter::formatInt(int64_t value, UErrorCode &status) const { +FormattedNumber LocalizedNumberFormatter::formatInt(int64_t value, UErrorCode& status) const { if (U_FAILURE(status)) { return FormattedNumber(U_ILLEGAL_ARGUMENT_ERROR); } - auto results = new NumberFormatterResults(); + auto results = new UFormattedNumberData(); if (results == nullptr) { status = U_MEMORY_ALLOCATION_ERROR; return FormattedNumber(status); } results->quantity.setToLong(value); - return formatImpl(results, status); + formatImpl(results, status); + + // Do not save the results object if we encountered a failure. + if (U_SUCCESS(status)) { + return FormattedNumber(results); + } else { + delete results; + return FormattedNumber(status); + } } -FormattedNumber LocalizedNumberFormatter::formatDouble(double value, UErrorCode &status) const { +FormattedNumber LocalizedNumberFormatter::formatDouble(double value, UErrorCode& status) const { if (U_FAILURE(status)) { return FormattedNumber(U_ILLEGAL_ARGUMENT_ERROR); } - auto results = new NumberFormatterResults(); + auto results = new UFormattedNumberData(); if (results == nullptr) { status = U_MEMORY_ALLOCATION_ERROR; return FormattedNumber(status); } results->quantity.setToDouble(value); - return formatImpl(results, status); + formatImpl(results, status); + + // Do not save the results object if we encountered a failure. + if (U_SUCCESS(status)) { + return FormattedNumber(results); + } else { + delete results; + return FormattedNumber(status); + } } -FormattedNumber LocalizedNumberFormatter::formatDecimal(StringPiece value, UErrorCode &status) const { +FormattedNumber LocalizedNumberFormatter::formatDecimal(StringPiece value, UErrorCode& status) const { if (U_FAILURE(status)) { return FormattedNumber(U_ILLEGAL_ARGUMENT_ERROR); } - auto results = new NumberFormatterResults(); + auto results = new UFormattedNumberData(); if (results == nullptr) { status = U_MEMORY_ALLOCATION_ERROR; return FormattedNumber(status); } - results->quantity.setToDecNumber(value); - return formatImpl(results, status); + results->quantity.setToDecNumber(value, status); + formatImpl(results, status); + + // Do not save the results object if we encountered a failure. + if (U_SUCCESS(status)) { + return FormattedNumber(results); + } else { + delete results; + return FormattedNumber(status); + } } FormattedNumber -LocalizedNumberFormatter::formatImpl(impl::NumberFormatterResults *results, UErrorCode &status) const { +LocalizedNumberFormatter::formatDecimalQuantity(const DecimalQuantity& dq, UErrorCode& status) const { + if (U_FAILURE(status)) { return FormattedNumber(U_ILLEGAL_ARGUMENT_ERROR); } + auto results = new UFormattedNumberData(); + if (results == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return FormattedNumber(status); + } + results->quantity = dq; + formatImpl(results, status); + + // Do not save the results object if we encountered a failure. + if (U_SUCCESS(status)) { + return FormattedNumber(results); + } else { + delete results; + return FormattedNumber(status); + } +} + +void LocalizedNumberFormatter::formatImpl(impl::UFormattedNumberData* results, UErrorCode& status) const { + if (computeCompiled(status)) { + fCompiled->apply(results->quantity, results->string, status); + } else { + NumberFormatterImpl::applyStatic(fMacros, results->quantity, results->string, status); + } +} + +void LocalizedNumberFormatter::getAffixImpl(bool isPrefix, bool isNegative, UnicodeString& result, + UErrorCode& status) const { + NumberStringBuilder string; + auto signum = static_cast(isNegative ? -1 : 1); + // Always return affixes for plural form OTHER. + static const StandardPlural::Form plural = StandardPlural::OTHER; + int32_t prefixLength; + if (computeCompiled(status)) { + prefixLength = fCompiled->getPrefixSuffix(signum, plural, string, status); + } else { + prefixLength = NumberFormatterImpl::getPrefixSuffixStatic(fMacros, signum, plural, string, status); + } + result.remove(); + if (isPrefix) { + result.append(string.toTempUnicodeString().tempSubStringBetween(0, prefixLength)); + } else { + result.append(string.toTempUnicodeString().tempSubStringBetween(prefixLength, string.length())); + } +} + +bool LocalizedNumberFormatter::computeCompiled(UErrorCode& status) const { // fUnsafeCallCount contains memory to be interpreted as an atomic int, most commonly // std::atomic. Since the type of atomic int is platform-dependent, we cast the // bytes in fUnsafeCallCount to u_atomic_int32_t, a typedef for the platform-dependent // atomic int type defined in umutex.h. - static_assert(sizeof(u_atomic_int32_t) <= sizeof(fUnsafeCallCount), - "Atomic integer size on this platform exceeds the size allocated by fUnsafeCallCount"); - u_atomic_int32_t* callCount = reinterpret_cast( - const_cast(this)->fUnsafeCallCount); + static_assert( + sizeof(u_atomic_int32_t) <= sizeof(fUnsafeCallCount), + "Atomic integer size on this platform exceeds the size allocated by fUnsafeCallCount"); + auto* callCount = reinterpret_cast( + const_cast(this)->fUnsafeCallCount); // A positive value in the atomic int indicates that the data structure is not yet ready; // a negative value indicates that it is ready. If, after the increment, the atomic int @@ -325,64 +706,144 @@ LocalizedNumberFormatter::formatImpl(impl::NumberFormatterResults *results, UErr if (currentCount == fMacros.threshold && fMacros.threshold > 0) { // Build the data structure and then use it (slow to fast path). - const NumberFormatterImpl* compiled = - NumberFormatterImpl::fromMacros(fMacros, status); + const NumberFormatterImpl* compiled = NumberFormatterImpl::fromMacros(fMacros, status); U_ASSERT(fCompiled == nullptr); - const_cast(this)->fCompiled = compiled; + const_cast(this)->fCompiled = compiled; umtx_storeRelease(*callCount, INT32_MIN); - compiled->apply(results->quantity, results->string, status); + return true; } else if (currentCount < 0) { // The data structure is already built; use it (fast path). U_ASSERT(fCompiled != nullptr); - fCompiled->apply(results->quantity, results->string, status); + return true; } else { // Format the number without building the data structure (slow path). - NumberFormatterImpl::applyStatic(fMacros, results->quantity, results->string, status); + return false; } +} - // Do not save the results object if we encountered a failure. - if (U_SUCCESS(status)) { - return FormattedNumber(results); - } else { - delete results; - return FormattedNumber(status); - } +const impl::NumberFormatterImpl* LocalizedNumberFormatter::getCompiled() const { + return fCompiled; +} + +int32_t LocalizedNumberFormatter::getCallCount() const { + auto* callCount = reinterpret_cast( + const_cast(this)->fUnsafeCallCount); + return umtx_loadAcquire(*callCount); +} + +Format* LocalizedNumberFormatter::toFormat(UErrorCode& status) const { + LocalPointer retval( + new LocalizedNumberFormatterAsFormat(*this, fMacros.locale), status); + return retval.orphan(); +} + + +FormattedNumber::FormattedNumber(FormattedNumber&& src) U_NOEXCEPT + : fResults(src.fResults), fErrorCode(src.fErrorCode) { + // Disown src.fResults to prevent double-deletion + src.fResults = nullptr; + src.fErrorCode = U_INVALID_STATE_ERROR; +} + +FormattedNumber& FormattedNumber::operator=(FormattedNumber&& src) U_NOEXCEPT { + delete fResults; + fResults = src.fResults; + fErrorCode = src.fErrorCode; + // Disown src.fResults to prevent double-deletion + src.fResults = nullptr; + src.fErrorCode = U_INVALID_STATE_ERROR; + return *this; } UnicodeString FormattedNumber::toString() const { + UErrorCode localStatus = U_ZERO_ERROR; + return toString(localStatus); +} + +UnicodeString FormattedNumber::toString(UErrorCode& status) const { + if (U_FAILURE(status)) { + return ICU_Utility::makeBogusString(); + } if (fResults == nullptr) { - // TODO: http://bugs.icu-project.org/trac/ticket/13437 - return {}; + status = fErrorCode; + return ICU_Utility::makeBogusString(); } return fResults->string.toUnicodeString(); } -Appendable &FormattedNumber::appendTo(Appendable &appendable) { +Appendable& FormattedNumber::appendTo(Appendable& appendable) { + UErrorCode localStatus = U_ZERO_ERROR; + return appendTo(appendable, localStatus); +} + +Appendable& FormattedNumber::appendTo(Appendable& appendable, UErrorCode& status) { + if (U_FAILURE(status)) { + return appendable; + } if (fResults == nullptr) { - // TODO: http://bugs.icu-project.org/trac/ticket/13437 + status = fErrorCode; return appendable; } appendable.appendString(fResults->string.chars(), fResults->string.length()); return appendable; } -void FormattedNumber::populateFieldPosition(FieldPosition &fieldPosition, UErrorCode &status) { - if (U_FAILURE(status)) { return; } +void FormattedNumber::populateFieldPosition(FieldPosition& fieldPosition, UErrorCode& status) { + if (U_FAILURE(status)) { + return; + } + if (fResults == nullptr) { + status = fErrorCode; + return; + } + // in case any users were depending on the old behavior: + fieldPosition.setBeginIndex(0); + fieldPosition.setEndIndex(0); + fResults->string.nextFieldPosition(fieldPosition, status); +} + +UBool FormattedNumber::nextFieldPosition(FieldPosition& fieldPosition, UErrorCode& status) const { + if (U_FAILURE(status)) { + return FALSE; + } + if (fResults == nullptr) { + status = fErrorCode; + return FALSE; + } + // NOTE: MSVC sometimes complains when implicitly converting between bool and UBool + return fResults->string.nextFieldPosition(fieldPosition, status) ? TRUE : FALSE; +} + +void FormattedNumber::populateFieldPositionIterator(FieldPositionIterator& iterator, UErrorCode& status) { + getAllFieldPositions(iterator, status); +} + +void FormattedNumber::getAllFieldPositions(FieldPositionIterator& iterator, UErrorCode& status) const { + FieldPositionIteratorHandler fpih(&iterator, status); + getAllFieldPositionsImpl(fpih, status); +} + +void FormattedNumber::getAllFieldPositionsImpl(FieldPositionIteratorHandler& fpih, + UErrorCode& status) const { + if (U_FAILURE(status)) { + return; + } if (fResults == nullptr) { status = fErrorCode; return; } - fResults->string.populateFieldPosition(fieldPosition, 0, status); + fResults->string.getAllFieldPositions(fpih, status); } -void -FormattedNumber::populateFieldPositionIterator(FieldPositionIterator &iterator, UErrorCode &status) { - if (U_FAILURE(status)) { return; } +void FormattedNumber::getDecimalQuantity(DecimalQuantity& output, UErrorCode& status) const { + if (U_FAILURE(status)) { + return; + } if (fResults == nullptr) { status = fErrorCode; return; } - fResults->string.populateFieldPositionIterator(iterator, status); + output = fResults->quantity; } FormattedNumber::~FormattedNumber() { diff --git a/deps/icu-small/source/i18n/number_formatimpl.cpp b/deps/icu-small/source/i18n/number_formatimpl.cpp index bc96cb15dabf90..3f887128bcc668 100644 --- a/deps/icu-small/source/i18n/number_formatimpl.cpp +++ b/deps/icu-small/source/i18n/number_formatimpl.cpp @@ -3,7 +3,7 @@ #include "unicode/utypes.h" -#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT +#if !UCONFIG_NO_FORMATTING #include "cstring.h" #include "unicode/ures.h" @@ -26,88 +26,28 @@ using namespace icu::number::impl; namespace { -// NOTE: In Java, the method to get a pattern from the resource bundle exists in NumberFormat. -// In C++, we have to implement that logic here. -// TODO: Make Java and C++ consistent? - -enum CldrPatternStyle { - CLDR_PATTERN_STYLE_DECIMAL, - CLDR_PATTERN_STYLE_CURRENCY, - CLDR_PATTERN_STYLE_ACCOUNTING, - CLDR_PATTERN_STYLE_PERCENT - // TODO: Consider scientific format. -}; - -const char16_t * -doGetPattern(UResourceBundle *res, const char *nsName, const char *patternKey, UErrorCode &publicStatus, - UErrorCode &localStatus) { - // Construct the path into the resource bundle - CharString key; - key.append("NumberElements/", publicStatus); - key.append(nsName, publicStatus); - key.append("/patterns/", publicStatus); - key.append(patternKey, publicStatus); - if (U_FAILURE(publicStatus)) { - return u""; - } - return ures_getStringByKeyWithFallback(res, key.data(), nullptr, &localStatus); -} - -const char16_t *getPatternForStyle(const Locale &locale, const char *nsName, CldrPatternStyle style, - UErrorCode &status) { - const char *patternKey; - switch (style) { - case CLDR_PATTERN_STYLE_DECIMAL: - patternKey = "decimalFormat"; - break; - case CLDR_PATTERN_STYLE_CURRENCY: - patternKey = "currencyFormat"; - break; - case CLDR_PATTERN_STYLE_ACCOUNTING: - patternKey = "accountingFormat"; - break; - case CLDR_PATTERN_STYLE_PERCENT: - default: - patternKey = "percentFormat"; - break; - } - LocalUResourceBundlePointer res(ures_open(nullptr, locale.getName(), &status)); - if (U_FAILURE(status)) { return u""; } - - // Attempt to get the pattern with the native numbering system. - UErrorCode localStatus = U_ZERO_ERROR; - const char16_t *pattern; - pattern = doGetPattern(res.getAlias(), nsName, patternKey, status, localStatus); - if (U_FAILURE(status)) { return u""; } - - // Fall back to latn if native numbering system does not have the right pattern - if (U_FAILURE(localStatus) && uprv_strcmp("latn", nsName) != 0) { - localStatus = U_ZERO_ERROR; - pattern = doGetPattern(res.getAlias(), "latn", patternKey, status, localStatus); - if (U_FAILURE(status)) { return u""; } - } - - return pattern; -} - struct CurrencyFormatInfoResult { bool exists; const char16_t* pattern; const char16_t* decimalSeparator; const char16_t* groupingSeparator; }; -CurrencyFormatInfoResult getCurrencyFormatInfo(const Locale& locale, const char* isoCode, UErrorCode& status) { + +CurrencyFormatInfoResult +getCurrencyFormatInfo(const Locale& locale, const char* isoCode, UErrorCode& status) { // TODO: Load this data in a centralized location like ICU4J? + // TODO: Move this into the CurrencySymbols class? // TODO: Parts of this same data are loaded in dcfmtsym.cpp; should clean up. - CurrencyFormatInfoResult result = { false, nullptr, nullptr, nullptr }; - if (U_FAILURE(status)) return result; + CurrencyFormatInfoResult result = {false, nullptr, nullptr, nullptr}; + if (U_FAILURE(status)) { return result; } CharString key; key.append("Currencies/", status); key.append(isoCode, status); UErrorCode localStatus = status; LocalUResourceBundlePointer bundle(ures_open(U_ICUDATA_CURR, locale.getName(), &localStatus)); ures_getByKeyWithFallback(bundle.getAlias(), key.data(), bundle.getAlias(), &localStatus); - if (U_SUCCESS(localStatus) && ures_getSize(bundle.getAlias())>2) { // the length is 3 if more data is present + if (U_SUCCESS(localStatus) && + ures_getSize(bundle.getAlias()) > 2) { // the length is 3 if more data is present ures_getByIndex(bundle.getAlias(), 2, bundle.getAlias(), &localStatus); int32_t dummy; result.exists = true; @@ -121,65 +61,84 @@ CurrencyFormatInfoResult getCurrencyFormatInfo(const Locale& locale, const char* return result; } -inline bool unitIsCurrency(const MeasureUnit &unit) { - return uprv_strcmp("currency", unit.getType()) == 0; -} - -inline bool unitIsNoUnit(const MeasureUnit &unit) { - return uprv_strcmp("none", unit.getType()) == 0; -} +} // namespace -inline bool unitIsPercent(const MeasureUnit &unit) { - return uprv_strcmp("percent", unit.getSubtype()) == 0; -} -inline bool unitIsPermille(const MeasureUnit &unit) { - return uprv_strcmp("permille", unit.getSubtype()) == 0; -} +MicroPropsGenerator::~MicroPropsGenerator() = default; -} // namespace -NumberFormatterImpl *NumberFormatterImpl::fromMacros(const MacroProps ¯os, UErrorCode &status) { +NumberFormatterImpl* NumberFormatterImpl::fromMacros(const MacroProps& macros, UErrorCode& status) { return new NumberFormatterImpl(macros, true, status); } -void NumberFormatterImpl::applyStatic(const MacroProps ¯os, DecimalQuantity &inValue, - NumberStringBuilder &outString, UErrorCode &status) { +void NumberFormatterImpl::applyStatic(const MacroProps& macros, DecimalQuantity& inValue, + NumberStringBuilder& outString, UErrorCode& status) { NumberFormatterImpl impl(macros, false, status); impl.applyUnsafe(inValue, outString, status); } +int32_t NumberFormatterImpl::getPrefixSuffixStatic(const MacroProps& macros, int8_t signum, + StandardPlural::Form plural, + NumberStringBuilder& outString, UErrorCode& status) { + NumberFormatterImpl impl(macros, false, status); + return impl.getPrefixSuffixUnsafe(signum, plural, outString, status); +} + // NOTE: C++ SPECIFIC DIFFERENCE FROM JAVA: // The "safe" apply method uses a new MicroProps. In the MicroPropsGenerator, fMicros is copied into the new instance. // The "unsafe" method simply re-uses fMicros, eliminating the extra copy operation. // See MicroProps::processQuantity() for details. -void NumberFormatterImpl::apply(DecimalQuantity &inValue, NumberStringBuilder &outString, - UErrorCode &status) const { +void NumberFormatterImpl::apply(DecimalQuantity& inValue, NumberStringBuilder& outString, + UErrorCode& status) const { if (U_FAILURE(status)) { return; } MicroProps micros; + if (!fMicroPropsGenerator) { return; } fMicroPropsGenerator->processQuantity(inValue, micros, status); if (U_FAILURE(status)) { return; } microsToString(micros, inValue, outString, status); } -void NumberFormatterImpl::applyUnsafe(DecimalQuantity &inValue, NumberStringBuilder &outString, - UErrorCode &status) { +void NumberFormatterImpl::applyUnsafe(DecimalQuantity& inValue, NumberStringBuilder& outString, + UErrorCode& status) { if (U_FAILURE(status)) { return; } fMicroPropsGenerator->processQuantity(inValue, fMicros, status); if (U_FAILURE(status)) { return; } microsToString(fMicros, inValue, outString, status); } -NumberFormatterImpl::NumberFormatterImpl(const MacroProps ¯os, bool safe, UErrorCode &status) { +int32_t NumberFormatterImpl::getPrefixSuffix(int8_t signum, StandardPlural::Form plural, + NumberStringBuilder& outString, UErrorCode& status) const { + if (U_FAILURE(status)) { return 0; } + // #13453: DecimalFormat wants the affixes from the pattern only (modMiddle, aka pattern modifier). + // Safe path: use fImmutablePatternModifier. + const Modifier* modifier = fImmutablePatternModifier->getModifier(signum, plural); + modifier->apply(outString, 0, 0, status); + if (U_FAILURE(status)) { return 0; } + return modifier->getPrefixLength(status); +} + +int32_t NumberFormatterImpl::getPrefixSuffixUnsafe(int8_t signum, StandardPlural::Form plural, + NumberStringBuilder& outString, UErrorCode& status) { + if (U_FAILURE(status)) { return 0; } + // #13453: DecimalFormat wants the affixes from the pattern only (modMiddle, aka pattern modifier). + // Unsafe path: use fPatternModifier. + fPatternModifier->setNumberProperties(signum, plural); + fPatternModifier->apply(outString, 0, 0, status); + if (U_FAILURE(status)) { return 0; } + return fPatternModifier->getPrefixLength(status); +} + +NumberFormatterImpl::NumberFormatterImpl(const MacroProps& macros, bool safe, UErrorCode& status) { fMicroPropsGenerator = macrosToMicroGenerator(macros, safe, status); } ////////// -const MicroPropsGenerator * -NumberFormatterImpl::macrosToMicroGenerator(const MacroProps ¯os, bool safe, UErrorCode &status) { - const MicroPropsGenerator *chain = &fMicros; +const MicroPropsGenerator* +NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe, UErrorCode& status) { + if (U_FAILURE(status)) { return nullptr; } + const MicroPropsGenerator* chain = &fMicros; // Check that macros is error-free before continuing. if (macros.copyErrorTo(status)) { @@ -189,18 +148,26 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps ¯os, bool safe, // TODO: Accept currency symbols from DecimalFormatSymbols? // Pre-compute a few values for efficiency. - bool isCurrency = unitIsCurrency(macros.unit); - bool isNoUnit = unitIsNoUnit(macros.unit); - bool isPercent = isNoUnit && unitIsPercent(macros.unit); - bool isPermille = isNoUnit && unitIsPermille(macros.unit); + bool isCurrency = utils::unitIsCurrency(macros.unit); + bool isNoUnit = utils::unitIsNoUnit(macros.unit); + bool isPercent = isNoUnit && utils::unitIsPercent(macros.unit); + bool isPermille = isNoUnit && utils::unitIsPermille(macros.unit); bool isCldrUnit = !isCurrency && !isNoUnit; - bool isAccounting = macros.sign == UNUM_SIGN_ACCOUNTING - || macros.sign == UNUM_SIGN_ACCOUNTING_ALWAYS - || macros.sign == UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO; - CurrencyUnit currency(kDefaultCurrency, status); + bool isAccounting = + macros.sign == UNUM_SIGN_ACCOUNTING || macros.sign == UNUM_SIGN_ACCOUNTING_ALWAYS || + macros.sign == UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO; + CurrencyUnit currency(nullptr, status); if (isCurrency) { currency = CurrencyUnit(macros.unit, status); // Restore CurrencyUnit from MeasureUnit } + const CurrencySymbols* currencySymbols; + if (macros.currencySymbols != nullptr) { + // Used by the DecimalFormat code path + currencySymbols = macros.currencySymbols; + } else { + fWarehouse.fCurrencySymbols = {currency, macros.locale, status}; + currencySymbols = &fWarehouse.fCurrencySymbols; + } UNumberUnitWidth unitWidth = UNUM_UNIT_WIDTH_SHORT; if (macros.unitWidth != UNUM_UNIT_WIDTH_COUNT) { unitWidth = macros.unitWidth; @@ -208,7 +175,7 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps ¯os, bool safe, // Select the numbering system. LocalPointer nsLocal; - const NumberingSystem *ns; + const NumberingSystem* ns; if (macros.symbols.isNumberingSystem()) { ns = macros.symbols.getNumberingSystem(); } else { @@ -217,7 +184,7 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps ¯os, bool safe, // Give ownership to the function scope. nsLocal.adoptInstead(ns); } - const char *nsName = U_SUCCESS(status) ? ns->getName() : "latn"; + const char* nsName = U_SUCCESS(status) ? ns->getName() : "latn"; // Resolve the symbols. Do this here because currency may need to customize them. if (macros.symbols.isDecimalFormatSymbols()) { @@ -232,21 +199,22 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps ¯os, bool safe, // If we are formatting currency, check for a currency-specific pattern. const char16_t* pattern = nullptr; if (isCurrency) { - CurrencyFormatInfoResult info = getCurrencyFormatInfo(macros.locale, currency.getSubtype(), status); + CurrencyFormatInfoResult info = getCurrencyFormatInfo( + macros.locale, currency.getSubtype(), status); if (info.exists) { pattern = info.pattern; // It's clunky to clone an object here, but this code is not frequently executed. - DecimalFormatSymbols* symbols = new DecimalFormatSymbols(*fMicros.symbols); + auto* symbols = new DecimalFormatSymbols(*fMicros.symbols); fMicros.symbols = symbols; fSymbols.adoptInstead(symbols); symbols->setSymbol( - DecimalFormatSymbols::ENumberFormatSymbol::kMonetarySeparatorSymbol, - UnicodeString(info.decimalSeparator), - FALSE); + DecimalFormatSymbols::ENumberFormatSymbol::kMonetarySeparatorSymbol, + UnicodeString(info.decimalSeparator), + FALSE); symbols->setSymbol( - DecimalFormatSymbols::ENumberFormatSymbol::kMonetaryGroupingSeparatorSymbol, - UnicodeString(info.groupingSeparator), - FALSE); + DecimalFormatSymbols::ENumberFormatSymbol::kMonetaryGroupingSeparatorSymbol, + UnicodeString(info.groupingSeparator), + FALSE); } } if (pattern == nullptr) { @@ -262,7 +230,7 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps ¯os, bool safe, } else { patternStyle = CLDR_PATTERN_STYLE_CURRENCY; } - pattern = getPatternForStyle(macros.locale, nsName, patternStyle, status); + pattern = utils::getPatternForStyle(macros.locale, nsName, patternStyle, status); } auto patternInfo = new ParsedPatternInfo(); fPatternInfo.adoptInstead(patternInfo); @@ -272,17 +240,31 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps ¯os, bool safe, /// START POPULATING THE DEFAULT MICROPROPS AND BUILDING THE MICROPROPS GENERATOR /// ///////////////////////////////////////////////////////////////////////////////////// + // Multiplier + if (macros.scale.isValid()) { + fMicros.helpers.multiplier.setAndChain(macros.scale, chain); + chain = &fMicros.helpers.multiplier; + } + // Rounding strategy - if (!macros.rounder.isBogus()) { - fMicros.rounding = macros.rounder; + Precision precision; + if (!macros.precision.isBogus()) { + precision = macros.precision; } else if (macros.notation.fType == Notation::NTN_COMPACT) { - fMicros.rounding = Rounder::integer().withMinDigits(2); + precision = Precision::integer().withMinDigits(2); } else if (isCurrency) { - fMicros.rounding = Rounder::currency(UCURR_USAGE_STANDARD); + precision = Precision::currency(UCURR_USAGE_STANDARD); + } else { + precision = Precision::maxFraction(6); + } + UNumberFormatRoundingMode roundingMode; + if (macros.roundingMode != kDefaultMode) { + roundingMode = macros.roundingMode; } else { - fMicros.rounding = Rounder::maxFraction(6); + // Temporary until ICU 64 + roundingMode = precision.fRoundingMode; } - fMicros.rounding.setLocaleData(currency, status); + fMicros.rounder = {precision, roundingMode, currency, status}; // Grouping strategy if (!macros.grouper.isBogus()) { @@ -306,7 +288,7 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps ¯os, bool safe, if (!macros.integerWidth.isBogus()) { fMicros.integerWidth = macros.integerWidth; } else { - fMicros.integerWidth = IntegerWidth::zeroFillTo(1); + fMicros.integerWidth = IntegerWidth::standard(); } // Sign display @@ -338,16 +320,18 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps ¯os, bool safe, // Middle modifier (patterns, positive/negative, currency symbols, percent) auto patternModifier = new MutablePatternModifier(false); fPatternModifier.adoptInstead(patternModifier); - patternModifier->setPatternInfo(fPatternInfo.getAlias()); + patternModifier->setPatternInfo( + macros.affixProvider != nullptr ? macros.affixProvider + : static_cast(fPatternInfo.getAlias())); patternModifier->setPatternAttributes(fMicros.sign, isPermille); if (patternModifier->needsPlurals()) { patternModifier->setSymbols( fMicros.symbols, - currency, + currencySymbols, unitWidth, resolvePluralRules(macros.rules, macros.locale, status)); } else { - patternModifier->setSymbols(fMicros.symbols, currency, unitWidth, nullptr); + patternModifier->setSymbols(fMicros.symbols, currencySymbols, unitWidth, nullptr); } if (safe) { fImmutablePatternModifier.adoptInstead(patternModifier->createImmutableAndChain(chain, status)); @@ -407,9 +391,9 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps ¯os, bool safe, return chain; } -const PluralRules * -NumberFormatterImpl::resolvePluralRules(const PluralRules *rulesPtr, const Locale &locale, - UErrorCode &status) { +const PluralRules* +NumberFormatterImpl::resolvePluralRules(const PluralRules* rulesPtr, const Locale& locale, + UErrorCode& status) { if (rulesPtr != nullptr) { return rulesPtr; } @@ -420,9 +404,9 @@ NumberFormatterImpl::resolvePluralRules(const PluralRules *rulesPtr, const Local return fRules.getAlias(); } -int32_t NumberFormatterImpl::microsToString(const MicroProps µs, DecimalQuantity &quantity, - NumberStringBuilder &string, UErrorCode &status) { - micros.rounding.apply(quantity, status); +int32_t NumberFormatterImpl::microsToString(const MicroProps& micros, DecimalQuantity& quantity, + NumberStringBuilder& string, UErrorCode& status) { + micros.rounder.apply(quantity, status); micros.integerWidth.apply(quantity, status); int32_t length = writeNumber(micros, quantity, string, status); // NOTE: When range formatting is added, these modifiers can bubble up. @@ -439,8 +423,8 @@ int32_t NumberFormatterImpl::microsToString(const MicroProps µs, DecimalQua return length; } -int32_t NumberFormatterImpl::writeNumber(const MicroProps µs, DecimalQuantity &quantity, - NumberStringBuilder &string, UErrorCode &status) { +int32_t NumberFormatterImpl::writeNumber(const MicroProps& micros, DecimalQuantity& quantity, + NumberStringBuilder& string, UErrorCode& status) { int32_t length = 0; if (quantity.isInfinite()) { length += string.insert( @@ -480,8 +464,8 @@ int32_t NumberFormatterImpl::writeNumber(const MicroProps µs, DecimalQuanti return length; } -int32_t NumberFormatterImpl::writeIntegerDigits(const MicroProps µs, DecimalQuantity &quantity, - NumberStringBuilder &string, UErrorCode &status) { +int32_t NumberFormatterImpl::writeIntegerDigits(const MicroProps& micros, DecimalQuantity& quantity, + NumberStringBuilder& string, UErrorCode& status) { int length = 0; int integerCount = quantity.getUpperDisplayMagnitude() + 1; for (int i = 0; i < integerCount; i++) { @@ -499,21 +483,21 @@ int32_t NumberFormatterImpl::writeIntegerDigits(const MicroProps µs, Decima // Get and append the next digit value int8_t nextDigit = quantity.getDigit(i); - length += string.insert( - 0, getDigitFromSymbols(nextDigit, *micros.symbols), UNUM_INTEGER_FIELD, status); + length += utils::insertDigitFromSymbols( + string, 0, nextDigit, *micros.symbols, UNUM_INTEGER_FIELD, status); } return length; } -int32_t NumberFormatterImpl::writeFractionDigits(const MicroProps µs, DecimalQuantity &quantity, - NumberStringBuilder &string, UErrorCode &status) { +int32_t NumberFormatterImpl::writeFractionDigits(const MicroProps& micros, DecimalQuantity& quantity, + NumberStringBuilder& string, UErrorCode& status) { int length = 0; int fractionCount = -quantity.getLowerDisplayMagnitude(); for (int i = 0; i < fractionCount; i++) { // Get and append the next digit value int8_t nextDigit = quantity.getDigit(-i - 1); - length += string.append( - getDigitFromSymbols(nextDigit, *micros.symbols), UNUM_FRACTION_FIELD, status); + length += utils::insertDigitFromSymbols( + string, string.length(), nextDigit, *micros.symbols, UNUM_FRACTION_FIELD, status); } return length; } diff --git a/deps/icu-small/source/i18n/number_formatimpl.h b/deps/icu-small/source/i18n/number_formatimpl.h index cbc04ba30df4c4..744fecec13f984 100644 --- a/deps/icu-small/source/i18n/number_formatimpl.h +++ b/deps/icu-small/source/i18n/number_formatimpl.h @@ -3,7 +3,7 @@ #include "unicode/utypes.h" -#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT +#if !UCONFIG_NO_FORMATTING #ifndef __NUMBER_FORMATIMPL_H__ #define __NUMBER_FORMATIMPL_H__ @@ -14,6 +14,7 @@ #include "number_patternmodifier.h" #include "number_longnames.h" #include "number_compact.h" +#include "number_microprops.h" U_NAMESPACE_BEGIN namespace number { namespace impl { @@ -37,10 +38,26 @@ class NumberFormatterImpl : public UMemory { applyStatic(const MacroProps ¯os, DecimalQuantity &inValue, NumberStringBuilder &outString, UErrorCode &status); + /** + * Prints only the prefix and suffix; used for DecimalFormat getters. + * + * @return The index into the output at which the prefix ends and the suffix starts; in other words, + * the prefix length. + */ + static int32_t getPrefixSuffixStatic(const MacroProps& macros, int8_t signum, + StandardPlural::Form plural, NumberStringBuilder& outString, + UErrorCode& status); + /** * Evaluates the "safe" MicroPropsGenerator created by "fromMacros". */ - void apply(DecimalQuantity &inValue, NumberStringBuilder &outString, UErrorCode &status) const; + void apply(DecimalQuantity& inValue, NumberStringBuilder& outString, UErrorCode& status) const; + + /** + * Like getPrefixSuffixStatic() but uses the safe compiled object. + */ + int32_t getPrefixSuffix(int8_t signum, StandardPlural::Form plural, NumberStringBuilder& outString, + UErrorCode& status) const; private: // Head of the MicroPropsGenerator linked list: @@ -50,21 +67,29 @@ class NumberFormatterImpl : public UMemory { MicroProps fMicros; // Other fields possibly used by the number formatting pipeline: - // TODO: Convert some of these LocalPointers to value objects to reduce the number of news? + // TODO: Convert more of these LocalPointers to value objects to reduce the number of news? LocalPointer fSymbols; LocalPointer fRules; LocalPointer fPatternInfo; LocalPointer fScientificHandler; - LocalPointer fPatternModifier; + LocalPointer fPatternModifier; LocalPointer fImmutablePatternModifier; LocalPointer fLongNameHandler; LocalPointer fCompactHandler; + // Value objects possibly used by the number formatting pipeline: + struct Warehouse { + CurrencySymbols fCurrencySymbols; + } fWarehouse; + NumberFormatterImpl(const MacroProps ¯os, bool safe, UErrorCode &status); void applyUnsafe(DecimalQuantity &inValue, NumberStringBuilder &outString, UErrorCode &status); + int32_t getPrefixSuffixUnsafe(int8_t signum, StandardPlural::Form plural, + NumberStringBuilder& outString, UErrorCode& status); + /** * If rulesPtr is non-null, return it. Otherwise, return a PluralRules owned by this object for the * specified locale, creating it if necessary. diff --git a/deps/icu-small/source/i18n/number_grouping.cpp b/deps/icu-small/source/i18n/number_grouping.cpp index a2b1bbd6b3388e..4a1cceb49948b9 100644 --- a/deps/icu-small/source/i18n/number_grouping.cpp +++ b/deps/icu-small/source/i18n/number_grouping.cpp @@ -3,7 +3,7 @@ #include "unicode/utypes.h" -#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT +#if !UCONFIG_NO_FORMATTING #include "unicode/numberformatter.h" #include "number_patternstring.h" @@ -37,20 +37,33 @@ int16_t getMinGroupingForLocale(const Locale& locale) { Grouper Grouper::forStrategy(UGroupingStrategy grouping) { switch (grouping) { case UNUM_GROUPING_OFF: - return {-1, -1, -2}; + return {-1, -1, -2, grouping}; case UNUM_GROUPING_AUTO: - return {-2, -2, -2}; + return {-2, -2, -2, grouping}; case UNUM_GROUPING_MIN2: - return {-2, -2, -3}; + return {-2, -2, -3, grouping}; case UNUM_GROUPING_ON_ALIGNED: - return {-4, -4, 1}; + return {-4, -4, 1, grouping}; case UNUM_GROUPING_THOUSANDS: - return {3, 3, 1}; + return {3, 3, 1, grouping}; default: U_ASSERT(FALSE); + return {}; // return a value: silence compiler warning } } +Grouper Grouper::forProperties(const DecimalFormatProperties& properties) { + if (!properties.groupingUsed) { + return forStrategy(UNUM_GROUPING_OFF); + } + auto grouping1 = static_cast(properties.groupingSize); + auto grouping2 = static_cast(properties.secondaryGroupingSize); + auto minGrouping = static_cast(properties.minimumGroupingDigits); + grouping1 = grouping1 > 0 ? grouping1 : grouping2 > 0 ? grouping2 : grouping1; + grouping2 = grouping2 > 0 ? grouping2 : grouping1; + return {grouping1, grouping2, minGrouping, UNUM_GROUPING_COUNT}; +} + void Grouper::setLocaleData(const impl::ParsedPatternInfo &patternInfo, const Locale& locale) { if (fGrouping1 != -2 && fGrouping2 != -4) { return; @@ -86,4 +99,12 @@ bool Grouper::groupAtPosition(int32_t position, const impl::DecimalQuantity &val && value.getUpperDisplayMagnitude() - fGrouping1 + 1 >= fMinGrouping; } +int16_t Grouper::getPrimary() const { + return fGrouping1; +} + +int16_t Grouper::getSecondary() const { + return fGrouping2; +} + #endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/number_integerwidth.cpp b/deps/icu-small/source/i18n/number_integerwidth.cpp index 4a612273f5e530..6416b292982e56 100644 --- a/deps/icu-small/source/i18n/number_integerwidth.cpp +++ b/deps/icu-small/source/i18n/number_integerwidth.cpp @@ -3,7 +3,7 @@ #include "unicode/utypes.h" -#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT +#if !UCONFIG_NO_FORMATTING #include "unicode/numberformatter.h" #include "number_types.h" @@ -13,14 +13,15 @@ using namespace icu; using namespace icu::number; using namespace icu::number::impl; -IntegerWidth::IntegerWidth(digits_t minInt, digits_t maxInt) { +IntegerWidth::IntegerWidth(digits_t minInt, digits_t maxInt, bool formatFailIfMoreThanMaxDigits) { fUnion.minMaxInt.fMinInt = minInt; fUnion.minMaxInt.fMaxInt = maxInt; + fUnion.minMaxInt.fFormatFailIfMoreThanMaxDigits = formatFailIfMoreThanMaxDigits; } IntegerWidth IntegerWidth::zeroFillTo(int32_t minInt) { if (minInt >= 0 && minInt <= kMaxIntFracSig) { - return {static_cast(minInt), -1}; + return {static_cast(minInt), -1, false}; } else { return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; } @@ -30,22 +31,37 @@ IntegerWidth IntegerWidth::truncateAt(int32_t maxInt) { if (fHasError) { return *this; } // No-op on error digits_t minInt = fUnion.minMaxInt.fMinInt; if (maxInt >= 0 && maxInt <= kMaxIntFracSig && minInt <= maxInt) { - return {minInt, static_cast(maxInt)}; + return {minInt, static_cast(maxInt), false}; } else if (maxInt == -1) { - return {minInt, -1}; + return {minInt, -1, false}; } else { return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; } } -void IntegerWidth::apply(impl::DecimalQuantity &quantity, UErrorCode &status) const { +void IntegerWidth::apply(impl::DecimalQuantity& quantity, UErrorCode& status) const { if (fHasError) { status = U_ILLEGAL_ARGUMENT_ERROR; } else if (fUnion.minMaxInt.fMaxInt == -1) { quantity.setIntegerLength(fUnion.minMaxInt.fMinInt, INT32_MAX); } else { + // Enforce the backwards-compatibility feature "FormatFailIfMoreThanMaxDigits" + if (fUnion.minMaxInt.fFormatFailIfMoreThanMaxDigits && + fUnion.minMaxInt.fMaxInt < quantity.getMagnitude()) { + status = U_ILLEGAL_ARGUMENT_ERROR; + } quantity.setIntegerLength(fUnion.minMaxInt.fMinInt, fUnion.minMaxInt.fMaxInt); } } +bool IntegerWidth::operator==(const IntegerWidth& other) const { + // Private operator==; do error and bogus checking first! + U_ASSERT(!fHasError); + U_ASSERT(!other.fHasError); + U_ASSERT(!isBogus()); + U_ASSERT(!other.isBogus()); + return fUnion.minMaxInt.fMinInt == other.fUnion.minMaxInt.fMinInt && + fUnion.minMaxInt.fMaxInt == other.fUnion.minMaxInt.fMaxInt; +} + #endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/number_longnames.cpp b/deps/icu-small/source/i18n/number_longnames.cpp index 5c363442e7c033..26f9af4c9bdbfe 100644 --- a/deps/icu-small/source/i18n/number_longnames.cpp +++ b/deps/icu-small/source/i18n/number_longnames.cpp @@ -3,7 +3,7 @@ #include "unicode/utypes.h" -#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT +#if !UCONFIG_NO_FORMATTING #include "unicode/simpleformatter.h" #include "unicode/ures.h" @@ -11,6 +11,7 @@ #include "charstr.h" #include "uresimp.h" #include "number_longnames.h" +#include "number_microprops.h" #include #include "cstring.h" @@ -260,8 +261,8 @@ void LongNameHandler::processQuantity(DecimalQuantity &quantity, MicroProps &mic parent->processQuantity(quantity, micros, status); // TODO: Avoid the copy here? DecimalQuantity copy(quantity); - micros.rounding.apply(copy, status); - micros.modOuter = &fModifiers[copy.getStandardPlural(rules)]; + micros.rounder.apply(copy, status); + micros.modOuter = &fModifiers[utils::getStandardPlural(rules, copy)]; } #endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/number_longnames.h b/deps/icu-small/source/i18n/number_longnames.h index 8738bb99e7d2e6..1d1e7dd3e864a6 100644 --- a/deps/icu-small/source/i18n/number_longnames.h +++ b/deps/icu-small/source/i18n/number_longnames.h @@ -3,7 +3,7 @@ #include "unicode/utypes.h" -#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT +#if !UCONFIG_NO_FORMATTING #ifndef __NUMBER_LONGNAMES_H__ #define __NUMBER_LONGNAMES_H__ diff --git a/deps/icu-small/source/i18n/number_mapper.cpp b/deps/icu-small/source/i18n/number_mapper.cpp new file mode 100644 index 00000000000000..d260632f93b0db --- /dev/null +++ b/deps/icu-small/source/i18n/number_mapper.cpp @@ -0,0 +1,502 @@ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +// Allow implicit conversion from char16_t* to UnicodeString for this file: +// Helpful in toString methods and elsewhere. +#define UNISTR_FROM_STRING_EXPLICIT + +#include "number_mapper.h" +#include "number_patternstring.h" +#include "unicode/errorcode.h" +#include "number_utils.h" +#include "number_currencysymbols.h" + +using namespace icu; +using namespace icu::number; +using namespace icu::number::impl; + + +UnlocalizedNumberFormatter NumberPropertyMapper::create(const DecimalFormatProperties& properties, + const DecimalFormatSymbols& symbols, + DecimalFormatWarehouse& warehouse, + UErrorCode& status) { + return NumberFormatter::with().macros(oldToNew(properties, symbols, warehouse, nullptr, status)); +} + +UnlocalizedNumberFormatter NumberPropertyMapper::create(const DecimalFormatProperties& properties, + const DecimalFormatSymbols& symbols, + DecimalFormatWarehouse& warehouse, + DecimalFormatProperties& exportedProperties, + UErrorCode& status) { + return NumberFormatter::with().macros( + oldToNew( + properties, symbols, warehouse, &exportedProperties, status)); +} + +MacroProps NumberPropertyMapper::oldToNew(const DecimalFormatProperties& properties, + const DecimalFormatSymbols& symbols, + DecimalFormatWarehouse& warehouse, + DecimalFormatProperties* exportedProperties, + UErrorCode& status) { + MacroProps macros; + Locale locale = symbols.getLocale(); + + ///////////// + // SYMBOLS // + ///////////// + + macros.symbols.setTo(symbols); + + ////////////////// + // PLURAL RULES // + ////////////////// + + if (!properties.currencyPluralInfo.fPtr.isNull()) { + macros.rules = properties.currencyPluralInfo.fPtr->getPluralRules(); + } + + ///////////// + // AFFIXES // + ///////////// + + AffixPatternProvider* affixProvider; + if (properties.currencyPluralInfo.fPtr.isNull()) { + warehouse.currencyPluralInfoAPP.setToBogus(); + warehouse.propertiesAPP.setTo(properties, status); + affixProvider = &warehouse.propertiesAPP; + } else { + warehouse.currencyPluralInfoAPP.setTo(*properties.currencyPluralInfo.fPtr, properties, status); + warehouse.propertiesAPP.setToBogus(); + affixProvider = &warehouse.currencyPluralInfoAPP; + } + macros.affixProvider = affixProvider; + + /////////// + // UNITS // + /////////// + + bool useCurrency = ( + !properties.currency.isNull() || !properties.currencyPluralInfo.fPtr.isNull() || + !properties.currencyUsage.isNull() || affixProvider->hasCurrencySign()); + CurrencyUnit currency = resolveCurrency(properties, locale, status); + UCurrencyUsage currencyUsage = properties.currencyUsage.getOrDefault(UCURR_USAGE_STANDARD); + if (useCurrency) { + // NOTE: Slicing is OK. + macros.unit = currency; // NOLINT + } + warehouse.currencySymbols = {currency, locale, symbols, status}; + macros.currencySymbols = &warehouse.currencySymbols; + + /////////////////////// + // ROUNDING STRATEGY // + /////////////////////// + + int32_t maxInt = properties.maximumIntegerDigits; + int32_t minInt = properties.minimumIntegerDigits; + int32_t maxFrac = properties.maximumFractionDigits; + int32_t minFrac = properties.minimumFractionDigits; + int32_t minSig = properties.minimumSignificantDigits; + int32_t maxSig = properties.maximumSignificantDigits; + double roundingIncrement = properties.roundingIncrement; + RoundingMode roundingMode = properties.roundingMode.getOrDefault(UNUM_ROUND_HALFEVEN); + bool explicitMinMaxFrac = minFrac != -1 || maxFrac != -1; + bool explicitMinMaxSig = minSig != -1 || maxSig != -1; + // Resolve min/max frac for currencies, required for the validation logic and for when minFrac or + // maxFrac was + // set (but not both) on a currency instance. + // NOTE: Increments are handled in "Precision.constructCurrency()". + if (useCurrency && (minFrac == -1 || maxFrac == -1)) { + int32_t digits = ucurr_getDefaultFractionDigitsForUsage( + currency.getISOCurrency(), currencyUsage, &status); + if (minFrac == -1 && maxFrac == -1) { + minFrac = digits; + maxFrac = digits; + } else if (minFrac == -1) { + minFrac = std::min(maxFrac, digits); + } else /* if (maxFrac == -1) */ { + maxFrac = std::max(minFrac, digits); + } + } + // Validate min/max int/frac. + // For backwards compatibility, minimum overrides maximum if the two conflict. + // The following logic ensures that there is always a minimum of at least one digit. + if (minInt == 0 && maxFrac != 0) { + // Force a digit after the decimal point. + minFrac = minFrac <= 0 ? 1 : minFrac; + maxFrac = maxFrac < 0 ? -1 : maxFrac < minFrac ? minFrac : maxFrac; + minInt = 0; + maxInt = maxInt < 0 ? -1 : maxInt > kMaxIntFracSig ? -1 : maxInt; + } else { + // Force a digit before the decimal point. + minFrac = minFrac < 0 ? 0 : minFrac; + maxFrac = maxFrac < 0 ? -1 : maxFrac < minFrac ? minFrac : maxFrac; + minInt = minInt <= 0 ? 1 : minInt > kMaxIntFracSig ? 1 : minInt; + maxInt = maxInt < 0 ? -1 : maxInt < minInt ? minInt : maxInt > kMaxIntFracSig ? -1 : maxInt; + } + Precision precision; + if (!properties.currencyUsage.isNull()) { + precision = Precision::constructCurrency(currencyUsage).withCurrency(currency); + } else if (roundingIncrement != 0.0) { + precision = Precision::constructIncrement(roundingIncrement, minFrac); + } else if (explicitMinMaxSig) { + minSig = minSig < 1 ? 1 : minSig > kMaxIntFracSig ? kMaxIntFracSig : minSig; + maxSig = maxSig < 0 ? kMaxIntFracSig : maxSig < minSig ? minSig : maxSig > kMaxIntFracSig + ? kMaxIntFracSig : maxSig; + precision = Precision::constructSignificant(minSig, maxSig); + } else if (explicitMinMaxFrac) { + precision = Precision::constructFraction(minFrac, maxFrac); + } else if (useCurrency) { + precision = Precision::constructCurrency(currencyUsage); + } + if (!precision.isBogus()) { + precision = precision.withMode(roundingMode); + macros.precision = precision; + } + + /////////////////// + // INTEGER WIDTH // + /////////////////// + + macros.integerWidth = IntegerWidth( + static_cast(minInt), + static_cast(maxInt), + properties.formatFailIfMoreThanMaxDigits); + + /////////////////////// + // GROUPING STRATEGY // + /////////////////////// + + macros.grouper = Grouper::forProperties(properties); + + ///////////// + // PADDING // + ///////////// + + if (properties.formatWidth != -1) { + macros.padder = Padder::forProperties(properties); + } + + /////////////////////////////// + // DECIMAL MARK ALWAYS SHOWN // + /////////////////////////////// + + macros.decimal = properties.decimalSeparatorAlwaysShown ? UNUM_DECIMAL_SEPARATOR_ALWAYS + : UNUM_DECIMAL_SEPARATOR_AUTO; + + /////////////////////// + // SIGN ALWAYS SHOWN // + /////////////////////// + + macros.sign = properties.signAlwaysShown ? UNUM_SIGN_ALWAYS : UNUM_SIGN_AUTO; + + ///////////////////////// + // SCIENTIFIC NOTATION // + ///////////////////////// + + if (properties.minimumExponentDigits != -1) { + // Scientific notation is required. + // This whole section feels like a hack, but it is needed for regression tests. + // The mapping from property bag to scientific notation is nontrivial due to LDML rules. + if (maxInt > 8) { + // But #13110: The maximum of 8 digits has unknown origins and is not in the spec. + // If maxInt is greater than 8, it is set to minInt, even if minInt is greater than 8. + maxInt = minInt; + macros.integerWidth = IntegerWidth::zeroFillTo(minInt).truncateAt(maxInt); + } else if (maxInt > minInt && minInt > 1) { + // Bug #13289: if maxInt > minInt > 1, then minInt should be 1. + minInt = 1; + macros.integerWidth = IntegerWidth::zeroFillTo(minInt).truncateAt(maxInt); + } + int engineering = maxInt < 0 ? -1 : maxInt; + macros.notation = ScientificNotation( + // Engineering interval: + static_cast(engineering), + // Enforce minimum integer digits (for patterns like "000.00E0"): + (engineering == minInt), + // Minimum exponent digits: + static_cast(properties.minimumExponentDigits), + // Exponent sign always shown: + properties.exponentSignAlwaysShown ? UNUM_SIGN_ALWAYS : UNUM_SIGN_AUTO); + // Scientific notation also involves overriding the rounding mode. + // TODO: Overriding here is a bit of a hack. Should this logic go earlier? + if (macros.precision.fType == Precision::PrecisionType::RND_FRACTION) { + // For the purposes of rounding, get the original min/max int/frac, since the local + // variables + // have been manipulated for display purposes. + int minInt_ = properties.minimumIntegerDigits; + int minFrac_ = properties.minimumFractionDigits; + int maxFrac_ = properties.maximumFractionDigits; + if (minInt_ == 0 && maxFrac_ == 0) { + // Patterns like "#E0" and "##E0", which mean no rounding! + macros.precision = Precision::unlimited().withMode(roundingMode); + } else if (minInt_ == 0 && minFrac_ == 0) { + // Patterns like "#.##E0" (no zeros in the mantissa), which mean round to maxFrac+1 + macros.precision = Precision::constructSignificant(1, maxFrac_ + 1).withMode(roundingMode); + } else { + // All other scientific patterns, which mean round to minInt+maxFrac + macros.precision = Precision::constructSignificant( + minInt_ + minFrac_, minInt_ + maxFrac_).withMode(roundingMode); + } + } + } + + ////////////////////// + // COMPACT NOTATION // + ////////////////////// + + if (!properties.compactStyle.isNull()) { + if (properties.compactStyle.getNoError() == UNumberCompactStyle::UNUM_LONG) { + macros.notation = Notation::compactLong(); + } else { + macros.notation = Notation::compactShort(); + } + // Do not forward the affix provider. + macros.affixProvider = nullptr; + } + + ///////////////// + // MULTIPLIERS // + ///////////////// + + macros.scale = scaleFromProperties(properties); + + ////////////////////// + // PROPERTY EXPORTS // + ////////////////////// + + if (exportedProperties != nullptr) { + + exportedProperties->currency = currency; + exportedProperties->roundingMode = roundingMode; + exportedProperties->minimumIntegerDigits = minInt; + exportedProperties->maximumIntegerDigits = maxInt == -1 ? INT32_MAX : maxInt; + + Precision rounding_; + if (precision.fType == Precision::PrecisionType::RND_CURRENCY) { + rounding_ = precision.withCurrency(currency, status); + } else { + rounding_ = precision; + } + int minFrac_ = minFrac; + int maxFrac_ = maxFrac; + int minSig_ = minSig; + int maxSig_ = maxSig; + double increment_ = 0.0; + if (rounding_.fType == Precision::PrecisionType::RND_FRACTION) { + minFrac_ = rounding_.fUnion.fracSig.fMinFrac; + maxFrac_ = rounding_.fUnion.fracSig.fMaxFrac; + } else if (rounding_.fType == Precision::PrecisionType::RND_INCREMENT) { + increment_ = rounding_.fUnion.increment.fIncrement; + minFrac_ = rounding_.fUnion.increment.fMinFrac; + maxFrac_ = rounding_.fUnion.increment.fMinFrac; + } else if (rounding_.fType == Precision::PrecisionType::RND_SIGNIFICANT) { + minSig_ = rounding_.fUnion.fracSig.fMinSig; + maxSig_ = rounding_.fUnion.fracSig.fMaxSig; + } + + exportedProperties->minimumFractionDigits = minFrac_; + exportedProperties->maximumFractionDigits = maxFrac_; + exportedProperties->minimumSignificantDigits = minSig_; + exportedProperties->maximumSignificantDigits = maxSig_; + exportedProperties->roundingIncrement = increment_; + } + + return macros; +} + + +void PropertiesAffixPatternProvider::setTo(const DecimalFormatProperties& properties, UErrorCode&) { + fBogus = false; + + // There are two ways to set affixes in DecimalFormat: via the pattern string (applyPattern), and via the + // explicit setters (setPositivePrefix and friends). The way to resolve the settings is as follows: + // + // 1) If the explicit setting is present for the field, use it. + // 2) Otherwise, follows UTS 35 rules based on the pattern string. + // + // Importantly, the explicit setters affect only the one field they override. If you set the positive + // prefix, that should not affect the negative prefix. Since it is impossible for the user of this class + // to know whether the origin for a string was the override or the pattern, we have to say that we always + // have a negative subpattern and perform all resolution logic here. + + // Convenience: Extract the properties into local variables. + // Variables are named with three chars: [p/n][p/s][o/p] + // [p/n] => p for positive, n for negative + // [p/s] => p for prefix, s for suffix + // [o/p] => o for escaped custom override string, p for pattern string + UnicodeString ppo = AffixUtils::escape(properties.positivePrefix); + UnicodeString pso = AffixUtils::escape(properties.positiveSuffix); + UnicodeString npo = AffixUtils::escape(properties.negativePrefix); + UnicodeString nso = AffixUtils::escape(properties.negativeSuffix); + const UnicodeString& ppp = properties.positivePrefixPattern; + const UnicodeString& psp = properties.positiveSuffixPattern; + const UnicodeString& npp = properties.negativePrefixPattern; + const UnicodeString& nsp = properties.negativeSuffixPattern; + + if (!properties.positivePrefix.isBogus()) { + posPrefix = ppo; + } else if (!ppp.isBogus()) { + posPrefix = ppp; + } else { + // UTS 35: Default positive prefix is empty string. + posPrefix = u""; + } + + if (!properties.positiveSuffix.isBogus()) { + posSuffix = pso; + } else if (!psp.isBogus()) { + posSuffix = psp; + } else { + // UTS 35: Default positive suffix is empty string. + posSuffix = u""; + } + + if (!properties.negativePrefix.isBogus()) { + negPrefix = npo; + } else if (!npp.isBogus()) { + negPrefix = npp; + } else { + // UTS 35: Default negative prefix is "-" with positive prefix. + // Important: We prepend the "-" to the pattern, not the override! + negPrefix = ppp.isBogus() ? u"-" : u"-" + ppp; + } + + if (!properties.negativeSuffix.isBogus()) { + negSuffix = nso; + } else if (!nsp.isBogus()) { + negSuffix = nsp; + } else { + // UTS 35: Default negative prefix is the positive prefix. + negSuffix = psp.isBogus() ? u"" : psp; + } +} + +char16_t PropertiesAffixPatternProvider::charAt(int flags, int i) const { + return getStringInternal(flags).charAt(i); +} + +int PropertiesAffixPatternProvider::length(int flags) const { + return getStringInternal(flags).length(); +} + +UnicodeString PropertiesAffixPatternProvider::getString(int32_t flags) const { + return getStringInternal(flags); +} + +const UnicodeString& PropertiesAffixPatternProvider::getStringInternal(int32_t flags) const { + bool prefix = (flags & AFFIX_PREFIX) != 0; + bool negative = (flags & AFFIX_NEGATIVE_SUBPATTERN) != 0; + if (prefix && negative) { + return negPrefix; + } else if (prefix) { + return posPrefix; + } else if (negative) { + return negSuffix; + } else { + return posSuffix; + } +} + +bool PropertiesAffixPatternProvider::positiveHasPlusSign() const { + // TODO: Change the internal APIs to propagate out the error? + ErrorCode localStatus; + return AffixUtils::containsType(posPrefix, TYPE_PLUS_SIGN, localStatus) || + AffixUtils::containsType(posSuffix, TYPE_PLUS_SIGN, localStatus); +} + +bool PropertiesAffixPatternProvider::hasNegativeSubpattern() const { + // See comments in the constructor for more information on why this is always true. + return true; +} + +bool PropertiesAffixPatternProvider::negativeHasMinusSign() const { + ErrorCode localStatus; + return AffixUtils::containsType(negPrefix, TYPE_MINUS_SIGN, localStatus) || + AffixUtils::containsType(negSuffix, TYPE_MINUS_SIGN, localStatus); +} + +bool PropertiesAffixPatternProvider::hasCurrencySign() const { + ErrorCode localStatus; + return AffixUtils::hasCurrencySymbols(posPrefix, localStatus) || + AffixUtils::hasCurrencySymbols(posSuffix, localStatus) || + AffixUtils::hasCurrencySymbols(negPrefix, localStatus) || + AffixUtils::hasCurrencySymbols(negSuffix, localStatus); +} + +bool PropertiesAffixPatternProvider::containsSymbolType(AffixPatternType type, UErrorCode& status) const { + return AffixUtils::containsType(posPrefix, type, status) || + AffixUtils::containsType(posSuffix, type, status) || + AffixUtils::containsType(negPrefix, type, status) || + AffixUtils::containsType(negSuffix, type, status); +} + +bool PropertiesAffixPatternProvider::hasBody() const { + return true; +} + + +void CurrencyPluralInfoAffixProvider::setTo(const CurrencyPluralInfo& cpi, + const DecimalFormatProperties& properties, + UErrorCode& status) { + // We need to use a PropertiesAffixPatternProvider, not the simpler version ParsedPatternInfo, + // because user-specified affix overrides still need to work. + fBogus = false; + DecimalFormatProperties pluralProperties(properties); + for (int32_t plural = 0; plural < StandardPlural::COUNT; plural++) { + const char* keyword = StandardPlural::getKeyword(static_cast(plural)); + UnicodeString patternString; + patternString = cpi.getCurrencyPluralPattern(keyword, patternString); + PatternParser::parseToExistingProperties( + patternString, + pluralProperties, + IGNORE_ROUNDING_NEVER, + status); + affixesByPlural[plural].setTo(pluralProperties, status); + } +} + +char16_t CurrencyPluralInfoAffixProvider::charAt(int32_t flags, int32_t i) const { + int32_t pluralOrdinal = (flags & AFFIX_PLURAL_MASK); + return affixesByPlural[pluralOrdinal].charAt(flags, i); +} + +int32_t CurrencyPluralInfoAffixProvider::length(int32_t flags) const { + int32_t pluralOrdinal = (flags & AFFIX_PLURAL_MASK); + return affixesByPlural[pluralOrdinal].length(flags); +} + +UnicodeString CurrencyPluralInfoAffixProvider::getString(int32_t flags) const { + int32_t pluralOrdinal = (flags & AFFIX_PLURAL_MASK); + return affixesByPlural[pluralOrdinal].getString(flags); +} + +bool CurrencyPluralInfoAffixProvider::positiveHasPlusSign() const { + return affixesByPlural[StandardPlural::OTHER].positiveHasPlusSign(); +} + +bool CurrencyPluralInfoAffixProvider::hasNegativeSubpattern() const { + return affixesByPlural[StandardPlural::OTHER].hasNegativeSubpattern(); +} + +bool CurrencyPluralInfoAffixProvider::negativeHasMinusSign() const { + return affixesByPlural[StandardPlural::OTHER].negativeHasMinusSign(); +} + +bool CurrencyPluralInfoAffixProvider::hasCurrencySign() const { + return affixesByPlural[StandardPlural::OTHER].hasCurrencySign(); +} + +bool CurrencyPluralInfoAffixProvider::containsSymbolType(AffixPatternType type, UErrorCode& status) const { + return affixesByPlural[StandardPlural::OTHER].containsSymbolType(type, status); +} + +bool CurrencyPluralInfoAffixProvider::hasBody() const { + return affixesByPlural[StandardPlural::OTHER].hasBody(); +} + + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/number_mapper.h b/deps/icu-small/source/i18n/number_mapper.h new file mode 100644 index 00000000000000..82c5711c8d0ce7 --- /dev/null +++ b/deps/icu-small/source/i18n/number_mapper.h @@ -0,0 +1,206 @@ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING +#ifndef __NUMBER_MAPPER_H__ +#define __NUMBER_MAPPER_H__ + +#include +#include "number_types.h" +#include "unicode/currpinf.h" +#include "standardplural.h" +#include "number_patternstring.h" +#include "number_currencysymbols.h" +#include "numparse_impl.h" + +U_NAMESPACE_BEGIN +namespace number { +namespace impl { + + +class PropertiesAffixPatternProvider : public AffixPatternProvider, public UMemory { + public: + bool isBogus() const { + return fBogus; + } + + void setToBogus() { + fBogus = true; + } + + void setTo(const DecimalFormatProperties& properties, UErrorCode& status); + + PropertiesAffixPatternProvider() = default; // puts instance in valid but undefined state + + PropertiesAffixPatternProvider(const DecimalFormatProperties& properties, UErrorCode& status) { + setTo(properties, status); + } + + // AffixPatternProvider Methods: + + char16_t charAt(int32_t flags, int32_t i) const U_OVERRIDE; + + int32_t length(int32_t flags) const U_OVERRIDE; + + UnicodeString getString(int32_t flags) const U_OVERRIDE; + + bool hasCurrencySign() const U_OVERRIDE; + + bool positiveHasPlusSign() const U_OVERRIDE; + + bool hasNegativeSubpattern() const U_OVERRIDE; + + bool negativeHasMinusSign() const U_OVERRIDE; + + bool containsSymbolType(AffixPatternType, UErrorCode&) const U_OVERRIDE; + + bool hasBody() const U_OVERRIDE; + + private: + UnicodeString posPrefix; + UnicodeString posSuffix; + UnicodeString negPrefix; + UnicodeString negSuffix; + + const UnicodeString& getStringInternal(int32_t flags) const; + + bool fBogus{true}; +}; + + +class CurrencyPluralInfoAffixProvider : public AffixPatternProvider, public UMemory { + public: + bool isBogus() const { + return fBogus; + } + + void setToBogus() { + fBogus = true; + } + + void setTo(const CurrencyPluralInfo& cpi, const DecimalFormatProperties& properties, + UErrorCode& status); + + // AffixPatternProvider Methods: + + char16_t charAt(int32_t flags, int32_t i) const U_OVERRIDE; + + int32_t length(int32_t flags) const U_OVERRIDE; + + UnicodeString getString(int32_t flags) const U_OVERRIDE; + + bool hasCurrencySign() const U_OVERRIDE; + + bool positiveHasPlusSign() const U_OVERRIDE; + + bool hasNegativeSubpattern() const U_OVERRIDE; + + bool negativeHasMinusSign() const U_OVERRIDE; + + bool containsSymbolType(AffixPatternType, UErrorCode&) const U_OVERRIDE; + + bool hasBody() const U_OVERRIDE; + + private: + PropertiesAffixPatternProvider affixesByPlural[StandardPlural::COUNT]; + + bool fBogus{true}; +}; + + +/** + * A struct for ownership of a few objects needed for formatting. + */ +struct DecimalFormatWarehouse { + PropertiesAffixPatternProvider propertiesAPP; + CurrencyPluralInfoAffixProvider currencyPluralInfoAPP; + CurrencySymbols currencySymbols; +}; + + +/** +* Internal fields for DecimalFormat. +* TODO: Make some of these fields by value instead of by LocalPointer? +*/ +struct DecimalFormatFields : public UMemory { + /** The property bag corresponding to user-specified settings and settings from the pattern string. */ + LocalPointer properties; + + /** The symbols for the current locale. */ + LocalPointer symbols; + + /** + * The pre-computed formatter object. Setters cause this to be re-computed atomically. The {@link + * #format} method uses the formatter directly without needing to synchronize. + */ + LocalPointer formatter; + + /** The lazy-computed parser for .parse() */ + std::atomic<::icu::numparse::impl::NumberParserImpl*> atomicParser = {}; + + /** The lazy-computed parser for .parseCurrency() */ + std::atomic<::icu::numparse::impl::NumberParserImpl*> atomicCurrencyParser = {}; + + /** Small object ownership warehouse for the formatter and parser */ + DecimalFormatWarehouse warehouse; + + /** The effective properties as exported from the formatter object. Used by some getters. */ + LocalPointer exportedProperties; + + // Data for fastpath + bool canUseFastFormat = false; + struct FastFormatData { + char16_t cpZero; + char16_t cpGroupingSeparator; + char16_t cpMinusSign; + int8_t minInt; + int8_t maxInt; + } fastData; +}; + + +/** + * Utilities for converting between a DecimalFormatProperties and a MacroProps. + */ +class NumberPropertyMapper { + public: + /** Convenience method to create a NumberFormatter directly from Properties. */ + static UnlocalizedNumberFormatter create(const DecimalFormatProperties& properties, + const DecimalFormatSymbols& symbols, + DecimalFormatWarehouse& warehouse, UErrorCode& status); + + /** Convenience method to create a NumberFormatter directly from Properties. */ + static UnlocalizedNumberFormatter create(const DecimalFormatProperties& properties, + const DecimalFormatSymbols& symbols, + DecimalFormatWarehouse& warehouse, + DecimalFormatProperties& exportedProperties, + UErrorCode& status); + + /** + * Creates a new {@link MacroProps} object based on the content of a {@link DecimalFormatProperties} + * object. In other words, maps Properties to MacroProps. This function is used by the + * JDK-compatibility API to call into the ICU 60 fluent number formatting pipeline. + * + * @param properties + * The property bag to be mapped. + * @param symbols + * The symbols associated with the property bag. + * @param exportedProperties + * A property bag in which to store validated properties. Used by some DecimalFormat + * getters. + * @return A new MacroProps containing all of the information in the Properties. + */ + static MacroProps oldToNew(const DecimalFormatProperties& properties, + const DecimalFormatSymbols& symbols, DecimalFormatWarehouse& warehouse, + DecimalFormatProperties* exportedProperties, UErrorCode& status); +}; + + +} // namespace impl +} // namespace numparse +U_NAMESPACE_END + +#endif //__NUMBER_MAPPER_H__ +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/number_microprops.h b/deps/icu-small/source/i18n/number_microprops.h new file mode 100644 index 00000000000000..daa887bb0dd861 --- /dev/null +++ b/deps/icu-small/source/i18n/number_microprops.h @@ -0,0 +1,82 @@ +// © 2017 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING +#ifndef __NUMBER_MICROPROPS_H__ +#define __NUMBER_MICROPROPS_H__ + +// TODO: minimize includes +#include "unicode/numberformatter.h" +#include "number_types.h" +#include "number_decimalquantity.h" +#include "number_scientific.h" +#include "number_patternstring.h" +#include "number_modifiers.h" +#include "number_multiplier.h" +#include "number_roundingutils.h" +#include "decNumber.h" +#include "charstr.h" + +U_NAMESPACE_BEGIN namespace number { +namespace impl { + +struct MicroProps : public MicroPropsGenerator { + + // NOTE: All of these fields are properly initialized in NumberFormatterImpl. + RoundingImpl rounder; + Grouper grouping; + Padder padding; + IntegerWidth integerWidth; + UNumberSignDisplay sign; + UNumberDecimalSeparatorDisplay decimal; + bool useCurrency; + + // Note: This struct has no direct ownership of the following pointers. + const DecimalFormatSymbols* symbols; + const Modifier* modOuter; + const Modifier* modMiddle; + const Modifier* modInner; + + // The following "helper" fields may optionally be used during the MicroPropsGenerator. + // They live here to retain memory. + struct { + ScientificModifier scientificModifier; + EmptyModifier emptyWeakModifier{false}; + EmptyModifier emptyStrongModifier{true}; + MultiplierFormatHandler multiplier; + } helpers; + + + MicroProps() = default; + + MicroProps(const MicroProps& other) = default; + + MicroProps& operator=(const MicroProps& other) = default; + + void processQuantity(DecimalQuantity&, MicroProps& micros, UErrorCode& status) const U_OVERRIDE { + (void) status; + if (this == µs) { + // Unsafe path: no need to perform a copy. + U_ASSERT(!exhausted); + micros.exhausted = true; + U_ASSERT(exhausted); + } else { + // Safe path: copy self into the output micros. + micros = *this; + } + } + + private: + // Internal fields: + bool exhausted = false; +}; + +} // namespace impl +} // namespace number +U_NAMESPACE_END + +#endif // __NUMBER_MICROPROPS_H__ + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/number_modifiers.cpp b/deps/icu-small/source/i18n/number_modifiers.cpp index 872b97010d74b7..4385499b54f603 100644 --- a/deps/icu-small/source/i18n/number_modifiers.cpp +++ b/deps/icu-small/source/i18n/number_modifiers.cpp @@ -3,7 +3,7 @@ #include "unicode/utypes.h" -#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT +#if !UCONFIG_NO_FORMATTING #include "umutex.h" #include "ucln_cmn.h" @@ -32,6 +32,7 @@ UBool U_CALLCONV cleanupDefaultCurrencySpacing() { UNISET_DIGIT = nullptr; delete UNISET_NOTS; UNISET_NOTS = nullptr; + gDefaultCurrencySpacingInitOnce.reset(); return TRUE; } @@ -50,6 +51,9 @@ void U_CALLCONV initDefaultCurrencySpacing(UErrorCode &status) { } // namespace +Modifier::~Modifier() = default; + + int32_t ConstantAffixModifier::apply(NumberStringBuilder &output, int leftIndex, int rightIndex, UErrorCode &status) const { // Insert the suffix first since inserting the prefix will change the rightIndex diff --git a/deps/icu-small/source/i18n/number_modifiers.h b/deps/icu-small/source/i18n/number_modifiers.h index 4762a6f6d37a2d..a553100cd92a9b 100644 --- a/deps/icu-small/source/i18n/number_modifiers.h +++ b/deps/icu-small/source/i18n/number_modifiers.h @@ -3,7 +3,7 @@ #include "unicode/utypes.h" -#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT +#if !UCONFIG_NO_FORMATTING #ifndef __NUMBER_MODIFIERS_H__ #define __NUMBER_MODIFIERS_H__ diff --git a/deps/icu-small/source/i18n/number_multiplier.cpp b/deps/icu-small/source/i18n/number_multiplier.cpp new file mode 100644 index 00000000000000..a27142c9bd689b --- /dev/null +++ b/deps/icu-small/source/i18n/number_multiplier.cpp @@ -0,0 +1,156 @@ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +// Allow implicit conversion from char16_t* to UnicodeString for this file: +// Helpful in toString methods and elsewhere. +#define UNISTR_FROM_STRING_EXPLICIT + +#include "number_decnum.h" +#include "number_types.h" +#include "number_multiplier.h" +#include "numparse_validators.h" +#include "number_utils.h" +#include "decNumber.h" + +using namespace icu; +using namespace icu::number; +using namespace icu::number::impl; +using namespace icu::numparse::impl; + + +Scale::Scale(int32_t magnitude, DecNum* arbitraryToAdopt) + : fMagnitude(magnitude), fArbitrary(arbitraryToAdopt), fError(U_ZERO_ERROR) { + if (fArbitrary != nullptr) { + // Attempt to convert the DecNum to a magnitude multiplier. + fArbitrary->normalize(); + if (fArbitrary->getRawDecNumber()->digits == 1 && fArbitrary->getRawDecNumber()->lsu[0] == 1 && + !fArbitrary->isNegative()) { + // Success! + fMagnitude += fArbitrary->getRawDecNumber()->exponent; + delete fArbitrary; + fArbitrary = nullptr; + } + } +} + +Scale::Scale(const Scale& other) + : fMagnitude(other.fMagnitude), fArbitrary(nullptr), fError(other.fError) { + if (other.fArbitrary != nullptr) { + UErrorCode localStatus = U_ZERO_ERROR; + fArbitrary = new DecNum(*other.fArbitrary, localStatus); + } +} + +Scale& Scale::operator=(const Scale& other) { + fMagnitude = other.fMagnitude; + if (other.fArbitrary != nullptr) { + UErrorCode localStatus = U_ZERO_ERROR; + fArbitrary = new DecNum(*other.fArbitrary, localStatus); + } else { + fArbitrary = nullptr; + } + fError = other.fError; + return *this; +} + +Scale::Scale(Scale&& src) U_NOEXCEPT + : fMagnitude(src.fMagnitude), fArbitrary(src.fArbitrary), fError(src.fError) { + // Take ownership away from src if necessary + src.fArbitrary = nullptr; +} + +Scale& Scale::operator=(Scale&& src) U_NOEXCEPT { + fMagnitude = src.fMagnitude; + fArbitrary = src.fArbitrary; + fError = src.fError; + // Take ownership away from src if necessary + src.fArbitrary = nullptr; + return *this; +} + +Scale::~Scale() { + delete fArbitrary; +} + + +Scale Scale::none() { + return {0, nullptr}; +} + +Scale Scale::powerOfTen(int32_t power) { + return {power, nullptr}; +} + +Scale Scale::byDecimal(StringPiece multiplicand) { + UErrorCode localError = U_ZERO_ERROR; + LocalPointer decnum(new DecNum(), localError); + if (U_FAILURE(localError)) { + return {localError}; + } + decnum->setTo(multiplicand, localError); + if (U_FAILURE(localError)) { + return {localError}; + } + return {0, decnum.orphan()}; +} + +Scale Scale::byDouble(double multiplicand) { + UErrorCode localError = U_ZERO_ERROR; + LocalPointer decnum(new DecNum(), localError); + if (U_FAILURE(localError)) { + return {localError}; + } + decnum->setTo(multiplicand, localError); + if (U_FAILURE(localError)) { + return {localError}; + } + return {0, decnum.orphan()}; +} + +Scale Scale::byDoubleAndPowerOfTen(double multiplicand, int32_t power) { + UErrorCode localError = U_ZERO_ERROR; + LocalPointer decnum(new DecNum(), localError); + if (U_FAILURE(localError)) { + return {localError}; + } + decnum->setTo(multiplicand, localError); + if (U_FAILURE(localError)) { + return {localError}; + } + return {power, decnum.orphan()}; +} + +void Scale::applyTo(impl::DecimalQuantity& quantity) const { + quantity.adjustMagnitude(fMagnitude); + if (fArbitrary != nullptr) { + UErrorCode localStatus = U_ZERO_ERROR; + quantity.multiplyBy(*fArbitrary, localStatus); + } +} + +void Scale::applyReciprocalTo(impl::DecimalQuantity& quantity) const { + quantity.adjustMagnitude(-fMagnitude); + if (fArbitrary != nullptr) { + UErrorCode localStatus = U_ZERO_ERROR; + quantity.divideBy(*fArbitrary, localStatus); + } +} + + +void +MultiplierFormatHandler::setAndChain(const Scale& multiplier, const MicroPropsGenerator* parent) { + this->multiplier = multiplier; + this->parent = parent; +} + +void MultiplierFormatHandler::processQuantity(DecimalQuantity& quantity, MicroProps& micros, + UErrorCode& status) const { + parent->processQuantity(quantity, micros, status); + multiplier.applyTo(quantity); +} + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/number_multiplier.h b/deps/icu-small/source/i18n/number_multiplier.h new file mode 100644 index 00000000000000..82c30c78426cdb --- /dev/null +++ b/deps/icu-small/source/i18n/number_multiplier.h @@ -0,0 +1,57 @@ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING +#ifndef __SOURCE_NUMBER_MULTIPLIER_H__ +#define __SOURCE_NUMBER_MULTIPLIER_H__ + +#include "numparse_types.h" +#include "number_decimfmtprops.h" + +U_NAMESPACE_BEGIN namespace number { +namespace impl { + + +/** + * Wraps a {@link Multiplier} for use in the number formatting pipeline. + */ +// Exported as U_I18N_API for tests +class U_I18N_API MultiplierFormatHandler : public MicroPropsGenerator, public UMemory { + public: + MultiplierFormatHandler() = default; // WARNING: Leaves object in an unusable state; call setAndChain() + + void setAndChain(const Scale& multiplier, const MicroPropsGenerator* parent); + + void processQuantity(DecimalQuantity& quantity, MicroProps& micros, + UErrorCode& status) const U_OVERRIDE; + + private: + Scale multiplier; + const MicroPropsGenerator *parent; +}; + + +/** Gets a Scale from a DecimalFormatProperties. In Java, defined in RoundingUtils.java */ +static inline Scale scaleFromProperties(const DecimalFormatProperties& properties) { + int32_t magnitudeMultiplier = properties.magnitudeMultiplier + properties.multiplierScale; + int32_t arbitraryMultiplier = properties.multiplier; + if (magnitudeMultiplier != 0 && arbitraryMultiplier != 1) { + return Scale::byDoubleAndPowerOfTen(arbitraryMultiplier, magnitudeMultiplier); + } else if (magnitudeMultiplier != 0) { + return Scale::powerOfTen(magnitudeMultiplier); + } else if (arbitraryMultiplier != 1) { + return Scale::byDouble(arbitraryMultiplier); + } else { + return Scale::none(); + } +} + + +} // namespace impl +} // namespace number +U_NAMESPACE_END + +#endif //__SOURCE_NUMBER_MULTIPLIER_H__ +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/number_notation.cpp b/deps/icu-small/source/i18n/number_notation.cpp index f4ad333354d0c7..b3cabb57a50ea7 100644 --- a/deps/icu-small/source/i18n/number_notation.cpp +++ b/deps/icu-small/source/i18n/number_notation.cpp @@ -3,7 +3,7 @@ #include "unicode/utypes.h" -#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT +#if !UCONFIG_NO_FORMATTING #include "unicode/numberformatter.h" #include "number_types.h" @@ -36,6 +36,19 @@ ScientificNotation Notation::engineering() { return {NTN_SCIENTIFIC, union_}; } +ScientificNotation::ScientificNotation(int8_t fEngineeringInterval, bool fRequireMinInt, + impl::digits_t fMinExponentDigits, + UNumberSignDisplay fExponentSignDisplay) { + ScientificSettings settings; + settings.fEngineeringInterval = fEngineeringInterval; + settings.fRequireMinInt = fRequireMinInt; + settings.fMinExponentDigits = fMinExponentDigits; + settings.fExponentSignDisplay = fExponentSignDisplay; + NotationUnion union_; + union_.scientific = settings; + *this = {NTN_SCIENTIFIC, union_}; +} + Notation Notation::compactShort() { NotationUnion union_; union_.compactStyle = CompactStyle::UNUM_SHORT; diff --git a/deps/icu-small/source/i18n/number_padding.cpp b/deps/icu-small/source/i18n/number_padding.cpp index b1db3490cd4489..97e7b6014f9aac 100644 --- a/deps/icu-small/source/i18n/number_padding.cpp +++ b/deps/icu-small/source/i18n/number_padding.cpp @@ -3,11 +3,12 @@ #include "unicode/utypes.h" -#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT +#if !UCONFIG_NO_FORMATTING #include "unicode/numberformatter.h" #include "number_types.h" #include "number_stringbuilder.h" +#include "number_decimfmtprops.h" using namespace icu; using namespace icu::number; @@ -28,6 +29,7 @@ addPaddingHelper(UChar32 paddingCp, int32_t requiredPadding, NumberStringBuilder } Padder::Padder(UChar32 cp, int32_t width, UNumberFormatPadPosition position) : fWidth(width) { + // TODO(13034): Consider making this a string instead of code point. fUnion.padding.fCp = cp; fUnion.padding.fPosition = position; } @@ -47,6 +49,16 @@ Padder Padder::codePoints(UChar32 cp, int32_t targetWidth, UNumberFormatPadPosit } } +Padder Padder::forProperties(const DecimalFormatProperties& properties) { + UChar32 padCp; + if (properties.padString.length() > 0) { + padCp = properties.padString.char32At(0); + } else { + padCp = kFallbackPaddingString[0]; + } + return {padCp, properties.formatWidth, properties.padPosition.getOrDefault(UNUM_PAD_BEFORE_PREFIX)}; +} + int32_t Padder::padAndApply(const Modifier &mod1, const Modifier &mod2, NumberStringBuilder &string, int32_t leftIndex, int32_t rightIndex, UErrorCode &status) const { diff --git a/deps/icu-small/source/i18n/number_patternmodifier.cpp b/deps/icu-small/source/i18n/number_patternmodifier.cpp index e182104c9116e7..6417e14378bea6 100644 --- a/deps/icu-small/source/i18n/number_patternmodifier.cpp +++ b/deps/icu-small/source/i18n/number_patternmodifier.cpp @@ -3,21 +3,27 @@ #include "unicode/utypes.h" -#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT +#if !UCONFIG_NO_FORMATTING #include "cstring.h" #include "number_patternmodifier.h" #include "unicode/dcfmtsym.h" #include "unicode/ucurr.h" #include "unicode/unistr.h" +#include "number_microprops.h" using namespace icu; using namespace icu::number; using namespace icu::number::impl; -MutablePatternModifier::MutablePatternModifier(bool isStrong) : fStrong(isStrong) {} -void MutablePatternModifier::setPatternInfo(const AffixPatternProvider *patternInfo) { +AffixPatternProvider::~AffixPatternProvider() = default; + + +MutablePatternModifier::MutablePatternModifier(bool isStrong) + : fStrong(isStrong) {} + +void MutablePatternModifier::setPatternInfo(const AffixPatternProvider* patternInfo) { this->patternInfo = patternInfo; } @@ -26,14 +32,12 @@ void MutablePatternModifier::setPatternAttributes(UNumberSignDisplay signDisplay this->perMilleReplacesPercent = perMille; } -void -MutablePatternModifier::setSymbols(const DecimalFormatSymbols *symbols, const CurrencyUnit ¤cy, - const UNumberUnitWidth unitWidth, const PluralRules *rules) { +void MutablePatternModifier::setSymbols(const DecimalFormatSymbols* symbols, + const CurrencySymbols* currencySymbols, + const UNumberUnitWidth unitWidth, const PluralRules* rules) { U_ASSERT((rules != nullptr) == needsPlurals()); this->symbols = symbols; - uprv_memcpy(static_cast(this->currencyCode), - currency.getISOCurrency(), - sizeof(char16_t) * 4); + this->currencySymbols = currencySymbols; this->unitWidth = unitWidth; this->rules = rules; } @@ -49,12 +53,12 @@ bool MutablePatternModifier::needsPlurals() const { // Silently ignore any error codes. } -ImmutablePatternModifier *MutablePatternModifier::createImmutable(UErrorCode &status) { +ImmutablePatternModifier* MutablePatternModifier::createImmutable(UErrorCode& status) { return createImmutableAndChain(nullptr, status); } -ImmutablePatternModifier * -MutablePatternModifier::createImmutableAndChain(const MicroPropsGenerator *parent, UErrorCode &status) { +ImmutablePatternModifier* +MutablePatternModifier::createImmutableAndChain(const MicroPropsGenerator* parent, UErrorCode& status) { // TODO: Move StandardPlural VALUES to standardplural.h static const StandardPlural::Form STANDARD_PLURAL_VALUES[] = { @@ -89,11 +93,11 @@ MutablePatternModifier::createImmutableAndChain(const MicroPropsGenerator *paren } else { // Faster path when plural keyword is not needed. setNumberProperties(1, StandardPlural::Form::COUNT); - Modifier *positive = createConstantModifier(status); + Modifier* positive = createConstantModifier(status); setNumberProperties(0, StandardPlural::Form::COUNT); - Modifier *zero = createConstantModifier(status); + Modifier* zero = createConstantModifier(status); setNumberProperties(-1, StandardPlural::Form::COUNT); - Modifier *negative = createConstantModifier(status); + Modifier* negative = createConstantModifier(status); pm->adoptPositiveNegativeModifiers(positive, zero, negative); if (U_FAILURE(status)) { delete pm; @@ -103,77 +107,91 @@ MutablePatternModifier::createImmutableAndChain(const MicroPropsGenerator *paren } } -ConstantMultiFieldModifier *MutablePatternModifier::createConstantModifier(UErrorCode &status) { +ConstantMultiFieldModifier* MutablePatternModifier::createConstantModifier(UErrorCode& status) { NumberStringBuilder a; NumberStringBuilder b; insertPrefix(a, 0, status); insertSuffix(b, 0, status); if (patternInfo->hasCurrencySign()) { - return new CurrencySpacingEnabledModifier(a, b, !patternInfo->hasBody(), fStrong, *symbols, status); + return new CurrencySpacingEnabledModifier( + a, b, !patternInfo->hasBody(), fStrong, *symbols, status); } else { return new ConstantMultiFieldModifier(a, b, !patternInfo->hasBody(), fStrong); } } -ImmutablePatternModifier::ImmutablePatternModifier(ParameterizedModifier *pm, const PluralRules *rules, - const MicroPropsGenerator *parent) +ImmutablePatternModifier::ImmutablePatternModifier(ParameterizedModifier* pm, const PluralRules* rules, + const MicroPropsGenerator* parent) : pm(pm), rules(rules), parent(parent) {} -void ImmutablePatternModifier::processQuantity(DecimalQuantity &quantity, MicroProps µs, - UErrorCode &status) const { +void ImmutablePatternModifier::processQuantity(DecimalQuantity& quantity, MicroProps& micros, + UErrorCode& status) const { parent->processQuantity(quantity, micros, status); applyToMicros(micros, quantity); } -void ImmutablePatternModifier::applyToMicros(MicroProps µs, DecimalQuantity &quantity) const { +void ImmutablePatternModifier::applyToMicros(MicroProps& micros, DecimalQuantity& quantity) const { if (rules == nullptr) { micros.modMiddle = pm->getModifier(quantity.signum()); } else { // TODO: Fix this. Avoid the copy. DecimalQuantity copy(quantity); copy.roundToInfinity(); - StandardPlural::Form plural = copy.getStandardPlural(rules); + StandardPlural::Form plural = utils::getStandardPlural(rules, copy); micros.modMiddle = pm->getModifier(quantity.signum(), plural); } } +const Modifier* ImmutablePatternModifier::getModifier(int8_t signum, StandardPlural::Form plural) const { + if (rules == nullptr) { + return pm->getModifier(signum); + } else { + return pm->getModifier(signum, plural); + } +} + + /** Used by the unsafe code path. */ -MicroPropsGenerator &MutablePatternModifier::addToChain(const MicroPropsGenerator *parent) { +MicroPropsGenerator& MutablePatternModifier::addToChain(const MicroPropsGenerator* parent) { this->parent = parent; return *this; } -void MutablePatternModifier::processQuantity(DecimalQuantity &fq, MicroProps µs, - UErrorCode &status) const { +void MutablePatternModifier::processQuantity(DecimalQuantity& fq, MicroProps& micros, + UErrorCode& status) const { parent->processQuantity(fq, micros, status); // The unsafe code path performs self-mutation, so we need a const_cast. // This method needs to be const because it overrides a const method in the parent class. - auto nonConstThis = const_cast(this); + auto nonConstThis = const_cast(this); if (needsPlurals()) { // TODO: Fix this. Avoid the copy. DecimalQuantity copy(fq); - micros.rounding.apply(copy, status); - nonConstThis->setNumberProperties(fq.signum(), copy.getStandardPlural(rules)); + micros.rounder.apply(copy, status); + nonConstThis->setNumberProperties(fq.signum(), utils::getStandardPlural(rules, copy)); } else { nonConstThis->setNumberProperties(fq.signum(), StandardPlural::Form::COUNT); } micros.modMiddle = this; } -int32_t MutablePatternModifier::apply(NumberStringBuilder &output, int32_t leftIndex, int32_t rightIndex, - UErrorCode &status) const { +int32_t MutablePatternModifier::apply(NumberStringBuilder& output, int32_t leftIndex, int32_t rightIndex, + UErrorCode& status) const { // The unsafe code path performs self-mutation, so we need a const_cast. // This method needs to be const because it overrides a const method in the parent class. - auto nonConstThis = const_cast(this); + auto nonConstThis = const_cast(this); int32_t prefixLen = nonConstThis->insertPrefix(output, leftIndex, status); int32_t suffixLen = nonConstThis->insertSuffix(output, rightIndex + prefixLen, status); // If the pattern had no decimal stem body (like #,##0.00), overwrite the value. int32_t overwriteLen = 0; if (!patternInfo->hasBody()) { overwriteLen = output.splice( - leftIndex + prefixLen, rightIndex + prefixLen, - UnicodeString(), 0, 0, UNUM_FIELD_COUNT, - status); + leftIndex + prefixLen, + rightIndex + prefixLen, + UnicodeString(), + 0, + 0, + UNUM_FIELD_COUNT, + status); } CurrencySpacingEnabledModifier::applyCurrencySpacing( output, @@ -186,30 +204,27 @@ int32_t MutablePatternModifier::apply(NumberStringBuilder &output, int32_t leftI return prefixLen + overwriteLen + suffixLen; } -int32_t MutablePatternModifier::getPrefixLength(UErrorCode &status) const { +int32_t MutablePatternModifier::getPrefixLength(UErrorCode& status) const { // The unsafe code path performs self-mutation, so we need a const_cast. // This method needs to be const because it overrides a const method in the parent class. - auto nonConstThis = const_cast(this); + auto nonConstThis = const_cast(this); // Enter and exit CharSequence Mode to get the length. - nonConstThis->enterCharSequenceMode(true); - int result = AffixUtils::unescapedCodePointCount(*this, *this, status); // prefix length - nonConstThis->exitCharSequenceMode(); + nonConstThis->prepareAffix(true); + int result = AffixUtils::unescapedCodePointCount(currentAffix, *this, status); // prefix length return result; } -int32_t MutablePatternModifier::getCodePointCount(UErrorCode &status) const { +int32_t MutablePatternModifier::getCodePointCount(UErrorCode& status) const { // The unsafe code path performs self-mutation, so we need a const_cast. // This method needs to be const because it overrides a const method in the parent class. - auto nonConstThis = const_cast(this); + auto nonConstThis = const_cast(this); - // Enter and exit CharSequence Mode to get the length. - nonConstThis->enterCharSequenceMode(true); - int result = AffixUtils::unescapedCodePointCount(*this, *this, status); // prefix length - nonConstThis->exitCharSequenceMode(); - nonConstThis->enterCharSequenceMode(false); - result += AffixUtils::unescapedCodePointCount(*this, *this, status); // suffix length - nonConstThis->exitCharSequenceMode(); + // Render the affixes to get the length + nonConstThis->prepareAffix(true); + int result = AffixUtils::unescapedCodePointCount(currentAffix, *this, status); // prefix length + nonConstThis->prepareAffix(false); + result += AffixUtils::unescapedCodePointCount(currentAffix, *this, status); // suffix length return result; } @@ -217,21 +232,26 @@ bool MutablePatternModifier::isStrong() const { return fStrong; } -int32_t MutablePatternModifier::insertPrefix(NumberStringBuilder &sb, int position, UErrorCode &status) { - enterCharSequenceMode(true); - int length = AffixUtils::unescape(*this, sb, position, *this, status); - exitCharSequenceMode(); +int32_t MutablePatternModifier::insertPrefix(NumberStringBuilder& sb, int position, UErrorCode& status) { + prepareAffix(true); + int length = AffixUtils::unescape(currentAffix, sb, position, *this, status); return length; } -int32_t MutablePatternModifier::insertSuffix(NumberStringBuilder &sb, int position, UErrorCode &status) { - enterCharSequenceMode(false); - int length = AffixUtils::unescape(*this, sb, position, *this, status); - exitCharSequenceMode(); +int32_t MutablePatternModifier::insertSuffix(NumberStringBuilder& sb, int position, UErrorCode& status) { + prepareAffix(false); + int length = AffixUtils::unescape(currentAffix, sb, position, *this, status); return length; } +/** This method contains the heart of the logic for rendering LDML affix strings. */ +void MutablePatternModifier::prepareAffix(bool isPrefix) { + PatternStringUtils::patternInfoToStringBuilder( + *patternInfo, isPrefix, signum, signDisplay, plural, perMilleReplacesPercent, currentAffix); +} + UnicodeString MutablePatternModifier::getSymbol(AffixPatternType type) const { + UErrorCode localStatus = U_ZERO_ERROR; switch (type) { case AffixPatternType::TYPE_MINUS_SIGN: return symbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kMinusSignSymbol); @@ -244,45 +264,23 @@ UnicodeString MutablePatternModifier::getSymbol(AffixPatternType type) const { case AffixPatternType::TYPE_CURRENCY_SINGLE: { // UnitWidth ISO and HIDDEN overrides the singular currency symbol. if (unitWidth == UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE) { - return UnicodeString(currencyCode, 3); + return currencySymbols->getIntlCurrencySymbol(localStatus); } else if (unitWidth == UNumberUnitWidth::UNUM_UNIT_WIDTH_HIDDEN) { return UnicodeString(); + } else if (unitWidth == UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW) { + return currencySymbols->getNarrowCurrencySymbol(localStatus); } else { - UCurrNameStyle selector = (unitWidth == UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW) - ? UCurrNameStyle::UCURR_NARROW_SYMBOL_NAME - : UCurrNameStyle::UCURR_SYMBOL_NAME; - UErrorCode status = U_ZERO_ERROR; - UBool isChoiceFormat = FALSE; - int32_t symbolLen = 0; - const char16_t *symbol = ucurr_getName( - currencyCode, - symbols->getLocale().getName(), - selector, - &isChoiceFormat, - &symbolLen, - &status); - return UnicodeString(symbol, symbolLen); + return currencySymbols->getCurrencySymbol(localStatus); } } case AffixPatternType::TYPE_CURRENCY_DOUBLE: - return UnicodeString(currencyCode, 3); - case AffixPatternType::TYPE_CURRENCY_TRIPLE: { + return currencySymbols->getIntlCurrencySymbol(localStatus); + case AffixPatternType::TYPE_CURRENCY_TRIPLE: // NOTE: This is the code path only for patterns containing "¤¤¤". // Plural currencies set via the API are formatted in LongNameHandler. // This code path is used by DecimalFormat via CurrencyPluralInfo. U_ASSERT(plural != StandardPlural::Form::COUNT); - UErrorCode status = U_ZERO_ERROR; - UBool isChoiceFormat = FALSE; - int32_t symbolLen = 0; - const char16_t *symbol = ucurr_getPluralName( - currencyCode, - symbols->getLocale().getName(), - &isChoiceFormat, - StandardPlural::getKeyword(plural), - &symbolLen, - &status); - return UnicodeString(symbol, symbolLen); - } + return currencySymbols->getPluralName(plural, localStatus); case AffixPatternType::TYPE_CURRENCY_QUAD: return UnicodeString(u"\uFFFD"); case AffixPatternType::TYPE_CURRENCY_QUINT: @@ -293,79 +291,6 @@ UnicodeString MutablePatternModifier::getSymbol(AffixPatternType type) const { } } -/** This method contains the heart of the logic for rendering LDML affix strings. */ -void MutablePatternModifier::enterCharSequenceMode(bool isPrefix) { - U_ASSERT(!inCharSequenceMode); - inCharSequenceMode = true; - - // Should the output render '+' where '-' would normally appear in the pattern? - plusReplacesMinusSign = signum != -1 - && (signDisplay == UNUM_SIGN_ALWAYS - || signDisplay == UNUM_SIGN_ACCOUNTING_ALWAYS - || (signum == 1 - && (signDisplay == UNUM_SIGN_EXCEPT_ZERO - || signDisplay == UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO))) - && patternInfo->positiveHasPlusSign() == false; - - // Should we use the affix from the negative subpattern? (If not, we will use the positive subpattern.) - bool useNegativeAffixPattern = patternInfo->hasNegativeSubpattern() && ( - signum == -1 || (patternInfo->negativeHasMinusSign() && plusReplacesMinusSign)); - - // Resolve the flags for the affix pattern. - fFlags = 0; - if (useNegativeAffixPattern) { - fFlags |= AffixPatternProvider::AFFIX_NEGATIVE_SUBPATTERN; - } - if (isPrefix) { - fFlags |= AffixPatternProvider::AFFIX_PREFIX; - } - if (plural != StandardPlural::Form::COUNT) { - U_ASSERT(plural == (AffixPatternProvider::AFFIX_PLURAL_MASK & plural)); - fFlags |= plural; - } - - // Should we prepend a sign to the pattern? - if (!isPrefix || useNegativeAffixPattern) { - prependSign = false; - } else if (signum == -1) { - prependSign = signDisplay != UNUM_SIGN_NEVER; - } else { - prependSign = plusReplacesMinusSign; - } - - // Finally, compute the length of the affix pattern. - fLength = patternInfo->length(fFlags) + (prependSign ? 1 : 0); -} - -void MutablePatternModifier::exitCharSequenceMode() { - U_ASSERT(inCharSequenceMode); - inCharSequenceMode = false; -} - -int32_t MutablePatternModifier::length() const { - U_ASSERT(inCharSequenceMode); - return fLength; -} - -char16_t MutablePatternModifier::charAt(int32_t index) const { - U_ASSERT(inCharSequenceMode); - char16_t candidate; - if (prependSign && index == 0) { - candidate = u'-'; - } else if (prependSign) { - candidate = patternInfo->charAt(fFlags, index - 1); - } else { - candidate = patternInfo->charAt(fFlags, index); - } - if (plusReplacesMinusSign && candidate == u'-') { - return u'+'; - } - if (perMilleReplacesPercent && candidate == u'%') { - return u'‰'; - } - return candidate; -} - UnicodeString MutablePatternModifier::toUnicodeString() const { // Never called by AffixUtils U_ASSERT(false); diff --git a/deps/icu-small/source/i18n/number_patternmodifier.h b/deps/icu-small/source/i18n/number_patternmodifier.h index 9c8b95f7764436..f1359bd5747d89 100644 --- a/deps/icu-small/source/i18n/number_patternmodifier.h +++ b/deps/icu-small/source/i18n/number_patternmodifier.h @@ -3,7 +3,7 @@ #include "unicode/utypes.h" -#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT +#if !UCONFIG_NO_FORMATTING #ifndef __NUMBER_PATTERNMODIFIER_H__ #define __NUMBER_PATTERNMODIFIER_H__ @@ -13,6 +13,7 @@ #include "number_types.h" #include "number_modifiers.h" #include "number_utils.h" +#include "number_currencysymbols.h" U_NAMESPACE_BEGIN @@ -35,20 +36,23 @@ class MutablePatternModifier; // Exported as U_I18N_API because it is needed for the unit test PatternModifierTest class U_I18N_API ImmutablePatternModifier : public MicroPropsGenerator, public UMemory { public: - ~ImmutablePatternModifier() U_OVERRIDE = default; + ~ImmutablePatternModifier() U_OVERRIDE = default; - void processQuantity(DecimalQuantity &, MicroProps µs, UErrorCode &status) const U_OVERRIDE; + void processQuantity(DecimalQuantity&, MicroProps& micros, UErrorCode& status) const U_OVERRIDE; + + void applyToMicros(MicroProps& micros, DecimalQuantity& quantity) const; - void applyToMicros(MicroProps µs, DecimalQuantity &quantity) const; + const Modifier* getModifier(int8_t signum, StandardPlural::Form plural) const; private: - ImmutablePatternModifier(ParameterizedModifier *pm, const PluralRules *rules, const MicroPropsGenerator *parent); + ImmutablePatternModifier(ParameterizedModifier* pm, const PluralRules* rules, + const MicroPropsGenerator* parent); const LocalPointer pm; - const PluralRules *rules; - const MicroPropsGenerator *parent; + const PluralRules* rules; + const MicroPropsGenerator* parent; - friend class MutablePatternModifier; + friend class MutablePatternModifier; }; /** @@ -74,7 +78,6 @@ class U_I18N_API MutablePatternModifier : public MicroPropsGenerator, public Modifier, public SymbolProvider, - public CharSequence, public UMemory { public: @@ -110,17 +113,16 @@ class U_I18N_API MutablePatternModifier * * @param symbols * The desired instance of DecimalFormatSymbols. - * @param currency - * The currency to be used when substituting currency values into the affixes. + * @param currencySymbols + * The currency symbols to be used when substituting currency values into the affixes. * @param unitWidth * The width used to render currencies. * @param rules * Required if the triple currency sign, "¤¤¤", appears in the pattern, which can be determined from the * convenience method {@link #needsPlurals()}. */ - void - setSymbols(const DecimalFormatSymbols *symbols, const CurrencyUnit ¤cy, UNumberUnitWidth unitWidth, - const PluralRules *rules); + void setSymbols(const DecimalFormatSymbols* symbols, const CurrencySymbols* currencySymbols, + UNumberUnitWidth unitWidth, const PluralRules* rules); /** * Sets attributes of the current number being processed. @@ -187,13 +189,7 @@ class U_I18N_API MutablePatternModifier */ UnicodeString getSymbol(AffixPatternType type) const U_OVERRIDE; - int32_t length() const U_OVERRIDE; - - char16_t charAt(int32_t index) const U_OVERRIDE; - - // Use default implementation of codePointAt - - UnicodeString toUnicodeString() const U_OVERRIDE; + UnicodeString toUnicodeString() const; private: // Modifier details (initialized in constructor) @@ -207,7 +203,7 @@ class U_I18N_API MutablePatternModifier // Symbol details (initialized in setSymbols) const DecimalFormatSymbols *symbols; UNumberUnitWidth unitWidth; - char16_t currencyCode[4]; + const CurrencySymbols *currencySymbols; const PluralRules *rules; // Number details (initialized in setNumberProperties) @@ -217,12 +213,8 @@ class U_I18N_API MutablePatternModifier // QuantityChain details (initialized in addToChain) const MicroPropsGenerator *parent; - // Transient CharSequence fields (initialized in enterCharSequenceMode) - bool inCharSequenceMode = false; - int32_t fFlags; - int32_t fLength; - bool prependSign; - bool plusReplacesMinusSign; + // Transient fields for rendering + UnicodeString currentAffix; /** * Uses the current properties to create a single {@link ConstantMultiFieldModifier} with currency spacing support @@ -244,9 +236,7 @@ class U_I18N_API MutablePatternModifier int32_t insertSuffix(NumberStringBuilder &sb, int position, UErrorCode &status); - void enterCharSequenceMode(bool isPrefix); - - void exitCharSequenceMode(); + void prepareAffix(bool isPrefix); }; diff --git a/deps/icu-small/source/i18n/number_patternstring.cpp b/deps/icu-small/source/i18n/number_patternstring.cpp index 20178824b0e20a..63195eed989e54 100644 --- a/deps/icu-small/source/i18n/number_patternstring.cpp +++ b/deps/icu-small/source/i18n/number_patternstring.cpp @@ -3,36 +3,51 @@ #include "unicode/utypes.h" -#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT +#if !UCONFIG_NO_FORMATTING + +// Allow implicit conversion from char16_t* to UnicodeString for this file: +// Helpful in toString methods and elsewhere. +#define UNISTR_FROM_STRING_EXPLICIT +#define UNISTR_FROM_CHAR_EXPLICIT #include "uassert.h" #include "number_patternstring.h" #include "unicode/utf16.h" #include "number_utils.h" +#include "number_roundingutils.h" using namespace icu; using namespace icu::number; using namespace icu::number::impl; -void PatternParser::parseToPatternInfo(const UnicodeString& patternString, ParsedPatternInfo& patternInfo, UErrorCode &status) { + +void PatternParser::parseToPatternInfo(const UnicodeString& patternString, ParsedPatternInfo& patternInfo, + UErrorCode& status) { patternInfo.consumePattern(patternString, status); } DecimalFormatProperties PatternParser::parseToProperties(const UnicodeString& pattern, IgnoreRounding ignoreRounding, - UErrorCode &status) { + UErrorCode& status) { DecimalFormatProperties properties; parseToExistingPropertiesImpl(pattern, properties, ignoreRounding, status); return properties; } -void PatternParser::parseToExistingProperties(const UnicodeString& pattern, DecimalFormatProperties& properties, - IgnoreRounding ignoreRounding, UErrorCode &status) { +DecimalFormatProperties PatternParser::parseToProperties(const UnicodeString& pattern, + UErrorCode& status) { + return parseToProperties(pattern, IGNORE_ROUNDING_NEVER, status); +} + +void +PatternParser::parseToExistingProperties(const UnicodeString& pattern, DecimalFormatProperties& properties, + IgnoreRounding ignoreRounding, UErrorCode& status) { parseToExistingPropertiesImpl(pattern, properties, ignoreRounding, status); } + char16_t ParsedPatternInfo::charAt(int32_t flags, int32_t index) const { - const Endpoints &endpoints = getEndpoints(flags); + const Endpoints& endpoints = getEndpoints(flags); if (index < 0 || index >= endpoints.end - endpoints.start) { U_ASSERT(false); } @@ -43,12 +58,12 @@ int32_t ParsedPatternInfo::length(int32_t flags) const { return getLengthFromEndpoints(getEndpoints(flags)); } -int32_t ParsedPatternInfo::getLengthFromEndpoints(const Endpoints &endpoints) { +int32_t ParsedPatternInfo::getLengthFromEndpoints(const Endpoints& endpoints) { return endpoints.end - endpoints.start; } UnicodeString ParsedPatternInfo::getString(int32_t flags) const { - const Endpoints &endpoints = getEndpoints(flags); + const Endpoints& endpoints = getEndpoints(flags); if (endpoints.start == endpoints.end) { return UnicodeString(); } @@ -56,7 +71,7 @@ UnicodeString ParsedPatternInfo::getString(int32_t flags) const { return UnicodeString(pattern, endpoints.start, endpoints.end - endpoints.start); } -const Endpoints &ParsedPatternInfo::getEndpoints(int32_t flags) const { +const Endpoints& ParsedPatternInfo::getEndpoints(int32_t flags) const { bool prefix = (flags & AFFIX_PREFIX) != 0; bool isNegative = (flags & AFFIX_NEGATIVE_SUBPATTERN) != 0; bool padding = (flags & AFFIX_PADDING) != 0; @@ -91,8 +106,8 @@ bool ParsedPatternInfo::hasCurrencySign() const { return positive.hasCurrencySign || (fHasNegativeSubpattern && negative.hasCurrencySign); } -bool ParsedPatternInfo::containsSymbolType(AffixPatternType type, UErrorCode &status) const { - return AffixUtils::containsType(UnicodeStringCharSequence(pattern), type, status); +bool ParsedPatternInfo::containsSymbolType(AffixPatternType type, UErrorCode& status) const { + return AffixUtils::containsType(pattern, type, status); } bool ParsedPatternInfo::hasBody() const { @@ -117,10 +132,14 @@ UChar32 ParsedPatternInfo::ParserState::next() { return codePoint; } -void ParsedPatternInfo::consumePattern(const UnicodeString& patternString, UErrorCode &status) { +void ParsedPatternInfo::consumePattern(const UnicodeString& patternString, UErrorCode& status) { if (U_FAILURE(status)) { return; } this->pattern = patternString; + // This class is not intended for writing twice! + // Use move assignment to overwrite instead. + U_ASSERT(state.offset == 0); + // pattern := subpattern (';' subpattern)? currentSubpattern = &positive; consumeSubpattern(status); @@ -141,7 +160,7 @@ void ParsedPatternInfo::consumePattern(const UnicodeString& patternString, UErro } } -void ParsedPatternInfo::consumeSubpattern(UErrorCode &status) { +void ParsedPatternInfo::consumeSubpattern(UErrorCode& status) { // subpattern := literals? number exponent? literals? consumePadding(PadPosition::UNUM_PAD_BEFORE_PREFIX, status); if (U_FAILURE(status)) { return; } @@ -161,23 +180,24 @@ void ParsedPatternInfo::consumeSubpattern(UErrorCode &status) { if (U_FAILURE(status)) { return; } } -void ParsedPatternInfo::consumePadding(PadPosition paddingLocation, UErrorCode &status) { +void ParsedPatternInfo::consumePadding(PadPosition paddingLocation, UErrorCode& status) { if (state.peek() != u'*') { return; } - if (!currentSubpattern->paddingLocation.isNull()) { + if (currentSubpattern->hasPadding) { state.toParseException(u"Cannot have multiple pad specifiers"); status = U_MULTIPLE_PAD_SPECIFIERS; return; } currentSubpattern->paddingLocation = paddingLocation; + currentSubpattern->hasPadding = true; state.next(); // consume the '*' currentSubpattern->paddingEndpoints.start = state.offset; consumeLiteral(status); currentSubpattern->paddingEndpoints.end = state.offset; } -void ParsedPatternInfo::consumeAffix(Endpoints &endpoints, UErrorCode &status) { +void ParsedPatternInfo::consumeAffix(Endpoints& endpoints, UErrorCode& status) { // literals := { literal } endpoints.start = state.offset; while (true) { @@ -233,7 +253,7 @@ void ParsedPatternInfo::consumeAffix(Endpoints &endpoints, UErrorCode &status) { endpoints.end = state.offset; } -void ParsedPatternInfo::consumeLiteral(UErrorCode &status) { +void ParsedPatternInfo::consumeLiteral(UErrorCode& status) { if (state.peek() == -1) { state.toParseException(u"Expected unquoted literal but found EOL"); status = U_PATTERN_SYNTAX_ERROR; @@ -256,7 +276,7 @@ void ParsedPatternInfo::consumeLiteral(UErrorCode &status) { } } -void ParsedPatternInfo::consumeFormat(UErrorCode &status) { +void ParsedPatternInfo::consumeFormat(UErrorCode& status) { consumeIntegerFormat(status); if (U_FAILURE(status)) { return; } if (state.peek() == u'.') { @@ -268,9 +288,9 @@ void ParsedPatternInfo::consumeFormat(UErrorCode &status) { } } -void ParsedPatternInfo::consumeIntegerFormat(UErrorCode &status) { +void ParsedPatternInfo::consumeIntegerFormat(UErrorCode& status) { // Convenience reference: - ParsedSubpatternInfo &result = *currentSubpattern; + ParsedSubpatternInfo& result = *currentSubpattern; while (true) { switch (state.peek()) { @@ -359,9 +379,9 @@ void ParsedPatternInfo::consumeIntegerFormat(UErrorCode &status) { } } -void ParsedPatternInfo::consumeFractionFormat(UErrorCode &status) { +void ParsedPatternInfo::consumeFractionFormat(UErrorCode& status) { // Convenience reference: - ParsedSubpatternInfo &result = *currentSubpattern; + ParsedSubpatternInfo& result = *currentSubpattern; int32_t zeroCounter = 0; while (true) { @@ -407,9 +427,9 @@ void ParsedPatternInfo::consumeFractionFormat(UErrorCode &status) { } } -void ParsedPatternInfo::consumeExponent(UErrorCode &status) { +void ParsedPatternInfo::consumeExponent(UErrorCode& status) { // Convenience reference: - ParsedSubpatternInfo &result = *currentSubpattern; + ParsedSubpatternInfo& result = *currentSubpattern; if (state.peek() != u'E') { return; @@ -437,9 +457,9 @@ void ParsedPatternInfo::consumeExponent(UErrorCode &status) { /// END RECURSIVE DESCENT PARSER IMPLEMENTATION /// /////////////////////////////////////////////////// -void -PatternParser::parseToExistingPropertiesImpl(const UnicodeString& pattern, DecimalFormatProperties &properties, - IgnoreRounding ignoreRounding, UErrorCode &status) { +void PatternParser::parseToExistingPropertiesImpl(const UnicodeString& pattern, + DecimalFormatProperties& properties, + IgnoreRounding ignoreRounding, UErrorCode& status) { if (pattern.length() == 0) { // Backwards compatibility requires that we reset to the default values. // TODO: Only overwrite the properties that "saveToProperties" normally touches? @@ -453,13 +473,13 @@ PatternParser::parseToExistingPropertiesImpl(const UnicodeString& pattern, Decim patternInfoToProperties(properties, patternInfo, ignoreRounding, status); } -void PatternParser::patternInfoToProperties(DecimalFormatProperties &properties, - ParsedPatternInfo& patternInfo, - IgnoreRounding _ignoreRounding, UErrorCode &status) { +void +PatternParser::patternInfoToProperties(DecimalFormatProperties& properties, ParsedPatternInfo& patternInfo, + IgnoreRounding _ignoreRounding, UErrorCode& status) { // Translate from PatternParseResult to Properties. // Note that most data from "negative" is ignored per the specification of DecimalFormat. - const ParsedSubpatternInfo &positive = patternInfo.positive; + const ParsedSubpatternInfo& positive = patternInfo.positive; bool ignoreRounding; if (_ignoreRounding == IGNORE_ROUNDING_NEVER) { @@ -477,8 +497,10 @@ void PatternParser::patternInfoToProperties(DecimalFormatProperties &properties, auto grouping3 = static_cast ((positive.groupingSizes >> 32) & 0xffff); if (grouping2 != -1) { properties.groupingSize = grouping1; + properties.groupingUsed = true; } else { properties.groupingSize = -1; + properties.groupingUsed = false; } if (grouping3 != -1) { properties.secondaryGroupingSize = grouping2; @@ -508,8 +530,7 @@ void PatternParser::patternInfoToProperties(DecimalFormatProperties &properties, properties.maximumFractionDigits = -1; properties.roundingIncrement = 0.0; properties.minimumSignificantDigits = positive.integerAtSigns; - properties.maximumSignificantDigits = - positive.integerAtSigns + positive.integerTrailingHashSigns; + properties.maximumSignificantDigits = positive.integerAtSigns + positive.integerTrailingHashSigns; } else if (!positive.rounding.isZero()) { if (!ignoreRounding) { properties.minimumFractionDigits = minFrac; @@ -568,11 +589,11 @@ void PatternParser::patternInfoToProperties(DecimalFormatProperties &properties, UnicodeString posSuffix = patternInfo.getString(0); // Padding settings - if (!positive.paddingLocation.isNull()) { + if (positive.hasPadding) { // The width of the positive prefix and suffix templates are included in the padding - int paddingWidth = - positive.widthExceptAffixes + AffixUtils::estimateLength(UnicodeStringCharSequence(posPrefix), status) + - AffixUtils::estimateLength(UnicodeStringCharSequence(posSuffix), status); + int paddingWidth = positive.widthExceptAffixes + + AffixUtils::estimateLength(posPrefix, status) + + AffixUtils::estimateLength(posSuffix, status); properties.formatWidth = paddingWidth; UnicodeString rawPaddingString = patternInfo.getString(AffixPatternProvider::AFFIX_PADDING); if (rawPaddingString.length() == 1) { @@ -622,8 +643,8 @@ void PatternParser::patternInfoToProperties(DecimalFormatProperties &properties, /// End PatternStringParser.java; begin PatternStringUtils.java /// /////////////////////////////////////////////////////////////////// -UnicodeString PatternStringUtils::propertiesToPatternString(const DecimalFormatProperties &properties, - UErrorCode &status) { +UnicodeString PatternStringUtils::propertiesToPatternString(const DecimalFormatProperties& properties, + UErrorCode& status) { UnicodeString sb; // Convenience references @@ -656,7 +677,7 @@ UnicodeString PatternStringUtils::propertiesToPatternString(const DecimalFormatP if (!ppp.isBogus()) { sb.append(ppp); } - sb.append(AffixUtils::escape(UnicodeStringCharSequence(pp))); + sb.append(AffixUtils::escape(pp)); int afterPrefixPos = sb.length(); // Figure out the grouping sizes. @@ -695,11 +716,11 @@ UnicodeString PatternStringUtils::propertiesToPatternString(const DecimalFormatP } } else if (roundingInterval != 0.0) { // Rounding Interval. - digitsStringScale = minFrac; + digitsStringScale = -roundingutils::doubleFractionLength(roundingInterval); // TODO: Check for DoS here? DecimalQuantity incrementQuantity; incrementQuantity.setToDouble(roundingInterval); - incrementQuantity.adjustMagnitude(minFrac); + incrementQuantity.adjustMagnitude(-digitsStringScale); incrementQuantity.roundToMagnitude(0, kDefaultMode, status); UnicodeString str = incrementQuantity.toPlainString(); if (str.charAt(0) == u'-') { @@ -753,7 +774,7 @@ UnicodeString PatternStringUtils::propertiesToPatternString(const DecimalFormatP if (!psp.isBogus()) { sb.append(psp); } - sb.append(AffixUtils::escape(UnicodeStringCharSequence(ps))); + sb.append(AffixUtils::escape(ps)); // Resolve Padding if (paddingWidth != -1 && !paddingLocation.isNull()) { @@ -795,22 +816,25 @@ UnicodeString PatternStringUtils::propertiesToPatternString(const DecimalFormatP if (!npp.isBogus()) { sb.append(npp); } - sb.append(AffixUtils::escape(UnicodeStringCharSequence(np))); + sb.append(AffixUtils::escape(np)); // Copy the positive digit format into the negative. // This is optional; the pattern is the same as if '#' were appended here instead. - sb.append(sb, afterPrefixPos, beforeSuffixPos); + // NOTE: It is not safe to append the UnicodeString to itself, so we need to copy. + // See http://bugs.icu-project.org/trac/ticket/13707 + UnicodeString copy(sb); + sb.append(copy, afterPrefixPos, beforeSuffixPos - afterPrefixPos); if (!nsp.isBogus()) { sb.append(nsp); } - sb.append(AffixUtils::escape(UnicodeStringCharSequence(ns))); + sb.append(AffixUtils::escape(ns)); } return sb; } int PatternStringUtils::escapePaddingString(UnicodeString input, UnicodeString& output, int startIndex, - UErrorCode &status) { - (void)status; + UErrorCode& status) { + (void) status; if (input.length() == 0) { input.setTo(kFallbackPaddingString, -1); } @@ -840,4 +864,207 @@ int PatternStringUtils::escapePaddingString(UnicodeString input, UnicodeString& return output.length() - startLength; } +UnicodeString +PatternStringUtils::convertLocalized(const UnicodeString& input, const DecimalFormatSymbols& symbols, + bool toLocalized, UErrorCode& status) { + // Construct a table of strings to be converted between localized and standard. + static constexpr int32_t LEN = 21; + UnicodeString table[LEN][2]; + int standIdx = toLocalized ? 0 : 1; + int localIdx = toLocalized ? 1 : 0; + table[0][standIdx] = u"%"; + table[0][localIdx] = symbols.getConstSymbol(DecimalFormatSymbols::kPercentSymbol); + table[1][standIdx] = u"‰"; + table[1][localIdx] = symbols.getConstSymbol(DecimalFormatSymbols::kPerMillSymbol); + table[2][standIdx] = u"."; + table[2][localIdx] = symbols.getConstSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol); + table[3][standIdx] = u","; + table[3][localIdx] = symbols.getConstSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol); + table[4][standIdx] = u"-"; + table[4][localIdx] = symbols.getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol); + table[5][standIdx] = u"+"; + table[5][localIdx] = symbols.getConstSymbol(DecimalFormatSymbols::kPlusSignSymbol); + table[6][standIdx] = u";"; + table[6][localIdx] = symbols.getConstSymbol(DecimalFormatSymbols::kPatternSeparatorSymbol); + table[7][standIdx] = u"@"; + table[7][localIdx] = symbols.getConstSymbol(DecimalFormatSymbols::kSignificantDigitSymbol); + table[8][standIdx] = u"E"; + table[8][localIdx] = symbols.getConstSymbol(DecimalFormatSymbols::kExponentialSymbol); + table[9][standIdx] = u"*"; + table[9][localIdx] = symbols.getConstSymbol(DecimalFormatSymbols::kPadEscapeSymbol); + table[10][standIdx] = u"#"; + table[10][localIdx] = symbols.getConstSymbol(DecimalFormatSymbols::kDigitSymbol); + for (int i = 0; i < 10; i++) { + table[11 + i][standIdx] = u'0' + i; + table[11 + i][localIdx] = symbols.getConstDigitSymbol(i); + } + + // Special case: quotes are NOT allowed to be in any localIdx strings. + // Substitute them with '’' instead. + for (int32_t i = 0; i < LEN; i++) { + table[i][localIdx].findAndReplace(u'\'', u'’'); + } + + // Iterate through the string and convert. + // State table: + // 0 => base state + // 1 => first char inside a quoted sequence in input and output string + // 2 => inside a quoted sequence in input and output string + // 3 => first char after a close quote in input string; + // close quote still needs to be written to output string + // 4 => base state in input string; inside quoted sequence in output string + // 5 => first char inside a quoted sequence in input string; + // inside quoted sequence in output string + UnicodeString result; + int state = 0; + for (int offset = 0; offset < input.length(); offset++) { + UChar ch = input.charAt(offset); + + // Handle a quote character (state shift) + if (ch == u'\'') { + if (state == 0) { + result.append(u'\''); + state = 1; + continue; + } else if (state == 1) { + result.append(u'\''); + state = 0; + continue; + } else if (state == 2) { + state = 3; + continue; + } else if (state == 3) { + result.append(u'\''); + result.append(u'\''); + state = 1; + continue; + } else if (state == 4) { + state = 5; + continue; + } else { + U_ASSERT(state == 5); + result.append(u'\''); + result.append(u'\''); + state = 4; + continue; + } + } + + if (state == 0 || state == 3 || state == 4) { + for (auto& pair : table) { + // Perform a greedy match on this symbol string + UnicodeString temp = input.tempSubString(offset, pair[0].length()); + if (temp == pair[0]) { + // Skip ahead past this region for the next iteration + offset += pair[0].length() - 1; + if (state == 3 || state == 4) { + result.append(u'\''); + state = 0; + } + result.append(pair[1]); + goto continue_outer; + } + } + // No replacement found. Check if a special quote is necessary + for (auto& pair : table) { + UnicodeString temp = input.tempSubString(offset, pair[1].length()); + if (temp == pair[1]) { + if (state == 0) { + result.append(u'\''); + state = 4; + } + result.append(ch); + goto continue_outer; + } + } + // Still nothing. Copy the char verbatim. (Add a close quote if necessary) + if (state == 3 || state == 4) { + result.append(u'\''); + state = 0; + } + result.append(ch); + } else { + U_ASSERT(state == 1 || state == 2 || state == 5); + result.append(ch); + state = 2; + } + continue_outer:; + } + // Resolve final quotes + if (state == 3 || state == 4) { + result.append(u'\''); + state = 0; + } + if (state != 0) { + // Malformed localized pattern: unterminated quote + status = U_PATTERN_SYNTAX_ERROR; + } + return result; +} + +void PatternStringUtils::patternInfoToStringBuilder(const AffixPatternProvider& patternInfo, bool isPrefix, + int8_t signum, UNumberSignDisplay signDisplay, + StandardPlural::Form plural, + bool perMilleReplacesPercent, UnicodeString& output) { + + // Should the output render '+' where '-' would normally appear in the pattern? + bool plusReplacesMinusSign = signum != -1 && ( + signDisplay == UNUM_SIGN_ALWAYS || signDisplay == UNUM_SIGN_ACCOUNTING_ALWAYS || ( + signum == 1 && ( + signDisplay == UNUM_SIGN_EXCEPT_ZERO || + signDisplay == UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO))) && + patternInfo.positiveHasPlusSign() == false; + + // Should we use the affix from the negative subpattern? (If not, we will use the positive + // subpattern.) + bool useNegativeAffixPattern = patternInfo.hasNegativeSubpattern() && ( + signum == -1 || (patternInfo.negativeHasMinusSign() && plusReplacesMinusSign)); + + // Resolve the flags for the affix pattern. + int flags = 0; + if (useNegativeAffixPattern) { + flags |= AffixPatternProvider::AFFIX_NEGATIVE_SUBPATTERN; + } + if (isPrefix) { + flags |= AffixPatternProvider::AFFIX_PREFIX; + } + if (plural != StandardPlural::Form::COUNT) { + U_ASSERT(plural == (AffixPatternProvider::AFFIX_PLURAL_MASK & plural)); + flags |= plural; + } + + // Should we prepend a sign to the pattern? + bool prependSign; + if (!isPrefix || useNegativeAffixPattern) { + prependSign = false; + } else if (signum == -1) { + prependSign = signDisplay != UNUM_SIGN_NEVER; + } else { + prependSign = plusReplacesMinusSign; + } + + // Compute the length of the affix pattern. + int length = patternInfo.length(flags) + (prependSign ? 1 : 0); + + // Finally, set the result into the StringBuilder. + output.remove(); + for (int index = 0; index < length; index++) { + char16_t candidate; + if (prependSign && index == 0) { + candidate = u'-'; + } else if (prependSign) { + candidate = patternInfo.charAt(flags, index - 1); + } else { + candidate = patternInfo.charAt(flags, index); + } + if (plusReplacesMinusSign && candidate == u'-') { + candidate = u'+'; + } + if (perMilleReplacesPercent && candidate == u'%') { + candidate = u'‰'; + } + output.append(candidate); + } +} + #endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/number_patternstring.h b/deps/icu-small/source/i18n/number_patternstring.h index ec44290d66397c..91e120c16a1a84 100644 --- a/deps/icu-small/source/i18n/number_patternstring.h +++ b/deps/icu-small/source/i18n/number_patternstring.h @@ -3,7 +3,7 @@ #include "unicode/utypes.h" -#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT +#if !UCONFIG_NO_FORMATTING #ifndef __NUMBER_PATTERNSTRING_H__ #define __NUMBER_PATTERNSTRING_H__ @@ -30,7 +30,7 @@ struct U_I18N_API Endpoints { // Exported as U_I18N_API because it is a public member field of exported ParsedPatternInfo struct U_I18N_API ParsedSubpatternInfo { - int64_t groupingSizes = 0x0000ffffffff0000L; + uint64_t groupingSizes = 0x0000ffffffff0000L; int32_t integerLeadingHashSigns = 0; int32_t integerTrailingHashSigns = 0; int32_t integerNumerals = 0; @@ -41,7 +41,9 @@ struct U_I18N_API ParsedSubpatternInfo { int32_t fractionTotal = 0; // for convenience bool hasDecimal = false; int32_t widthExceptAffixes = 0; - NullableValue paddingLocation; + // Note: NullableValue causes issues here with std::move. + bool hasPadding = false; + UNumberFormatPadPosition paddingLocation = UNUM_PAD_BEFORE_PREFIX; DecimalQuantity rounding; bool exponentHasPlusSign = false; int32_t exponentZeros = 0; @@ -62,17 +64,21 @@ struct U_I18N_API ParsedPatternInfo : public AffixPatternProvider, public UMemor ParsedSubpatternInfo positive; ParsedSubpatternInfo negative; - ParsedPatternInfo() : state(this->pattern), currentSubpattern(nullptr) {} + ParsedPatternInfo() + : state(this->pattern), currentSubpattern(nullptr) {} ~ParsedPatternInfo() U_OVERRIDE = default; - static int32_t getLengthFromEndpoints(const Endpoints &endpoints); + // Need to declare this explicitly because of the destructor + ParsedPatternInfo& operator=(ParsedPatternInfo&& src) U_NOEXCEPT = default; + + static int32_t getLengthFromEndpoints(const Endpoints& endpoints); char16_t charAt(int32_t flags, int32_t index) const U_OVERRIDE; int32_t length(int32_t flags) const U_OVERRIDE; - UnicodeString getString(int32_t flags) const; + UnicodeString getString(int32_t flags) const U_OVERRIDE; bool positiveHasPlusSign() const U_OVERRIDE; @@ -82,16 +88,24 @@ struct U_I18N_API ParsedPatternInfo : public AffixPatternProvider, public UMemor bool hasCurrencySign() const U_OVERRIDE; - bool containsSymbolType(AffixPatternType type, UErrorCode &status) const U_OVERRIDE; + bool containsSymbolType(AffixPatternType type, UErrorCode& status) const U_OVERRIDE; bool hasBody() const U_OVERRIDE; private: struct U_I18N_API ParserState { - const UnicodeString &pattern; // reference to the parent + const UnicodeString& pattern; // reference to the parent int32_t offset = 0; - explicit ParserState(const UnicodeString &_pattern) : pattern(_pattern) {}; + explicit ParserState(const UnicodeString& _pattern) + : pattern(_pattern) {}; + + ParserState& operator=(ParserState&& src) U_NOEXCEPT { + // Leave pattern reference alone; it will continue to point to the same place in memory, + // which gets overwritten by ParsedPatternInfo's implicit move assignment. + offset = src.offset; + return *this; + } UChar32 peek(); @@ -99,45 +113,48 @@ struct U_I18N_API ParsedPatternInfo : public AffixPatternProvider, public UMemor // TODO: We don't currently do anything with the message string. // This method is here as a shell for Java compatibility. - inline void toParseException(const char16_t *message) { (void)message; } - } - state; + inline void toParseException(const char16_t* message) { (void) message; } + } state; // NOTE: In Java, these are written as pure functions. // In C++, they're written as methods. // The behavior is the same. // Mutable transient pointer: - ParsedSubpatternInfo *currentSubpattern; + ParsedSubpatternInfo* currentSubpattern; // In Java, "negative == null" tells us whether or not we had a negative subpattern. // In C++, we need to remember in another boolean. bool fHasNegativeSubpattern = false; - const Endpoints &getEndpoints(int32_t flags) const; + const Endpoints& getEndpoints(int32_t flags) const; /** Run the recursive descent parser. */ - void consumePattern(const UnicodeString &patternString, UErrorCode &status); + void consumePattern(const UnicodeString& patternString, UErrorCode& status); - void consumeSubpattern(UErrorCode &status); + void consumeSubpattern(UErrorCode& status); - void consumePadding(PadPosition paddingLocation, UErrorCode &status); + void consumePadding(PadPosition paddingLocation, UErrorCode& status); - void consumeAffix(Endpoints &endpoints, UErrorCode &status); + void consumeAffix(Endpoints& endpoints, UErrorCode& status); - void consumeLiteral(UErrorCode &status); + void consumeLiteral(UErrorCode& status); - void consumeFormat(UErrorCode &status); + void consumeFormat(UErrorCode& status); - void consumeIntegerFormat(UErrorCode &status); + void consumeIntegerFormat(UErrorCode& status); - void consumeFractionFormat(UErrorCode &status); + void consumeFractionFormat(UErrorCode& status); - void consumeExponent(UErrorCode &status); + void consumeExponent(UErrorCode& status); friend class PatternParser; }; +enum IgnoreRounding { + IGNORE_ROUNDING_NEVER = 0, IGNORE_ROUNDING_IF_CURRENCY = 1, IGNORE_ROUNDING_ALWAYS = 2 +}; + class U_I18N_API PatternParser { public: /** @@ -153,12 +170,8 @@ class U_I18N_API PatternParser { * The LDML decimal format pattern (Excel-style pattern) to parse. * @return The results of the parse. */ - static void - parseToPatternInfo(const UnicodeString& patternString, ParsedPatternInfo &patternInfo, UErrorCode &status); - - enum IgnoreRounding { - IGNORE_ROUNDING_NEVER = 0, IGNORE_ROUNDING_IF_CURRENCY = 1, IGNORE_ROUNDING_ALWAYS = 2 - }; + static void parseToPatternInfo(const UnicodeString& patternString, ParsedPatternInfo& patternInfo, + UErrorCode& status); /** * Parses a pattern string into a new property bag. @@ -173,8 +186,10 @@ class U_I18N_API PatternParser { * @throws IllegalArgumentException * If there is a syntax error in the pattern string. */ - static DecimalFormatProperties - parseToProperties(const UnicodeString& pattern, IgnoreRounding ignoreRounding, UErrorCode &status); + static DecimalFormatProperties parseToProperties(const UnicodeString& pattern, + IgnoreRounding ignoreRounding, UErrorCode& status); + + static DecimalFormatProperties parseToProperties(const UnicodeString& pattern, UErrorCode& status); /** * Parses a pattern string into an existing property bag. All properties that can be encoded into a pattern string @@ -190,18 +205,19 @@ class U_I18N_API PatternParser { * @throws IllegalArgumentException * If there was a syntax error in the pattern string. */ - static void parseToExistingProperties(const UnicodeString& pattern, DecimalFormatProperties& properties, - IgnoreRounding ignoreRounding, UErrorCode &status); + static void parseToExistingProperties(const UnicodeString& pattern, + DecimalFormatProperties& properties, + IgnoreRounding ignoreRounding, UErrorCode& status); private: - static void - parseToExistingPropertiesImpl(const UnicodeString& pattern, DecimalFormatProperties &properties, - IgnoreRounding ignoreRounding, UErrorCode &status); + static void parseToExistingPropertiesImpl(const UnicodeString& pattern, + DecimalFormatProperties& properties, + IgnoreRounding ignoreRounding, UErrorCode& status); /** Finalizes the temporary data stored in the ParsedPatternInfo to the Properties. */ - static void - patternInfoToProperties(DecimalFormatProperties &properties, ParsedPatternInfo& patternInfo, - IgnoreRounding _ignoreRounding, UErrorCode &status); + static void patternInfoToProperties(DecimalFormatProperties& properties, + ParsedPatternInfo& patternInfo, IgnoreRounding _ignoreRounding, + UErrorCode& status); }; class U_I18N_API PatternStringUtils { @@ -217,8 +233,8 @@ class U_I18N_API PatternStringUtils { * The property bag to serialize. * @return A pattern string approximately serializing the property bag. */ - static UnicodeString - propertiesToPatternString(const DecimalFormatProperties &properties, UErrorCode &status); + static UnicodeString propertiesToPatternString(const DecimalFormatProperties& properties, + UErrorCode& status); /** @@ -248,14 +264,23 @@ class U_I18N_API PatternStringUtils { * notation. * @return The pattern expressed in the other notation. */ - static UnicodeString - convertLocalized(UnicodeString input, DecimalFormatSymbols symbols, bool toLocalized, - UErrorCode &status); + static UnicodeString convertLocalized(const UnicodeString& input, const DecimalFormatSymbols& symbols, + bool toLocalized, UErrorCode& status); + + /** + * This method contains the heart of the logic for rendering LDML affix strings. It handles + * sign-always-shown resolution, whether to use the positive or negative subpattern, permille + * substitution, and plural forms for CurrencyPluralInfo. + */ + static void patternInfoToStringBuilder(const AffixPatternProvider& patternInfo, bool isPrefix, + int8_t signum, UNumberSignDisplay signDisplay, + StandardPlural::Form plural, bool perMilleReplacesPercent, + UnicodeString& output); private: /** @return The number of chars inserted. */ - static int - escapePaddingString(UnicodeString input, UnicodeString &output, int startIndex, UErrorCode &status); + static int escapePaddingString(UnicodeString input, UnicodeString& output, int startIndex, + UErrorCode& status); }; } // namespace impl diff --git a/deps/icu-small/source/i18n/number_rounding.cpp b/deps/icu-small/source/i18n/number_rounding.cpp index fd4dafdf983b61..ae4b8849fbe956 100644 --- a/deps/icu-small/source/i18n/number_rounding.cpp +++ b/deps/icu-small/source/i18n/number_rounding.cpp @@ -3,17 +3,23 @@ #include "unicode/utypes.h" -#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT +#if !UCONFIG_NO_FORMATTING #include "uassert.h" #include "unicode/numberformatter.h" #include "number_types.h" #include "number_decimalquantity.h" +#include "double-conversion.h" +#include "number_roundingutils.h" +#include "putilimp.h" using namespace icu; using namespace icu::number; using namespace icu::number::impl; + +using double_conversion::DoubleToStringConverter; + namespace { int32_t getRoundingMagnitudeFraction(int maxFrac) { @@ -46,15 +52,38 @@ int32_t getDisplayMagnitudeSignificant(const DecimalQuantity &value, int minSig) } -Rounder Rounder::unlimited() { - return Rounder(RND_NONE, {}, kDefaultMode); +MultiplierProducer::~MultiplierProducer() = default; + + +digits_t roundingutils::doubleFractionLength(double input) { + char buffer[DoubleToStringConverter::kBase10MaximalLength + 1]; + bool sign; // unused; always positive + int32_t length; + int32_t point; + DoubleToStringConverter::DoubleToAscii( + input, + DoubleToStringConverter::DtoaMode::SHORTEST, + 0, + buffer, + sizeof(buffer), + &sign, + &length, + &point + ); + + return static_cast(length - point); } -FractionRounder Rounder::integer() { + +Precision Precision::unlimited() { + return Precision(RND_NONE, {}, kDefaultMode); +} + +FractionPrecision Precision::integer() { return constructFraction(0, 0); } -FractionRounder Rounder::fixedFraction(int32_t minMaxFractionPlaces) { +FractionPrecision Precision::fixedFraction(int32_t minMaxFractionPlaces) { if (minMaxFractionPlaces >= 0 && minMaxFractionPlaces <= kMaxIntFracSig) { return constructFraction(minMaxFractionPlaces, minMaxFractionPlaces); } else { @@ -62,7 +91,7 @@ FractionRounder Rounder::fixedFraction(int32_t minMaxFractionPlaces) { } } -FractionRounder Rounder::minFraction(int32_t minFractionPlaces) { +FractionPrecision Precision::minFraction(int32_t minFractionPlaces) { if (minFractionPlaces >= 0 && minFractionPlaces <= kMaxIntFracSig) { return constructFraction(minFractionPlaces, -1); } else { @@ -70,7 +99,7 @@ FractionRounder Rounder::minFraction(int32_t minFractionPlaces) { } } -FractionRounder Rounder::maxFraction(int32_t maxFractionPlaces) { +FractionPrecision Precision::maxFraction(int32_t maxFractionPlaces) { if (maxFractionPlaces >= 0 && maxFractionPlaces <= kMaxIntFracSig) { return constructFraction(0, maxFractionPlaces); } else { @@ -78,7 +107,7 @@ FractionRounder Rounder::maxFraction(int32_t maxFractionPlaces) { } } -FractionRounder Rounder::minMaxFraction(int32_t minFractionPlaces, int32_t maxFractionPlaces) { +FractionPrecision Precision::minMaxFraction(int32_t minFractionPlaces, int32_t maxFractionPlaces) { if (minFractionPlaces >= 0 && maxFractionPlaces <= kMaxIntFracSig && minFractionPlaces <= maxFractionPlaces) { return constructFraction(minFractionPlaces, maxFractionPlaces); @@ -87,7 +116,7 @@ FractionRounder Rounder::minMaxFraction(int32_t minFractionPlaces, int32_t maxFr } } -Rounder Rounder::fixedDigits(int32_t minMaxSignificantDigits) { +Precision Precision::fixedSignificantDigits(int32_t minMaxSignificantDigits) { if (minMaxSignificantDigits >= 1 && minMaxSignificantDigits <= kMaxIntFracSig) { return constructSignificant(minMaxSignificantDigits, minMaxSignificantDigits); } else { @@ -95,7 +124,7 @@ Rounder Rounder::fixedDigits(int32_t minMaxSignificantDigits) { } } -Rounder Rounder::minDigits(int32_t minSignificantDigits) { +Precision Precision::minSignificantDigits(int32_t minSignificantDigits) { if (minSignificantDigits >= 1 && minSignificantDigits <= kMaxIntFracSig) { return constructSignificant(minSignificantDigits, -1); } else { @@ -103,7 +132,7 @@ Rounder Rounder::minDigits(int32_t minSignificantDigits) { } } -Rounder Rounder::maxDigits(int32_t maxSignificantDigits) { +Precision Precision::maxSignificantDigits(int32_t maxSignificantDigits) { if (maxSignificantDigits >= 1 && maxSignificantDigits <= kMaxIntFracSig) { return constructSignificant(1, maxSignificantDigits); } else { @@ -111,7 +140,7 @@ Rounder Rounder::maxDigits(int32_t maxSignificantDigits) { } } -Rounder Rounder::minMaxDigits(int32_t minSignificantDigits, int32_t maxSignificantDigits) { +Precision Precision::minMaxSignificantDigits(int32_t minSignificantDigits, int32_t maxSignificantDigits) { if (minSignificantDigits >= 1 && maxSignificantDigits <= kMaxIntFracSig && minSignificantDigits <= maxSignificantDigits) { return constructSignificant(minSignificantDigits, maxSignificantDigits); @@ -120,7 +149,7 @@ Rounder Rounder::minMaxDigits(int32_t minSignificantDigits, int32_t maxSignifica } } -IncrementRounder Rounder::increment(double roundingIncrement) { +IncrementPrecision Precision::increment(double roundingIncrement) { if (roundingIncrement > 0.0) { return constructIncrement(roundingIncrement, 0); } else { @@ -128,16 +157,18 @@ IncrementRounder Rounder::increment(double roundingIncrement) { } } -CurrencyRounder Rounder::currency(UCurrencyUsage currencyUsage) { +CurrencyPrecision Precision::currency(UCurrencyUsage currencyUsage) { return constructCurrency(currencyUsage); } -Rounder Rounder::withMode(RoundingMode roundingMode) const { +Precision Precision::withMode(RoundingMode roundingMode) const { if (fType == RND_ERROR) { return *this; } // no-op in error state - return {fType, fUnion, roundingMode}; + Precision retval = *this; + retval.fRoundingMode = roundingMode; + return retval; } -Rounder FractionRounder::withMinDigits(int32_t minSignificantDigits) const { +Precision FractionPrecision::withMinDigits(int32_t minSignificantDigits) const { if (fType == RND_ERROR) { return *this; } // no-op in error state if (minSignificantDigits >= 1 && minSignificantDigits <= kMaxIntFracSig) { return constructFractionSignificant(*this, minSignificantDigits, -1); @@ -146,7 +177,7 @@ Rounder FractionRounder::withMinDigits(int32_t minSignificantDigits) const { } } -Rounder FractionRounder::withMaxDigits(int32_t maxSignificantDigits) const { +Precision FractionPrecision::withMaxDigits(int32_t maxSignificantDigits) const { if (fType == RND_ERROR) { return *this; } // no-op in error state if (maxSignificantDigits >= 1 && maxSignificantDigits <= kMaxIntFracSig) { return constructFractionSignificant(*this, -1, maxSignificantDigits); @@ -156,7 +187,7 @@ Rounder FractionRounder::withMaxDigits(int32_t maxSignificantDigits) const { } // Private method on base class -Rounder Rounder::withCurrency(const CurrencyUnit ¤cy, UErrorCode &status) const { +Precision Precision::withCurrency(const CurrencyUnit ¤cy, UErrorCode &status) const { if (fType == RND_ERROR) { return *this; } // no-op in error state U_ASSERT(fType == RND_CURRENCY); const char16_t *isoCode = currency.getISOCurrency(); @@ -170,17 +201,17 @@ Rounder Rounder::withCurrency(const CurrencyUnit ¤cy, UErrorCode &status) } } -// Public method on CurrencyRounder subclass -Rounder CurrencyRounder::withCurrency(const CurrencyUnit ¤cy) const { +// Public method on CurrencyPrecision subclass +Precision CurrencyPrecision::withCurrency(const CurrencyUnit ¤cy) const { UErrorCode localStatus = U_ZERO_ERROR; - Rounder result = Rounder::withCurrency(currency, localStatus); + Precision result = Precision::withCurrency(currency, localStatus); if (U_FAILURE(localStatus)) { return {localStatus}; } return result; } -Rounder IncrementRounder::withMinFraction(int32_t minFrac) const { +Precision IncrementPrecision::withMinFraction(int32_t minFrac) const { if (fType == RND_ERROR) { return *this; } // no-op in error state if (minFrac >= 0 && minFrac <= kMaxIntFracSig) { return constructIncrement(fUnion.increment.fIncrement, minFrac); @@ -189,67 +220,77 @@ Rounder IncrementRounder::withMinFraction(int32_t minFrac) const { } } -FractionRounder Rounder::constructFraction(int32_t minFrac, int32_t maxFrac) { +FractionPrecision Precision::constructFraction(int32_t minFrac, int32_t maxFrac) { FractionSignificantSettings settings; settings.fMinFrac = static_cast(minFrac); settings.fMaxFrac = static_cast(maxFrac); settings.fMinSig = -1; settings.fMaxSig = -1; - RounderUnion union_; + PrecisionUnion union_; union_.fracSig = settings; return {RND_FRACTION, union_, kDefaultMode}; } -Rounder Rounder::constructSignificant(int32_t minSig, int32_t maxSig) { +Precision Precision::constructSignificant(int32_t minSig, int32_t maxSig) { FractionSignificantSettings settings; settings.fMinFrac = -1; settings.fMaxFrac = -1; settings.fMinSig = static_cast(minSig); settings.fMaxSig = static_cast(maxSig); - RounderUnion union_; + PrecisionUnion union_; union_.fracSig = settings; return {RND_SIGNIFICANT, union_, kDefaultMode}; } -Rounder -Rounder::constructFractionSignificant(const FractionRounder &base, int32_t minSig, int32_t maxSig) { +Precision +Precision::constructFractionSignificant(const FractionPrecision &base, int32_t minSig, int32_t maxSig) { FractionSignificantSettings settings = base.fUnion.fracSig; settings.fMinSig = static_cast(minSig); settings.fMaxSig = static_cast(maxSig); - RounderUnion union_; + PrecisionUnion union_; union_.fracSig = settings; return {RND_FRACTION_SIGNIFICANT, union_, kDefaultMode}; } -IncrementRounder Rounder::constructIncrement(double increment, int32_t minFrac) { +IncrementPrecision Precision::constructIncrement(double increment, int32_t minFrac) { IncrementSettings settings; settings.fIncrement = increment; settings.fMinFrac = static_cast(minFrac); - RounderUnion union_; + // One of the few pre-computed quantities: + // Note: it is possible for minFrac to be more than maxFrac... (misleading) + settings.fMaxFrac = roundingutils::doubleFractionLength(increment); + PrecisionUnion union_; union_.increment = settings; return {RND_INCREMENT, union_, kDefaultMode}; } -CurrencyRounder Rounder::constructCurrency(UCurrencyUsage usage) { - RounderUnion union_; +CurrencyPrecision Precision::constructCurrency(UCurrencyUsage usage) { + PrecisionUnion union_; union_.currencyUsage = usage; return {RND_CURRENCY, union_, kDefaultMode}; } -Rounder Rounder::constructPassThrough() { - RounderUnion union_; - union_.errorCode = U_ZERO_ERROR; // initialize the variable - return {RND_PASS_THROUGH, union_, kDefaultMode}; -} -void Rounder::setLocaleData(const CurrencyUnit ¤cy, UErrorCode &status) { - if (fType == RND_CURRENCY) { - *this = withCurrency(currency, status); +RoundingImpl::RoundingImpl(const Precision& precision, UNumberFormatRoundingMode roundingMode, + const CurrencyUnit& currency, UErrorCode& status) + : fPrecision(precision), fRoundingMode(roundingMode), fPassThrough(false) { + if (precision.fType == Precision::RND_CURRENCY) { + fPrecision = precision.withCurrency(currency, status); } } +RoundingImpl RoundingImpl::passThrough() { + RoundingImpl retval; + retval.fPassThrough = true; + return retval; +} + +bool RoundingImpl::isSignificantDigits() const { + return fPrecision.fType == Precision::RND_SIGNIFICANT; +} + int32_t -Rounder::chooseMultiplierAndApply(impl::DecimalQuantity &input, const impl::MultiplierProducer &producer, +RoundingImpl::chooseMultiplierAndApply(impl::DecimalQuantity &input, const impl::MultiplierProducer &producer, UErrorCode &status) { // Do not call this method with zero. U_ASSERT(!input.isZero()); @@ -287,45 +328,59 @@ Rounder::chooseMultiplierAndApply(impl::DecimalQuantity &input, const impl::Mult } /** This is the method that contains the actual rounding logic. */ -void Rounder::apply(impl::DecimalQuantity &value, UErrorCode& status) const { - switch (fType) { - case RND_BOGUS: - case RND_ERROR: +void RoundingImpl::apply(impl::DecimalQuantity &value, UErrorCode& status) const { + if (fPassThrough) { + return; + } + switch (fPrecision.fType) { + case Precision::RND_BOGUS: + case Precision::RND_ERROR: // Errors should be caught before the apply() method is called status = U_INTERNAL_PROGRAM_ERROR; break; - case RND_NONE: + case Precision::RND_NONE: value.roundToInfinity(); break; - case RND_FRACTION: + case Precision::RND_FRACTION: value.roundToMagnitude( - getRoundingMagnitudeFraction(fUnion.fracSig.fMaxFrac), fRoundingMode, status); + getRoundingMagnitudeFraction(fPrecision.fUnion.fracSig.fMaxFrac), + fRoundingMode, + status); value.setFractionLength( - uprv_max(0, -getDisplayMagnitudeFraction(fUnion.fracSig.fMinFrac)), INT32_MAX); + uprv_max(0, -getDisplayMagnitudeFraction(fPrecision.fUnion.fracSig.fMinFrac)), + INT32_MAX); break; - case RND_SIGNIFICANT: + case Precision::RND_SIGNIFICANT: value.roundToMagnitude( - getRoundingMagnitudeSignificant(value, fUnion.fracSig.fMaxSig), + getRoundingMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMaxSig), fRoundingMode, status); value.setFractionLength( - uprv_max(0, -getDisplayMagnitudeSignificant(value, fUnion.fracSig.fMinSig)), + uprv_max(0, -getDisplayMagnitudeSignificant(value, fPrecision.fUnion.fracSig.fMinSig)), INT32_MAX); + // Make sure that digits are displayed on zero. + if (value.isZero() && fPrecision.fUnion.fracSig.fMinSig > 0) { + value.setIntegerLength(1, INT32_MAX); + } break; - case RND_FRACTION_SIGNIFICANT: { - int32_t displayMag = getDisplayMagnitudeFraction(fUnion.fracSig.fMinFrac); - int32_t roundingMag = getRoundingMagnitudeFraction(fUnion.fracSig.fMaxFrac); - if (fUnion.fracSig.fMinSig == -1) { + case Precision::RND_FRACTION_SIGNIFICANT: { + int32_t displayMag = getDisplayMagnitudeFraction(fPrecision.fUnion.fracSig.fMinFrac); + int32_t roundingMag = getRoundingMagnitudeFraction(fPrecision.fUnion.fracSig.fMaxFrac); + if (fPrecision.fUnion.fracSig.fMinSig == -1) { // Max Sig override - int32_t candidate = getRoundingMagnitudeSignificant(value, fUnion.fracSig.fMaxSig); + int32_t candidate = getRoundingMagnitudeSignificant( + value, + fPrecision.fUnion.fracSig.fMaxSig); roundingMag = uprv_max(roundingMag, candidate); } else { // Min Sig override - int32_t candidate = getDisplayMagnitudeSignificant(value, fUnion.fracSig.fMinSig); + int32_t candidate = getDisplayMagnitudeSignificant( + value, + fPrecision.fUnion.fracSig.fMinSig); roundingMag = uprv_min(roundingMag, candidate); } value.roundToMagnitude(roundingMag, fRoundingMode, status); @@ -333,27 +388,27 @@ void Rounder::apply(impl::DecimalQuantity &value, UErrorCode& status) const { break; } - case RND_INCREMENT: + case Precision::RND_INCREMENT: value.roundToIncrement( - fUnion.increment.fIncrement, fRoundingMode, fUnion.increment.fMinFrac, status); - value.setFractionLength(fUnion.increment.fMinFrac, fUnion.increment.fMinFrac); + fPrecision.fUnion.increment.fIncrement, + fRoundingMode, + fPrecision.fUnion.increment.fMaxFrac, + status); + value.setFractionLength(fPrecision.fUnion.increment.fMinFrac, INT32_MAX); break; - case RND_CURRENCY: + case Precision::RND_CURRENCY: // Call .withCurrency() before .apply()! U_ASSERT(false); break; - - case RND_PASS_THROUGH: - break; } } -void Rounder::apply(impl::DecimalQuantity &value, int32_t minInt, UErrorCode /*status*/) { +void RoundingImpl::apply(impl::DecimalQuantity &value, int32_t minInt, UErrorCode /*status*/) { // This method is intended for the one specific purpose of helping print "00.000E0". - U_ASSERT(fType == RND_SIGNIFICANT); + U_ASSERT(isSignificantDigits()); U_ASSERT(value.isZero()); - value.setFractionLength(fUnion.fracSig.fMinSig - minInt, INT32_MAX); + value.setFractionLength(fPrecision.fUnion.fracSig.fMinSig - minInt, INT32_MAX); } #endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/number_roundingutils.h b/deps/icu-small/source/i18n/number_roundingutils.h index 6868ee0b86817e..66d58bb775bbee 100644 --- a/deps/icu-small/source/i18n/number_roundingutils.h +++ b/deps/icu-small/source/i18n/number_roundingutils.h @@ -3,7 +3,7 @@ #include "unicode/utypes.h" -#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT +#if !UCONFIG_NO_FORMATTING #ifndef __NUMBER_ROUNDINGUTILS_H__ #define __NUMBER_ROUNDINGUTILS_H__ @@ -131,7 +131,62 @@ inline bool roundsAtMidpoint(int roundingMode) { } } +/** + * Computes the number of fraction digits in a double. Used for computing maxFrac for an increment. + * Calls into the DoubleToStringConverter library to do so. + */ +digits_t doubleFractionLength(double input); + } // namespace roundingutils + + +/** + * Encapsulates a Precision and a RoundingMode and performs rounding on a DecimalQuantity. + * + * This class does not exist in Java: instead, the base Precision class is used. + */ +class RoundingImpl { + public: + RoundingImpl() = default; // default constructor: leaves object in undefined state + + RoundingImpl(const Precision& precision, UNumberFormatRoundingMode roundingMode, + const CurrencyUnit& currency, UErrorCode& status); + + static RoundingImpl passThrough(); + + /** Required for ScientificFormatter */ + bool isSignificantDigits() const; + + /** + * Rounding endpoint used by Engineering and Compact notation. Chooses the most appropriate multiplier (magnitude + * adjustment), applies the adjustment, rounds, and returns the chosen multiplier. + * + *

+ * In most cases, this is simple. However, when rounding the number causes it to cross a multiplier boundary, we + * need to re-do the rounding. For example, to display 999,999 in Engineering notation with 2 sigfigs, first you + * guess the multiplier to be -3. However, then you end up getting 1000E3, which is not the correct output. You then + * change your multiplier to be -6, and you get 1.0E6, which is correct. + * + * @param input The quantity to process. + * @param producer Function to call to return a multiplier based on a magnitude. + * @return The number of orders of magnitude the input was adjusted by this method. + */ + int32_t + chooseMultiplierAndApply(impl::DecimalQuantity &input, const impl::MultiplierProducer &producer, + UErrorCode &status); + + void apply(impl::DecimalQuantity &value, UErrorCode &status) const; + + /** Version of {@link #apply} that obeys minInt constraints. Used for scientific notation compatibility mode. */ + void apply(impl::DecimalQuantity &value, int32_t minInt, UErrorCode status); + + private: + Precision fPrecision; + UNumberFormatRoundingMode fRoundingMode; + bool fPassThrough; +}; + + } // namespace impl } // namespace number U_NAMESPACE_END diff --git a/deps/icu-small/source/i18n/number_scientific.cpp b/deps/icu-small/source/i18n/number_scientific.cpp index a2f2bf85a1fc9d..40952024e995cc 100644 --- a/deps/icu-small/source/i18n/number_scientific.cpp +++ b/deps/icu-small/source/i18n/number_scientific.cpp @@ -3,13 +3,14 @@ #include "unicode/utypes.h" -#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT +#if !UCONFIG_NO_FORMATTING #include #include "number_scientific.h" #include "number_utils.h" #include "number_stringbuilder.h" #include "unicode/unum.h" +#include "number_microprops.h" using namespace icu; using namespace icu::number; @@ -64,8 +65,13 @@ int32_t ScientificModifier::apply(NumberStringBuilder &output, int32_t /*leftInd int32_t disp = std::abs(fExponent); for (int j = 0; j < fHandler->fSettings.fMinExponentDigits || disp > 0; j++, disp /= 10) { auto d = static_cast(disp % 10); - const UnicodeString &digitString = getDigitFromSymbols(d, *fHandler->fSymbols); - i += output.insert(i - j, digitString, UNUM_EXPONENT_FIELD, status); + i += utils::insertDigitFromSymbols( + output, + i - j, + d, + *fHandler->fSymbols, + UNUM_EXPONENT_FIELD, + status); } return i - rightIndex; } @@ -101,22 +107,25 @@ void ScientificHandler::processQuantity(DecimalQuantity &quantity, MicroProps &m // Treat zero as if it had magnitude 0 int32_t exponent; if (quantity.isZero()) { - if (fSettings.fRequireMinInt && micros.rounding.fType == Rounder::RND_SIGNIFICANT) { + if (fSettings.fRequireMinInt && micros.rounder.isSignificantDigits()) { // Show "00.000E0" on pattern "00.000E0" - micros.rounding.apply(quantity, fSettings.fEngineeringInterval, status); + micros.rounder.apply(quantity, fSettings.fEngineeringInterval, status); exponent = 0; } else { - micros.rounding.apply(quantity, status); + micros.rounder.apply(quantity, status); exponent = 0; } } else { - exponent = -micros.rounding.chooseMultiplierAndApply(quantity, *this, status); + exponent = -micros.rounder.chooseMultiplierAndApply(quantity, *this, status); } // Use MicroProps's helper ScientificModifier and save it as the modInner. ScientificModifier &mod = micros.helpers.scientificModifier; mod.set(exponent, this); micros.modInner = &mod; + + // We already performed rounding. Do not perform it again. + micros.rounder = RoundingImpl::passThrough(); } int32_t ScientificHandler::getMultiplier(int32_t magnitude) const { diff --git a/deps/icu-small/source/i18n/number_scientific.h b/deps/icu-small/source/i18n/number_scientific.h index f5e4d30e6a9737..974ab3adb614ca 100644 --- a/deps/icu-small/source/i18n/number_scientific.h +++ b/deps/icu-small/source/i18n/number_scientific.h @@ -3,7 +3,7 @@ #include "unicode/utypes.h" -#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT +#if !UCONFIG_NO_FORMATTING #ifndef __NUMBER_SCIENTIFIC_H__ #define __NUMBER_SCIENTIFIC_H__ diff --git a/deps/icu-small/source/i18n/number_skeletons.cpp b/deps/icu-small/source/i18n/number_skeletons.cpp new file mode 100644 index 00000000000000..c7bb18b5f3d2b5 --- /dev/null +++ b/deps/icu-small/source/i18n/number_skeletons.cpp @@ -0,0 +1,1510 @@ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +// Allow implicit conversion from char16_t* to UnicodeString for this file: +// Helpful in toString methods and elsewhere. +#define UNISTR_FROM_STRING_EXPLICIT + +#include "number_decnum.h" +#include "number_skeletons.h" +#include "umutex.h" +#include "ucln_in.h" +#include "patternprops.h" +#include "unicode/ucharstriebuilder.h" +#include "number_utils.h" +#include "number_decimalquantity.h" +#include "unicode/numberformatter.h" +#include "uinvchar.h" +#include "charstr.h" + +using namespace icu; +using namespace icu::number; +using namespace icu::number::impl; +using namespace icu::number::impl::skeleton; + +namespace { + +icu::UInitOnce gNumberSkeletonsInitOnce = U_INITONCE_INITIALIZER; + +char16_t* kSerializedStemTrie = nullptr; + +UBool U_CALLCONV cleanupNumberSkeletons() { + uprv_free(kSerializedStemTrie); + kSerializedStemTrie = nullptr; + gNumberSkeletonsInitOnce.reset(); + return TRUE; +} + +void U_CALLCONV initNumberSkeletons(UErrorCode& status) { + ucln_i18n_registerCleanup(UCLN_I18N_NUMBER_SKELETONS, cleanupNumberSkeletons); + + UCharsTrieBuilder b(status); + if (U_FAILURE(status)) { return; } + + // Section 1: + b.add(u"compact-short", STEM_COMPACT_SHORT, status); + b.add(u"compact-long", STEM_COMPACT_LONG, status); + b.add(u"scientific", STEM_SCIENTIFIC, status); + b.add(u"engineering", STEM_ENGINEERING, status); + b.add(u"notation-simple", STEM_NOTATION_SIMPLE, status); + b.add(u"base-unit", STEM_BASE_UNIT, status); + b.add(u"percent", STEM_PERCENT, status); + b.add(u"permille", STEM_PERMILLE, status); + b.add(u"precision-integer", STEM_PRECISION_INTEGER, status); + b.add(u"precision-unlimited", STEM_PRECISION_UNLIMITED, status); + b.add(u"precision-currency-standard", STEM_PRECISION_CURRENCY_STANDARD, status); + b.add(u"precision-currency-cash", STEM_PRECISION_CURRENCY_CASH, status); + b.add(u"rounding-mode-ceiling", STEM_ROUNDING_MODE_CEILING, status); + b.add(u"rounding-mode-floor", STEM_ROUNDING_MODE_FLOOR, status); + b.add(u"rounding-mode-down", STEM_ROUNDING_MODE_DOWN, status); + b.add(u"rounding-mode-up", STEM_ROUNDING_MODE_UP, status); + b.add(u"rounding-mode-half-even", STEM_ROUNDING_MODE_HALF_EVEN, status); + b.add(u"rounding-mode-half-down", STEM_ROUNDING_MODE_HALF_DOWN, status); + b.add(u"rounding-mode-half-up", STEM_ROUNDING_MODE_HALF_UP, status); + b.add(u"rounding-mode-unnecessary", STEM_ROUNDING_MODE_UNNECESSARY, status); + b.add(u"group-off", STEM_GROUP_OFF, status); + b.add(u"group-min2", STEM_GROUP_MIN2, status); + b.add(u"group-auto", STEM_GROUP_AUTO, status); + b.add(u"group-on-aligned", STEM_GROUP_ON_ALIGNED, status); + b.add(u"group-thousands", STEM_GROUP_THOUSANDS, status); + b.add(u"latin", STEM_LATIN, status); + b.add(u"unit-width-narrow", STEM_UNIT_WIDTH_NARROW, status); + b.add(u"unit-width-short", STEM_UNIT_WIDTH_SHORT, status); + b.add(u"unit-width-full-name", STEM_UNIT_WIDTH_FULL_NAME, status); + b.add(u"unit-width-iso-code", STEM_UNIT_WIDTH_ISO_CODE, status); + b.add(u"unit-width-hidden", STEM_UNIT_WIDTH_HIDDEN, status); + b.add(u"sign-auto", STEM_SIGN_AUTO, status); + b.add(u"sign-always", STEM_SIGN_ALWAYS, status); + b.add(u"sign-never", STEM_SIGN_NEVER, status); + b.add(u"sign-accounting", STEM_SIGN_ACCOUNTING, status); + b.add(u"sign-accounting-always", STEM_SIGN_ACCOUNTING_ALWAYS, status); + b.add(u"sign-except-zero", STEM_SIGN_EXCEPT_ZERO, status); + b.add(u"sign-accounting-except-zero", STEM_SIGN_ACCOUNTING_EXCEPT_ZERO, status); + b.add(u"decimal-auto", STEM_DECIMAL_AUTO, status); + b.add(u"decimal-always", STEM_DECIMAL_ALWAYS, status); + if (U_FAILURE(status)) { return; } + + // Section 2: + b.add(u"precision-increment", STEM_PRECISION_INCREMENT, status); + b.add(u"measure-unit", STEM_MEASURE_UNIT, status); + b.add(u"per-measure-unit", STEM_PER_MEASURE_UNIT, status); + b.add(u"currency", STEM_CURRENCY, status); + b.add(u"integer-width", STEM_INTEGER_WIDTH, status); + b.add(u"numbering-system", STEM_NUMBERING_SYSTEM, status); + b.add(u"scale", STEM_SCALE, status); + if (U_FAILURE(status)) { return; } + + // Build the CharsTrie + // TODO: Use SLOW or FAST here? + UnicodeString result; + b.buildUnicodeString(USTRINGTRIE_BUILD_FAST, result, status); + if (U_FAILURE(status)) { return; } + + // Copy the result into the global constant pointer + size_t numBytes = result.length() * sizeof(char16_t); + kSerializedStemTrie = static_cast(uprv_malloc(numBytes)); + uprv_memcpy(kSerializedStemTrie, result.getBuffer(), numBytes); +} + + +inline void appendMultiple(UnicodeString& sb, UChar32 cp, int32_t count) { + for (int i = 0; i < count; i++) { + sb.append(cp); + } +} + + +#define CHECK_NULL(seen, field, status) (void)(seen); /* for auto-format line wrapping */ \ +{ \ + if ((seen).field) { \ + (status) = U_NUMBER_SKELETON_SYNTAX_ERROR; \ + return STATE_NULL; \ + } \ + (seen).field = true; \ +} + + +#define SKELETON_UCHAR_TO_CHAR(dest, src, start, end, status) (void)(dest); \ +{ \ + UErrorCode conversionStatus = U_ZERO_ERROR; \ + (dest).appendInvariantChars({FALSE, (src).getBuffer() + (start), (end) - (start)}, conversionStatus); \ + if (conversionStatus == U_INVARIANT_CONVERSION_ERROR) { \ + /* Don't propagate the invariant conversion error; it is a skeleton syntax error */ \ + (status) = U_NUMBER_SKELETON_SYNTAX_ERROR; \ + return; \ + } else if (U_FAILURE(conversionStatus)) { \ + (status) = conversionStatus; \ + return; \ + } \ +} + + +} // anonymous namespace + + +Notation stem_to_object::notation(skeleton::StemEnum stem) { + switch (stem) { + case STEM_COMPACT_SHORT: + return Notation::compactShort(); + case STEM_COMPACT_LONG: + return Notation::compactLong(); + case STEM_SCIENTIFIC: + return Notation::scientific(); + case STEM_ENGINEERING: + return Notation::engineering(); + case STEM_NOTATION_SIMPLE: + return Notation::simple(); + default: + U_ASSERT(false); + return Notation::simple(); // return a value: silence compiler warning + } +} + +MeasureUnit stem_to_object::unit(skeleton::StemEnum stem) { + switch (stem) { + case STEM_BASE_UNIT: + // Slicing is okay + return NoUnit::base(); // NOLINT + case STEM_PERCENT: + // Slicing is okay + return NoUnit::percent(); // NOLINT + case STEM_PERMILLE: + // Slicing is okay + return NoUnit::permille(); // NOLINT + default: + U_ASSERT(false); + return {}; // return a value: silence compiler warning + } +} + +Precision stem_to_object::precision(skeleton::StemEnum stem) { + switch (stem) { + case STEM_PRECISION_INTEGER: + return Precision::integer(); + case STEM_PRECISION_UNLIMITED: + return Precision::unlimited(); + case STEM_PRECISION_CURRENCY_STANDARD: + return Precision::currency(UCURR_USAGE_STANDARD); + case STEM_PRECISION_CURRENCY_CASH: + return Precision::currency(UCURR_USAGE_CASH); + default: + U_ASSERT(false); + return Precision::integer(); // return a value: silence compiler warning + } +} + +UNumberFormatRoundingMode stem_to_object::roundingMode(skeleton::StemEnum stem) { + switch (stem) { + case STEM_ROUNDING_MODE_CEILING: + return UNUM_ROUND_CEILING; + case STEM_ROUNDING_MODE_FLOOR: + return UNUM_ROUND_FLOOR; + case STEM_ROUNDING_MODE_DOWN: + return UNUM_ROUND_DOWN; + case STEM_ROUNDING_MODE_UP: + return UNUM_ROUND_UP; + case STEM_ROUNDING_MODE_HALF_EVEN: + return UNUM_ROUND_HALFEVEN; + case STEM_ROUNDING_MODE_HALF_DOWN: + return UNUM_ROUND_HALFDOWN; + case STEM_ROUNDING_MODE_HALF_UP: + return UNUM_ROUND_HALFUP; + case STEM_ROUNDING_MODE_UNNECESSARY: + return UNUM_ROUND_UNNECESSARY; + default: + U_ASSERT(false); + return UNUM_ROUND_UNNECESSARY; + } +} + +UGroupingStrategy stem_to_object::groupingStrategy(skeleton::StemEnum stem) { + switch (stem) { + case STEM_GROUP_OFF: + return UNUM_GROUPING_OFF; + case STEM_GROUP_MIN2: + return UNUM_GROUPING_MIN2; + case STEM_GROUP_AUTO: + return UNUM_GROUPING_AUTO; + case STEM_GROUP_ON_ALIGNED: + return UNUM_GROUPING_ON_ALIGNED; + case STEM_GROUP_THOUSANDS: + return UNUM_GROUPING_THOUSANDS; + default: + return UNUM_GROUPING_COUNT; // for objects, throw; for enums, return COUNT + } +} + +UNumberUnitWidth stem_to_object::unitWidth(skeleton::StemEnum stem) { + switch (stem) { + case STEM_UNIT_WIDTH_NARROW: + return UNUM_UNIT_WIDTH_NARROW; + case STEM_UNIT_WIDTH_SHORT: + return UNUM_UNIT_WIDTH_SHORT; + case STEM_UNIT_WIDTH_FULL_NAME: + return UNUM_UNIT_WIDTH_FULL_NAME; + case STEM_UNIT_WIDTH_ISO_CODE: + return UNUM_UNIT_WIDTH_ISO_CODE; + case STEM_UNIT_WIDTH_HIDDEN: + return UNUM_UNIT_WIDTH_HIDDEN; + default: + return UNUM_UNIT_WIDTH_COUNT; // for objects, throw; for enums, return COUNT + } +} + +UNumberSignDisplay stem_to_object::signDisplay(skeleton::StemEnum stem) { + switch (stem) { + case STEM_SIGN_AUTO: + return UNUM_SIGN_AUTO; + case STEM_SIGN_ALWAYS: + return UNUM_SIGN_ALWAYS; + case STEM_SIGN_NEVER: + return UNUM_SIGN_NEVER; + case STEM_SIGN_ACCOUNTING: + return UNUM_SIGN_ACCOUNTING; + case STEM_SIGN_ACCOUNTING_ALWAYS: + return UNUM_SIGN_ACCOUNTING_ALWAYS; + case STEM_SIGN_EXCEPT_ZERO: + return UNUM_SIGN_EXCEPT_ZERO; + case STEM_SIGN_ACCOUNTING_EXCEPT_ZERO: + return UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO; + default: + return UNUM_SIGN_COUNT; // for objects, throw; for enums, return COUNT + } +} + +UNumberDecimalSeparatorDisplay stem_to_object::decimalSeparatorDisplay(skeleton::StemEnum stem) { + switch (stem) { + case STEM_DECIMAL_AUTO: + return UNUM_DECIMAL_SEPARATOR_AUTO; + case STEM_DECIMAL_ALWAYS: + return UNUM_DECIMAL_SEPARATOR_ALWAYS; + default: + return UNUM_DECIMAL_SEPARATOR_COUNT; // for objects, throw; for enums, return COUNT + } +} + + +void enum_to_stem_string::roundingMode(UNumberFormatRoundingMode value, UnicodeString& sb) { + switch (value) { + case UNUM_ROUND_CEILING: + sb.append(u"rounding-mode-ceiling", -1); + break; + case UNUM_ROUND_FLOOR: + sb.append(u"rounding-mode-floor", -1); + break; + case UNUM_ROUND_DOWN: + sb.append(u"rounding-mode-down", -1); + break; + case UNUM_ROUND_UP: + sb.append(u"rounding-mode-up", -1); + break; + case UNUM_ROUND_HALFEVEN: + sb.append(u"rounding-mode-half-even", -1); + break; + case UNUM_ROUND_HALFDOWN: + sb.append(u"rounding-mode-half-down", -1); + break; + case UNUM_ROUND_HALFUP: + sb.append(u"rounding-mode-half-up", -1); + break; + case UNUM_ROUND_UNNECESSARY: + sb.append(u"rounding-mode-unnecessary", -1); + break; + default: + U_ASSERT(false); + } +} + +void enum_to_stem_string::groupingStrategy(UGroupingStrategy value, UnicodeString& sb) { + switch (value) { + case UNUM_GROUPING_OFF: + sb.append(u"group-off", -1); + break; + case UNUM_GROUPING_MIN2: + sb.append(u"group-min2", -1); + break; + case UNUM_GROUPING_AUTO: + sb.append(u"group-auto", -1); + break; + case UNUM_GROUPING_ON_ALIGNED: + sb.append(u"group-on-aligned", -1); + break; + case UNUM_GROUPING_THOUSANDS: + sb.append(u"group-thousands", -1); + break; + default: + U_ASSERT(false); + } +} + +void enum_to_stem_string::unitWidth(UNumberUnitWidth value, UnicodeString& sb) { + switch (value) { + case UNUM_UNIT_WIDTH_NARROW: + sb.append(u"unit-width-narrow", -1); + break; + case UNUM_UNIT_WIDTH_SHORT: + sb.append(u"unit-width-short", -1); + break; + case UNUM_UNIT_WIDTH_FULL_NAME: + sb.append(u"unit-width-full-name", -1); + break; + case UNUM_UNIT_WIDTH_ISO_CODE: + sb.append(u"unit-width-iso-code", -1); + break; + case UNUM_UNIT_WIDTH_HIDDEN: + sb.append(u"unit-width-hidden", -1); + break; + default: + U_ASSERT(false); + } +} + +void enum_to_stem_string::signDisplay(UNumberSignDisplay value, UnicodeString& sb) { + switch (value) { + case UNUM_SIGN_AUTO: + sb.append(u"sign-auto", -1); + break; + case UNUM_SIGN_ALWAYS: + sb.append(u"sign-always", -1); + break; + case UNUM_SIGN_NEVER: + sb.append(u"sign-never", -1); + break; + case UNUM_SIGN_ACCOUNTING: + sb.append(u"sign-accounting", -1); + break; + case UNUM_SIGN_ACCOUNTING_ALWAYS: + sb.append(u"sign-accounting-always", -1); + break; + case UNUM_SIGN_EXCEPT_ZERO: + sb.append(u"sign-except-zero", -1); + break; + case UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO: + sb.append(u"sign-accounting-except-zero", -1); + break; + default: + U_ASSERT(false); + } +} + +void +enum_to_stem_string::decimalSeparatorDisplay(UNumberDecimalSeparatorDisplay value, UnicodeString& sb) { + switch (value) { + case UNUM_DECIMAL_SEPARATOR_AUTO: + sb.append(u"decimal-auto", -1); + break; + case UNUM_DECIMAL_SEPARATOR_ALWAYS: + sb.append(u"decimal-always", -1); + break; + default: + U_ASSERT(false); + } +} + + +UnlocalizedNumberFormatter skeleton::create(const UnicodeString& skeletonString, UErrorCode& status) { + umtx_initOnce(gNumberSkeletonsInitOnce, &initNumberSkeletons, status); + MacroProps macros = parseSkeleton(skeletonString, status); + return NumberFormatter::with().macros(macros); +} + +UnicodeString skeleton::generate(const MacroProps& macros, UErrorCode& status) { + umtx_initOnce(gNumberSkeletonsInitOnce, &initNumberSkeletons, status); + UnicodeString sb; + GeneratorHelpers::generateSkeleton(macros, sb, status); + return sb; +} + +MacroProps skeleton::parseSkeleton(const UnicodeString& skeletonString, UErrorCode& status) { + if (U_FAILURE(status)) { return MacroProps(); } + + // Add a trailing whitespace to the end of the skeleton string to make code cleaner. + UnicodeString tempSkeletonString(skeletonString); + tempSkeletonString.append(u' '); + + SeenMacroProps seen; + MacroProps macros; + StringSegment segment(tempSkeletonString, false); + UCharsTrie stemTrie(kSerializedStemTrie); + ParseState stem = STATE_NULL; + int32_t offset = 0; + + // Primary skeleton parse loop: + while (offset < segment.length()) { + UChar32 cp = segment.codePointAt(offset); + bool isTokenSeparator = PatternProps::isWhiteSpace(cp); + bool isOptionSeparator = (cp == u'/'); + + if (!isTokenSeparator && !isOptionSeparator) { + // Non-separator token; consume it. + offset += U16_LENGTH(cp); + if (stem == STATE_NULL) { + // We are currently consuming a stem. + // Go to the next state in the stem trie. + stemTrie.nextForCodePoint(cp); + } + continue; + } + + // We are looking at a token or option separator. + // If the segment is nonempty, parse it and reset the segment. + // Otherwise, make sure it is a valid repeating separator. + if (offset != 0) { + segment.setLength(offset); + if (stem == STATE_NULL) { + // The first separator after the start of a token. Parse it as a stem. + stem = parseStem(segment, stemTrie, seen, macros, status); + stemTrie.reset(); + } else { + // A separator after the first separator of a token. Parse it as an option. + stem = parseOption(stem, segment, macros, status); + } + segment.resetLength(); + if (U_FAILURE(status)) { return macros; } + + // Consume the segment: + segment.adjustOffset(offset); + offset = 0; + + } else if (stem != STATE_NULL) { + // A separator ('/' or whitespace) following an option separator ('/') + // segment.setLength(U16_LENGTH(cp)); // for error message + // throw new SkeletonSyntaxException("Unexpected separator character", segment); + status = U_NUMBER_SKELETON_SYNTAX_ERROR; + return macros; + + } else { + // Two spaces in a row; this is OK. + } + + // Does the current stem forbid options? + if (isOptionSeparator && stem == STATE_NULL) { + // segment.setLength(U16_LENGTH(cp)); // for error message + // throw new SkeletonSyntaxException("Unexpected option separator", segment); + status = U_NUMBER_SKELETON_SYNTAX_ERROR; + return macros; + } + + // Does the current stem require an option? + if (isTokenSeparator && stem != STATE_NULL) { + switch (stem) { + case STATE_INCREMENT_PRECISION: + case STATE_MEASURE_UNIT: + case STATE_PER_MEASURE_UNIT: + case STATE_CURRENCY_UNIT: + case STATE_INTEGER_WIDTH: + case STATE_NUMBERING_SYSTEM: + case STATE_SCALE: + // segment.setLength(U16_LENGTH(cp)); // for error message + // throw new SkeletonSyntaxException("Stem requires an option", segment); + status = U_NUMBER_SKELETON_SYNTAX_ERROR; + return macros; + default: + break; + } + stem = STATE_NULL; + } + + // Consume the separator: + segment.adjustOffset(U16_LENGTH(cp)); + } + U_ASSERT(stem == STATE_NULL); + return macros; +} + +ParseState +skeleton::parseStem(const StringSegment& segment, const UCharsTrie& stemTrie, SeenMacroProps& seen, + MacroProps& macros, UErrorCode& status) { + // First check for "blueprint" stems, which start with a "signal char" + switch (segment.charAt(0)) { + case u'.': + CHECK_NULL(seen, precision, status); + blueprint_helpers::parseFractionStem(segment, macros, status); + return STATE_FRACTION_PRECISION; + case u'@': + CHECK_NULL(seen, precision, status); + blueprint_helpers::parseDigitsStem(segment, macros, status); + return STATE_NULL; + default: + break; + } + + // Now look at the stemsTrie, which is already be pointing at our stem. + UStringTrieResult stemResult = stemTrie.current(); + + if (stemResult != USTRINGTRIE_INTERMEDIATE_VALUE && stemResult != USTRINGTRIE_FINAL_VALUE) { + // throw new SkeletonSyntaxException("Unknown stem", segment); + status = U_NUMBER_SKELETON_SYNTAX_ERROR; + return STATE_NULL; + } + + auto stem = static_cast(stemTrie.getValue()); + switch (stem) { + + // Stems with meaning on their own, not requiring an option: + + case STEM_COMPACT_SHORT: + case STEM_COMPACT_LONG: + case STEM_SCIENTIFIC: + case STEM_ENGINEERING: + case STEM_NOTATION_SIMPLE: + CHECK_NULL(seen, notation, status); + macros.notation = stem_to_object::notation(stem); + switch (stem) { + case STEM_SCIENTIFIC: + case STEM_ENGINEERING: + return STATE_SCIENTIFIC; // allows for scientific options + default: + return STATE_NULL; + } + + case STEM_BASE_UNIT: + case STEM_PERCENT: + case STEM_PERMILLE: + CHECK_NULL(seen, unit, status); + macros.unit = stem_to_object::unit(stem); + return STATE_NULL; + + case STEM_PRECISION_INTEGER: + case STEM_PRECISION_UNLIMITED: + case STEM_PRECISION_CURRENCY_STANDARD: + case STEM_PRECISION_CURRENCY_CASH: + CHECK_NULL(seen, precision, status); + macros.precision = stem_to_object::precision(stem); + switch (stem) { + case STEM_PRECISION_INTEGER: + return STATE_FRACTION_PRECISION; // allows for "precision-integer/@##" + default: + return STATE_NULL; + } + + case STEM_ROUNDING_MODE_CEILING: + case STEM_ROUNDING_MODE_FLOOR: + case STEM_ROUNDING_MODE_DOWN: + case STEM_ROUNDING_MODE_UP: + case STEM_ROUNDING_MODE_HALF_EVEN: + case STEM_ROUNDING_MODE_HALF_DOWN: + case STEM_ROUNDING_MODE_HALF_UP: + case STEM_ROUNDING_MODE_UNNECESSARY: + CHECK_NULL(seen, roundingMode, status); + macros.roundingMode = stem_to_object::roundingMode(stem); + return STATE_NULL; + + case STEM_GROUP_OFF: + case STEM_GROUP_MIN2: + case STEM_GROUP_AUTO: + case STEM_GROUP_ON_ALIGNED: + case STEM_GROUP_THOUSANDS: + CHECK_NULL(seen, grouper, status); + macros.grouper = Grouper::forStrategy(stem_to_object::groupingStrategy(stem)); + return STATE_NULL; + + case STEM_LATIN: + CHECK_NULL(seen, symbols, status); + macros.symbols.setTo(NumberingSystem::createInstanceByName("latn", status)); + return STATE_NULL; + + case STEM_UNIT_WIDTH_NARROW: + case STEM_UNIT_WIDTH_SHORT: + case STEM_UNIT_WIDTH_FULL_NAME: + case STEM_UNIT_WIDTH_ISO_CODE: + case STEM_UNIT_WIDTH_HIDDEN: + CHECK_NULL(seen, unitWidth, status); + macros.unitWidth = stem_to_object::unitWidth(stem); + return STATE_NULL; + + case STEM_SIGN_AUTO: + case STEM_SIGN_ALWAYS: + case STEM_SIGN_NEVER: + case STEM_SIGN_ACCOUNTING: + case STEM_SIGN_ACCOUNTING_ALWAYS: + case STEM_SIGN_EXCEPT_ZERO: + case STEM_SIGN_ACCOUNTING_EXCEPT_ZERO: + CHECK_NULL(seen, sign, status); + macros.sign = stem_to_object::signDisplay(stem); + return STATE_NULL; + + case STEM_DECIMAL_AUTO: + case STEM_DECIMAL_ALWAYS: + CHECK_NULL(seen, decimal, status); + macros.decimal = stem_to_object::decimalSeparatorDisplay(stem); + return STATE_NULL; + + // Stems requiring an option: + + case STEM_PRECISION_INCREMENT: + CHECK_NULL(seen, precision, status); + return STATE_INCREMENT_PRECISION; + + case STEM_MEASURE_UNIT: + CHECK_NULL(seen, unit, status); + return STATE_MEASURE_UNIT; + + case STEM_PER_MEASURE_UNIT: + CHECK_NULL(seen, perUnit, status); + return STATE_PER_MEASURE_UNIT; + + case STEM_CURRENCY: + CHECK_NULL(seen, unit, status); + return STATE_CURRENCY_UNIT; + + case STEM_INTEGER_WIDTH: + CHECK_NULL(seen, integerWidth, status); + return STATE_INTEGER_WIDTH; + + case STEM_NUMBERING_SYSTEM: + CHECK_NULL(seen, symbols, status); + return STATE_NUMBERING_SYSTEM; + + case STEM_SCALE: + CHECK_NULL(seen, scale, status); + return STATE_SCALE; + + default: + U_ASSERT(false); + return STATE_NULL; // return a value: silence compiler warning + } +} + +ParseState skeleton::parseOption(ParseState stem, const StringSegment& segment, MacroProps& macros, + UErrorCode& status) { + + ///// Required options: ///// + + switch (stem) { + case STATE_CURRENCY_UNIT: + blueprint_helpers::parseCurrencyOption(segment, macros, status); + return STATE_NULL; + case STATE_MEASURE_UNIT: + blueprint_helpers::parseMeasureUnitOption(segment, macros, status); + return STATE_NULL; + case STATE_PER_MEASURE_UNIT: + blueprint_helpers::parseMeasurePerUnitOption(segment, macros, status); + return STATE_NULL; + case STATE_INCREMENT_PRECISION: + blueprint_helpers::parseIncrementOption(segment, macros, status); + return STATE_NULL; + case STATE_INTEGER_WIDTH: + blueprint_helpers::parseIntegerWidthOption(segment, macros, status); + return STATE_NULL; + case STATE_NUMBERING_SYSTEM: + blueprint_helpers::parseNumberingSystemOption(segment, macros, status); + return STATE_NULL; + case STATE_SCALE: + blueprint_helpers::parseScaleOption(segment, macros, status); + return STATE_NULL; + default: + break; + } + + ///// Non-required options: ///// + + // Scientific options + switch (stem) { + case STATE_SCIENTIFIC: + if (blueprint_helpers::parseExponentWidthOption(segment, macros, status)) { + return STATE_SCIENTIFIC; + } + if (U_FAILURE(status)) { + return {}; + } + if (blueprint_helpers::parseExponentSignOption(segment, macros, status)) { + return STATE_SCIENTIFIC; + } + if (U_FAILURE(status)) { + return {}; + } + break; + default: + break; + } + + // Frac-sig option + switch (stem) { + case STATE_FRACTION_PRECISION: + if (blueprint_helpers::parseFracSigOption(segment, macros, status)) { + return STATE_NULL; + } + if (U_FAILURE(status)) { + return {}; + } + break; + default: + break; + } + + // Unknown option + // throw new SkeletonSyntaxException("Invalid option", segment); + status = U_NUMBER_SKELETON_SYNTAX_ERROR; + return STATE_NULL; +} + +void GeneratorHelpers::generateSkeleton(const MacroProps& macros, UnicodeString& sb, UErrorCode& status) { + if (U_FAILURE(status)) { return; } + + // Supported options + if (GeneratorHelpers::notation(macros, sb, status)) { + sb.append(u' '); + } + if (U_FAILURE(status)) { return; } + if (GeneratorHelpers::unit(macros, sb, status)) { + sb.append(u' '); + } + if (U_FAILURE(status)) { return; } + if (GeneratorHelpers::perUnit(macros, sb, status)) { + sb.append(u' '); + } + if (U_FAILURE(status)) { return; } + if (GeneratorHelpers::precision(macros, sb, status)) { + sb.append(u' '); + } + if (U_FAILURE(status)) { return; } + if (GeneratorHelpers::roundingMode(macros, sb, status)) { + sb.append(u' '); + } + if (U_FAILURE(status)) { return; } + if (GeneratorHelpers::grouping(macros, sb, status)) { + sb.append(u' '); + } + if (U_FAILURE(status)) { return; } + if (GeneratorHelpers::integerWidth(macros, sb, status)) { + sb.append(u' '); + } + if (U_FAILURE(status)) { return; } + if (GeneratorHelpers::symbols(macros, sb, status)) { + sb.append(u' '); + } + if (U_FAILURE(status)) { return; } + if (GeneratorHelpers::unitWidth(macros, sb, status)) { + sb.append(u' '); + } + if (U_FAILURE(status)) { return; } + if (GeneratorHelpers::sign(macros, sb, status)) { + sb.append(u' '); + } + if (U_FAILURE(status)) { return; } + if (GeneratorHelpers::decimal(macros, sb, status)) { + sb.append(u' '); + } + if (U_FAILURE(status)) { return; } + if (GeneratorHelpers::scale(macros, sb, status)) { + sb.append(u' '); + } + if (U_FAILURE(status)) { return; } + + // Unsupported options + if (!macros.padder.isBogus()) { + status = U_UNSUPPORTED_ERROR; + return; + } + if (macros.affixProvider != nullptr) { + status = U_UNSUPPORTED_ERROR; + return; + } + if (macros.rules != nullptr) { + status = U_UNSUPPORTED_ERROR; + return; + } + if (macros.currencySymbols != nullptr) { + status = U_UNSUPPORTED_ERROR; + return; + } + + // Remove the trailing space + if (sb.length() > 0) { + sb.truncate(sb.length() - 1); + } +} + + +bool blueprint_helpers::parseExponentWidthOption(const StringSegment& segment, MacroProps& macros, + UErrorCode&) { + if (segment.charAt(0) != u'+') { + return false; + } + int32_t offset = 1; + int32_t minExp = 0; + for (; offset < segment.length(); offset++) { + if (segment.charAt(offset) == u'e') { + minExp++; + } else { + break; + } + } + if (offset < segment.length()) { + return false; + } + // Use the public APIs to enforce bounds checking + macros.notation = static_cast(macros.notation).withMinExponentDigits(minExp); + return true; +} + +void +blueprint_helpers::generateExponentWidthOption(int32_t minExponentDigits, UnicodeString& sb, UErrorCode&) { + sb.append(u'+'); + appendMultiple(sb, u'e', minExponentDigits); +} + +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.toTempUnicodeString().getBuffer(), + segment.length()); + if (result != USTRINGTRIE_INTERMEDIATE_VALUE && result != USTRINGTRIE_FINAL_VALUE) { + return false; + } + auto sign = stem_to_object::signDisplay(static_cast(tempStemTrie.getValue())); + if (sign == UNUM_SIGN_COUNT) { + return false; + } + macros.notation = static_cast(macros.notation).withExponentSignDisplay(sign); + return true; +} + +void blueprint_helpers::parseCurrencyOption(const StringSegment& segment, MacroProps& macros, + UErrorCode& status) { + // Unlike ICU4J, have to check length manually because ICU4C CurrencyUnit does not check it for us + if (segment.length() != 3) { + status = U_NUMBER_SKELETON_SYNTAX_ERROR; + return; + } + const UChar* currencyCode = segment.toTempUnicodeString().getBuffer(); + UErrorCode localStatus = U_ZERO_ERROR; + CurrencyUnit currency(currencyCode, localStatus); + if (U_FAILURE(localStatus)) { + // Not 3 ascii chars + // throw new SkeletonSyntaxException("Invalid currency", segment); + status = U_NUMBER_SKELETON_SYNTAX_ERROR; + return; + } + // Slicing is OK + macros.unit = currency; // NOLINT +} + +void +blueprint_helpers::generateCurrencyOption(const CurrencyUnit& currency, UnicodeString& sb, UErrorCode&) { + sb.append(currency.getISOCurrency(), -1); +} + +void blueprint_helpers::parseMeasureUnitOption(const StringSegment& segment, MacroProps& macros, + UErrorCode& status) { + 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 + int firstHyphen = 0; + while (firstHyphen < stemString.length() && stemString.charAt(firstHyphen) != '-') { + firstHyphen++; + } + if (firstHyphen == stemString.length()) { + // throw new SkeletonSyntaxException("Invalid measure unit option", segment); + status = U_NUMBER_SKELETON_SYNTAX_ERROR; + return; + } + + // Need to do char <-> UChar conversion... + U_ASSERT(U_SUCCESS(status)); + CharString type; + SKELETON_UCHAR_TO_CHAR(type, stemString, 0, firstHyphen, status); + CharString subType; + SKELETON_UCHAR_TO_CHAR(subType, stemString, firstHyphen + 1, stemString.length(), status); + + // Note: the largest type as of this writing (March 2018) is "volume", which has 24 units. + static constexpr int32_t CAPACITY = 30; + MeasureUnit units[CAPACITY]; + UErrorCode localStatus = U_ZERO_ERROR; + int32_t numUnits = MeasureUnit::getAvailable(type.data(), units, CAPACITY, localStatus); + if (U_FAILURE(localStatus)) { + // More than 30 units in this type? + status = U_INTERNAL_PROGRAM_ERROR; + return; + } + for (int32_t i = 0; i < numUnits; i++) { + auto& unit = units[i]; + if (uprv_strcmp(subType.data(), unit.getSubtype()) == 0) { + macros.unit = unit; + return; + } + } + + // throw new SkeletonSyntaxException("Unknown measure unit", segment); + status = U_NUMBER_SKELETON_SYNTAX_ERROR; +} + +void blueprint_helpers::generateMeasureUnitOption(const MeasureUnit& measureUnit, UnicodeString& sb, + UErrorCode&) { + // Need to do char <-> UChar conversion... + sb.append(UnicodeString(measureUnit.getType(), -1, US_INV)); + sb.append(u'-'); + sb.append(UnicodeString(measureUnit.getSubtype(), -1, US_INV)); +} + +void blueprint_helpers::parseMeasurePerUnitOption(const StringSegment& segment, MacroProps& macros, + UErrorCode& status) { + // A little bit of a hack: safe the current unit (numerator), call the main measure unit + // parsing code, put back the numerator unit, and put the new unit into per-unit. + MeasureUnit numerator = macros.unit; + parseMeasureUnitOption(segment, macros, status); + if (U_FAILURE(status)) { return; } + macros.perUnit = macros.unit; + macros.unit = numerator; +} + +void blueprint_helpers::parseFractionStem(const StringSegment& segment, MacroProps& macros, + UErrorCode& status) { + U_ASSERT(segment.charAt(0) == u'.'); + int32_t offset = 1; + int32_t minFrac = 0; + int32_t maxFrac; + for (; offset < segment.length(); offset++) { + if (segment.charAt(offset) == u'0') { + minFrac++; + } else { + break; + } + } + if (offset < segment.length()) { + if (segment.charAt(offset) == u'+') { + maxFrac = -1; + offset++; + } else { + maxFrac = minFrac; + for (; offset < segment.length(); offset++) { + if (segment.charAt(offset) == u'#') { + maxFrac++; + } else { + break; + } + } + } + } else { + maxFrac = minFrac; + } + if (offset < segment.length()) { + // throw new SkeletonSyntaxException("Invalid fraction stem", segment); + status = U_NUMBER_SKELETON_SYNTAX_ERROR; + return; + } + // Use the public APIs to enforce bounds checking + if (maxFrac == -1) { + macros.precision = Precision::minFraction(minFrac); + } else { + macros.precision = Precision::minMaxFraction(minFrac, maxFrac); + } +} + +void +blueprint_helpers::generateFractionStem(int32_t minFrac, int32_t maxFrac, UnicodeString& sb, UErrorCode&) { + if (minFrac == 0 && maxFrac == 0) { + sb.append(u"precision-integer", -1); + return; + } + sb.append(u'.'); + appendMultiple(sb, u'0', minFrac); + if (maxFrac == -1) { + sb.append(u'+'); + } else { + appendMultiple(sb, u'#', maxFrac - minFrac); + } +} + +void +blueprint_helpers::parseDigitsStem(const StringSegment& segment, MacroProps& macros, UErrorCode& status) { + U_ASSERT(segment.charAt(0) == u'@'); + int offset = 0; + int minSig = 0; + int maxSig; + for (; offset < segment.length(); offset++) { + if (segment.charAt(offset) == u'@') { + minSig++; + } else { + break; + } + } + if (offset < segment.length()) { + if (segment.charAt(offset) == u'+') { + maxSig = -1; + offset++; + } else { + maxSig = minSig; + for (; offset < segment.length(); offset++) { + if (segment.charAt(offset) == u'#') { + maxSig++; + } else { + break; + } + } + } + } else { + maxSig = minSig; + } + if (offset < segment.length()) { + // throw new SkeletonSyntaxException("Invalid significant digits stem", segment); + status = U_NUMBER_SKELETON_SYNTAX_ERROR; + return; + } + // Use the public APIs to enforce bounds checking + if (maxSig == -1) { + macros.precision = Precision::minSignificantDigits(minSig); + } else { + macros.precision = Precision::minMaxSignificantDigits(minSig, maxSig); + } +} + +void +blueprint_helpers::generateDigitsStem(int32_t minSig, int32_t maxSig, UnicodeString& sb, UErrorCode&) { + appendMultiple(sb, u'@', minSig); + if (maxSig == -1) { + sb.append(u'+'); + } else { + appendMultiple(sb, u'#', maxSig - minSig); + } +} + +bool blueprint_helpers::parseFracSigOption(const StringSegment& segment, MacroProps& macros, + UErrorCode& status) { + if (segment.charAt(0) != u'@') { + return false; + } + int offset = 0; + int minSig = 0; + int maxSig; + for (; offset < segment.length(); offset++) { + if (segment.charAt(offset) == u'@') { + minSig++; + } else { + break; + } + } + // For the frac-sig option, there must be minSig or maxSig but not both. + // Valid: @+, @@+, @@@+ + // Valid: @#, @##, @### + // Invalid: @, @@, @@@ + // Invalid: @@#, @@##, @@@# + if (offset < segment.length()) { + if (segment.charAt(offset) == u'+') { + maxSig = -1; + offset++; + } else if (minSig > 1) { + // @@#, @@##, @@@# + // throw new SkeletonSyntaxException("Invalid digits option for fraction rounder", segment); + status = U_NUMBER_SKELETON_SYNTAX_ERROR; + return false; + } else { + maxSig = minSig; + for (; offset < segment.length(); offset++) { + if (segment.charAt(offset) == u'#') { + maxSig++; + } else { + break; + } + } + } + } else { + // @, @@, @@@ + // throw new SkeletonSyntaxException("Invalid digits option for fraction rounder", segment); + status = U_NUMBER_SKELETON_SYNTAX_ERROR; + return false; + } + if (offset < segment.length()) { + // throw new SkeletonSyntaxException("Invalid digits option for fraction rounder", segment); + status = U_NUMBER_SKELETON_SYNTAX_ERROR; + return false; + } + + auto& oldPrecision = static_cast(macros.precision); + if (maxSig == -1) { + macros.precision = oldPrecision.withMinDigits(minSig); + } else { + macros.precision = oldPrecision.withMaxDigits(maxSig); + } + return true; +} + +void blueprint_helpers::parseIncrementOption(const StringSegment& segment, MacroProps& macros, + UErrorCode& status) { + // Need to do char <-> UChar conversion... + U_ASSERT(U_SUCCESS(status)); + CharString buffer; + SKELETON_UCHAR_TO_CHAR(buffer, segment.toTempUnicodeString(), 0, segment.length(), status); + + // Utilize DecimalQuantity/decNumber to parse this for us. + DecimalQuantity dq; + UErrorCode localStatus = U_ZERO_ERROR; + dq.setToDecNumber({buffer.data(), buffer.length()}, localStatus); + if (U_FAILURE(localStatus)) { + // throw new SkeletonSyntaxException("Invalid rounding increment", segment, e); + status = U_NUMBER_SKELETON_SYNTAX_ERROR; + return; + } + double increment = dq.toDouble(); + + // We also need to figure out how many digits. Do a brute force string operation. + int decimalOffset = 0; + while (decimalOffset < segment.length() && segment.charAt(decimalOffset) != '.') { + decimalOffset++; + } + if (decimalOffset == segment.length()) { + macros.precision = Precision::increment(increment); + } else { + int32_t fractionLength = segment.length() - decimalOffset - 1; + macros.precision = Precision::increment(increment).withMinFraction(fractionLength); + } +} + +void blueprint_helpers::generateIncrementOption(double increment, int32_t trailingZeros, UnicodeString& sb, + UErrorCode&) { + // Utilize DecimalQuantity/double_conversion to format this for us. + DecimalQuantity dq; + dq.setToDouble(increment); + dq.roundToInfinity(); + sb.append(dq.toPlainString()); + + // We might need to append extra trailing zeros for min fraction... + if (trailingZeros > 0) { + appendMultiple(sb, u'0', trailingZeros); + } +} + +void blueprint_helpers::parseIntegerWidthOption(const StringSegment& segment, MacroProps& macros, + UErrorCode& status) { + int32_t offset = 0; + int32_t minInt = 0; + int32_t maxInt; + if (segment.charAt(0) == u'+') { + maxInt = -1; + offset++; + } else { + maxInt = 0; + } + for (; offset < segment.length(); offset++) { + if (segment.charAt(offset) == u'#') { + maxInt++; + } else { + break; + } + } + if (offset < segment.length()) { + for (; offset < segment.length(); offset++) { + if (segment.charAt(offset) == u'0') { + minInt++; + } else { + break; + } + } + } + if (maxInt != -1) { + maxInt += minInt; + } + if (offset < segment.length()) { + // throw new SkeletonSyntaxException("Invalid integer width stem", segment); + status = U_NUMBER_SKELETON_SYNTAX_ERROR; + return; + } + // Use the public APIs to enforce bounds checking + if (maxInt == -1) { + macros.integerWidth = IntegerWidth::zeroFillTo(minInt); + } else { + macros.integerWidth = IntegerWidth::zeroFillTo(minInt).truncateAt(maxInt); + } +} + +void blueprint_helpers::generateIntegerWidthOption(int32_t minInt, int32_t maxInt, UnicodeString& sb, + UErrorCode&) { + if (maxInt == -1) { + sb.append(u'+'); + } else { + appendMultiple(sb, u'#', maxInt - minInt); + } + appendMultiple(sb, u'0', minInt); +} + +void blueprint_helpers::parseNumberingSystemOption(const StringSegment& segment, MacroProps& macros, + UErrorCode& status) { + // Need to do char <-> UChar conversion... + U_ASSERT(U_SUCCESS(status)); + CharString buffer; + SKELETON_UCHAR_TO_CHAR(buffer, segment.toTempUnicodeString(), 0, segment.length(), status); + + NumberingSystem* ns = NumberingSystem::createInstanceByName(buffer.data(), status); + if (ns == nullptr || U_FAILURE(status)) { + // This is a skeleton syntax error; don't bubble up the low-level NumberingSystem error + // throw new SkeletonSyntaxException("Unknown numbering system", segment); + status = U_NUMBER_SKELETON_SYNTAX_ERROR; + return; + } + macros.symbols.setTo(ns); +} + +void blueprint_helpers::generateNumberingSystemOption(const NumberingSystem& ns, UnicodeString& sb, + UErrorCode&) { + // Need to do char <-> UChar conversion... + sb.append(UnicodeString(ns.getName(), -1, US_INV)); +} + +void blueprint_helpers::parseScaleOption(const StringSegment& segment, MacroProps& macros, + UErrorCode& status) { + // Need to do char <-> UChar conversion... + U_ASSERT(U_SUCCESS(status)); + CharString buffer; + SKELETON_UCHAR_TO_CHAR(buffer, segment.toTempUnicodeString(), 0, segment.length(), status); + + LocalPointer decnum(new DecNum(), status); + if (U_FAILURE(status)) { return; } + decnum->setTo({buffer.data(), buffer.length()}, status); + if (U_FAILURE(status)) { + // This is a skeleton syntax error; don't let the low-level decnum error bubble up + status = U_NUMBER_SKELETON_SYNTAX_ERROR; + return; + } + + // NOTE: The constructor will optimize the decnum for us if possible. + macros.scale = {0, decnum.orphan()}; +} + +void blueprint_helpers::generateScaleOption(int32_t magnitude, const DecNum* arbitrary, UnicodeString& sb, + UErrorCode& status) { + // Utilize DecimalQuantity/double_conversion to format this for us. + DecimalQuantity dq; + if (arbitrary != nullptr) { + dq.setToDecNum(*arbitrary, status); + if (U_FAILURE(status)) { return; } + } else { + dq.setToInt(1); + } + dq.adjustMagnitude(magnitude); + dq.roundToInfinity(); + sb.append(dq.toPlainString()); +} + + +bool GeneratorHelpers::notation(const MacroProps& macros, UnicodeString& sb, UErrorCode& status) { + if (macros.notation.fType == Notation::NTN_COMPACT) { + UNumberCompactStyle style = macros.notation.fUnion.compactStyle; + if (style == UNumberCompactStyle::UNUM_LONG) { + sb.append(u"compact-long", -1); + return true; + } else if (style == UNumberCompactStyle::UNUM_SHORT) { + sb.append(u"compact-short", -1); + return true; + } else { + // Compact notation generated from custom data (not supported in skeleton) + // The other compact notations are literals + status = U_UNSUPPORTED_ERROR; + return false; + } + } else if (macros.notation.fType == Notation::NTN_SCIENTIFIC) { + const Notation::ScientificSettings& impl = macros.notation.fUnion.scientific; + if (impl.fEngineeringInterval == 3) { + sb.append(u"engineering", -1); + } else { + sb.append(u"scientific", -1); + } + if (impl.fMinExponentDigits > 1) { + sb.append(u'/'); + blueprint_helpers::generateExponentWidthOption(impl.fMinExponentDigits, sb, status); + if (U_FAILURE(status)) { + return false; + } + } + if (impl.fExponentSignDisplay != UNUM_SIGN_AUTO) { + sb.append(u'/'); + enum_to_stem_string::signDisplay(impl.fExponentSignDisplay, sb); + } + return true; + } else { + // Default value is not shown in normalized form + return false; + } +} + +bool GeneratorHelpers::unit(const MacroProps& macros, UnicodeString& sb, UErrorCode& status) { + if (utils::unitIsCurrency(macros.unit)) { + sb.append(u"currency/", -1); + CurrencyUnit currency(macros.unit, status); + if (U_FAILURE(status)) { + return false; + } + blueprint_helpers::generateCurrencyOption(currency, sb, status); + return true; + } else if (utils::unitIsNoUnit(macros.unit)) { + if (utils::unitIsPercent(macros.unit)) { + sb.append(u"percent", -1); + return true; + } else if (utils::unitIsPermille(macros.unit)) { + sb.append(u"permille", -1); + return true; + } else { + // Default value is not shown in normalized form + return false; + } + } else { + sb.append(u"measure-unit/", -1); + blueprint_helpers::generateMeasureUnitOption(macros.unit, sb, status); + return true; + } +} + +bool GeneratorHelpers::perUnit(const MacroProps& macros, UnicodeString& sb, UErrorCode& status) { + // Per-units are currently expected to be only MeasureUnits. + if (utils::unitIsNoUnit(macros.perUnit)) { + if (utils::unitIsPercent(macros.perUnit) || utils::unitIsPermille(macros.perUnit)) { + status = U_UNSUPPORTED_ERROR; + return false; + } else { + // Default value: ok to ignore + return false; + } + } else if (utils::unitIsCurrency(macros.perUnit)) { + status = U_UNSUPPORTED_ERROR; + return false; + } else { + sb.append(u"per-measure-unit/", -1); + blueprint_helpers::generateMeasureUnitOption(macros.perUnit, sb, status); + return true; + } +} + +bool GeneratorHelpers::precision(const MacroProps& macros, UnicodeString& sb, UErrorCode& status) { + if (macros.precision.fType == Precision::RND_NONE) { + sb.append(u"precision-unlimited", -1); + } else if (macros.precision.fType == Precision::RND_FRACTION) { + const Precision::FractionSignificantSettings& impl = macros.precision.fUnion.fracSig; + blueprint_helpers::generateFractionStem(impl.fMinFrac, impl.fMaxFrac, sb, status); + } else if (macros.precision.fType == Precision::RND_SIGNIFICANT) { + const Precision::FractionSignificantSettings& impl = macros.precision.fUnion.fracSig; + blueprint_helpers::generateDigitsStem(impl.fMinSig, impl.fMaxSig, sb, status); + } else if (macros.precision.fType == Precision::RND_FRACTION_SIGNIFICANT) { + const Precision::FractionSignificantSettings& impl = macros.precision.fUnion.fracSig; + blueprint_helpers::generateFractionStem(impl.fMinFrac, impl.fMaxFrac, sb, status); + sb.append(u'/'); + if (impl.fMinSig == -1) { + blueprint_helpers::generateDigitsStem(1, impl.fMaxSig, sb, status); + } else { + blueprint_helpers::generateDigitsStem(impl.fMinSig, -1, sb, status); + } + } else if (macros.precision.fType == Precision::RND_INCREMENT) { + const Precision::IncrementSettings& impl = macros.precision.fUnion.increment; + sb.append(u"precision-increment/", -1); + blueprint_helpers::generateIncrementOption( + impl.fIncrement, + impl.fMinFrac - impl.fMaxFrac, + sb, + status); + } else if (macros.precision.fType == Precision::RND_CURRENCY) { + UCurrencyUsage usage = macros.precision.fUnion.currencyUsage; + if (usage == UCURR_USAGE_STANDARD) { + sb.append(u"precision-currency-standard", -1); + } else { + sb.append(u"precision-currency-cash", -1); + } + } else { + // Bogus or Error + return false; + } + + // NOTE: Always return true for rounding because the default value depends on other options. + return true; +} + +bool GeneratorHelpers::roundingMode(const MacroProps& macros, UnicodeString& sb, UErrorCode&) { + if (macros.roundingMode == kDefaultMode) { + return false; // Default + } + enum_to_stem_string::roundingMode(macros.roundingMode, sb); + return true; +} + +bool GeneratorHelpers::grouping(const MacroProps& macros, UnicodeString& sb, UErrorCode& status) { + if (macros.grouper.isBogus()) { + return false; // No value + } else if (macros.grouper.fStrategy == UNUM_GROUPING_COUNT) { + status = U_UNSUPPORTED_ERROR; + return false; + } else if (macros.grouper.fStrategy == UNUM_GROUPING_AUTO) { + return false; // Default value + } else { + enum_to_stem_string::groupingStrategy(macros.grouper.fStrategy, sb); + return true; + } +} + +bool GeneratorHelpers::integerWidth(const MacroProps& macros, UnicodeString& sb, UErrorCode& status) { + if (macros.integerWidth.fHasError || macros.integerWidth.isBogus() || + macros.integerWidth == IntegerWidth::standard()) { + // Error or Default + return false; + } + sb.append(u"integer-width/", -1); + blueprint_helpers::generateIntegerWidthOption( + macros.integerWidth.fUnion.minMaxInt.fMinInt, + macros.integerWidth.fUnion.minMaxInt.fMaxInt, + sb, + status); + return true; +} + +bool GeneratorHelpers::symbols(const MacroProps& macros, UnicodeString& sb, UErrorCode& status) { + if (macros.symbols.isNumberingSystem()) { + const NumberingSystem& ns = *macros.symbols.getNumberingSystem(); + if (uprv_strcmp(ns.getName(), "latn") == 0) { + sb.append(u"latin", -1); + } else { + sb.append(u"numbering-system/", -1); + blueprint_helpers::generateNumberingSystemOption(ns, sb, status); + } + return true; + } else if (macros.symbols.isDecimalFormatSymbols()) { + status = U_UNSUPPORTED_ERROR; + return false; + } else { + // No custom symbols + return false; + } +} + +bool GeneratorHelpers::unitWidth(const MacroProps& macros, UnicodeString& sb, UErrorCode&) { + if (macros.unitWidth == UNUM_UNIT_WIDTH_SHORT || macros.unitWidth == UNUM_UNIT_WIDTH_COUNT) { + return false; // Default or Bogus + } + enum_to_stem_string::unitWidth(macros.unitWidth, sb); + return true; +} + +bool GeneratorHelpers::sign(const MacroProps& macros, UnicodeString& sb, UErrorCode&) { + if (macros.sign == UNUM_SIGN_AUTO || macros.sign == UNUM_SIGN_COUNT) { + return false; // Default or Bogus + } + enum_to_stem_string::signDisplay(macros.sign, sb); + return true; +} + +bool GeneratorHelpers::decimal(const MacroProps& macros, UnicodeString& sb, UErrorCode&) { + if (macros.decimal == UNUM_DECIMAL_SEPARATOR_AUTO || macros.decimal == UNUM_DECIMAL_SEPARATOR_COUNT) { + return false; // Default or Bogus + } + enum_to_stem_string::decimalSeparatorDisplay(macros.decimal, sb); + return true; +} + +bool GeneratorHelpers::scale(const MacroProps& macros, UnicodeString& sb, UErrorCode& status) { + if (!macros.scale.isValid()) { + return false; // Default or Bogus + } + sb.append(u"scale/", -1); + blueprint_helpers::generateScaleOption( + macros.scale.fMagnitude, + macros.scale.fArbitrary, + sb, + status); + return true; +} + + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/number_skeletons.h b/deps/icu-small/source/i18n/number_skeletons.h new file mode 100644 index 00000000000000..0161f5f0ba8c1a --- /dev/null +++ b/deps/icu-small/source/i18n/number_skeletons.h @@ -0,0 +1,327 @@ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING +#ifndef __SOURCE_NUMBER_SKELETONS_H__ +#define __SOURCE_NUMBER_SKELETONS_H__ + +#include "number_types.h" +#include "numparse_types.h" +#include "unicode/ucharstrie.h" + +using icu::numparse::impl::StringSegment; + +U_NAMESPACE_BEGIN namespace number { +namespace impl { + +// Forward-declaration +struct SeenMacroProps; + +// namespace for enums and entrypoint functions +namespace skeleton { + +/////////////////////////////////////////////////////////////////////////////////////// +// NOTE: For an example of how to add a new stem to the number skeleton parser, see: // +// http://bugs.icu-project.org/trac/changeset/41193 // +/////////////////////////////////////////////////////////////////////////////////////// + +/** + * While parsing a skeleton, this enum records what type of option we expect to find next. + */ +enum ParseState { + + // Section 0: We expect whitespace or a stem, but not an option: + + STATE_NULL, + + // Section 1: We might accept an option, but it is not required: + + STATE_SCIENTIFIC, + STATE_FRACTION_PRECISION, + + // Section 2: An option is required: + + STATE_INCREMENT_PRECISION, + STATE_MEASURE_UNIT, + STATE_PER_MEASURE_UNIT, + STATE_CURRENCY_UNIT, + STATE_INTEGER_WIDTH, + STATE_NUMBERING_SYSTEM, + STATE_SCALE, +}; + +/** + * All possible stem literals have an entry in the StemEnum. The enum name is the kebab case stem + * string literal written in upper snake case. + * + * @see StemToObject + * @see #SERIALIZED_STEM_TRIE + */ +enum StemEnum { + + // Section 1: Stems that do not require an option: + + STEM_COMPACT_SHORT, + STEM_COMPACT_LONG, + STEM_SCIENTIFIC, + STEM_ENGINEERING, + STEM_NOTATION_SIMPLE, + STEM_BASE_UNIT, + STEM_PERCENT, + STEM_PERMILLE, + STEM_PRECISION_INTEGER, + STEM_PRECISION_UNLIMITED, + STEM_PRECISION_CURRENCY_STANDARD, + STEM_PRECISION_CURRENCY_CASH, + STEM_ROUNDING_MODE_CEILING, + STEM_ROUNDING_MODE_FLOOR, + STEM_ROUNDING_MODE_DOWN, + STEM_ROUNDING_MODE_UP, + STEM_ROUNDING_MODE_HALF_EVEN, + STEM_ROUNDING_MODE_HALF_DOWN, + STEM_ROUNDING_MODE_HALF_UP, + STEM_ROUNDING_MODE_UNNECESSARY, + STEM_GROUP_OFF, + STEM_GROUP_MIN2, + STEM_GROUP_AUTO, + STEM_GROUP_ON_ALIGNED, + STEM_GROUP_THOUSANDS, + STEM_LATIN, + STEM_UNIT_WIDTH_NARROW, + STEM_UNIT_WIDTH_SHORT, + STEM_UNIT_WIDTH_FULL_NAME, + STEM_UNIT_WIDTH_ISO_CODE, + STEM_UNIT_WIDTH_HIDDEN, + STEM_SIGN_AUTO, + STEM_SIGN_ALWAYS, + STEM_SIGN_NEVER, + STEM_SIGN_ACCOUNTING, + STEM_SIGN_ACCOUNTING_ALWAYS, + STEM_SIGN_EXCEPT_ZERO, + STEM_SIGN_ACCOUNTING_EXCEPT_ZERO, + STEM_DECIMAL_AUTO, + STEM_DECIMAL_ALWAYS, + + // Section 2: Stems that DO require an option: + + STEM_PRECISION_INCREMENT, + STEM_MEASURE_UNIT, + STEM_PER_MEASURE_UNIT, + STEM_CURRENCY, + STEM_INTEGER_WIDTH, + STEM_NUMBERING_SYSTEM, + STEM_SCALE, +}; + +/** + * Creates a NumberFormatter corresponding to the given skeleton string. + * + * @param skeletonString + * A number skeleton string, possibly not in its shortest form. + * @return An UnlocalizedNumberFormatter with behavior defined by the given skeleton string. + */ +UnlocalizedNumberFormatter create(const UnicodeString& skeletonString, UErrorCode& status); + +/** + * Create a skeleton string corresponding to the given NumberFormatter. + * + * @param macros + * The NumberFormatter options object. + * @return A skeleton string in normalized form. + */ +UnicodeString generate(const MacroProps& macros, UErrorCode& status); + +/** + * Converts from a skeleton string to a MacroProps. This method contains the primary parse loop. + * + * Internal: use the create() endpoint instead of this function. + */ +MacroProps parseSkeleton(const UnicodeString& skeletonString, UErrorCode& status); + +/** + * Given that the current segment represents a stem, parse it and save the result. + * + * @return The next state after parsing this stem, corresponding to what subset of options to expect. + */ +ParseState parseStem(const StringSegment& segment, const UCharsTrie& stemTrie, SeenMacroProps& seen, + MacroProps& macros, UErrorCode& status); + +/** + * Given that the current segment represents an option, parse it and save the result. + * + * @return The next state after parsing this option, corresponding to what subset of options to + * expect next. + */ +ParseState +parseOption(ParseState stem, const StringSegment& segment, MacroProps& macros, UErrorCode& status); + +} // namespace skeleton + + +/** + * Namespace for utility methods that convert from StemEnum to corresponding objects or enums. This + * applies to only the "Section 1" stems, those that are well-defined without an option. + */ +namespace stem_to_object { + +Notation notation(skeleton::StemEnum stem); + +MeasureUnit unit(skeleton::StemEnum stem); + +Precision precision(skeleton::StemEnum stem); + +UNumberFormatRoundingMode roundingMode(skeleton::StemEnum stem); + +UGroupingStrategy groupingStrategy(skeleton::StemEnum stem); + +UNumberUnitWidth unitWidth(skeleton::StemEnum stem); + +UNumberSignDisplay signDisplay(skeleton::StemEnum stem); + +UNumberDecimalSeparatorDisplay decimalSeparatorDisplay(skeleton::StemEnum stem); + +} // namespace stem_to_object + +/** + * Namespace for utility methods that convert from enums to stem strings. More complex object conversions + * take place in the object_to_stem_string namespace. + */ +namespace enum_to_stem_string { + +void roundingMode(UNumberFormatRoundingMode value, UnicodeString& sb); + +void groupingStrategy(UGroupingStrategy value, UnicodeString& sb); + +void unitWidth(UNumberUnitWidth value, UnicodeString& sb); + +void signDisplay(UNumberSignDisplay value, UnicodeString& sb); + +void decimalSeparatorDisplay(UNumberDecimalSeparatorDisplay value, UnicodeString& sb); + +} // namespace enum_to_stem_string + +/** + * Namespace for utility methods for processing stems and options that cannot be interpreted literally. + */ +namespace blueprint_helpers { + +/** @return Whether we successfully found and parsed an exponent width option. */ +bool parseExponentWidthOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status); + +void generateExponentWidthOption(int32_t minExponentDigits, UnicodeString& sb, UErrorCode& status); + +/** @return Whether we successfully found and parsed an exponent sign option. */ +bool parseExponentSignOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status); + +void parseCurrencyOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status); + +void generateCurrencyOption(const CurrencyUnit& currency, UnicodeString& sb, UErrorCode& status); + +void parseMeasureUnitOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status); + +void generateMeasureUnitOption(const MeasureUnit& measureUnit, UnicodeString& sb, UErrorCode& status); + +void parseMeasurePerUnitOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status); + +void parseFractionStem(const StringSegment& segment, MacroProps& macros, UErrorCode& status); + +void generateFractionStem(int32_t minFrac, int32_t maxFrac, UnicodeString& sb, UErrorCode& status); + +void parseDigitsStem(const StringSegment& segment, MacroProps& macros, UErrorCode& status); + +void generateDigitsStem(int32_t minSig, int32_t maxSig, UnicodeString& sb, UErrorCode& status); + +/** @return Whether we successfully found and parsed a frac-sig option. */ +bool parseFracSigOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status); + +void parseIncrementOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status); + +void +generateIncrementOption(double increment, int32_t trailingZeros, UnicodeString& sb, UErrorCode& status); + +void parseIntegerWidthOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status); + +void generateIntegerWidthOption(int32_t minInt, int32_t maxInt, UnicodeString& sb, UErrorCode& status); + +void parseNumberingSystemOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status); + +void generateNumberingSystemOption(const NumberingSystem& ns, UnicodeString& sb, UErrorCode& status); + +void parseScaleOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status); + +void generateScaleOption(int32_t magnitude, const DecNum* arbitrary, UnicodeString& sb, + UErrorCode& status); + +} // namespace blueprint_helpers + +/** + * Class for utility methods for generating a token corresponding to each macro-prop. Each method + * returns whether or not a token was written to the string builder. + * + * This needs to be a class, not a namespace, so it can be friended. + */ +class GeneratorHelpers { + public: + /** + * Main skeleton generator function. Appends the normalized skeleton for the MacroProps to the given + * StringBuilder. + * + * Internal: use the create() endpoint instead of this function. + */ + static void generateSkeleton(const MacroProps& macros, UnicodeString& sb, UErrorCode& status); + + private: + static bool notation(const MacroProps& macros, UnicodeString& sb, UErrorCode& status); + + static bool unit(const MacroProps& macros, UnicodeString& sb, UErrorCode& status); + + static bool perUnit(const MacroProps& macros, UnicodeString& sb, UErrorCode& status); + + static bool precision(const MacroProps& macros, UnicodeString& sb, UErrorCode& status); + + static bool roundingMode(const MacroProps& macros, UnicodeString& sb, UErrorCode& status); + + static bool grouping(const MacroProps& macros, UnicodeString& sb, UErrorCode& status); + + static bool integerWidth(const MacroProps& macros, UnicodeString& sb, UErrorCode& status); + + static bool symbols(const MacroProps& macros, UnicodeString& sb, UErrorCode& status); + + static bool unitWidth(const MacroProps& macros, UnicodeString& sb, UErrorCode& status); + + static bool sign(const MacroProps& macros, UnicodeString& sb, UErrorCode& status); + + static bool decimal(const MacroProps& macros, UnicodeString& sb, UErrorCode& status); + + static bool scale(const MacroProps& macros, UnicodeString& sb, UErrorCode& status); + +}; + +/** + * Struct for null-checking. + * In Java, we can just check the object reference. In C++, we need a different method. + */ +struct SeenMacroProps { + bool notation = false; + bool unit = false; + bool perUnit = false; + bool precision = false; + bool roundingMode = false; + bool grouper = false; + bool padder = false; + bool integerWidth = false; + bool symbols = false; + bool unitWidth = false; + bool sign = false; + bool decimal = false; + bool scale = false; +}; + +} // namespace impl +} // namespace number +U_NAMESPACE_END + +#endif //__SOURCE_NUMBER_SKELETONS_H__ +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/number_stringbuilder.cpp b/deps/icu-small/source/i18n/number_stringbuilder.cpp index 37159d7e53a60a..37770d11d51dfb 100644 --- a/deps/icu-small/source/i18n/number_stringbuilder.cpp +++ b/deps/icu-small/source/i18n/number_stringbuilder.cpp @@ -3,11 +3,10 @@ #include "unicode/utypes.h" -#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT +#if !UCONFIG_NO_FORMATTING #include "number_stringbuilder.h" #include "unicode/utf16.h" -#include "uvectr32.h" using namespace icu; using namespace icu::number; @@ -337,6 +336,11 @@ UnicodeString NumberStringBuilder::toUnicodeString() const { return UnicodeString(getCharPtr() + fZero, fLength); } +const UnicodeString NumberStringBuilder::toTempUnicodeString() const { + // Readonly-alias constructor: + return UnicodeString(FALSE, getCharPtr() + fZero, fLength); +} + UnicodeString NumberStringBuilder::toDebugString() const { UnicodeString sb; sb.append(u"= UNUM_FIELD_COUNT) { status = U_ILLEGAL_ARGUMENT_ERROR; - return; + return FALSE; } auto field = static_cast(rawField); bool seenStart = false; int32_t fractionStart = -1; - for (int i = fZero; i <= fZero + fLength; i++) { + int32_t startIndex = fp.getEndIndex(); + for (int i = fZero + startIndex; i <= fZero + fLength; i++) { Field _field = UNUM_FIELD_COUNT; if (i < fZero + fLength) { _field = getFieldPtr()[i]; @@ -434,10 +439,10 @@ void NumberStringBuilder::populateFieldPosition(FieldPosition &fp, int32_t offse if (field == UNUM_INTEGER_FIELD && _field == UNUM_GROUPING_SEPARATOR_FIELD) { continue; } - fp.setEndIndex(i - fZero + offset); + fp.setEndIndex(i - fZero); break; } else if (!seenStart && field == _field) { - fp.setBeginIndex(i - fZero + offset); + fp.setBeginIndex(i - fZero); seenStart = true; } if (_field == UNUM_INTEGER_FIELD || _field == UNUM_DECIMAL_SEPARATOR_FIELD) { @@ -445,36 +450,28 @@ void NumberStringBuilder::populateFieldPosition(FieldPosition &fp, int32_t offse } } - // Backwards compatibility: FRACTION needs to start after INTEGER if empty - if (field == UNUM_FRACTION_FIELD && !seenStart) { - fp.setBeginIndex(fractionStart + offset); - fp.setEndIndex(fractionStart + offset); + // Backwards compatibility: FRACTION needs to start after INTEGER if empty. + // Do not return that a field was found, though, since there is not actually a fraction part. + if (field == UNUM_FRACTION_FIELD && !seenStart && fractionStart != -1) { + fp.setBeginIndex(fractionStart); + fp.setEndIndex(fractionStart); } -} -void NumberStringBuilder::populateFieldPositionIterator(FieldPositionIterator &fpi, UErrorCode &status) const { - // TODO: Set an initial capacity on uvec? - LocalPointer uvec(new UVector32(status)); - if (U_FAILURE(status)) { - return; - } + return seenStart; +} +void NumberStringBuilder::getAllFieldPositions(FieldPositionIteratorHandler& fpih, + UErrorCode& status) const { Field current = UNUM_FIELD_COUNT; int32_t currentStart = -1; for (int32_t i = 0; i < fLength; i++) { Field field = fieldAt(i); if (current == UNUM_INTEGER_FIELD && field == UNUM_GROUPING_SEPARATOR_FIELD) { // Special case: GROUPING_SEPARATOR counts as an INTEGER. - // Add the field, followed by the start index, followed by the end index to uvec. - uvec->addElement(UNUM_GROUPING_SEPARATOR_FIELD, status); - uvec->addElement(i, status); - uvec->addElement(i + 1, status); + fpih.addAttribute(UNUM_GROUPING_SEPARATOR_FIELD, i, i + 1); } else if (current != field) { if (current != UNUM_FIELD_COUNT) { - // Add the field, followed by the start index, followed by the end index to uvec. - uvec->addElement(current, status); - uvec->addElement(currentStart, status); - uvec->addElement(i, status); + fpih.addAttribute(current, currentStart, i); } current = field; currentStart = i; @@ -484,14 +481,8 @@ void NumberStringBuilder::populateFieldPositionIterator(FieldPositionIterator &f } } if (current != UNUM_FIELD_COUNT) { - // Add the field, followed by the start index, followed by the end index to uvec. - uvec->addElement(current, status); - uvec->addElement(currentStart, status); - uvec->addElement(fLength, status); + fpih.addAttribute(current, currentStart, fLength); } - - // Give uvec to the FieldPositionIterator, which adopts it. - fpi.setData(uvec.orphan(), status); } #endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/number_stringbuilder.h b/deps/icu-small/source/i18n/number_stringbuilder.h index a97cc9ca026ad0..cd8ce2f805e994 100644 --- a/deps/icu-small/source/i18n/number_stringbuilder.h +++ b/deps/icu-small/source/i18n/number_stringbuilder.h @@ -3,7 +3,7 @@ #include "unicode/utypes.h" -#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT +#if !UCONFIG_NO_FORMATTING #ifndef __NUMBER_STRINGBUILDER_H__ #define __NUMBER_STRINGBUILDER_H__ @@ -14,6 +14,7 @@ #include "cstring.h" #include "uassert.h" #include "number_types.h" +#include "fphdlimp.h" U_NAMESPACE_BEGIN namespace number { namespace impl { @@ -84,17 +85,26 @@ 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; bool contentEquals(const NumberStringBuilder &other) const; - void populateFieldPosition(FieldPosition &fp, int32_t offset, UErrorCode &status) const; + bool nextFieldPosition(FieldPosition& fp, UErrorCode& status) const; - void populateFieldPositionIterator(FieldPositionIterator &fpi, UErrorCode &status) const; + void getAllFieldPositions(FieldPositionIteratorHandler& fpih, UErrorCode& status) const; private: bool fUsingHeap = false; diff --git a/deps/icu-small/source/i18n/number_types.h b/deps/icu-small/source/i18n/number_types.h index c01765e2cea6c6..57da72f8aa0ac1 100644 --- a/deps/icu-small/source/i18n/number_types.h +++ b/deps/icu-small/source/i18n/number_types.h @@ -3,7 +3,7 @@ #include "unicode/utypes.h" -#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT +#if !UCONFIG_NO_FORMATTING #ifndef __NUMBER_TYPES_H__ #define __NUMBER_TYPES_H__ @@ -15,9 +15,9 @@ #include "unicode/utf16.h" #include "uassert.h" #include "unicode/platform.h" +#include "unicode/uniset.h" -U_NAMESPACE_BEGIN -namespace number { +U_NAMESPACE_BEGIN namespace number { namespace impl { // Typedef several enums for brevity and for easier comparison to Java. @@ -39,9 +39,6 @@ static constexpr RoundingMode kDefaultMode = RoundingMode::UNUM_FOUND_HALFEVEN; // ICU4J Equivalent: Padder.FALLBACK_PADDING_STRING static constexpr char16_t kFallbackPaddingString[] = u" "; -// ICU4J Equivalent: NumberFormatterImpl.DEFAULT_CURRENCY -static constexpr char16_t kDefaultCurrency[] = u"XXX"; - // Forward declarations: class Modifier; @@ -87,35 +84,10 @@ enum AffixPatternType { }; enum CompactType { - TYPE_DECIMAL, - TYPE_CURRENCY + TYPE_DECIMAL, TYPE_CURRENCY }; -// TODO: Should this be moved somewhere else, maybe where other ICU classes can use it? -// Exported as U_I18N_API because it is a base class for other exported types -class U_I18N_API CharSequence { -public: - virtual ~CharSequence() = default; - - virtual int32_t length() const = 0; - - virtual char16_t charAt(int32_t index) const = 0; - - virtual UChar32 codePointAt(int32_t index) const { - // Default implementation; can be overridden with a more efficient version - char16_t leading = charAt(index); - if (U16_IS_LEAD(leading) && length() > index + 1) { - char16_t trailing = charAt(index + 1); - return U16_GET_SUPPLEMENTARY(leading, trailing); - } else { - return leading; - } - } - - virtual UnicodeString toUnicodeString() const = 0; -}; - class U_I18N_API AffixPatternProvider { public: static const int32_t AFFIX_PLURAL_MASK = 0xff; @@ -123,12 +95,20 @@ class U_I18N_API AffixPatternProvider { static const int32_t AFFIX_NEGATIVE_SUBPATTERN = 0x200; static const int32_t AFFIX_PADDING = 0x400; - virtual ~AffixPatternProvider() = default; + // Convenience compound flags + static const int32_t AFFIX_POS_PREFIX = AFFIX_PREFIX; + static const int32_t AFFIX_POS_SUFFIX = 0; + static const int32_t AFFIX_NEG_PREFIX = AFFIX_PREFIX | AFFIX_NEGATIVE_SUBPATTERN; + static const int32_t AFFIX_NEG_SUFFIX = AFFIX_NEGATIVE_SUBPATTERN; + + virtual ~AffixPatternProvider(); virtual char16_t charAt(int flags, int i) const = 0; virtual int length(int flags) const = 0; + virtual UnicodeString getString(int flags) const = 0; + virtual bool hasCurrencySign() const = 0; virtual bool positiveHasPlusSign() const = 0; @@ -137,7 +117,7 @@ class U_I18N_API AffixPatternProvider { virtual bool negativeHasMinusSign() const = 0; - virtual bool containsSymbolType(AffixPatternType, UErrorCode &) const = 0; + virtual bool containsSymbolType(AffixPatternType, UErrorCode&) const = 0; /** * True if the pattern has a number placeholder like "0" or "#,##0.00"; false if the pattern does not @@ -159,7 +139,7 @@ class U_I18N_API AffixPatternProvider { */ class U_I18N_API Modifier { public: - virtual ~Modifier() = default; + virtual ~Modifier(); /** * Apply this Modifier to the string builder. @@ -173,8 +153,8 @@ class U_I18N_API Modifier { * formatted. * @return The number of characters (UTF-16 code units) that were added to the string builder. */ - virtual int32_t - apply(NumberStringBuilder &output, int leftIndex, int rightIndex, UErrorCode &status) const = 0; + virtual int32_t apply(NumberStringBuilder& output, int leftIndex, int rightIndex, + UErrorCode& status) const = 0; /** * Gets the length of the prefix. This information can be used in combination with {@link #apply} to extract the @@ -187,7 +167,7 @@ class U_I18N_API Modifier { /** * Returns the number of code points in the modifier, prefix plus suffix. */ - virtual int32_t getCodePointCount(UErrorCode &status) const = 0; + virtual int32_t getCodePointCount(UErrorCode& status) const = 0; /** * Whether this modifier is strong. If a modifier is strong, it should always be applied immediately and not allowed @@ -219,7 +199,7 @@ class U_I18N_API Modifier { */ class U_I18N_API MicroPropsGenerator { public: - virtual ~MicroPropsGenerator() = default; + virtual ~MicroPropsGenerator(); /** * Considers the given {@link DecimalQuantity}, optionally mutates it, and returns a {@link MicroProps}. @@ -230,7 +210,8 @@ class U_I18N_API MicroPropsGenerator { * The MicroProps instance to populate. * @return A MicroProps instance resolved for the quantity. */ - virtual void processQuantity(DecimalQuantity& quantity, MicroProps& micros, UErrorCode& status) const = 0; + virtual void processQuantity(DecimalQuantity& quantity, MicroProps& micros, + UErrorCode& status) const = 0; }; /** @@ -238,7 +219,7 @@ class U_I18N_API MicroPropsGenerator { */ class MultiplierProducer { public: - virtual ~MultiplierProducer() = default; + virtual ~MultiplierProducer(); /** * Maps a magnitude to a multiplier in powers of ten. For example, in compact notation in English, a magnitude of 5 @@ -255,24 +236,31 @@ class MultiplierProducer { template class U_I18N_API NullableValue { public: - NullableValue() : fNull(true) {} + NullableValue() + : fNull(true) {} - NullableValue(const NullableValue &other) = default; + NullableValue(const NullableValue& other) = default; - explicit NullableValue(const T &other) { + explicit NullableValue(const T& other) { fValue = other; fNull = false; } - NullableValue &operator=(const NullableValue &other) = default; + NullableValue& operator=(const NullableValue& other) { + fNull = other.fNull; + if (!fNull) { + fValue = other.fValue; + } + return *this; + } - NullableValue &operator=(const T &other) { + NullableValue& operator=(const T& other) { fValue = other; fNull = false; return *this; } - bool operator==(const NullableValue &other) const { + bool operator==(const NullableValue& other) const { // "fValue == other.fValue" returns UBool, not bool (causes compiler warnings) return fNull ? other.fNull : (other.fNull ? false : static_cast(fValue == other.fValue)); } @@ -286,13 +274,21 @@ class U_I18N_API NullableValue { return fNull; } - T get(UErrorCode &status) const { + T get(UErrorCode& status) const { if (fNull) { status = U_UNDEFINED_VARIABLE; } return fValue; } + T getNoError() const { + return fValue; + } + + T getOrDefault(T defaultValue) const { + return fNull ? defaultValue : fValue; + } + private: bool fNull; T fValue; diff --git a/deps/icu-small/source/i18n/number_utils.cpp b/deps/icu-small/source/i18n/number_utils.cpp new file mode 100644 index 00000000000000..c79d2de9fa3f55 --- /dev/null +++ b/deps/icu-small/source/i18n/number_utils.cpp @@ -0,0 +1,253 @@ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +// Allow implicit conversion from char16_t* to UnicodeString for this file: +// Helpful in toString methods and elsewhere. +#define UNISTR_FROM_STRING_EXPLICIT + +#include +#include +#include "number_decnum.h" +#include "number_types.h" +#include "number_utils.h" +#include "charstr.h" +#include "decContext.h" +#include "decNumber.h" +#include "double-conversion.h" +#include "fphdlimp.h" +#include "uresimp.h" +#include "ureslocs.h" + +using namespace icu; +using namespace icu::number; +using namespace icu::number::impl; + +using icu::double_conversion::DoubleToStringConverter; + + +namespace { + +const char16_t* +doGetPattern(UResourceBundle* res, const char* nsName, const char* patternKey, UErrorCode& publicStatus, + UErrorCode& localStatus) { + // Construct the path into the resource bundle + CharString key; + key.append("NumberElements/", publicStatus); + key.append(nsName, publicStatus); + key.append("/patterns/", publicStatus); + key.append(patternKey, publicStatus); + if (U_FAILURE(publicStatus)) { + return u""; + } + return ures_getStringByKeyWithFallback(res, key.data(), nullptr, &localStatus); +} + +} + + +const char16_t* utils::getPatternForStyle(const Locale& locale, const char* nsName, CldrPatternStyle style, + UErrorCode& status) { + const char* patternKey; + switch (style) { + case CLDR_PATTERN_STYLE_DECIMAL: + patternKey = "decimalFormat"; + break; + case CLDR_PATTERN_STYLE_CURRENCY: + patternKey = "currencyFormat"; + break; + case CLDR_PATTERN_STYLE_ACCOUNTING: + patternKey = "accountingFormat"; + break; + case CLDR_PATTERN_STYLE_PERCENT: + patternKey = "percentFormat"; + break; + case CLDR_PATTERN_STYLE_SCIENTIFIC: + patternKey = "scientificFormat"; + break; + default: + patternKey = "decimalFormat"; // silence compiler error + U_ASSERT(false); + } + LocalUResourceBundlePointer res(ures_open(nullptr, locale.getName(), &status)); + if (U_FAILURE(status)) { return u""; } + + // Attempt to get the pattern with the native numbering system. + UErrorCode localStatus = U_ZERO_ERROR; + const char16_t* pattern; + pattern = doGetPattern(res.getAlias(), nsName, patternKey, status, localStatus); + if (U_FAILURE(status)) { return u""; } + + // Fall back to latn if native numbering system does not have the right pattern + if (U_FAILURE(localStatus) && uprv_strcmp("latn", nsName) != 0) { + localStatus = U_ZERO_ERROR; + pattern = doGetPattern(res.getAlias(), "latn", patternKey, status, localStatus); + if (U_FAILURE(status)) { return u""; } + } + + return pattern; +} + + +DecNum::DecNum() { + uprv_decContextDefault(&fContext, DEC_INIT_BASE); + uprv_decContextSetRounding(&fContext, DEC_ROUND_HALF_EVEN); + fContext.traps = 0; // no traps, thank you (what does this even mean?) +} + +DecNum::DecNum(const DecNum& other, UErrorCode& status) + : fContext(other.fContext) { + // Allocate memory for the new DecNum. + U_ASSERT(fContext.digits == other.fData.getCapacity()); + if (fContext.digits > kDefaultDigits) { + void* p = fData.resize(fContext.digits, 0); + if (p == nullptr) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + } + + // Copy the data from the old DecNum to the new one. + uprv_memcpy(fData.getAlias(), other.fData.getAlias(), sizeof(decNumber)); + uprv_memcpy(fData.getArrayStart(), + other.fData.getArrayStart(), + other.fData.getArrayLimit() - other.fData.getArrayStart()); +} + +void DecNum::setTo(StringPiece str, UErrorCode& status) { + // We need NUL-terminated for decNumber; CharString guarantees this, but not StringPiece. + CharString cstr(str, status); + if (U_FAILURE(status)) { return; } + _setTo(cstr.data(), str.length(), status); +} + +void DecNum::setTo(const char* str, UErrorCode& status) { + _setTo(str, static_cast(uprv_strlen(str)), status); +} + +void DecNum::setTo(double d, UErrorCode& status) { + // Need to check for NaN and Infinity before going into DoubleToStringConverter + if (std::isnan(d) != 0 || std::isfinite(d) == 0) { + status = U_UNSUPPORTED_ERROR; + return; + } + + // First convert from double to string, then string to DecNum. + // Allocate enough room for: all digits, "E-324", and NUL-terminator. + char buffer[DoubleToStringConverter::kBase10MaximalLength + 6]; + bool sign; // unused; always positive + int32_t length; + int32_t point; + DoubleToStringConverter::DoubleToAscii( + d, + DoubleToStringConverter::DtoaMode::SHORTEST, + 0, + buffer, + sizeof(buffer), + &sign, + &length, + &point + ); + + // Read initial result as a string. + _setTo(buffer, length, status); + + // Set exponent and bitmask. Note that DoubleToStringConverter does not do negatives. + fData.getAlias()->exponent += point - length; + fData.getAlias()->bits |= static_cast(std::signbit(d) ? DECNEG : 0); +} + +void DecNum::_setTo(const char* str, int32_t maxDigits, UErrorCode& status) { + if (maxDigits > kDefaultDigits) { + fData.resize(maxDigits, 0); + fContext.digits = maxDigits; + } else { + fContext.digits = kDefaultDigits; + } + + static_assert(DECDPUN == 1, "Assumes that DECDPUN is set to 1"); + uprv_decNumberFromString(fData.getAlias(), str, &fContext); + + // Check for invalid syntax and set the corresponding error code. + if ((fContext.status & DEC_Conversion_syntax) != 0) { + status = U_DECIMAL_NUMBER_SYNTAX_ERROR; + return; + } else if (fContext.status != 0) { + // Not a syntax error, but some other error, like an exponent that is too large. + status = U_UNSUPPORTED_ERROR; + return; + } + + // For consistency with Java BigDecimal, no support for DecNum that is NaN or Infinity! + if (decNumberIsSpecial(fData.getAlias())) { + status = U_UNSUPPORTED_ERROR; + return; + } +} + +void +DecNum::setTo(const uint8_t* bcd, int32_t length, int32_t scale, bool isNegative, UErrorCode& status) { + if (length > kDefaultDigits) { + fData.resize(length, 0); + fContext.digits = length; + } else { + fContext.digits = kDefaultDigits; + } + + // "digits is of type int32_t, and must have a value in the range 1 through 999,999,999." + if (length < 1 || length > 999999999) { + // Too large for decNumber + status = U_UNSUPPORTED_ERROR; + return; + } + // "The exponent field holds the exponent of the number. Its range is limited by the requirement that + // "the range of the adjusted exponent of the number be balanced and fit within a whole number of + // "decimal digits (in this implementation, be –999,999,999 through +999,999,999). The adjusted + // "exponent is the exponent that would result if the number were expressed with a single digit before + // "the decimal point, and is therefore given by exponent+digits-1." + if (scale > 999999999 - length + 1 || scale < -999999999 - length + 1) { + // Too large for decNumber + status = U_UNSUPPORTED_ERROR; + return; + } + + fData.getAlias()->digits = length; + fData.getAlias()->exponent = scale; + fData.getAlias()->bits = static_cast(isNegative ? DECNEG : 0); + uprv_decNumberSetBCD(fData, bcd, static_cast(length)); + if (fContext.status != 0) { + // Some error occurred while constructing the decNumber. + status = U_INTERNAL_PROGRAM_ERROR; + } +} + +void DecNum::normalize() { + uprv_decNumberReduce(fData, fData, &fContext); +} + +void DecNum::multiplyBy(const DecNum& rhs, UErrorCode& status) { + uprv_decNumberMultiply(fData, fData, rhs.fData, &fContext); + if (fContext.status != 0) { + status = U_INTERNAL_PROGRAM_ERROR; + } +} + +void DecNum::divideBy(const DecNum& rhs, UErrorCode& status) { + uprv_decNumberDivide(fData, fData, rhs.fData, &fContext); + if (fContext.status != 0) { + status = U_INTERNAL_PROGRAM_ERROR; + } +} + +bool DecNum::isNegative() const { + return decNumberIsNegative(fData.getAlias()); +} + +bool DecNum::isZero() const { + return decNumberIsZero(fData.getAlias()); +} + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/number_utils.h b/deps/icu-small/source/i18n/number_utils.h index 3a408d6007a2cd..c367166009c0dc 100644 --- a/deps/icu-small/source/i18n/number_utils.h +++ b/deps/icu-small/source/i18n/number_utils.h @@ -3,7 +3,7 @@ #include "unicode/utypes.h" -#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT +#if !UCONFIG_NO_FORMATTING #ifndef __NUMBER_UTILS_H__ #define __NUMBER_UTILS_H__ @@ -13,116 +13,80 @@ #include "number_scientific.h" #include "number_patternstring.h" #include "number_modifiers.h" +#include "number_multiplier.h" +#include "number_roundingutils.h" +#include "decNumber.h" +#include "charstr.h" -U_NAMESPACE_BEGIN namespace number { -namespace impl { +U_NAMESPACE_BEGIN -class UnicodeStringCharSequence : public CharSequence { - public: - explicit UnicodeStringCharSequence(const UnicodeString &other) { - fStr = other; - } +namespace number { +namespace impl { - ~UnicodeStringCharSequence() U_OVERRIDE = default; +enum CldrPatternStyle { + CLDR_PATTERN_STYLE_DECIMAL, + CLDR_PATTERN_STYLE_CURRENCY, + CLDR_PATTERN_STYLE_ACCOUNTING, + CLDR_PATTERN_STYLE_PERCENT, + CLDR_PATTERN_STYLE_SCIENTIFIC, + CLDR_PATTERN_STYLE_COUNT, +}; - int32_t length() const U_OVERRIDE { - return fStr.length(); - } +// Namespace for naked functions +namespace utils { - char16_t charAt(int32_t index) const U_OVERRIDE { - return fStr.charAt(index); +inline int32_t insertDigitFromSymbols(NumberStringBuilder& output, int32_t index, int8_t digit, + const DecimalFormatSymbols& symbols, Field field, + UErrorCode& status) { + if (symbols.getCodePointZero() != -1) { + return output.insertCodePoint(index, symbols.getCodePointZero() + digit, field, status); } + return output.insert(index, symbols.getConstDigitSymbol(digit), field, status); +} - UChar32 codePointAt(int32_t index) const U_OVERRIDE { - return fStr.char32At(index); - } +inline bool unitIsCurrency(const MeasureUnit& unit) { + return uprv_strcmp("currency", unit.getType()) == 0; +} - 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; - } +inline bool unitIsNoUnit(const MeasureUnit& unit) { + return uprv_strcmp("none", unit.getType()) == 0; +} - private: - UnicodeString fStr; -}; +inline bool unitIsPercent(const MeasureUnit& unit) { + return uprv_strcmp("percent", unit.getSubtype()) == 0; +} -struct MicroProps : public MicroPropsGenerator { - - // NOTE: All of these fields are properly initialized in NumberFormatterImpl. - Rounder rounding; - Grouper grouping; - Padder padding; - IntegerWidth integerWidth; - UNumberSignDisplay sign; - UNumberDecimalSeparatorDisplay decimal; - bool useCurrency; - - // Note: This struct has no direct ownership of the following pointers. - const DecimalFormatSymbols *symbols; - const Modifier *modOuter; - const Modifier *modMiddle; - const Modifier *modInner; - - // The following "helper" fields may optionally be used during the MicroPropsGenerator. - // They live here to retain memory. - struct { - ScientificModifier scientificModifier; - EmptyModifier emptyWeakModifier{false}; - EmptyModifier emptyStrongModifier{true}; - } helpers; - - - MicroProps() = default; - - MicroProps(const MicroProps &other) = default; - - MicroProps &operator=(const MicroProps &other) = default; - - void processQuantity(DecimalQuantity &, MicroProps µs, UErrorCode &status) const U_OVERRIDE { - (void)status; - if (this == µs) { - // Unsafe path: no need to perform a copy. - U_ASSERT(!exhausted); - micros.exhausted = true; - U_ASSERT(exhausted); - } else { - // Safe path: copy self into the output micros. - micros = *this; - } - } +inline bool unitIsPermille(const MeasureUnit& unit) { + return uprv_strcmp("permille", unit.getSubtype()) == 0; +} - private: - // Internal fields: - bool exhausted = false; -}; +// NOTE: In Java, this method is in NumberFormat.java +const char16_t* +getPatternForStyle(const Locale& locale, const char* nsName, CldrPatternStyle style, UErrorCode& status); /** - * This struct provides the result of the number formatting pipeline to FormattedNumber. + * Computes the plural form for this number based on the specified set of rules. * - * The DecimalQuantity is not currently being used by FormattedNumber, but at some point it could be used - * to add a toDecNumber() or similar method. + * @param rules A {@link PluralRules} object representing the set of rules. + * @return The {@link StandardPlural} according to the PluralRules. If the plural form is not in + * the set of standard plurals, {@link StandardPlural#OTHER} is returned instead. */ -struct NumberFormatterResults : public UMemory { - DecimalQuantity quantity; - NumberStringBuilder string; -}; - -inline const UnicodeString getDigitFromSymbols(int8_t digit, const DecimalFormatSymbols &symbols) { - // TODO: Implement DecimalFormatSymbols.getCodePointZero()? - if (digit == 0) { - return symbols.getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kZeroDigitSymbol); +inline StandardPlural::Form getStandardPlural(const PluralRules *rules, + const IFixedDecimal &fdec) { + if (rules == nullptr) { + // Fail gracefully if the user didn't provide a PluralRules + return StandardPlural::Form::OTHER; } else { - return symbols.getSymbol(static_cast( - DecimalFormatSymbols::ENumberFormatSymbol::kOneDigitSymbol + digit - 1)); + UnicodeString ruleString = rules->select(fdec); + return StandardPlural::orOtherFromString(ruleString); } } +} // namespace utils + } // namespace impl } // namespace number + U_NAMESPACE_END #endif //__NUMBER_UTILS_H__ diff --git a/deps/icu-small/source/i18n/number_utypes.h b/deps/icu-small/source/i18n/number_utypes.h new file mode 100644 index 00000000000000..48bfce1969754a --- /dev/null +++ b/deps/icu-small/source/i18n/number_utypes.h @@ -0,0 +1,79 @@ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING +#ifndef __SOURCE_NUMBER_UTYPES_H__ +#define __SOURCE_NUMBER_UTYPES_H__ + +#include "unicode/numberformatter.h" +#include "number_types.h" +#include "number_decimalquantity.h" +#include "number_stringbuilder.h" + +U_NAMESPACE_BEGIN namespace number { +namespace impl { + + +/** + * Implementation class for UNumberFormatter with a magic number for safety. + * + * Wraps a LocalizedNumberFormatter by value. + */ +struct UNumberFormatterData : public UMemory { + // The magic number to identify incoming objects. + // Reads in ASCII as "NFR" (NumberFormatteR with room at the end) + static constexpr int32_t kMagic = 0x4E465200; + + // Data members: + int32_t fMagic = kMagic; + LocalizedNumberFormatter fFormatter; + + /** Convert from UNumberFormatter -> UNumberFormatterData. */ + static UNumberFormatterData* validate(UNumberFormatter* input, UErrorCode& status); + + /** Convert from UNumberFormatter -> UNumberFormatterData (const version). */ + static const UNumberFormatterData* validate(const UNumberFormatter* input, UErrorCode& status); + + /** Convert from UNumberFormatterData -> UNumberFormatter. */ + UNumberFormatter* exportForC(); +}; + + +/** + * Implementation class for UFormattedNumber with magic number for safety. + * + * This struct is also held internally by the C++ version FormattedNumber since the member types are not + * declared in the public header file. + * + * The DecimalQuantity is not currently being used by FormattedNumber, but at some point it could be used + * to add a toDecNumber() or similar method. + */ +struct UFormattedNumberData : public UMemory { + // The magic number to identify incoming objects. + // Reads in ASCII as "FDN" (FormatteDNumber with room at the end) + static constexpr int32_t kMagic = 0x46444E00; + + // Data members: + int32_t fMagic = kMagic; + DecimalQuantity quantity; + NumberStringBuilder string; + + /** Convert from UFormattedNumber -> UFormattedNumberData. */ + static UFormattedNumberData* validate(UFormattedNumber* input, UErrorCode& status); + + /** Convert from UFormattedNumber -> UFormattedNumberData (const version). */ + static const UFormattedNumberData* validate(const UFormattedNumber* input, UErrorCode& status); + + /** Convert from UFormattedNumberData -> UFormattedNumber. */ + UFormattedNumber* exportForC(); +}; + + +} // namespace impl +} // namespace number +U_NAMESPACE_END + +#endif //__SOURCE_NUMBER_UTYPES_H__ +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/numfmt.cpp b/deps/icu-small/source/i18n/numfmt.cpp index fee3bed8bfb352..13f23131b1851b 100644 --- a/deps/icu-small/source/i18n/numfmt.cpp +++ b/deps/icu-small/source/i18n/numfmt.cpp @@ -51,10 +51,11 @@ #include "uassert.h" #include "umutex.h" #include "mutex.h" -#include "digitlst.h" #include #include "sharednumberformat.h" #include "unifiedcache.h" +#include "number_decimalquantity.h" +#include "number_utils.h" //#define FMT_DEBUG @@ -129,31 +130,28 @@ static const UChar * const gLastResortNumberPatterns[UNUM_FORMAT_STYLE_COUNT] = // Keys used for accessing resource bundles -static const char *gNumberElements = "NumberElements"; -static const char *gLatn = "latn"; -static const char *gPatterns = "patterns"; -static const char *gFormatKeys[UNUM_FORMAT_STYLE_COUNT] = { - NULL, // UNUM_PATTERN_DECIMAL - "decimalFormat", // UNUM_DECIMAL - "currencyFormat", // UNUM_CURRENCY - "percentFormat", // UNUM_PERCENT - "scientificFormat", // UNUM_SCIENTIFIC - NULL, // UNUM_SPELLOUT - NULL, // UNUM_ORDINAL - NULL, // UNUM_DURATION - NULL, // UNUM_NUMBERING_SYSTEM - NULL, // UNUM_PATTERN_RULEBASED +static const icu::number::impl::CldrPatternStyle gFormatCldrStyles[UNUM_FORMAT_STYLE_COUNT] = { + /* NULL */ icu::number::impl::CLDR_PATTERN_STYLE_COUNT, // UNUM_PATTERN_DECIMAL + icu::number::impl::CLDR_PATTERN_STYLE_DECIMAL, // UNUM_DECIMAL + icu::number::impl::CLDR_PATTERN_STYLE_CURRENCY, // UNUM_CURRENCY + icu::number::impl::CLDR_PATTERN_STYLE_PERCENT, // UNUM_PERCENT + icu::number::impl::CLDR_PATTERN_STYLE_SCIENTIFIC, // UNUM_SCIENTIFIC + /* NULL */ icu::number::impl::CLDR_PATTERN_STYLE_COUNT, // UNUM_SPELLOUT + /* NULL */ icu::number::impl::CLDR_PATTERN_STYLE_COUNT, // UNUM_ORDINAL + /* NULL */ icu::number::impl::CLDR_PATTERN_STYLE_COUNT, // UNUM_DURATION + /* NULL */ icu::number::impl::CLDR_PATTERN_STYLE_COUNT, // UNUM_NUMBERING_SYSTEM + /* NULL */ icu::number::impl::CLDR_PATTERN_STYLE_COUNT, // UNUM_PATTERN_RULEBASED // For UNUM_CURRENCY_ISO and UNUM_CURRENCY_PLURAL, // the pattern is the same as the pattern of UNUM_CURRENCY // except for replacing the single currency sign with // double currency sign or triple currency sign. - "currencyFormat", // UNUM_CURRENCY_ISO - "currencyFormat", // UNUM_CURRENCY_PLURAL - "accountingFormat", // UNUM_CURRENCY_ACCOUNTING - "currencyFormat", // UNUM_CASH_CURRENCY - NULL, // UNUM_DECIMAL_COMPACT_SHORT - NULL, // UNUM_DECIMAL_COMPACT_LONG - "currencyFormat", // UNUM_CURRENCY_STANDARD + icu::number::impl::CLDR_PATTERN_STYLE_CURRENCY, // UNUM_CURRENCY_ISO + icu::number::impl::CLDR_PATTERN_STYLE_CURRENCY, // UNUM_CURRENCY_PLURAL + icu::number::impl::CLDR_PATTERN_STYLE_ACCOUNTING, // UNUM_CURRENCY_ACCOUNTING + icu::number::impl::CLDR_PATTERN_STYLE_CURRENCY, // UNUM_CASH_CURRENCY + /* NULL */ icu::number::impl::CLDR_PATTERN_STYLE_COUNT, // UNUM_DECIMAL_COMPACT_SHORT + /* NULL */ icu::number::impl::CLDR_PATTERN_STYLE_COUNT, // UNUM_DECIMAL_COMPACT_LONG + icu::number::impl::CLDR_PATTERN_STYLE_CURRENCY, // UNUM_CURRENCY_STANDARD }; // Static hashtable cache of NumberingSystem objects used by NumberFormat @@ -524,7 +522,7 @@ ArgExtractor::ArgExtractor(const NumberFormat& /*nf*/, const Formattable& obj, U ArgExtractor::~ArgExtractor() { } -UnicodeString& NumberFormat::format(const DigitList &number, +UnicodeString& NumberFormat::format(const number::impl::DecimalQuantity &number, UnicodeString& appendTo, FieldPositionIterator* posIter, UErrorCode& status) const { @@ -534,7 +532,7 @@ UnicodeString& NumberFormat::format(const DigitList &number, if (U_FAILURE(status)) { return appendTo; } - double dnum = number.getDouble(); + double dnum = number.toDouble(); format(dnum, appendTo, posIter, status); return appendTo; } @@ -542,7 +540,7 @@ UnicodeString& NumberFormat::format(const DigitList &number, UnicodeString& -NumberFormat::format(const DigitList &number, +NumberFormat::format(const number::impl::DecimalQuantity &number, UnicodeString& appendTo, FieldPosition& pos, UErrorCode &status) const { @@ -552,7 +550,7 @@ NumberFormat::format(const DigitList &number, if (U_FAILURE(status)) { return appendTo; } - double dnum = number.getDouble(); + double dnum = number.toDouble(); format(dnum, appendTo, pos, status); return appendTo; } @@ -578,7 +576,7 @@ NumberFormat::format(const Formattable& obj, return cloneFmt->format(*n, appendTo, pos, status); } - if (n->isNumeric() && n->getDigitList() != NULL) { + if (n->isNumeric() && n->getDecimalQuantity() != NULL) { // Decimal Number. We will have a DigitList available if the value was // set to a decimal number, or if the value originated with a parse. // @@ -587,17 +585,17 @@ NumberFormat::format(const Formattable& obj, // know about DigitList to continue to operate as they had. // // DecimalFormat overrides the DigitList formatting functions. - format(*n->getDigitList(), appendTo, pos, status); + format(*n->getDecimalQuantity(), appendTo, pos, status); } else { switch (n->getType()) { case Formattable::kDouble: - format(n->getDouble(), appendTo, pos); + format(n->getDouble(), appendTo, pos, status); break; case Formattable::kLong: - format(n->getLong(), appendTo, pos); + format(n->getLong(), appendTo, pos, status); break; case Formattable::kInt64: - format(n->getInt64(), appendTo, pos); + format(n->getInt64(), appendTo, pos, status); break; default: status = U_INVALID_FORMAT_ERROR; @@ -633,9 +631,9 @@ NumberFormat::format(const Formattable& obj, return cloneFmt->format(*n, appendTo, posIter, status); } - if (n->isNumeric() && n->getDigitList() != NULL) { + if (n->isNumeric() && n->getDecimalQuantity() != NULL) { // Decimal Number - format(*n->getDigitList(), appendTo, posIter, status); + format(*n->getDecimalQuantity(), appendTo, posIter, status); } else { switch (n->getType()) { case Formattable::kDouble: @@ -1400,27 +1398,13 @@ NumberFormat::makeInstance(const Locale& desiredLocale, return NULL; } - UResourceBundle *resource = ownedResource.orphan(); - UResourceBundle *numElements = ures_getByKeyWithFallback(resource, gNumberElements, NULL, &status); - resource = ures_getByKeyWithFallback(numElements, ns->getName(), resource, &status); - resource = ures_getByKeyWithFallback(resource, gPatterns, resource, &status); - ownedResource.adoptInstead(resource); - - int32_t patLen = 0; - const UChar *patResStr = ures_getStringByKeyWithFallback(resource, gFormatKeys[style], &patLen, &status); - - // Didn't find a pattern specific to the numbering system, so fall back to "latn" - if ( status == U_MISSING_RESOURCE_ERROR && uprv_strcmp(gLatn,ns->getName())) { - status = U_ZERO_ERROR; - resource = ures_getByKeyWithFallback(numElements, gLatn, resource, &status); - resource = ures_getByKeyWithFallback(resource, gPatterns, resource, &status); - patResStr = ures_getStringByKeyWithFallback(resource, gFormatKeys[style], &patLen, &status); - } - - ures_close(numElements); - - // Creates the specified decimal format style of the desired locale. - pattern.setTo(TRUE, patResStr, patLen); + // Load the pattern from data using the common library function + const char16_t* patternPtr = number::impl::utils::getPatternForStyle( + desiredLocale, + ns->getName(), + gFormatCldrStyles[style], + status); + pattern = UnicodeString(TRUE, patternPtr, -1); } if (U_FAILURE(status)) { return NULL; diff --git a/deps/icu-small/source/i18n/numparse_affixes.cpp b/deps/icu-small/source/i18n/numparse_affixes.cpp new file mode 100644 index 00000000000000..c30d241693c8bf --- /dev/null +++ b/deps/icu-small/source/i18n/numparse_affixes.cpp @@ -0,0 +1,470 @@ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +// Allow implicit conversion from char16_t* to UnicodeString for this file: +// Helpful in toString methods and elsewhere. +#define UNISTR_FROM_STRING_EXPLICIT + +#include "numparse_types.h" +#include "numparse_affixes.h" +#include "numparse_utils.h" +#include "number_utils.h" + +using namespace icu; +using namespace icu::numparse; +using namespace icu::numparse::impl; +using namespace icu::number; +using namespace icu::number::impl; + + +namespace { + +/** + * Helper method to return whether the given AffixPatternMatcher equals the given pattern string. + * Either both arguments must be null or the pattern string inside the AffixPatternMatcher must equal + * the given pattern string. + */ +static bool matched(const AffixPatternMatcher* affix, const UnicodeString& patternString) { + return (affix == nullptr && patternString.isBogus()) || + (affix != nullptr && affix->getPattern() == patternString); +} + +/** + * Helper method to return the length of the given AffixPatternMatcher. Returns 0 for null. + */ +static int32_t length(const AffixPatternMatcher* matcher) { + return matcher == nullptr ? 0 : matcher->getPattern().length(); +} + +/** + * Helper method to return whether (1) both lhs and rhs are null/invalid, or (2) if they are both + * valid, whether they are equal according to operator==. Similar to Java Objects.equals() + */ +static bool equals(const AffixPatternMatcher* lhs, const AffixPatternMatcher* rhs) { + if (lhs == nullptr && rhs == nullptr) { + return true; + } + if (lhs == nullptr || rhs == nullptr) { + return false; + } + return *lhs == *rhs; +} + +} + + +AffixPatternMatcherBuilder::AffixPatternMatcherBuilder(const UnicodeString& pattern, + AffixTokenMatcherWarehouse& warehouse, + IgnorablesMatcher* ignorables) + : fMatchersLen(0), + fLastTypeOrCp(0), + fPattern(pattern), + fWarehouse(warehouse), + fIgnorables(ignorables) {} + +void AffixPatternMatcherBuilder::consumeToken(AffixPatternType type, UChar32 cp, UErrorCode& status) { + // This is called by AffixUtils.iterateWithConsumer() for each token. + + // Add an ignorables matcher between tokens except between two literals, and don't put two + // ignorables matchers in a row. + if (fIgnorables != nullptr && fMatchersLen > 0 && + (fLastTypeOrCp < 0 || !fIgnorables->getSet()->contains(fLastTypeOrCp))) { + addMatcher(*fIgnorables); + } + + if (type != TYPE_CODEPOINT) { + // Case 1: the token is a symbol. + switch (type) { + case TYPE_MINUS_SIGN: + addMatcher(fWarehouse.minusSign()); + break; + case TYPE_PLUS_SIGN: + addMatcher(fWarehouse.plusSign()); + break; + case TYPE_PERCENT: + addMatcher(fWarehouse.percent()); + break; + case TYPE_PERMILLE: + addMatcher(fWarehouse.permille()); + break; + case TYPE_CURRENCY_SINGLE: + case TYPE_CURRENCY_DOUBLE: + case TYPE_CURRENCY_TRIPLE: + case TYPE_CURRENCY_QUAD: + case TYPE_CURRENCY_QUINT: + // All currency symbols use the same matcher + addMatcher(fWarehouse.currency(status)); + break; + default: + U_ASSERT(FALSE); + } + + } else if (fIgnorables != nullptr && fIgnorables->getSet()->contains(cp)) { + // Case 2: the token is an ignorable literal. + // No action necessary: the ignorables matcher has already been added. + + } else { + // Case 3: the token is a non-ignorable literal. + addMatcher(fWarehouse.nextCodePointMatcher(cp)); + } + fLastTypeOrCp = type != TYPE_CODEPOINT ? type : cp; +} + +void AffixPatternMatcherBuilder::addMatcher(NumberParseMatcher& matcher) { + if (fMatchersLen >= fMatchers.getCapacity()) { + fMatchers.resize(fMatchersLen * 2, fMatchersLen); + } + fMatchers[fMatchersLen++] = &matcher; +} + +AffixPatternMatcher AffixPatternMatcherBuilder::build() { + return AffixPatternMatcher(fMatchers, fMatchersLen, fPattern); +} + + +CodePointMatcherWarehouse::CodePointMatcherWarehouse() + : codePointCount(0), codePointNumBatches(0) {} + +CodePointMatcherWarehouse::~CodePointMatcherWarehouse() { + // Delete the variable number of batches of code point matchers + for (int32_t i = 0; i < codePointNumBatches; i++) { + delete[] codePointsOverflow[i]; + } +} + +CodePointMatcherWarehouse::CodePointMatcherWarehouse(CodePointMatcherWarehouse&& src) U_NOEXCEPT + : codePoints(std::move(src.codePoints)), + codePointsOverflow(std::move(src.codePointsOverflow)), + codePointCount(src.codePointCount), + codePointNumBatches(src.codePointNumBatches) {} + +CodePointMatcherWarehouse& +CodePointMatcherWarehouse::operator=(CodePointMatcherWarehouse&& src) U_NOEXCEPT { + codePoints = std::move(src.codePoints); + codePointsOverflow = std::move(src.codePointsOverflow); + codePointCount = src.codePointCount; + codePointNumBatches = src.codePointNumBatches; + return *this; +} + +NumberParseMatcher& CodePointMatcherWarehouse::nextCodePointMatcher(UChar32 cp) { + if (codePointCount < CODE_POINT_STACK_CAPACITY) { + return codePoints[codePointCount++] = {cp}; + } + int32_t totalCapacity = CODE_POINT_STACK_CAPACITY + codePointNumBatches * CODE_POINT_BATCH_SIZE; + if (codePointCount >= totalCapacity) { + // Need a new batch + auto* nextBatch = new CodePointMatcher[CODE_POINT_BATCH_SIZE]; + if (codePointNumBatches >= codePointsOverflow.getCapacity()) { + // Need more room for storing pointers to batches + codePointsOverflow.resize(codePointNumBatches * 2, codePointNumBatches); + } + codePointsOverflow[codePointNumBatches++] = nextBatch; + } + return codePointsOverflow[codePointNumBatches - 1][(codePointCount++ - CODE_POINT_STACK_CAPACITY) % + CODE_POINT_BATCH_SIZE] = {cp}; +} + + +AffixTokenMatcherWarehouse::AffixTokenMatcherWarehouse(const AffixTokenMatcherSetupData* setupData) + : fSetupData(setupData) {} + +NumberParseMatcher& AffixTokenMatcherWarehouse::minusSign() { + return fMinusSign = {fSetupData->dfs, true}; +} + +NumberParseMatcher& AffixTokenMatcherWarehouse::plusSign() { + return fPlusSign = {fSetupData->dfs, true}; +} + +NumberParseMatcher& AffixTokenMatcherWarehouse::percent() { + return fPercent = {fSetupData->dfs}; +} + +NumberParseMatcher& AffixTokenMatcherWarehouse::permille() { + return fPermille = {fSetupData->dfs}; +} + +NumberParseMatcher& AffixTokenMatcherWarehouse::currency(UErrorCode& status) { + return fCurrency = {fSetupData->currencySymbols, fSetupData->dfs, fSetupData->parseFlags, status}; +} + +IgnorablesMatcher& AffixTokenMatcherWarehouse::ignorables() { + return fSetupData->ignorables; +} + +NumberParseMatcher& AffixTokenMatcherWarehouse::nextCodePointMatcher(UChar32 cp) { + return fCodePoints.nextCodePointMatcher(cp); +} + + +CodePointMatcher::CodePointMatcher(UChar32 cp) + : fCp(cp) {} + +bool CodePointMatcher::match(StringSegment& segment, ParsedNumber& result, UErrorCode&) const { + if (segment.startsWith(fCp)) { + segment.adjustOffsetByCodePoint(); + result.setCharsConsumed(segment); + } + return false; +} + +bool CodePointMatcher::smokeTest(const StringSegment& segment) const { + return segment.startsWith(fCp); +} + +UnicodeString CodePointMatcher::toString() const { + return u""; +} + + +AffixPatternMatcher AffixPatternMatcher::fromAffixPattern(const UnicodeString& affixPattern, + AffixTokenMatcherWarehouse& tokenWarehouse, + parse_flags_t parseFlags, bool* success, + UErrorCode& status) { + if (affixPattern.isEmpty()) { + *success = false; + return {}; + } + *success = true; + + IgnorablesMatcher* ignorables; + if (0 != (parseFlags & PARSE_FLAG_EXACT_AFFIX)) { + ignorables = nullptr; + } else { + ignorables = &tokenWarehouse.ignorables(); + } + + AffixPatternMatcherBuilder builder(affixPattern, tokenWarehouse, ignorables); + AffixUtils::iterateWithConsumer(affixPattern, builder, status); + return builder.build(); +} + +AffixPatternMatcher::AffixPatternMatcher(MatcherArray& matchers, int32_t matchersLen, + const UnicodeString& pattern) + : ArraySeriesMatcher(matchers, matchersLen), fPattern(pattern) {} + +UnicodeString AffixPatternMatcher::getPattern() const { + return fPattern.toAliasedUnicodeString(); +} + +bool AffixPatternMatcher::operator==(const AffixPatternMatcher& other) const { + return fPattern == other.fPattern; +} + + +AffixMatcherWarehouse::AffixMatcherWarehouse(AffixTokenMatcherWarehouse* tokenWarehouse) + : fTokenWarehouse(tokenWarehouse) { +} + +bool AffixMatcherWarehouse::isInteresting(const AffixPatternProvider& patternInfo, + const IgnorablesMatcher& ignorables, parse_flags_t parseFlags, + UErrorCode& status) { + UnicodeString posPrefixString = patternInfo.getString(AffixPatternProvider::AFFIX_POS_PREFIX); + UnicodeString posSuffixString = patternInfo.getString(AffixPatternProvider::AFFIX_POS_SUFFIX); + UnicodeString negPrefixString; + UnicodeString negSuffixString; + if (patternInfo.hasNegativeSubpattern()) { + negPrefixString = patternInfo.getString(AffixPatternProvider::AFFIX_NEG_PREFIX); + negSuffixString = patternInfo.getString(AffixPatternProvider::AFFIX_NEG_SUFFIX); + } + + if (0 == (parseFlags & PARSE_FLAG_USE_FULL_AFFIXES) && + AffixUtils::containsOnlySymbolsAndIgnorables(posPrefixString, *ignorables.getSet(), status) && + AffixUtils::containsOnlySymbolsAndIgnorables(posSuffixString, *ignorables.getSet(), status) && + AffixUtils::containsOnlySymbolsAndIgnorables(negPrefixString, *ignorables.getSet(), status) && + AffixUtils::containsOnlySymbolsAndIgnorables(negSuffixString, *ignorables.getSet(), status) + // HACK: Plus and minus sign are a special case: we accept them trailing only if they are + // trailing in the pattern string. + && !AffixUtils::containsType(posSuffixString, TYPE_PLUS_SIGN, status) && + !AffixUtils::containsType(posSuffixString, TYPE_MINUS_SIGN, status) && + !AffixUtils::containsType(negSuffixString, TYPE_PLUS_SIGN, status) && + !AffixUtils::containsType(negSuffixString, TYPE_MINUS_SIGN, status)) { + // The affixes contain only symbols and ignorables. + // No need to generate affix matchers. + return false; + } + return true; +} + +void AffixMatcherWarehouse::createAffixMatchers(const AffixPatternProvider& patternInfo, + MutableMatcherCollection& output, + const IgnorablesMatcher& ignorables, + parse_flags_t parseFlags, UErrorCode& status) { + if (!isInteresting(patternInfo, ignorables, parseFlags, status)) { + return; + } + + // The affixes have interesting characters, or we are in strict mode. + // Use initial capacity of 6, the highest possible number of AffixMatchers. + UnicodeString sb; + bool includeUnpaired = 0 != (parseFlags & PARSE_FLAG_INCLUDE_UNPAIRED_AFFIXES); + UNumberSignDisplay signDisplay = (0 != (parseFlags & PARSE_FLAG_PLUS_SIGN_ALLOWED)) ? UNUM_SIGN_ALWAYS + : UNUM_SIGN_AUTO; + + int32_t numAffixMatchers = 0; + int32_t numAffixPatternMatchers = 0; + + AffixPatternMatcher* posPrefix = nullptr; + AffixPatternMatcher* posSuffix = nullptr; + + // Pre-process the affix strings to resolve LDML rules like sign display. + for (int8_t signum = 1; signum >= -1; signum--) { + // Generate Prefix + bool hasPrefix = false; + PatternStringUtils::patternInfoToStringBuilder( + patternInfo, true, signum, signDisplay, StandardPlural::OTHER, false, sb); + fAffixPatternMatchers[numAffixPatternMatchers] = AffixPatternMatcher::fromAffixPattern( + sb, *fTokenWarehouse, parseFlags, &hasPrefix, status); + AffixPatternMatcher* prefix = hasPrefix ? &fAffixPatternMatchers[numAffixPatternMatchers++] + : nullptr; + + // Generate Suffix + bool hasSuffix = false; + PatternStringUtils::patternInfoToStringBuilder( + patternInfo, false, signum, signDisplay, StandardPlural::OTHER, false, sb); + fAffixPatternMatchers[numAffixPatternMatchers] = AffixPatternMatcher::fromAffixPattern( + sb, *fTokenWarehouse, parseFlags, &hasSuffix, status); + AffixPatternMatcher* suffix = hasSuffix ? &fAffixPatternMatchers[numAffixPatternMatchers++] + : nullptr; + + if (signum == 1) { + posPrefix = prefix; + posSuffix = suffix; + } else if (equals(prefix, posPrefix) && equals(suffix, posSuffix)) { + // Skip adding these matchers (we already have equivalents) + continue; + } + + // Flags for setting in the ParsedNumber; the token matchers may add more. + int flags = (signum == -1) ? FLAG_NEGATIVE : 0; + + // Note: it is indeed possible for posPrefix and posSuffix to both be null. + // We still need to add that matcher for strict mode to work. + fAffixMatchers[numAffixMatchers++] = {prefix, suffix, flags}; + if (includeUnpaired && prefix != nullptr && suffix != nullptr) { + // The following if statements are designed to prevent adding two identical matchers. + if (signum == 1 || !equals(prefix, posPrefix)) { + fAffixMatchers[numAffixMatchers++] = {prefix, nullptr, flags}; + } + if (signum == 1 || !equals(suffix, posSuffix)) { + fAffixMatchers[numAffixMatchers++] = {nullptr, suffix, flags}; + } + } + } + + // Put the AffixMatchers in order, and then add them to the output. + // Since there are at most 9 elements, do a simple-to-implement bubble sort. + bool madeChanges; + do { + madeChanges = false; + for (int32_t i = 1; i < numAffixMatchers; i++) { + if (fAffixMatchers[i - 1].compareTo(fAffixMatchers[i]) > 0) { + madeChanges = true; + AffixMatcher temp = std::move(fAffixMatchers[i - 1]); + fAffixMatchers[i - 1] = std::move(fAffixMatchers[i]); + fAffixMatchers[i] = std::move(temp); + } + } + } while (madeChanges); + + for (int32_t i = 0; i < numAffixMatchers; i++) { + // Enable the following line to debug affixes + //std::cout << "Adding affix matcher: " << CStr(fAffixMatchers[i].toString())() << std::endl; + output.addMatcher(fAffixMatchers[i]); + } +} + + +AffixMatcher::AffixMatcher(AffixPatternMatcher* prefix, AffixPatternMatcher* suffix, result_flags_t flags) + : fPrefix(prefix), fSuffix(suffix), fFlags(flags) {} + +bool AffixMatcher::match(StringSegment& segment, ParsedNumber& result, UErrorCode& status) const { + if (!result.seenNumber()) { + // Prefix + // Do not match if: + // 1. We have already seen a prefix (result.prefix != null) + // 2. The prefix in this AffixMatcher is empty (prefix == null) + if (!result.prefix.isBogus() || fPrefix == nullptr) { + return false; + } + + // Attempt to match the prefix. + int initialOffset = segment.getOffset(); + bool maybeMore = fPrefix->match(segment, result, status); + if (initialOffset != segment.getOffset()) { + result.prefix = fPrefix->getPattern(); + } + return maybeMore; + + } else { + // Suffix + // Do not match if: + // 1. We have already seen a suffix (result.suffix != null) + // 2. The suffix in this AffixMatcher is empty (suffix == null) + // 3. The matched prefix does not equal this AffixMatcher's prefix + if (!result.suffix.isBogus() || fSuffix == nullptr || !matched(fPrefix, result.prefix)) { + return false; + } + + // Attempt to match the suffix. + int initialOffset = segment.getOffset(); + bool maybeMore = fSuffix->match(segment, result, status); + if (initialOffset != segment.getOffset()) { + result.suffix = fSuffix->getPattern(); + } + return maybeMore; + } +} + +bool AffixMatcher::smokeTest(const StringSegment& segment) const { + return (fPrefix != nullptr && fPrefix->smokeTest(segment)) || + (fSuffix != nullptr && fSuffix->smokeTest(segment)); +} + +void AffixMatcher::postProcess(ParsedNumber& result) const { + // Check to see if our affix is the one that was matched. If so, set the flags in the result. + if (matched(fPrefix, result.prefix) && matched(fSuffix, result.suffix)) { + // Fill in the result prefix and suffix with non-null values (empty string). + // Used by strict mode to determine whether an entire affix pair was matched. + if (result.prefix.isBogus()) { + result.prefix = UnicodeString(); + } + if (result.suffix.isBogus()) { + result.suffix = UnicodeString(); + } + result.flags |= fFlags; + if (fPrefix != nullptr) { + fPrefix->postProcess(result); + } + if (fSuffix != nullptr) { + fSuffix->postProcess(result); + } + } +} + +int8_t AffixMatcher::compareTo(const AffixMatcher& rhs) const { + const AffixMatcher& lhs = *this; + if (length(lhs.fPrefix) != length(rhs.fPrefix)) { + return length(lhs.fPrefix) > length(rhs.fPrefix) ? -1 : 1; + } else if (length(lhs.fSuffix) != length(rhs.fSuffix)) { + return length(lhs.fSuffix) > length(rhs.fSuffix) ? -1 : 1; + } else { + return 0; + } +} + +UnicodeString AffixMatcher::toString() const { + bool isNegative = 0 != (fFlags & FLAG_NEGATIVE); + return UnicodeString(u"getPattern() : u"null") + u"#" + + (fSuffix ? fSuffix->getPattern() : u"null") + u">"; + +} + + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/numparse_affixes.h b/deps/icu-small/source/i18n/numparse_affixes.h new file mode 100644 index 00000000000000..be8c4fb56473d1 --- /dev/null +++ b/deps/icu-small/source/i18n/numparse_affixes.h @@ -0,0 +1,255 @@ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING +#ifndef __NUMPARSE_AFFIXES_H__ +#define __NUMPARSE_AFFIXES_H__ + +#include "numparse_types.h" +#include "numparse_symbols.h" +#include "numparse_currency.h" +#include "number_affixutils.h" +#include "number_currencysymbols.h" + +#include + +U_NAMESPACE_BEGIN +namespace numparse { +namespace impl { + +// Forward-declaration of implementation classes for friending +class AffixPatternMatcherBuilder; +class AffixPatternMatcher; + +using ::icu::number::impl::AffixPatternProvider; +using ::icu::number::impl::TokenConsumer; +using ::icu::number::impl::CurrencySymbols; + + +class CodePointMatcher : public NumberParseMatcher, public UMemory { + public: + CodePointMatcher() = default; // WARNING: Leaves the object in an unusable state + + CodePointMatcher(UChar32 cp); + + bool match(StringSegment& segment, ParsedNumber& result, UErrorCode& status) const override; + + bool smokeTest(const StringSegment& segment) const override; + + UnicodeString toString() const override; + + private: + UChar32 fCp; +}; + +} // namespace impl +} // namespace numparse + +// Export a explicit template instantiations of MaybeStackArray and CompactUnicodeString. +// When building DLLs for Windows this is required even though no direct access leaks out of the i18n library. +// (See digitlst.h, pluralaffix.h, datefmt.h, and others for similar examples.) +// Note: These need to be outside of the impl::numparse namespace, or Clang will generate a compile error. +#if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN +template class U_I18N_API MaybeStackArray; +template class U_I18N_API MaybeStackArray; +template class U_I18N_API numparse::impl::CompactUnicodeString<4>; +#endif + +namespace numparse { +namespace impl { + +/** + * A warehouse to retain ownership of CodePointMatchers. + */ +// Exported as U_I18N_API for tests +class U_I18N_API CodePointMatcherWarehouse : public UMemory { + private: + static constexpr int32_t CODE_POINT_STACK_CAPACITY = 5; // Number of entries directly on the stack + static constexpr int32_t CODE_POINT_BATCH_SIZE = 10; // Number of entries per heap allocation + + public: + CodePointMatcherWarehouse(); + + // A custom destructor is needed to free the memory from MaybeStackArray. + // A custom move constructor and move assignment seem to be needed because of the custom destructor. + + ~CodePointMatcherWarehouse(); + + CodePointMatcherWarehouse(CodePointMatcherWarehouse&& src) U_NOEXCEPT; + + CodePointMatcherWarehouse& operator=(CodePointMatcherWarehouse&& src) U_NOEXCEPT; + + NumberParseMatcher& nextCodePointMatcher(UChar32 cp); + + private: + std::array codePoints; // By value + MaybeStackArray codePointsOverflow; // On heap in "batches" + int32_t codePointCount; // Total for both the ones by value and on heap + int32_t codePointNumBatches; // Number of batches in codePointsOverflow +}; + + +struct AffixTokenMatcherSetupData { + const CurrencySymbols& currencySymbols; + const DecimalFormatSymbols& dfs; + IgnorablesMatcher& ignorables; + const Locale& locale; + parse_flags_t parseFlags; +}; + + +/** + * Small helper class that generates matchers for individual tokens for AffixPatternMatcher. + * + * In Java, this is called AffixTokenMatcherFactory (a "factory"). However, in C++, it is called a + * "warehouse", because in addition to generating the matchers, it also retains ownership of them. The + * warehouse must stay in scope for the whole lifespan of the AffixPatternMatcher that uses matchers from + * the warehouse. + * + * @author sffc + */ +// Exported as U_I18N_API for tests +class U_I18N_API AffixTokenMatcherWarehouse : public UMemory { + public: + AffixTokenMatcherWarehouse() = default; // WARNING: Leaves the object in an unusable state + + AffixTokenMatcherWarehouse(const AffixTokenMatcherSetupData* setupData); + + NumberParseMatcher& minusSign(); + + NumberParseMatcher& plusSign(); + + NumberParseMatcher& percent(); + + NumberParseMatcher& permille(); + + NumberParseMatcher& currency(UErrorCode& status); + + IgnorablesMatcher& ignorables(); + + NumberParseMatcher& nextCodePointMatcher(UChar32 cp); + + private: + // NOTE: The following field may be unsafe to access after construction is done! + const AffixTokenMatcherSetupData* fSetupData; + + // NOTE: These are default-constructed and should not be used until initialized. + MinusSignMatcher fMinusSign; + PlusSignMatcher fPlusSign; + PercentMatcher fPercent; + PermilleMatcher fPermille; + CombinedCurrencyMatcher fCurrency; + + // Use a child class for code point matchers, since it requires non-default operators. + CodePointMatcherWarehouse fCodePoints; + + friend class AffixPatternMatcherBuilder; + friend class AffixPatternMatcher; +}; + + +class AffixPatternMatcherBuilder : public TokenConsumer, public MutableMatcherCollection { + public: + AffixPatternMatcherBuilder(const UnicodeString& pattern, AffixTokenMatcherWarehouse& warehouse, + IgnorablesMatcher* ignorables); + + void consumeToken(::icu::number::impl::AffixPatternType type, UChar32 cp, UErrorCode& status) override; + + /** NOTE: You can build only once! */ + AffixPatternMatcher build(); + + private: + ArraySeriesMatcher::MatcherArray fMatchers; + int32_t fMatchersLen; + int32_t fLastTypeOrCp; + + const UnicodeString& fPattern; + AffixTokenMatcherWarehouse& fWarehouse; + IgnorablesMatcher* fIgnorables; + + void addMatcher(NumberParseMatcher& matcher) override; +}; + + +// Exported as U_I18N_API for tests +class U_I18N_API AffixPatternMatcher : public ArraySeriesMatcher { + public: + AffixPatternMatcher() = default; // WARNING: Leaves the object in an unusable state + + static AffixPatternMatcher fromAffixPattern(const UnicodeString& affixPattern, + AffixTokenMatcherWarehouse& warehouse, + parse_flags_t parseFlags, bool* success, + UErrorCode& status); + + UnicodeString getPattern() const; + + bool operator==(const AffixPatternMatcher& other) const; + + private: + CompactUnicodeString<4> fPattern; + + AffixPatternMatcher(MatcherArray& matchers, int32_t matchersLen, const UnicodeString& pattern); + + friend class AffixPatternMatcherBuilder; +}; + + +class AffixMatcher : public NumberParseMatcher, public UMemory { + public: + AffixMatcher() = default; // WARNING: Leaves the object in an unusable state + + AffixMatcher(AffixPatternMatcher* prefix, AffixPatternMatcher* suffix, result_flags_t flags); + + bool match(StringSegment& segment, ParsedNumber& result, UErrorCode& status) const override; + + void postProcess(ParsedNumber& result) const override; + + bool smokeTest(const StringSegment& segment) const override; + + int8_t compareTo(const AffixMatcher& rhs) const; + + UnicodeString toString() const override; + + private: + AffixPatternMatcher* fPrefix; + AffixPatternMatcher* fSuffix; + result_flags_t fFlags; +}; + + +/** + * A C++-only class to retain ownership of the AffixMatchers needed for parsing. + */ +class AffixMatcherWarehouse { + public: + AffixMatcherWarehouse() = default; // WARNING: Leaves the object in an unusable state + + AffixMatcherWarehouse(AffixTokenMatcherWarehouse* tokenWarehouse); + + void createAffixMatchers(const AffixPatternProvider& patternInfo, MutableMatcherCollection& output, + const IgnorablesMatcher& ignorables, parse_flags_t parseFlags, + UErrorCode& status); + + private: + // 9 is the limit: positive, zero, and negative, each with prefix, suffix, and prefix+suffix + AffixMatcher fAffixMatchers[9]; + // 6 is the limit: positive, zero, and negative, a prefix and a suffix for each + AffixPatternMatcher fAffixPatternMatchers[6]; + // Reference to the warehouse for tokens used by the AffixPatternMatchers + AffixTokenMatcherWarehouse* fTokenWarehouse; + + friend class AffixMatcher; + + static bool isInteresting(const AffixPatternProvider& patternInfo, const IgnorablesMatcher& ignorables, + parse_flags_t parseFlags, UErrorCode& status); +}; + + +} // namespace impl +} // namespace numparse +U_NAMESPACE_END + +#endif //__NUMPARSE_AFFIXES_H__ +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/numparse_compositions.cpp b/deps/icu-small/source/i18n/numparse_compositions.cpp new file mode 100644 index 00000000000000..19253da805f0bf --- /dev/null +++ b/deps/icu-small/source/i18n/numparse_compositions.cpp @@ -0,0 +1,107 @@ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +// Allow implicit conversion from char16_t* to UnicodeString for this file: +// Helpful in toString methods and elsewhere. +#define UNISTR_FROM_STRING_EXPLICIT + +#include "numparse_types.h" +#include "numparse_compositions.h" +#include "unicode/uniset.h" + +using namespace icu; +using namespace icu::numparse; +using namespace icu::numparse::impl; + + +bool SeriesMatcher::match(StringSegment& segment, ParsedNumber& result, UErrorCode& status) const { + ParsedNumber backup(result); + + int32_t initialOffset = segment.getOffset(); + bool maybeMore = true; + for (auto* it = begin(); it < end();) { + const NumberParseMatcher* matcher = *it; + int matcherOffset = segment.getOffset(); + if (segment.length() != 0) { + maybeMore = matcher->match(segment, result, status); + } else { + // Nothing for this matcher to match; ask for more. + maybeMore = true; + } + + bool success = (segment.getOffset() != matcherOffset); + bool isFlexible = matcher->isFlexible(); + if (success && isFlexible) { + // Match succeeded, and this is a flexible matcher. Re-run it. + } else if (success) { + // Match succeeded, and this is NOT a flexible matcher. Proceed to the next matcher. + it++; + // Small hack: if there is another matcher coming, do not accept trailing weak chars. + // Needed for proper handling of currency spacing. + if (it < end() && segment.getOffset() != result.charEnd && result.charEnd > matcherOffset) { + segment.setOffset(result.charEnd); + } + } else if (isFlexible) { + // Match failed, and this is a flexible matcher. Try again with the next matcher. + it++; + } else { + // Match failed, and this is NOT a flexible matcher. Exit. + segment.setOffset(initialOffset); + result = backup; + return maybeMore; + } + } + + // All matchers in the series succeeded. + return maybeMore; +} + +bool SeriesMatcher::smokeTest(const StringSegment& segment) const { + // NOTE: The range-based for loop calls the virtual begin() and end() methods. + // NOTE: We only want the first element. Use the for loop for boundary checking. + for (auto& matcher : *this) { + // SeriesMatchers are never allowed to start with a Flexible matcher. + U_ASSERT(!matcher->isFlexible()); + return matcher->smokeTest(segment); + } + return false; +} + +void SeriesMatcher::postProcess(ParsedNumber& result) const { + // NOTE: The range-based for loop calls the virtual begin() and end() methods. + for (auto* matcher : *this) { + matcher->postProcess(result); + } +} + + +ArraySeriesMatcher::ArraySeriesMatcher() + : fMatchersLen(0) { +} + +ArraySeriesMatcher::ArraySeriesMatcher(MatcherArray& matchers, int32_t matchersLen) + : fMatchers(std::move(matchers)), fMatchersLen(matchersLen) { +} + +int32_t ArraySeriesMatcher::length() const { + return fMatchersLen; +} + +const NumberParseMatcher* const* ArraySeriesMatcher::begin() const { + return fMatchers.getAlias(); +} + +const NumberParseMatcher* const* ArraySeriesMatcher::end() const { + return fMatchers.getAlias() + fMatchersLen; +} + +UnicodeString ArraySeriesMatcher::toString() const { + return u""; +} + + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/numparse_compositions.h b/deps/icu-small/source/i18n/numparse_compositions.h new file mode 100644 index 00000000000000..f085912def1533 --- /dev/null +++ b/deps/icu-small/source/i18n/numparse_compositions.h @@ -0,0 +1,124 @@ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING +#ifndef __SOURCE_NUMPARSE_COMPOSITIONS__ +#define __SOURCE_NUMPARSE_COMPOSITIONS__ + +#include "numparse_types.h" + +U_NAMESPACE_BEGIN + +// Export an explicit template instantiation of the MaybeStackArray that is used as a data member of ArraySeriesMatcher. +// When building DLLs for Windows this is required even though no direct access to the MaybeStackArray leaks out of the i18n library. +// (See digitlst.h, pluralaffix.h, datefmt.h, and others for similar examples.) +#if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN +template class U_I18N_API MaybeStackArray; +#endif + +namespace numparse { +namespace impl { + +/** + * Base class for AnyMatcher and SeriesMatcher. + */ +// Exported as U_I18N_API for tests +class U_I18N_API CompositionMatcher : public NumberParseMatcher { + protected: + // No construction except by subclasses! + CompositionMatcher() = default; + + // To be overridden by subclasses (used for iteration): + virtual const NumberParseMatcher* const* begin() const = 0; + + // To be overridden by subclasses (used for iteration): + virtual const NumberParseMatcher* const* end() const = 0; +}; + + +// NOTE: AnyMatcher is no longer being used. The previous definition is shown below. +// The implementation can be found in SVN source control, deleted around March 30, 2018. +///** +// * Composes a number of matchers, and succeeds if any of the matchers succeed. Always greedily chooses +// * the first matcher in the list to succeed. +// * +// * NOTE: In C++, this is a base class, unlike ICU4J, which uses a factory-style interface. +// * +// * @author sffc +// * @see SeriesMatcher +// */ +//class AnyMatcher : public CompositionMatcher { +// public: +// bool match(StringSegment& segment, ParsedNumber& result, UErrorCode& status) const override; +// +// bool smokeTest(const StringSegment& segment) const override; +// +// void postProcess(ParsedNumber& result) const override; +// +// protected: +// // No construction except by subclasses! +// AnyMatcher() = default; +//}; + + +/** + * Composes a number of matchers, running one after another. Matches the input string only if all of the + * matchers in the series succeed. Performs greedy matches within the context of the series. + * + * @author sffc + * @see AnyMatcher + */ +// Exported as U_I18N_API for tests +class U_I18N_API SeriesMatcher : public CompositionMatcher { + public: + bool match(StringSegment& segment, ParsedNumber& result, UErrorCode& status) const override; + + bool smokeTest(const StringSegment& segment) const override; + + void postProcess(ParsedNumber& result) const override; + + virtual int32_t length() const = 0; + + protected: + // No construction except by subclasses! + SeriesMatcher() = default; +}; + +/** + * An implementation of SeriesMatcher that references an array of matchers. + * + * The object adopts the array, but NOT the matchers contained inside the array. + */ +// Exported as U_I18N_API for tests +class U_I18N_API ArraySeriesMatcher : public SeriesMatcher { + public: + ArraySeriesMatcher(); // WARNING: Leaves the object in an unusable state + + typedef MaybeStackArray MatcherArray; + + /** The array is std::move'd */ + ArraySeriesMatcher(MatcherArray& matchers, int32_t matchersLen); + + UnicodeString toString() const override; + + int32_t length() const override; + + protected: + const NumberParseMatcher* const* begin() const override; + + const NumberParseMatcher* const* end() const override; + + private: + MatcherArray fMatchers; + int32_t fMatchersLen; +}; + + +} // namespace impl +} // namespace numparse +U_NAMESPACE_END + +#endif //__SOURCE_NUMPARSE_COMPOSITIONS__ +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/numparse_currency.cpp b/deps/icu-small/source/i18n/numparse_currency.cpp new file mode 100644 index 00000000000000..ae8196ec483799 --- /dev/null +++ b/deps/icu-small/source/i18n/numparse_currency.cpp @@ -0,0 +1,186 @@ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +// Allow implicit conversion from char16_t* to UnicodeString for this file: +// Helpful in toString methods and elsewhere. +#define UNISTR_FROM_STRING_EXPLICIT + +#include "numparse_types.h" +#include "numparse_currency.h" +#include "ucurrimp.h" +#include "unicode/errorcode.h" +#include "numparse_utils.h" + +using namespace icu; +using namespace icu::numparse; +using namespace icu::numparse::impl; + + +CombinedCurrencyMatcher::CombinedCurrencyMatcher(const CurrencySymbols& currencySymbols, const DecimalFormatSymbols& dfs, + parse_flags_t parseFlags, UErrorCode& status) + : fCurrency1(currencySymbols.getCurrencySymbol(status)), + fCurrency2(currencySymbols.getIntlCurrencySymbol(status)), + fUseFullCurrencyData(0 == (parseFlags & PARSE_FLAG_NO_FOREIGN_CURRENCY)), + afterPrefixInsert(dfs.getPatternForCurrencySpacing(UNUM_CURRENCY_INSERT, false, status)), + beforeSuffixInsert(dfs.getPatternForCurrencySpacing(UNUM_CURRENCY_INSERT, true, status)), + fLocaleName(dfs.getLocale().getName(), -1, status) { + utils::copyCurrencyCode(fCurrencyCode, currencySymbols.getIsoCode()); + + // Pre-load the long names for the current locale and currency + // if we are parsing without the full currency data. + if (!fUseFullCurrencyData) { + for (int32_t i=0; i(i); + fLocalLongNames[i] = currencySymbols.getPluralName(plural, status); + } + } + + // TODO: Figure out how to make this faster and re-enable. + // Computing the "lead code points" set for fastpathing is too slow to use in production. + // See http://bugs.icu-project.org/trac/ticket/13584 +// // Compute the full set of characters that could be the first in a currency to allow for +// // efficient smoke test. +// fLeadCodePoints.add(fCurrency1.char32At(0)); +// fLeadCodePoints.add(fCurrency2.char32At(0)); +// fLeadCodePoints.add(beforeSuffixInsert.char32At(0)); +// uprv_currencyLeads(fLocaleName.data(), fLeadCodePoints, status); +// // Always apply case mapping closure for currencies +// fLeadCodePoints.closeOver(USET_ADD_CASE_MAPPINGS); +// fLeadCodePoints.freeze(); +} + +bool +CombinedCurrencyMatcher::match(StringSegment& segment, ParsedNumber& result, UErrorCode& status) const { + if (result.currencyCode[0] != 0) { + return false; + } + + // Try to match a currency spacing separator. + int32_t initialOffset = segment.getOffset(); + bool maybeMore = false; + if (result.seenNumber() && !beforeSuffixInsert.isEmpty()) { + int32_t overlap = segment.getCommonPrefixLength(beforeSuffixInsert); + if (overlap == beforeSuffixInsert.length()) { + segment.adjustOffset(overlap); + // Note: let currency spacing be a weak match. Don't update chars consumed. + } + maybeMore = maybeMore || overlap == segment.length(); + } + + // Match the currency string, and reset if we didn't find one. + maybeMore = maybeMore || matchCurrency(segment, result, status); + if (result.currencyCode[0] == 0) { + segment.setOffset(initialOffset); + return maybeMore; + } + + // Try to match a currency spacing separator. + if (!result.seenNumber() && !afterPrefixInsert.isEmpty()) { + int32_t overlap = segment.getCommonPrefixLength(afterPrefixInsert); + if (overlap == afterPrefixInsert.length()) { + segment.adjustOffset(overlap); + // Note: let currency spacing be a weak match. Don't update chars consumed. + } + maybeMore = maybeMore || overlap == segment.length(); + } + + return maybeMore; +} + +bool CombinedCurrencyMatcher::matchCurrency(StringSegment& segment, ParsedNumber& result, + UErrorCode& status) const { + bool maybeMore = false; + + int32_t overlap1; + if (!fCurrency1.isEmpty()) { + overlap1 = segment.getCaseSensitivePrefixLength(fCurrency1); + } else { + overlap1 = -1; + } + maybeMore = maybeMore || overlap1 == segment.length(); + if (overlap1 == fCurrency1.length()) { + utils::copyCurrencyCode(result.currencyCode, fCurrencyCode); + segment.adjustOffset(overlap1); + result.setCharsConsumed(segment); + return maybeMore; + } + + int32_t overlap2; + if (!fCurrency2.isEmpty()) { + overlap2 = segment.getCaseSensitivePrefixLength(fCurrency2); + } else { + overlap2 = -1; + } + maybeMore = maybeMore || overlap2 == segment.length(); + if (overlap2 == fCurrency2.length()) { + utils::copyCurrencyCode(result.currencyCode, fCurrencyCode); + segment.adjustOffset(overlap2); + result.setCharsConsumed(segment); + return maybeMore; + } + + if (fUseFullCurrencyData) { + // Use the full currency data. + // NOTE: This call site should be improved with #13584. + const UnicodeString segmentString = segment.toTempUnicodeString(); + + // Try to parse the currency + ParsePosition ppos(0); + int32_t partialMatchLen = 0; + uprv_parseCurrency( + fLocaleName.data(), + segmentString, + ppos, + UCURR_SYMBOL_NAME, // checks for both UCURR_SYMBOL_NAME and UCURR_LONG_NAME + &partialMatchLen, + result.currencyCode, + status); + maybeMore = maybeMore || partialMatchLen == segment.length(); + + if (U_SUCCESS(status) && ppos.getIndex() != 0) { + // Complete match. + // NOTE: The currency code should already be saved in the ParsedNumber. + segment.adjustOffset(ppos.getIndex()); + result.setCharsConsumed(segment); + return maybeMore; + } + + } else { + // Use the locale long names. + int32_t longestFullMatch = 0; + for (int32_t i=0; i longestFullMatch) { + longestFullMatch = name.length(); + } + maybeMore = maybeMore || overlap > 0; + } + if (longestFullMatch > 0) { + utils::copyCurrencyCode(result.currencyCode, fCurrencyCode); + segment.adjustOffset(longestFullMatch); + result.setCharsConsumed(segment); + return maybeMore; + } + } + + // No match found. + return maybeMore; +} + +bool CombinedCurrencyMatcher::smokeTest(const StringSegment&) const { + // TODO: See constructor + return true; + //return segment.startsWith(fLeadCodePoints); +} + +UnicodeString CombinedCurrencyMatcher::toString() const { + return u""; +} + + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/numparse_currency.h b/deps/icu-small/source/i18n/numparse_currency.h new file mode 100644 index 00000000000000..a94943312fde9d --- /dev/null +++ b/deps/icu-small/source/i18n/numparse_currency.h @@ -0,0 +1,74 @@ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING +#ifndef __NUMPARSE_CURRENCY_H__ +#define __NUMPARSE_CURRENCY_H__ + +#include "numparse_types.h" +#include "numparse_compositions.h" +#include "charstr.h" +#include "number_currencysymbols.h" +#include "unicode/uniset.h" + +U_NAMESPACE_BEGIN namespace numparse { +namespace impl { + +using ::icu::number::impl::CurrencySymbols; + +/** + * Matches a currency, either a custom currency or one from the data bundle. The class is called + * "combined" to emphasize that the currency string may come from one of multiple sources. + * + * Will match currency spacing either before or after the number depending on whether we are currently in + * the prefix or suffix. + * + * The implementation of this class is slightly different between J and C. See #13584 for a follow-up. + * + * @author sffc + */ +// Exported as U_I18N_API for tests +class U_I18N_API CombinedCurrencyMatcher : public NumberParseMatcher, public UMemory { + public: + CombinedCurrencyMatcher() = default; // WARNING: Leaves the object in an unusable state + + CombinedCurrencyMatcher(const CurrencySymbols& currencySymbols, const DecimalFormatSymbols& dfs, + parse_flags_t parseFlags, UErrorCode& status); + + bool match(StringSegment& segment, ParsedNumber& result, UErrorCode& status) const override; + + bool smokeTest(const StringSegment& segment) const override; + + UnicodeString toString() const override; + + private: + UChar fCurrencyCode[4]; + UnicodeString fCurrency1; + UnicodeString fCurrency2; + + bool fUseFullCurrencyData; + UnicodeString fLocalLongNames[StandardPlural::COUNT]; + + UnicodeString afterPrefixInsert; + UnicodeString beforeSuffixInsert; + + // We could use Locale instead of CharString here, but + // Locale has a non-trivial default constructor. + CharString fLocaleName; + + // TODO: See comments in constructor in numparse_currency.cpp + // UnicodeSet fLeadCodePoints; + + /** Matches the currency string without concern for currency spacing. */ + bool matchCurrency(StringSegment& segment, ParsedNumber& result, UErrorCode& status) const; +}; + + +} // namespace impl +} // namespace numparse +U_NAMESPACE_END + +#endif //__NUMPARSE_CURRENCY_H__ +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/numparse_decimal.cpp b/deps/icu-small/source/i18n/numparse_decimal.cpp new file mode 100644 index 00000000000000..b120c5c6ad2f91 --- /dev/null +++ b/deps/icu-small/source/i18n/numparse_decimal.cpp @@ -0,0 +1,458 @@ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +// Allow implicit conversion from char16_t* to UnicodeString for this file: +// Helpful in toString methods and elsewhere. +#define UNISTR_FROM_STRING_EXPLICIT + +#include "numparse_types.h" +#include "numparse_decimal.h" +#include "static_unicode_sets.h" +#include "numparse_utils.h" +#include "unicode/uchar.h" +#include "putilimp.h" +#include "number_decimalquantity.h" + +using namespace icu; +using namespace icu::numparse; +using namespace icu::numparse::impl; + + +DecimalMatcher::DecimalMatcher(const DecimalFormatSymbols& symbols, const Grouper& grouper, + parse_flags_t parseFlags) { + if (0 != (parseFlags & PARSE_FLAG_MONETARY_SEPARATORS)) { + groupingSeparator = symbols.getConstSymbol(DecimalFormatSymbols::kMonetaryGroupingSeparatorSymbol); + decimalSeparator = symbols.getConstSymbol(DecimalFormatSymbols::kMonetarySeparatorSymbol); + } else { + groupingSeparator = symbols.getConstSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol); + decimalSeparator = symbols.getConstSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol); + } + bool strictSeparators = 0 != (parseFlags & PARSE_FLAG_STRICT_SEPARATORS); + unisets::Key groupingKey = strictSeparators ? unisets::STRICT_ALL_SEPARATORS + : unisets::ALL_SEPARATORS; + + // Attempt to find separators in the static cache + + groupingUniSet = unisets::get(groupingKey); + unisets::Key decimalKey = unisets::chooseFrom( + decimalSeparator, + strictSeparators ? unisets::STRICT_COMMA : unisets::COMMA, + strictSeparators ? unisets::STRICT_PERIOD : unisets::PERIOD); + if (decimalKey >= 0) { + decimalUniSet = unisets::get(decimalKey); + } else if (!decimalSeparator.isEmpty()) { + auto* set = new UnicodeSet(); + set->add(decimalSeparator.char32At(0)); + set->freeze(); + decimalUniSet = set; + fLocalDecimalUniSet.adoptInstead(set); + } else { + decimalUniSet = unisets::get(unisets::EMPTY); + } + + if (groupingKey >= 0 && decimalKey >= 0) { + // Everything is available in the static cache + separatorSet = groupingUniSet; + leadSet = unisets::get( + strictSeparators ? unisets::DIGITS_OR_ALL_SEPARATORS + : unisets::DIGITS_OR_STRICT_ALL_SEPARATORS); + } else { + auto* set = new UnicodeSet(); + set->addAll(*groupingUniSet); + set->addAll(*decimalUniSet); + set->freeze(); + separatorSet = set; + fLocalSeparatorSet.adoptInstead(set); + leadSet = nullptr; + } + + UChar32 cpZero = symbols.getCodePointZero(); + if (cpZero == -1 || !u_isdigit(cpZero) || u_digit(cpZero, 10) != 0) { + // Uncommon case: okay to allocate. + auto digitStrings = new UnicodeString[10]; + fLocalDigitStrings.adoptInstead(digitStrings); + for (int32_t i = 0; i <= 9; i++) { + digitStrings[i] = symbols.getConstDigitSymbol(i); + } + } + + requireGroupingMatch = 0 != (parseFlags & PARSE_FLAG_STRICT_GROUPING_SIZE); + groupingDisabled = 0 != (parseFlags & PARSE_FLAG_GROUPING_DISABLED); + integerOnly = 0 != (parseFlags & PARSE_FLAG_INTEGER_ONLY); + grouping1 = grouper.getPrimary(); + grouping2 = grouper.getSecondary(); + + // Fraction grouping parsing is disabled for now but could be enabled later. + // See http://bugs.icu-project.org/trac/ticket/10794 + // fractionGrouping = 0 != (parseFlags & PARSE_FLAG_FRACTION_GROUPING_ENABLED); +} + +bool DecimalMatcher::match(StringSegment& segment, ParsedNumber& result, UErrorCode& status) const { + return match(segment, result, 0, status); +} + +bool DecimalMatcher::match(StringSegment& segment, ParsedNumber& result, int8_t exponentSign, + UErrorCode&) const { + if (result.seenNumber() && exponentSign == 0) { + // A number has already been consumed. + return false; + } else if (exponentSign != 0) { + // scientific notation always comes after the number + U_ASSERT(!result.quantity.bogus); + } + + // Initial offset before any character consumption. + int32_t initialOffset = segment.getOffset(); + + // Return value: whether to ask for more characters. + bool maybeMore = false; + + // All digits consumed so far. + number::impl::DecimalQuantity digitsConsumed; + digitsConsumed.bogus = true; + + // The total number of digits after the decimal place, used for scaling the result. + int32_t digitsAfterDecimalPlace = 0; + + // The actual grouping and decimal separators used in the string. + // If non-null, we have seen that token. + UnicodeString actualGroupingString; + UnicodeString actualDecimalString; + actualGroupingString.setToBogus(); + actualDecimalString.setToBogus(); + + // Information for two groups: the previous group and the current group. + // + // Each group has three pieces of information: + // + // Offset: the string position of the beginning of the group, including a leading separator + // if there was a leading separator. This is needed in case we need to rewind the parse to + // that position. + // + // Separator type: + // 0 => beginning of string + // 1 => lead separator is a grouping separator + // 2 => lead separator is a decimal separator + // + // Count: the number of digits in the group. If -1, the group has been validated. + int32_t currGroupOffset = 0; + int32_t currGroupSepType = 0; + int32_t currGroupCount = 0; + int32_t prevGroupOffset = -1; + int32_t prevGroupSepType = -1; + int32_t prevGroupCount = -1; + + while (segment.length() > 0) { + maybeMore = false; + + // Attempt to match a digit. + int8_t digit = -1; + + // Try by code point digit value. + UChar32 cp = segment.getCodePoint(); + if (u_isdigit(cp)) { + segment.adjustOffset(U16_LENGTH(cp)); + digit = static_cast(u_digit(cp, 10)); + } + + // Try by digit string. + if (digit == -1 && !fLocalDigitStrings.isNull()) { + for (int32_t i = 0; i < 10; i++) { + const UnicodeString& str = fLocalDigitStrings[i]; + if (str.isEmpty()) { + continue; + } + int32_t overlap = segment.getCommonPrefixLength(str); + if (overlap == str.length()) { + segment.adjustOffset(overlap); + digit = static_cast(i); + break; + } + maybeMore = maybeMore || (overlap == segment.length()); + } + } + + if (digit >= 0) { + // Digit was found. + if (digitsConsumed.bogus) { + digitsConsumed.bogus = false; + digitsConsumed.clear(); + } + digitsConsumed.appendDigit(digit, 0, true); + currGroupCount++; + if (!actualDecimalString.isBogus()) { + digitsAfterDecimalPlace++; + } + continue; + } + + // Attempt to match a literal grouping or decimal separator. + bool isDecimal = false; + bool isGrouping = false; + + // 1) Attempt the decimal separator string literal. + // if (we have not seen a decimal separator yet) { ... } + if (actualDecimalString.isBogus() && !decimalSeparator.isEmpty()) { + int32_t overlap = segment.getCommonPrefixLength(decimalSeparator); + maybeMore = maybeMore || (overlap == segment.length()); + if (overlap == decimalSeparator.length()) { + isDecimal = true; + actualDecimalString = decimalSeparator; + } + } + + // 2) Attempt to match the actual grouping string literal. + if (!actualGroupingString.isBogus()) { + int32_t overlap = segment.getCommonPrefixLength(actualGroupingString); + maybeMore = maybeMore || (overlap == segment.length()); + if (overlap == actualGroupingString.length()) { + isGrouping = true; + } + } + + // 2.5) Attempt to match a new the grouping separator string literal. + // if (we have not seen a grouping or decimal separator yet) { ... } + if (!groupingDisabled && actualGroupingString.isBogus() && actualDecimalString.isBogus() && + !groupingSeparator.isEmpty()) { + int32_t overlap = segment.getCommonPrefixLength(groupingSeparator); + maybeMore = maybeMore || (overlap == segment.length()); + if (overlap == groupingSeparator.length()) { + isGrouping = true; + actualGroupingString = groupingSeparator; + } + } + + // 3) Attempt to match a decimal separator from the equivalence set. + // if (we have not seen a decimal separator yet) { ... } + // The !isGrouping is to confirm that we haven't yet matched the current character. + if (!isGrouping && actualDecimalString.isBogus()) { + if (decimalUniSet->contains(cp)) { + isDecimal = true; + actualDecimalString = UnicodeString(cp); + } + } + + // 4) Attempt to match a grouping separator from the equivalence set. + // if (we have not seen a grouping or decimal separator yet) { ... } + if (!groupingDisabled && actualGroupingString.isBogus() && actualDecimalString.isBogus()) { + if (groupingUniSet->contains(cp)) { + isGrouping = true; + actualGroupingString = UnicodeString(cp); + } + } + + // Leave if we failed to match this as a separator. + if (!isDecimal && !isGrouping) { + break; + } + + // Check for conditions when we don't want to accept the separator. + if (isDecimal && integerOnly) { + break; + } else if (currGroupSepType == 2 && isGrouping) { + // Fraction grouping + break; + } + + // Validate intermediate grouping sizes. + bool prevValidSecondary = validateGroup(prevGroupSepType, prevGroupCount, false); + bool currValidPrimary = validateGroup(currGroupSepType, currGroupCount, true); + if (!prevValidSecondary || (isDecimal && !currValidPrimary)) { + // Invalid grouping sizes. + if (isGrouping && currGroupCount == 0) { + // Trailing grouping separators: these are taken care of below + U_ASSERT(currGroupSepType == 1); + } else if (requireGroupingMatch) { + // Strict mode: reject the parse + digitsConsumed.clear(); + digitsConsumed.bogus = true; + } + break; + } else if (requireGroupingMatch && currGroupCount == 0 && currGroupSepType == 1) { + break; + } else { + // Grouping sizes OK so far. + prevGroupOffset = currGroupOffset; + prevGroupCount = currGroupCount; + if (isDecimal) { + // Do not validate this group any more. + prevGroupSepType = -1; + } else { + prevGroupSepType = currGroupSepType; + } + } + + // OK to accept the separator. + // Special case: don't update currGroup if it is empty; this allows two grouping + // separators in a row in lenient mode. + if (currGroupCount != 0) { + currGroupOffset = segment.getOffset(); + } + currGroupSepType = isGrouping ? 1 : 2; + currGroupCount = 0; + if (isGrouping) { + segment.adjustOffset(actualGroupingString.length()); + } else { + segment.adjustOffset(actualDecimalString.length()); + } + } + + // End of main loop. + // Back up if there was a trailing grouping separator. + // Shift prev -> curr so we can check it as a final group. + if (currGroupSepType != 2 && currGroupCount == 0) { + maybeMore = true; + segment.setOffset(currGroupOffset); + currGroupOffset = prevGroupOffset; + currGroupSepType = prevGroupSepType; + currGroupCount = prevGroupCount; + prevGroupOffset = -1; + prevGroupSepType = 0; + prevGroupCount = 1; + } + + // Validate final grouping sizes. + bool prevValidSecondary = validateGroup(prevGroupSepType, prevGroupCount, false); + bool currValidPrimary = validateGroup(currGroupSepType, currGroupCount, true); + if (!requireGroupingMatch) { + // The cases we need to handle here are lone digits. + // Examples: "1,1" "1,1," "1,1,1" "1,1,1," ",1" (all parse as 1) + // See more examples in numberformattestspecification.txt + int32_t digitsToRemove = 0; + if (!prevValidSecondary) { + segment.setOffset(prevGroupOffset); + digitsToRemove += prevGroupCount; + digitsToRemove += currGroupCount; + } else if (!currValidPrimary && (prevGroupSepType != 0 || prevGroupCount != 0)) { + maybeMore = true; + segment.setOffset(currGroupOffset); + digitsToRemove += currGroupCount; + } + if (digitsToRemove != 0) { + digitsConsumed.adjustMagnitude(-digitsToRemove); + digitsConsumed.truncate(); + } + prevValidSecondary = true; + currValidPrimary = true; + } + if (currGroupSepType != 2 && (!prevValidSecondary || !currValidPrimary)) { + // Grouping failure. + digitsConsumed.bogus = true; + } + + // Strings that start with a separator but have no digits, + // or strings that failed a grouping size check. + if (digitsConsumed.bogus) { + maybeMore = maybeMore || (segment.length() == 0); + segment.setOffset(initialOffset); + return maybeMore; + } + + // We passed all inspections. Start post-processing. + + // Adjust for fraction part. + digitsConsumed.adjustMagnitude(-digitsAfterDecimalPlace); + + // Set the digits, either normal or exponent. + if (exponentSign != 0 && segment.getOffset() != initialOffset) { + bool overflow = false; + if (digitsConsumed.fitsInLong()) { + int64_t exponentLong = digitsConsumed.toLong(false); + U_ASSERT(exponentLong >= 0); + if (exponentLong <= INT32_MAX) { + auto exponentInt = static_cast(exponentLong); + if (result.quantity.adjustMagnitude(exponentSign * exponentInt)) { + overflow = true; + } + } else { + overflow = true; + } + } else { + overflow = true; + } + if (overflow) { + if (exponentSign == -1) { + // Set to zero + result.quantity.clear(); + } else { + // Set to infinity + result.quantity.bogus = true; + result.flags |= FLAG_INFINITY; + } + } + } else { + result.quantity = digitsConsumed; + } + + // Set other information into the result and return. + if (!actualDecimalString.isBogus()) { + result.flags |= FLAG_HAS_DECIMAL_SEPARATOR; + } + result.setCharsConsumed(segment); + return segment.length() == 0 || maybeMore; +} + +bool DecimalMatcher::validateGroup(int32_t sepType, int32_t count, bool isPrimary) const { + if (requireGroupingMatch) { + if (sepType == -1) { + // No such group (prevGroup before first shift). + return true; + } else if (sepType == 0) { + // First group. + if (isPrimary) { + // No grouping separators is OK. + return true; + } else { + return count != 0 && count <= grouping2; + } + } else if (sepType == 1) { + // Middle group. + if (isPrimary) { + return count == grouping1; + } else { + return count == grouping2; + } + } else { + U_ASSERT(sepType == 2); + // After the decimal separator. + return true; + } + } else { + if (sepType == 1) { + // #11230: don't accept middle groups with only 1 digit. + return count != 1; + } else { + return true; + } + } +} + +bool DecimalMatcher::smokeTest(const StringSegment& segment) const { + // The common case uses a static leadSet for efficiency. + if (fLocalDigitStrings.isNull() && leadSet != nullptr) { + return segment.startsWith(*leadSet); + } + if (segment.startsWith(*separatorSet) || u_isdigit(segment.getCodePoint())) { + return true; + } + if (fLocalDigitStrings.isNull()) { + return false; + } + for (int32_t i = 0; i < 10; i++) { + if (segment.startsWith(fLocalDigitStrings[i])) { + return true; + } + } + return false; +} + +UnicodeString DecimalMatcher::toString() const { + return u""; +} + + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/numparse_decimal.h b/deps/icu-small/source/i18n/numparse_decimal.h new file mode 100644 index 00000000000000..ec6c76487e44fc --- /dev/null +++ b/deps/icu-small/source/i18n/numparse_decimal.h @@ -0,0 +1,76 @@ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING +#ifndef __NUMPARSE_DECIMAL_H__ +#define __NUMPARSE_DECIMAL_H__ + +#include "unicode/uniset.h" +#include "numparse_types.h" + +U_NAMESPACE_BEGIN namespace numparse { +namespace impl { + +using ::icu::number::impl::Grouper; + +class DecimalMatcher : public NumberParseMatcher, public UMemory { + public: + DecimalMatcher() = default; // WARNING: Leaves the object in an unusable state + + DecimalMatcher(const DecimalFormatSymbols& symbols, const Grouper& grouper, + parse_flags_t parseFlags); + + bool match(StringSegment& segment, ParsedNumber& result, UErrorCode& status) const override; + + bool + match(StringSegment& segment, ParsedNumber& result, int8_t exponentSign, UErrorCode& status) const; + + bool smokeTest(const StringSegment& segment) const override; + + UnicodeString toString() const override; + + private: + /** If true, only accept strings whose grouping sizes match the locale */ + bool requireGroupingMatch; + + /** If true, do not accept grouping separators at all */ + bool groupingDisabled; + + // Fraction grouping parsing is disabled for now but could be enabled later. + // See http://bugs.icu-project.org/trac/ticket/10794 + // bool fractionGrouping; + + /** If true, do not accept numbers in the fraction */ + bool integerOnly; + + int16_t grouping1; + int16_t grouping2; + + UnicodeString groupingSeparator; + UnicodeString decimalSeparator; + + // Assumption: these sets all consist of single code points. If this assumption needs to be broken, + // fix getLeadCodePoints() as well as matching logic. Be careful of the performance impact. + const UnicodeSet* groupingUniSet; + const UnicodeSet* decimalUniSet; + const UnicodeSet* separatorSet; + const UnicodeSet* leadSet; + + // Make this class the owner of a few objects that could be allocated. + // The first three LocalPointers are used for assigning ownership only. + LocalPointer fLocalDecimalUniSet; + LocalPointer fLocalSeparatorSet; + LocalArray fLocalDigitStrings; + + bool validateGroup(int32_t sepType, int32_t count, bool isPrimary) const; +}; + + +} // namespace impl +} // namespace numparse +U_NAMESPACE_END + +#endif //__NUMPARSE_DECIMAL_H__ +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/numparse_impl.cpp b/deps/icu-small/source/i18n/numparse_impl.cpp new file mode 100644 index 00000000000000..5fa52f63351c31 --- /dev/null +++ b/deps/icu-small/source/i18n/numparse_impl.cpp @@ -0,0 +1,361 @@ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +// Allow implicit conversion from char16_t* to UnicodeString for this file: +// Helpful in toString methods and elsewhere. +#define UNISTR_FROM_STRING_EXPLICIT + +#include +#include +#include "number_types.h" +#include "number_patternstring.h" +#include "numparse_types.h" +#include "numparse_impl.h" +#include "numparse_symbols.h" +#include "numparse_decimal.h" +#include "unicode/numberformatter.h" +#include "cstr.h" +#include "number_mapper.h" +#include "static_unicode_sets.h" + +using namespace icu; +using namespace icu::number; +using namespace icu::number::impl; +using namespace icu::numparse; +using namespace icu::numparse::impl; + + +NumberParseMatcher::~NumberParseMatcher() = default; + + +NumberParserImpl* +NumberParserImpl::createSimpleParser(const Locale& locale, const UnicodeString& patternString, + parse_flags_t parseFlags, UErrorCode& status) { + + LocalPointer parser(new NumberParserImpl(parseFlags)); + DecimalFormatSymbols symbols(locale, status); + + parser->fLocalMatchers.ignorables = {unisets::DEFAULT_IGNORABLES}; + IgnorablesMatcher& ignorables = parser->fLocalMatchers.ignorables; + + DecimalFormatSymbols dfs(locale, status); + dfs.setSymbol(DecimalFormatSymbols::kCurrencySymbol, u"IU$"); + dfs.setSymbol(DecimalFormatSymbols::kIntlCurrencySymbol, u"ICU"); + CurrencySymbols currencySymbols({u"ICU", status}, locale, dfs, status); + + ParsedPatternInfo patternInfo; + PatternParser::parseToPatternInfo(patternString, patternInfo, status); + + // The following statements set up the affix matchers. + AffixTokenMatcherSetupData affixSetupData = { + currencySymbols, symbols, ignorables, locale, parseFlags}; + parser->fLocalMatchers.affixTokenMatcherWarehouse = {&affixSetupData}; + parser->fLocalMatchers.affixMatcherWarehouse = {&parser->fLocalMatchers.affixTokenMatcherWarehouse}; + parser->fLocalMatchers.affixMatcherWarehouse.createAffixMatchers( + patternInfo, *parser, ignorables, parseFlags, status); + + Grouper grouper = Grouper::forStrategy(UNUM_GROUPING_AUTO); + grouper.setLocaleData(patternInfo, locale); + + parser->addMatcher(parser->fLocalMatchers.ignorables); + parser->addMatcher(parser->fLocalMatchers.decimal = {symbols, grouper, parseFlags}); + parser->addMatcher(parser->fLocalMatchers.minusSign = {symbols, false}); + parser->addMatcher(parser->fLocalMatchers.plusSign = {symbols, false}); + parser->addMatcher(parser->fLocalMatchers.percent = {symbols}); + parser->addMatcher(parser->fLocalMatchers.permille = {symbols}); + parser->addMatcher(parser->fLocalMatchers.nan = {symbols}); + parser->addMatcher(parser->fLocalMatchers.infinity = {symbols}); + parser->addMatcher(parser->fLocalMatchers.padding = {u"@"}); + parser->addMatcher(parser->fLocalMatchers.scientific = {symbols, grouper}); + parser->addMatcher(parser->fLocalMatchers.currency = {currencySymbols, symbols, parseFlags, status}); +// parser.addMatcher(new RequireNumberMatcher()); + + parser->freeze(); + return parser.orphan(); +} + +NumberParserImpl* +NumberParserImpl::createParserFromProperties(const number::impl::DecimalFormatProperties& properties, + const DecimalFormatSymbols& symbols, bool parseCurrency, + UErrorCode& status) { + Locale locale = symbols.getLocale(); + PropertiesAffixPatternProvider localPAPP; + CurrencyPluralInfoAffixProvider localCPIAP; + AffixPatternProvider* affixProvider; + if (properties.currencyPluralInfo.fPtr.isNull()) { + localPAPP.setTo(properties, status); + affixProvider = &localPAPP; + } else { + localCPIAP.setTo(*properties.currencyPluralInfo.fPtr, properties, status); + affixProvider = &localCPIAP; + } + if (affixProvider == nullptr || U_FAILURE(status)) { return nullptr; } + CurrencyUnit currency = resolveCurrency(properties, locale, status); + CurrencySymbols currencySymbols(currency, locale, symbols, status); + bool isStrict = properties.parseMode.getOrDefault(PARSE_MODE_STRICT) == PARSE_MODE_STRICT; + Grouper grouper = Grouper::forProperties(properties); + int parseFlags = 0; + if (affixProvider == nullptr || U_FAILURE(status)) { return nullptr; } + if (!properties.parseCaseSensitive) { + parseFlags |= PARSE_FLAG_IGNORE_CASE; + } + if (properties.parseIntegerOnly) { + parseFlags |= PARSE_FLAG_INTEGER_ONLY; + } + if (properties.signAlwaysShown) { + parseFlags |= PARSE_FLAG_PLUS_SIGN_ALLOWED; + } + if (isStrict) { + parseFlags |= PARSE_FLAG_STRICT_GROUPING_SIZE; + parseFlags |= PARSE_FLAG_STRICT_SEPARATORS; + parseFlags |= PARSE_FLAG_USE_FULL_AFFIXES; + parseFlags |= PARSE_FLAG_EXACT_AFFIX; + } else { + parseFlags |= PARSE_FLAG_INCLUDE_UNPAIRED_AFFIXES; + } + if (grouper.getPrimary() <= 0) { + parseFlags |= PARSE_FLAG_GROUPING_DISABLED; + } + if (parseCurrency || affixProvider->hasCurrencySign()) { + parseFlags |= PARSE_FLAG_MONETARY_SEPARATORS; + } + if (!parseCurrency) { + parseFlags |= PARSE_FLAG_NO_FOREIGN_CURRENCY; + } + + LocalPointer parser(new NumberParserImpl(parseFlags)); + + parser->fLocalMatchers.ignorables = { + isStrict ? unisets::STRICT_IGNORABLES : unisets::DEFAULT_IGNORABLES}; + IgnorablesMatcher& ignorables = parser->fLocalMatchers.ignorables; + + ////////////////////// + /// AFFIX MATCHERS /// + ////////////////////// + + // The following statements set up the affix matchers. + AffixTokenMatcherSetupData affixSetupData = { + currencySymbols, symbols, ignorables, locale, parseFlags}; + parser->fLocalMatchers.affixTokenMatcherWarehouse = {&affixSetupData}; + parser->fLocalMatchers.affixMatcherWarehouse = {&parser->fLocalMatchers.affixTokenMatcherWarehouse}; + parser->fLocalMatchers.affixMatcherWarehouse.createAffixMatchers( + *affixProvider, *parser, ignorables, parseFlags, status); + + //////////////////////// + /// CURRENCY MATCHER /// + //////////////////////// + + if (parseCurrency || affixProvider->hasCurrencySign()) { + parser->addMatcher(parser->fLocalMatchers.currency = {currencySymbols, symbols, parseFlags, status}); + } + + /////////////// + /// PERCENT /// + /////////////// + + // ICU-TC meeting, April 11, 2018: accept percent/permille only if it is in the pattern, + // and to maintain regressive behavior, divide by 100 even if no percent sign is present. + if (affixProvider->containsSymbolType(AffixPatternType::TYPE_PERCENT, status)) { + parser->addMatcher(parser->fLocalMatchers.percent = {symbols}); + } + if (affixProvider->containsSymbolType(AffixPatternType::TYPE_PERMILLE, status)) { + parser->addMatcher(parser->fLocalMatchers.permille = {symbols}); + } + + /////////////////////////////// + /// OTHER STANDARD MATCHERS /// + /////////////////////////////// + + if (!isStrict) { + parser->addMatcher(parser->fLocalMatchers.plusSign = {symbols, false}); + parser->addMatcher(parser->fLocalMatchers.minusSign = {symbols, false}); + } + parser->addMatcher(parser->fLocalMatchers.nan = {symbols}); + parser->addMatcher(parser->fLocalMatchers.infinity = {symbols}); + UnicodeString padString = properties.padString; + if (!padString.isBogus() && !ignorables.getSet()->contains(padString)) { + parser->addMatcher(parser->fLocalMatchers.padding = {padString}); + } + parser->addMatcher(parser->fLocalMatchers.ignorables); + parser->addMatcher(parser->fLocalMatchers.decimal = {symbols, grouper, parseFlags}); + // NOTE: parseNoExponent doesn't disable scientific parsing if we have a scientific formatter + if (!properties.parseNoExponent || properties.minimumExponentDigits > 0) { + parser->addMatcher(parser->fLocalMatchers.scientific = {symbols, grouper}); + } + + ////////////////// + /// VALIDATORS /// + ////////////////// + + parser->addMatcher(parser->fLocalValidators.number = {}); + if (isStrict) { + parser->addMatcher(parser->fLocalValidators.affix = {}); + } + if (parseCurrency) { + parser->addMatcher(parser->fLocalValidators.currency = {}); + } + if (properties.decimalPatternMatchRequired) { + bool patternHasDecimalSeparator = + properties.decimalSeparatorAlwaysShown || properties.maximumFractionDigits != 0; + parser->addMatcher(parser->fLocalValidators.decimalSeparator = {patternHasDecimalSeparator}); + } + // The multiplier takes care of scaling percentages. + Scale multiplier = scaleFromProperties(properties); + if (multiplier.isValid()) { + parser->addMatcher(parser->fLocalValidators.multiplier = {multiplier}); + } + + parser->freeze(); + return parser.orphan(); +} + +NumberParserImpl::NumberParserImpl(parse_flags_t parseFlags) + : fParseFlags(parseFlags) { +} + +NumberParserImpl::~NumberParserImpl() { + fNumMatchers = 0; +} + +void NumberParserImpl::addMatcher(NumberParseMatcher& matcher) { + if (fNumMatchers + 1 > fMatchers.getCapacity()) { + fMatchers.resize(fNumMatchers * 2, fNumMatchers); + } + fMatchers[fNumMatchers] = &matcher; + fNumMatchers++; +} + +void NumberParserImpl::freeze() { + fFrozen = true; +} + +parse_flags_t NumberParserImpl::getParseFlags() const { + return fParseFlags; +} + +void NumberParserImpl::parse(const UnicodeString& input, bool greedy, ParsedNumber& result, + UErrorCode& status) const { + return parse(input, 0, greedy, result, status); +} + +void NumberParserImpl::parse(const UnicodeString& input, int32_t start, bool greedy, ParsedNumber& result, + UErrorCode& status) const { + if (U_FAILURE(status)) { + return; + } + U_ASSERT(fFrozen); + // TODO: Check start >= 0 and start < input.length() + StringSegment segment(input, 0 != (fParseFlags & PARSE_FLAG_IGNORE_CASE)); + segment.adjustOffset(start); + if (greedy) { + parseGreedyRecursive(segment, result, status); + } else { + parseLongestRecursive(segment, result, status); + } + for (int32_t i = 0; i < fNumMatchers; i++) { + fMatchers[i]->postProcess(result); + } + result.postProcess(); +} + +void NumberParserImpl::parseGreedyRecursive(StringSegment& segment, ParsedNumber& result, + UErrorCode& status) const { + // Base Case + if (segment.length() == 0) { + return; + } + + int initialOffset = segment.getOffset(); + for (int32_t i = 0; i < fNumMatchers; i++) { + const NumberParseMatcher* matcher = fMatchers[i]; + if (!matcher->smokeTest(segment)) { + continue; + } + matcher->match(segment, result, status); + if (U_FAILURE(status)) { + return; + } + if (segment.getOffset() != initialOffset) { + // In a greedy parse, recurse on only the first match. + parseGreedyRecursive(segment, result, status); + // The following line resets the offset so that the StringSegment says the same across + // the function + // call boundary. Since we recurse only once, this line is not strictly necessary. + segment.setOffset(initialOffset); + return; + } + } + + // NOTE: If we get here, the greedy parse completed without consuming the entire string. +} + +void NumberParserImpl::parseLongestRecursive(StringSegment& segment, ParsedNumber& result, + UErrorCode& status) const { + // Base Case + if (segment.length() == 0) { + return; + } + + // TODO: Give a nice way for the matcher to reset the ParsedNumber? + ParsedNumber initial(result); + ParsedNumber candidate; + + int initialOffset = segment.getOffset(); + for (int32_t i = 0; i < fNumMatchers; i++) { + const NumberParseMatcher* matcher = fMatchers[i]; + if (!matcher->smokeTest(segment)) { + continue; + } + + // In a non-greedy parse, we attempt all possible matches and pick the best. + for (int32_t charsToConsume = 0; charsToConsume < segment.length();) { + charsToConsume += U16_LENGTH(segment.codePointAt(charsToConsume)); + + // Run the matcher on a segment of the current length. + candidate = initial; + segment.setLength(charsToConsume); + bool maybeMore = matcher->match(segment, candidate, status); + segment.resetLength(); + if (U_FAILURE(status)) { + return; + } + + // If the entire segment was consumed, recurse. + if (segment.getOffset() - initialOffset == charsToConsume) { + parseLongestRecursive(segment, candidate, status); + if (U_FAILURE(status)) { + return; + } + if (candidate.isBetterThan(result)) { + result = candidate; + } + } + + // Since the segment can be re-used, reset the offset. + // This does not have an effect if the matcher did not consume any chars. + segment.setOffset(initialOffset); + + // Unless the matcher wants to see the next char, continue to the next matcher. + if (!maybeMore) { + break; + } + } + } +} + +UnicodeString NumberParserImpl::toString() const { + UnicodeString result(u"toString()); + } + result.append(u" ]>", -1); + return result; +} + + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/numparse_impl.h b/deps/icu-small/source/i18n/numparse_impl.h new file mode 100644 index 00000000000000..992114c7edee18 --- /dev/null +++ b/deps/icu-small/source/i18n/numparse_impl.h @@ -0,0 +1,109 @@ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING +#ifndef __NUMPARSE_IMPL_H__ +#define __NUMPARSE_IMPL_H__ + +#include "numparse_types.h" +#include "numparse_decimal.h" +#include "numparse_symbols.h" +#include "numparse_scientific.h" +#include "unicode/uniset.h" +#include "numparse_currency.h" +#include "numparse_affixes.h" +#include "number_decimfmtprops.h" +#include "unicode/localpointer.h" +#include "numparse_validators.h" +#include "number_multiplier.h" + +U_NAMESPACE_BEGIN + +// Export an explicit template instantiation of the MaybeStackArray that is used as a data member of NumberParserImpl. +// When building DLLs for Windows this is required even though no direct access to the MaybeStackArray leaks out of the i18n library. +// (See numparse_compositions.h, numparse_affixes.h, datefmt.h, and others for similar examples.) +#if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN +template class U_I18N_API MaybeStackArray; +#endif + +namespace numparse { +namespace impl { + +// Exported as U_I18N_API for tests +class U_I18N_API NumberParserImpl : public MutableMatcherCollection, public UMemory { + public: + virtual ~NumberParserImpl(); + + static NumberParserImpl* createSimpleParser(const Locale& locale, const UnicodeString& patternString, + parse_flags_t parseFlags, UErrorCode& status); + + static NumberParserImpl* createParserFromProperties( + const number::impl::DecimalFormatProperties& properties, const DecimalFormatSymbols& symbols, + bool parseCurrency, UErrorCode& status); + + /** + * Does NOT take ownership of the matcher. The matcher MUST remain valid for the lifespan of the + * NumberParserImpl. + * @param matcher The matcher to reference. + */ + void addMatcher(NumberParseMatcher& matcher) override; + + void freeze(); + + parse_flags_t getParseFlags() const; + + void parse(const UnicodeString& input, bool greedy, ParsedNumber& result, UErrorCode& status) const; + + void parse(const UnicodeString& input, int32_t start, bool greedy, ParsedNumber& result, + UErrorCode& status) const; + + UnicodeString toString() const; + + private: + parse_flags_t fParseFlags; + int32_t fNumMatchers = 0; + // NOTE: The stack capacity for fMatchers and fLeads should be the same + MaybeStackArray fMatchers; + bool fFrozen = false; + + // WARNING: All of these matchers start in an undefined state (default-constructed). + // You must use an assignment operator on them before using. + struct { + IgnorablesMatcher ignorables; + InfinityMatcher infinity; + MinusSignMatcher minusSign; + NanMatcher nan; + PaddingMatcher padding; + PercentMatcher percent; + PermilleMatcher permille; + PlusSignMatcher plusSign; + DecimalMatcher decimal; + ScientificMatcher scientific; + CombinedCurrencyMatcher currency; + AffixMatcherWarehouse affixMatcherWarehouse; + AffixTokenMatcherWarehouse affixTokenMatcherWarehouse; + } fLocalMatchers; + struct { + RequireAffixValidator affix; + RequireCurrencyValidator currency; + RequireDecimalSeparatorValidator decimalSeparator; + RequireNumberValidator number; + MultiplierParseHandler multiplier; + } fLocalValidators; + + explicit NumberParserImpl(parse_flags_t parseFlags); + + void parseGreedyRecursive(StringSegment& segment, ParsedNumber& result, UErrorCode& status) const; + + void parseLongestRecursive(StringSegment& segment, ParsedNumber& result, UErrorCode& status) const; +}; + + +} // namespace impl +} // namespace numparse +U_NAMESPACE_END + +#endif //__NUMPARSE_IMPL_H__ +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/numparse_parsednumber.cpp b/deps/icu-small/source/i18n/numparse_parsednumber.cpp new file mode 100644 index 00000000000000..98da4e8319227a --- /dev/null +++ b/deps/icu-small/source/i18n/numparse_parsednumber.cpp @@ -0,0 +1,122 @@ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +// Allow implicit conversion from char16_t* to UnicodeString for this file: +// Helpful in toString methods and elsewhere. +#define UNISTR_FROM_STRING_EXPLICIT + +#include "numparse_types.h" +#include "number_decimalquantity.h" +#include "putilimp.h" +#include + +using namespace icu; +using namespace icu::number; +using namespace icu::number::impl; +using namespace icu::numparse; +using namespace icu::numparse::impl; + + +ParsedNumber::ParsedNumber() { + clear(); +} + +void ParsedNumber::clear() { + quantity.bogus = true; + charEnd = 0; + flags = 0; + prefix.setToBogus(); + suffix.setToBogus(); + currencyCode[0] = 0; +} + +void ParsedNumber::setCharsConsumed(const StringSegment& segment) { + charEnd = segment.getOffset(); +} + +void ParsedNumber::postProcess() { + if (!quantity.bogus && 0 != (flags & FLAG_NEGATIVE)) { + quantity.negate(); + } +} + +bool ParsedNumber::success() const { + return charEnd > 0 && 0 == (flags & FLAG_FAIL); +} + +bool ParsedNumber::seenNumber() const { + return !quantity.bogus || 0 != (flags & FLAG_NAN) || 0 != (flags & FLAG_INFINITY); +} + +double ParsedNumber::getDouble() const { + bool sawNaN = 0 != (flags & FLAG_NAN); + bool sawInfinity = 0 != (flags & FLAG_INFINITY); + + // Check for NaN, infinity, and -0.0 + if (sawNaN) { + // Can't use NAN or std::nan because the byte pattern is platform-dependent; + // MSVC sets the sign bit, but Clang and GCC do not + return uprv_getNaN(); + } + if (sawInfinity) { + if (0 != (flags & FLAG_NEGATIVE)) { + return -INFINITY; + } else { + return INFINITY; + } + } + U_ASSERT(!quantity.bogus); + if (quantity.isZero() && quantity.isNegative()) { + return -0.0; + } + + if (quantity.fitsInLong()) { + return static_cast(quantity.toLong()); + } else { + return quantity.toDouble(); + } +} + +void ParsedNumber::populateFormattable(Formattable& output, parse_flags_t parseFlags) const { + bool sawNaN = 0 != (flags & FLAG_NAN); + bool sawInfinity = 0 != (flags & FLAG_INFINITY); + bool integerOnly = 0 != (parseFlags & PARSE_FLAG_INTEGER_ONLY); + + // Check for NaN, infinity, and -0.0 + if (sawNaN) { + // Can't use NAN or std::nan because the byte pattern is platform-dependent; + // MSVC sets the sign bit, but Clang and GCC do not + output.setDouble(uprv_getNaN()); + return; + } + if (sawInfinity) { + if (0 != (flags & FLAG_NEGATIVE)) { + output.setDouble(-INFINITY); + return; + } else { + output.setDouble(INFINITY); + return; + } + } + U_ASSERT(!quantity.bogus); + if (quantity.isZero() && quantity.isNegative() && !integerOnly) { + output.setDouble(-0.0); + return; + } + + // All other numbers + output.adoptDecimalQuantity(new DecimalQuantity(quantity)); +} + +bool ParsedNumber::isBetterThan(const ParsedNumber& other) { + // Favor results with strictly more characters consumed. + return charEnd > other.charEnd; +} + + + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/numparse_scientific.cpp b/deps/icu-small/source/i18n/numparse_scientific.cpp new file mode 100644 index 00000000000000..611695e57d4743 --- /dev/null +++ b/deps/icu-small/source/i18n/numparse_scientific.cpp @@ -0,0 +1,133 @@ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +// Allow implicit conversion from char16_t* to UnicodeString for this file: +// Helpful in toString methods and elsewhere. +#define UNISTR_FROM_STRING_EXPLICIT + +#include "numparse_types.h" +#include "numparse_scientific.h" +#include "static_unicode_sets.h" + +using namespace icu; +using namespace icu::numparse; +using namespace icu::numparse::impl; + + +namespace { + +inline const UnicodeSet& minusSignSet() { + return *unisets::get(unisets::MINUS_SIGN); +} + +inline const UnicodeSet& plusSignSet() { + return *unisets::get(unisets::PLUS_SIGN); +} + +} // namespace + + +ScientificMatcher::ScientificMatcher(const DecimalFormatSymbols& dfs, const Grouper& grouper) + : fExponentSeparatorString(dfs.getConstSymbol(DecimalFormatSymbols::kExponentialSymbol)), + fExponentMatcher(dfs, grouper, PARSE_FLAG_INTEGER_ONLY | PARSE_FLAG_GROUPING_DISABLED) { + + const UnicodeString& minusSign = dfs.getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol); + if (minusSignSet().contains(minusSign)) { + fCustomMinusSign.setToBogus(); + } else { + fCustomMinusSign = minusSign; + } + + const UnicodeString& plusSign = dfs.getConstSymbol(DecimalFormatSymbols::kPlusSignSymbol); + if (plusSignSet().contains(plusSign)) { + fCustomPlusSign.setToBogus(); + } else { + fCustomPlusSign = plusSign; + } +} + +bool ScientificMatcher::match(StringSegment& segment, ParsedNumber& result, UErrorCode& status) const { + // Only accept scientific notation after the mantissa. + if (!result.seenNumber()) { + return false; + } + + // First match the scientific separator, and then match another number after it. + // NOTE: This is guarded by the smoke test; no need to check fExponentSeparatorString length again. + int overlap1 = segment.getCommonPrefixLength(fExponentSeparatorString); + if (overlap1 == fExponentSeparatorString.length()) { + // Full exponent separator match. + + // First attempt to get a code point, returning true if we can't get one. + if (segment.length() == overlap1) { + return true; + } + segment.adjustOffset(overlap1); + + // Allow a sign, and then try to match digits. + int8_t exponentSign = 1; + if (segment.startsWith(minusSignSet())) { + exponentSign = -1; + segment.adjustOffsetByCodePoint(); + } else if (segment.startsWith(plusSignSet())) { + segment.adjustOffsetByCodePoint(); + } else if (segment.startsWith(fCustomMinusSign)) { + // Note: call site is guarded with startsWith, which returns false on empty string + int32_t overlap2 = segment.getCommonPrefixLength(fCustomMinusSign); + if (overlap2 != fCustomMinusSign.length()) { + // Partial custom sign match; un-match the exponent separator. + segment.adjustOffset(-overlap1); + return true; + } + exponentSign = -1; + segment.adjustOffset(overlap2); + } else if (segment.startsWith(fCustomPlusSign)) { + // Note: call site is guarded with startsWith, which returns false on empty string + int32_t overlap2 = segment.getCommonPrefixLength(fCustomPlusSign); + if (overlap2 != fCustomPlusSign.length()) { + // Partial custom sign match; un-match the exponent separator. + segment.adjustOffset(-overlap1); + return true; + } + segment.adjustOffset(overlap2); + } + + // We are supposed to accept E0 after NaN, so we need to make sure result.quantity is available. + bool wasBogus = result.quantity.bogus; + result.quantity.bogus = false; + int digitsOffset = segment.getOffset(); + bool digitsReturnValue = fExponentMatcher.match(segment, result, exponentSign, status); + result.quantity.bogus = wasBogus; + + if (segment.getOffset() != digitsOffset) { + // At least one exponent digit was matched. + result.flags |= FLAG_HAS_EXPONENT; + } else { + // No exponent digits were matched; un-match the exponent separator. + segment.adjustOffset(-overlap1); + } + return digitsReturnValue; + + } else if (overlap1 == segment.length()) { + // Partial exponent separator match + return true; + } + + // No match + return false; +} + +bool ScientificMatcher::smokeTest(const StringSegment& segment) const { + return segment.startsWith(fExponentSeparatorString); +} + +UnicodeString ScientificMatcher::toString() const { + return u""; +} + + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/numparse_scientific.h b/deps/icu-small/source/i18n/numparse_scientific.h new file mode 100644 index 00000000000000..ddecf858af3584 --- /dev/null +++ b/deps/icu-small/source/i18n/numparse_scientific.h @@ -0,0 +1,45 @@ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING +#ifndef __NUMPARSE_SCIENTIFIC_H__ +#define __NUMPARSE_SCIENTIFIC_H__ + +#include "numparse_types.h" +#include "numparse_decimal.h" +#include "unicode/numberformatter.h" + +using icu::number::impl::Grouper; + +U_NAMESPACE_BEGIN namespace numparse { +namespace impl { + + +class ScientificMatcher : public NumberParseMatcher, public UMemory { + public: + ScientificMatcher() = default; // WARNING: Leaves the object in an unusable state + + ScientificMatcher(const DecimalFormatSymbols& dfs, const Grouper& grouper); + + bool match(StringSegment& segment, ParsedNumber& result, UErrorCode& status) const override; + + bool smokeTest(const StringSegment& segment) const override; + + UnicodeString toString() const override; + + private: + UnicodeString fExponentSeparatorString; + DecimalMatcher fExponentMatcher; + UnicodeString fCustomMinusSign; + UnicodeString fCustomPlusSign; +}; + + +} // namespace impl +} // namespace numparse +U_NAMESPACE_END + +#endif //__NUMPARSE_SCIENTIFIC_H__ +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/numparse_stringsegment.cpp b/deps/icu-small/source/i18n/numparse_stringsegment.cpp new file mode 100644 index 00000000000000..3db4fe618a603a --- /dev/null +++ b/deps/icu-small/source/i18n/numparse_stringsegment.cpp @@ -0,0 +1,146 @@ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +// Allow implicit conversion from char16_t* to UnicodeString for this file: +// Helpful in toString methods and elsewhere. +#define UNISTR_FROM_STRING_EXPLICIT + +#include "numparse_types.h" +#include "numparse_stringsegment.h" +#include "putilimp.h" +#include "unicode/utf16.h" +#include "unicode/uniset.h" + +using namespace icu; +using namespace icu::numparse; +using namespace icu::numparse::impl; + + +StringSegment::StringSegment(const UnicodeString& str, bool ignoreCase) + : fStr(str), fStart(0), fEnd(str.length()), + fFoldCase(ignoreCase) {} + +int32_t StringSegment::getOffset() const { + return fStart; +} + +void StringSegment::setOffset(int32_t start) { + fStart = start; +} + +void StringSegment::adjustOffset(int32_t delta) { + fStart += delta; +} + +void StringSegment::adjustOffsetByCodePoint() { + fStart += U16_LENGTH(getCodePoint()); +} + +void StringSegment::setLength(int32_t length) { + fEnd = fStart + length; +} + +void StringSegment::resetLength() { + fEnd = fStr.length(); +} + +int32_t StringSegment::length() const { + return fEnd - fStart; +} + +char16_t StringSegment::charAt(int32_t index) const { + return fStr.charAt(index + fStart); +} + +UChar32 StringSegment::codePointAt(int32_t index) const { + return fStr.char32At(index + fStart); +} + +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); +} + +UChar32 StringSegment::getCodePoint() const { + char16_t lead = fStr.charAt(fStart); + if (U16_IS_LEAD(lead) && fStart + 1 < fEnd) { + return fStr.char32At(fStart); + } else if (U16_IS_SURROGATE(lead)) { + return -1; + } else { + return lead; + } +} + +bool StringSegment::startsWith(UChar32 otherCp) const { + return codePointsEqual(getCodePoint(), otherCp, fFoldCase); +} + +bool StringSegment::startsWith(const UnicodeSet& uniset) const { + // TODO: Move UnicodeSet case-folding logic here. + // TODO: Handle string matches here instead of separately. + UChar32 cp = getCodePoint(); + if (cp == -1) { + return false; + } + return uniset.contains(cp); +} + +bool StringSegment::startsWith(const UnicodeString& other) const { + if (other.isBogus() || other.length() == 0 || length() == 0) { + return false; + } + int cp1 = getCodePoint(); + int cp2 = other.char32At(0); + return codePointsEqual(cp1, cp2, fFoldCase); +} + +int32_t StringSegment::getCommonPrefixLength(const UnicodeString& other) { + return getPrefixLengthInternal(other, fFoldCase); +} + +int32_t StringSegment::getCaseSensitivePrefixLength(const UnicodeString& other) { + return getPrefixLengthInternal(other, false); +} + +int32_t StringSegment::getPrefixLengthInternal(const UnicodeString& other, bool foldCase) { + U_ASSERT(other.length() > 0); + int32_t offset = 0; + for (; offset < uprv_min(length(), other.length());) { + // TODO: case-fold code points, not chars + char16_t c1 = charAt(offset); + char16_t c2 = other.charAt(offset); + if (!codePointsEqual(c1, c2, foldCase)) { + break; + } + offset++; + } + return offset; +} + +bool StringSegment::codePointsEqual(UChar32 cp1, UChar32 cp2, bool foldCase) { + if (cp1 == cp2) { + return true; + } + if (!foldCase) { + return false; + } + cp1 = u_foldCase(cp1, TRUE); + cp2 = u_foldCase(cp2, TRUE); + return cp1 == cp2; +} + +bool StringSegment::operator==(const UnicodeString& other) const { + return toTempUnicodeString() == other; +} + + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/numparse_stringsegment.h b/deps/icu-small/source/i18n/numparse_stringsegment.h new file mode 100644 index 00000000000000..7a84444d414889 --- /dev/null +++ b/deps/icu-small/source/i18n/numparse_stringsegment.h @@ -0,0 +1,24 @@ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING +#ifndef __NUMPARSE_STRINGSEGMENT_H__ +#define __NUMPARSE_STRINGSEGMENT_H__ + +#include "numparse_types.h" +#include "number_types.h" +#include "unicode/unistr.h" + +U_NAMESPACE_BEGIN +namespace numparse { +namespace impl { + + +} // namespace impl +} // namespace numparse +U_NAMESPACE_END + +#endif //__NUMPARSE_STRINGSEGMENT_H__ +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/numparse_symbols.cpp b/deps/icu-small/source/i18n/numparse_symbols.cpp new file mode 100644 index 00000000000000..9ccceec8475d01 --- /dev/null +++ b/deps/icu-small/source/i18n/numparse_symbols.cpp @@ -0,0 +1,193 @@ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +// Allow implicit conversion from char16_t* to UnicodeString for this file: +// Helpful in toString methods and elsewhere. +#define UNISTR_FROM_STRING_EXPLICIT + +#include "numparse_types.h" +#include "numparse_symbols.h" +#include "numparse_utils.h" + +using namespace icu; +using namespace icu::numparse; +using namespace icu::numparse::impl; + + +SymbolMatcher::SymbolMatcher(const UnicodeString& symbolString, unisets::Key key) { + fUniSet = unisets::get(key); + if (fUniSet->contains(symbolString)) { + fString.setToBogus(); + } else { + fString = symbolString; + } +} + +const UnicodeSet* SymbolMatcher::getSet() const { + return fUniSet; +} + +bool SymbolMatcher::match(StringSegment& segment, ParsedNumber& result, UErrorCode&) const { + // Smoke test first; this matcher might be disabled. + if (isDisabled(result)) { + return false; + } + + // Test the string first in order to consume trailing chars greedily. + int overlap = 0; + if (!fString.isEmpty()) { + overlap = segment.getCommonPrefixLength(fString); + if (overlap == fString.length()) { + segment.adjustOffset(fString.length()); + accept(segment, result); + return false; + } + } + + int cp = segment.getCodePoint(); + if (cp != -1 && fUniSet->contains(cp)) { + segment.adjustOffset(U16_LENGTH(cp)); + accept(segment, result); + return false; + } + + return overlap == segment.length(); +} + +bool SymbolMatcher::smokeTest(const StringSegment& segment) const { + return segment.startsWith(*fUniSet) || segment.startsWith(fString); +} + +UnicodeString SymbolMatcher::toString() const { + // TODO: Customize output for each symbol + return u""; +} + + +IgnorablesMatcher::IgnorablesMatcher(unisets::Key key) + : SymbolMatcher({}, key) { +} + +bool IgnorablesMatcher::isFlexible() const { + return true; +} + +UnicodeString IgnorablesMatcher::toString() const { + return u""; +} + +bool IgnorablesMatcher::isDisabled(const ParsedNumber&) const { + return false; +} + +void IgnorablesMatcher::accept(StringSegment&, ParsedNumber&) const { + // No-op +} + + +InfinityMatcher::InfinityMatcher(const DecimalFormatSymbols& dfs) + : SymbolMatcher(dfs.getConstSymbol(DecimalFormatSymbols::kInfinitySymbol), unisets::INFINITY_KEY) { +} + +bool InfinityMatcher::isDisabled(const ParsedNumber& result) const { + return 0 != (result.flags & FLAG_INFINITY); +} + +void InfinityMatcher::accept(StringSegment& segment, ParsedNumber& result) const { + result.flags |= FLAG_INFINITY; + result.setCharsConsumed(segment); +} + + +MinusSignMatcher::MinusSignMatcher(const DecimalFormatSymbols& dfs, bool allowTrailing) + : SymbolMatcher(dfs.getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol), unisets::MINUS_SIGN), + fAllowTrailing(allowTrailing) { +} + +bool MinusSignMatcher::isDisabled(const ParsedNumber& result) const { + return !fAllowTrailing && result.seenNumber(); +} + +void MinusSignMatcher::accept(StringSegment& segment, ParsedNumber& result) const { + result.flags |= FLAG_NEGATIVE; + result.setCharsConsumed(segment); +} + + +NanMatcher::NanMatcher(const DecimalFormatSymbols& dfs) + : SymbolMatcher(dfs.getConstSymbol(DecimalFormatSymbols::kNaNSymbol), unisets::EMPTY) { +} + +bool NanMatcher::isDisabled(const ParsedNumber& result) const { + return result.seenNumber(); +} + +void NanMatcher::accept(StringSegment& segment, ParsedNumber& result) const { + result.flags |= FLAG_NAN; + result.setCharsConsumed(segment); +} + + +PaddingMatcher::PaddingMatcher(const UnicodeString& padString) + : SymbolMatcher(padString, unisets::EMPTY) {} + +bool PaddingMatcher::isFlexible() const { + return true; +} + +bool PaddingMatcher::isDisabled(const ParsedNumber&) const { + return false; +} + +void PaddingMatcher::accept(StringSegment&, ParsedNumber&) const { + // No-op +} + + +PercentMatcher::PercentMatcher(const DecimalFormatSymbols& dfs) + : SymbolMatcher(dfs.getConstSymbol(DecimalFormatSymbols::kPercentSymbol), unisets::PERCENT_SIGN) { +} + +bool PercentMatcher::isDisabled(const ParsedNumber& result) const { + return 0 != (result.flags & FLAG_PERCENT); +} + +void PercentMatcher::accept(StringSegment& segment, ParsedNumber& result) const { + result.flags |= FLAG_PERCENT; + result.setCharsConsumed(segment); +} + + +PermilleMatcher::PermilleMatcher(const DecimalFormatSymbols& dfs) + : SymbolMatcher(dfs.getConstSymbol(DecimalFormatSymbols::kPerMillSymbol), unisets::PERMILLE_SIGN) { +} + +bool PermilleMatcher::isDisabled(const ParsedNumber& result) const { + return 0 != (result.flags & FLAG_PERMILLE); +} + +void PermilleMatcher::accept(StringSegment& segment, ParsedNumber& result) const { + result.flags |= FLAG_PERMILLE; + result.setCharsConsumed(segment); +} + + +PlusSignMatcher::PlusSignMatcher(const DecimalFormatSymbols& dfs, bool allowTrailing) + : SymbolMatcher(dfs.getConstSymbol(DecimalFormatSymbols::kPlusSignSymbol), unisets::PLUS_SIGN), + fAllowTrailing(allowTrailing) { +} + +bool PlusSignMatcher::isDisabled(const ParsedNumber& result) const { + return !fAllowTrailing && result.seenNumber(); +} + +void PlusSignMatcher::accept(StringSegment& segment, ParsedNumber& result) const { + result.setCharsConsumed(segment); +} + + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/numparse_symbols.h b/deps/icu-small/source/i18n/numparse_symbols.h new file mode 100644 index 00000000000000..8912ee95b0d009 --- /dev/null +++ b/deps/icu-small/source/i18n/numparse_symbols.h @@ -0,0 +1,173 @@ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING +#ifndef __NUMPARSE_SYMBOLS_H__ +#define __NUMPARSE_SYMBOLS_H__ + +#include "numparse_types.h" +#include "unicode/uniset.h" +#include "static_unicode_sets.h" + +U_NAMESPACE_BEGIN namespace numparse { +namespace impl { + + +/** + * A base class for many matchers that performs a simple match against a UnicodeString and/or UnicodeSet. + * + * @author sffc + */ +// Exported as U_I18N_API for tests +class U_I18N_API SymbolMatcher : public NumberParseMatcher, public UMemory { + public: + SymbolMatcher() = default; // WARNING: Leaves the object in an unusable state + + const UnicodeSet* getSet() const; + + bool match(StringSegment& segment, ParsedNumber& result, UErrorCode& status) const override; + + bool smokeTest(const StringSegment& segment) const override; + + UnicodeString toString() const override; + + virtual bool isDisabled(const ParsedNumber& result) const = 0; + + virtual void accept(StringSegment& segment, ParsedNumber& result) const = 0; + + protected: + UnicodeString fString; + const UnicodeSet* fUniSet; // a reference from numparse_unisets.h; never owned + + SymbolMatcher(const UnicodeString& symbolString, unisets::Key key); +}; + + +// Exported as U_I18N_API for tests +class U_I18N_API IgnorablesMatcher : public SymbolMatcher { + public: + IgnorablesMatcher() = default; // WARNING: Leaves the object in an unusable state + + IgnorablesMatcher(unisets::Key key); + + bool isFlexible() const override; + + UnicodeString toString() const override; + + protected: + bool isDisabled(const ParsedNumber& result) const override; + + void accept(StringSegment& segment, ParsedNumber& result) const override; +}; + + +class InfinityMatcher : public SymbolMatcher { + public: + InfinityMatcher() = default; // WARNING: Leaves the object in an unusable state + + InfinityMatcher(const DecimalFormatSymbols& dfs); + + protected: + bool isDisabled(const ParsedNumber& result) const override; + + void accept(StringSegment& segment, ParsedNumber& result) const override; +}; + + +// Exported as U_I18N_API for tests +class U_I18N_API MinusSignMatcher : public SymbolMatcher { + public: + MinusSignMatcher() = default; // WARNING: Leaves the object in an unusable state + + MinusSignMatcher(const DecimalFormatSymbols& dfs, bool allowTrailing); + + protected: + bool isDisabled(const ParsedNumber& result) const override; + + void accept(StringSegment& segment, ParsedNumber& result) const override; + + private: + bool fAllowTrailing; +}; + + +class NanMatcher : public SymbolMatcher { + public: + NanMatcher() = default; // WARNING: Leaves the object in an unusable state + + NanMatcher(const DecimalFormatSymbols& dfs); + + protected: + bool isDisabled(const ParsedNumber& result) const override; + + void accept(StringSegment& segment, ParsedNumber& result) const override; +}; + + +class PaddingMatcher : public SymbolMatcher { + public: + PaddingMatcher() = default; // WARNING: Leaves the object in an unusable state + + PaddingMatcher(const UnicodeString& padString); + + bool isFlexible() const override; + + protected: + bool isDisabled(const ParsedNumber& result) const override; + + void accept(StringSegment& segment, ParsedNumber& result) const override; +}; + + +// Exported as U_I18N_API for tests +class U_I18N_API PercentMatcher : public SymbolMatcher { + public: + PercentMatcher() = default; // WARNING: Leaves the object in an unusable state + + PercentMatcher(const DecimalFormatSymbols& dfs); + + protected: + bool isDisabled(const ParsedNumber& result) const override; + + void accept(StringSegment& segment, ParsedNumber& result) const override; +}; + +// Exported as U_I18N_API for tests +class U_I18N_API PermilleMatcher : public SymbolMatcher { + public: + PermilleMatcher() = default; // WARNING: Leaves the object in an unusable state + + PermilleMatcher(const DecimalFormatSymbols& dfs); + + protected: + bool isDisabled(const ParsedNumber& result) const override; + + void accept(StringSegment& segment, ParsedNumber& result) const override; +}; + + +// Exported as U_I18N_API for tests +class U_I18N_API PlusSignMatcher : public SymbolMatcher { + public: + PlusSignMatcher() = default; // WARNING: Leaves the object in an unusable state + + PlusSignMatcher(const DecimalFormatSymbols& dfs, bool allowTrailing); + + protected: + bool isDisabled(const ParsedNumber& result) const override; + + void accept(StringSegment& segment, ParsedNumber& result) const override; + + private: + bool fAllowTrailing; +}; + + +} // namespace impl +} // namespace numparse +U_NAMESPACE_END + +#endif //__NUMPARSE_SYMBOLS_H__ +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/numparse_types.h b/deps/icu-small/source/i18n/numparse_types.h new file mode 100644 index 00000000000000..ab591eaba83af5 --- /dev/null +++ b/deps/icu-small/source/i18n/numparse_types.h @@ -0,0 +1,377 @@ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING +#ifndef __NUMPARSE_TYPES_H__ +#define __NUMPARSE_TYPES_H__ + +#include "unicode/uobject.h" +#include "number_decimalquantity.h" + +U_NAMESPACE_BEGIN namespace numparse { +namespace impl { + +// Forward-declarations +class StringSegment; +class ParsedNumber; + +typedef int32_t result_flags_t; +typedef int32_t parse_flags_t; + +/** Flags for the type result_flags_t */ +enum ResultFlags { + FLAG_NEGATIVE = 0x0001, + FLAG_PERCENT = 0x0002, + FLAG_PERMILLE = 0x0004, + FLAG_HAS_EXPONENT = 0x0008, + // FLAG_HAS_DEFAULT_CURRENCY = 0x0010, // no longer used + FLAG_HAS_DECIMAL_SEPARATOR = 0x0020, + FLAG_NAN = 0x0040, + FLAG_INFINITY = 0x0080, + FLAG_FAIL = 0x0100, +}; + +/** Flags for the type parse_flags_t */ +enum ParseFlags { + PARSE_FLAG_IGNORE_CASE = 0x0001, + PARSE_FLAG_MONETARY_SEPARATORS = 0x0002, + PARSE_FLAG_STRICT_SEPARATORS = 0x0004, + PARSE_FLAG_STRICT_GROUPING_SIZE = 0x0008, + PARSE_FLAG_INTEGER_ONLY = 0x0010, + PARSE_FLAG_GROUPING_DISABLED = 0x0020, + // PARSE_FLAG_FRACTION_GROUPING_ENABLED = 0x0040, // see #10794 + PARSE_FLAG_INCLUDE_UNPAIRED_AFFIXES = 0x0080, + PARSE_FLAG_USE_FULL_AFFIXES = 0x0100, + PARSE_FLAG_EXACT_AFFIX = 0x0200, + PARSE_FLAG_PLUS_SIGN_ALLOWED = 0x0400, + // PARSE_FLAG_OPTIMIZE = 0x0800, // no longer used + // PARSE_FLAG_FORCE_BIG_DECIMAL = 0x1000, // not used in ICU4C + PARSE_FLAG_NO_FOREIGN_CURRENCY = 0x2000, +}; + + +// TODO: Is this class worthwhile? +template +class CompactUnicodeString { + public: + CompactUnicodeString() { + static_assert(stackCapacity > 0, "cannot have zero space on stack"); + fBuffer[0] = 0; + } + + CompactUnicodeString(const UnicodeString& text) + : fBuffer(text.length() + 1) { + memcpy(fBuffer.getAlias(), text.getBuffer(), sizeof(UChar) * text.length()); + fBuffer[text.length()] = 0; + } + + inline UnicodeString toAliasedUnicodeString() const { + return UnicodeString(TRUE, fBuffer.getAlias(), -1); + } + + bool operator==(const CompactUnicodeString& other) const { + // Use the alias-only constructor and then call UnicodeString operator== + return toAliasedUnicodeString() == other.toAliasedUnicodeString(); + } + + private: + MaybeStackArray fBuffer; +}; + + +/** + * Struct-like class to hold the results of a parsing routine. + * + * @author sffc + */ +// Exported as U_I18N_API for tests +class U_I18N_API ParsedNumber { + public: + + /** + * The numerical value that was parsed. + */ + ::icu::number::impl::DecimalQuantity quantity; + + /** + * The index of the last char consumed during parsing. If parsing started at index 0, this is equal + * to the number of chars consumed. This is NOT necessarily the same as the StringSegment offset; + * "weak" chars, like whitespace, change the offset, but the charsConsumed is not touched until a + * "strong" char is encountered. + */ + int32_t charEnd; + + /** + * Boolean flags (see constants above). + */ + result_flags_t flags; + + /** + * The pattern string corresponding to the prefix that got consumed. + */ + UnicodeString prefix; + + /** + * The pattern string corresponding to the suffix that got consumed. + */ + UnicodeString suffix; + + /** + * The currency that got consumed. + */ + UChar currencyCode[4]; + + ParsedNumber(); + + ParsedNumber(const ParsedNumber& other) = default; + + ParsedNumber& operator=(const ParsedNumber& other) = default; + + void clear(); + + /** + * Call this method to register that a "strong" char was consumed. This should be done after calling + * {@link StringSegment#setOffset} or {@link StringSegment#adjustOffset} except when the char is + * "weak", like whitespace. + * + *

+ * What is a strong versus weak char? The behavior of number parsing is to "stop" + * after reading the number, even if there is other content following the number. For example, after + * parsing the string "123 " (123 followed by a space), the cursor should be set to 3, not 4, even + * though there are matchers that accept whitespace. In this example, the digits are strong, whereas + * the whitespace is weak. Grouping separators are weak, whereas decimal separators are strong. Most + * other chars are strong. + * + * @param segment + * The current StringSegment, usually immediately following a call to setOffset. + */ + void setCharsConsumed(const StringSegment& segment); + + /** Apply certain number-related flags to the DecimalQuantity. */ + void postProcess(); + + /** + * Returns whether this the parse was successful. To be successful, at least one char must have been + * consumed, and the failure flag must not be set. + */ + bool success() const; + + bool seenNumber() const; + + double getDouble() const; + + void populateFormattable(Formattable& output, parse_flags_t parseFlags) const; + + bool isBetterThan(const ParsedNumber& other); +}; + + +/** + * A mutable class allowing for a String with a variable offset and length. The charAt, length, and + * subSequence methods all operate relative to the fixed offset into the String. + * + * @author sffc + */ +// Exported as U_I18N_API for tests +class U_I18N_API StringSegment : public UMemory { + public: + StringSegment(const UnicodeString& str, bool ignoreCase); + + int32_t getOffset() const; + + void setOffset(int32_t start); + + /** + * Equivalent to setOffset(getOffset()+delta). + * + *

+ * This method is usually called by a Matcher to register that a char was consumed. If the char is + * strong (it usually is, except for things like whitespace), follow this with a call to + * {@link ParsedNumber#setCharsConsumed}. For more information on strong chars, see that method. + */ + void adjustOffset(int32_t delta); + + /** + * Adjusts the offset by the width of the current code point, either 1 or 2 chars. + */ + void adjustOffsetByCodePoint(); + + void setLength(int32_t length); + + void resetLength(); + + int32_t length() const; + + char16_t charAt(int32_t index) const; + + UChar32 codePointAt(int32_t index) const; + + UnicodeString toUnicodeString() const; + + const UnicodeString toTempUnicodeString() const; + + /** + * Returns the first code point in the string segment, or -1 if the string starts with an invalid + * code point. + * + *

+ * Important: Most of the time, you should use {@link #matches}, which handles case + * folding logic, instead of this method. + */ + UChar32 getCodePoint() const; + + /** + * Returns true if the first code point of this StringSegment equals the given code point. + * + *

+ * This method will perform case folding if case folding is enabled for the parser. + */ + bool startsWith(UChar32 otherCp) const; + + /** + * Returns true if the first code point of this StringSegment is in the given UnicodeSet. + */ + bool startsWith(const UnicodeSet& uniset) const; + + /** + * Returns true if there is at least one code point of overlap between this StringSegment and the + * given UnicodeString. + */ + bool startsWith(const UnicodeString& other) const; + + /** + * Returns the length of the prefix shared by this StringSegment and the given CharSequence. For + * example, if this string segment is "aab", and the char sequence is "aac", this method returns 2, + * since the first 2 characters are the same. + * + *

+ * This method only returns offsets along code point boundaries. + * + *

+ * This method will perform case folding if case folding was enabled in the constructor. + * + *

+ * IMPORTANT: The given UnicodeString must not be empty! It is the caller's responsibility to check. + */ + int32_t getCommonPrefixLength(const UnicodeString& other); + + /** + * Like {@link #getCommonPrefixLength}, but never performs case folding, even if case folding is + * enabled for the parser. + */ + int32_t getCaseSensitivePrefixLength(const UnicodeString& other); + + bool operator==(const UnicodeString& other) const; + + private: + const UnicodeString fStr; + int32_t fStart; + int32_t fEnd; + bool fFoldCase; + + int32_t getPrefixLengthInternal(const UnicodeString& other, bool foldCase); + + static bool codePointsEqual(UChar32 cp1, UChar32 cp2, bool foldCase); +}; + + +/** + * The core interface implemented by all matchers used for number parsing. + * + * Given a string, there should NOT be more than one way to consume the string with the same matcher + * applied multiple times. If there is, the non-greedy parsing algorithm will be unhappy and may enter an + * exponential-time loop. For example, consider the "A Matcher" that accepts "any number of As". Given + * the string "AAAA", there are 2^N = 8 ways to apply the A Matcher to this string: you could have the A + * Matcher apply 4 times to each character; you could have it apply just once to all the characters; you + * could have it apply to the first 2 characters and the second 2 characters; and so on. A better version + * of the "A Matcher" would be for it to accept exactly one A, and allow the algorithm to run it + * repeatedly to consume a string of multiple As. The A Matcher can implement the Flexible interface + * below to signal that it can be applied multiple times in a row. + * + * @author sffc + */ +// Exported as U_I18N_API for tests +class U_I18N_API NumberParseMatcher { + public: + virtual ~NumberParseMatcher(); + + /** + * Matchers can override this method to return true to indicate that they are optional and can be run + * repeatedly. Used by SeriesMatcher, primarily in the context of IgnorablesMatcher. + */ + virtual bool isFlexible() const { + return false; + } + + /** + * Runs this matcher starting at the beginning of the given StringSegment. If this matcher finds + * something interesting in the StringSegment, it should update the offset of the StringSegment + * corresponding to how many chars were matched. + * + * This method is thread-safe. + * + * @param segment + * The StringSegment to match against. Matches always start at the beginning of the + * segment. The segment is guaranteed to contain at least one char. + * @param result + * The data structure to store results if the match succeeds. + * @return Whether this matcher thinks there may be more interesting chars beyond the end of the + * string segment. + */ + virtual bool match(StringSegment& segment, ParsedNumber& result, UErrorCode& status) const = 0; + + /** + * Performs a fast "smoke check" for whether or not this matcher could possibly match against the + * given string segment. The test should be as fast as possible but also as restrictive as possible. + * For example, matchers can maintain a UnicodeSet of all code points that count possibly start a + * match. Matchers should use the {@link StringSegment#startsWith} method in order to correctly + * handle case folding. + * + * @param segment + * The segment to check against. + * @return true if the matcher might be able to match against this segment; false if it definitely + * will not be able to match. + */ + virtual bool smokeTest(const StringSegment& segment) const = 0; + + /** + * Method called at the end of a parse, after all matchers have failed to consume any more chars. + * Allows a matcher to make final modifications to the result given the knowledge that no more + * matches are possible. + * + * @param result + * The data structure to store results. + */ + virtual void postProcess(ParsedNumber&) const { + // Default implementation: no-op + }; + + // String for debugging + virtual UnicodeString toString() const = 0; + + protected: + // No construction except by subclasses! + NumberParseMatcher() = default; +}; + + +/** + * Interface for use in arguments. + */ +// Exported as U_I18N_API for tests +class U_I18N_API MutableMatcherCollection { + public: + virtual ~MutableMatcherCollection() = default; + + virtual void addMatcher(NumberParseMatcher& matcher) = 0; +}; + + +} // namespace impl +} // namespace numparse +U_NAMESPACE_END + +#endif //__NUMPARSE_TYPES_H__ +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/numparse_utils.h b/deps/icu-small/source/i18n/numparse_utils.h new file mode 100644 index 00000000000000..162954bae09444 --- /dev/null +++ b/deps/icu-small/source/i18n/numparse_utils.h @@ -0,0 +1,43 @@ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING +#ifndef __NUMPARSE_UTILS_H__ +#define __NUMPARSE_UTILS_H__ + +#include "numparse_types.h" +#include "unicode/uniset.h" + +U_NAMESPACE_BEGIN namespace numparse { +namespace impl { +namespace utils { + + +inline static void putLeadCodePoints(const UnicodeSet* input, UnicodeSet* output) { + for (int32_t i = 0; i < input->getRangeCount(); i++) { + output->add(input->getRangeStart(i), input->getRangeEnd(i)); + } + // TODO: ANDY: How to iterate over the strings in ICU4C UnicodeSet? +} + +inline static void putLeadCodePoint(const UnicodeString& input, UnicodeSet* output) { + if (!input.isEmpty()) { + output->add(input.char32At(0)); + } +} + +inline static void copyCurrencyCode(UChar* dest, const UChar* src) { + uprv_memcpy(dest, src, sizeof(UChar) * 3); + dest[3] = 0; +} + + +} // namespace utils +} // namespace impl +} // namespace numparse +U_NAMESPACE_END + +#endif //__NUMPARSE_UTILS_H__ +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/numparse_validators.cpp b/deps/icu-small/source/i18n/numparse_validators.cpp new file mode 100644 index 00000000000000..12d3465c4ef3a4 --- /dev/null +++ b/deps/icu-small/source/i18n/numparse_validators.cpp @@ -0,0 +1,85 @@ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +// Allow implicit conversion from char16_t* to UnicodeString for this file: +// Helpful in toString methods and elsewhere. +#define UNISTR_FROM_STRING_EXPLICIT + +#include "numparse_types.h" +#include "numparse_validators.h" +#include "static_unicode_sets.h" + +using namespace icu; +using namespace icu::numparse; +using namespace icu::numparse::impl; + + +void RequireAffixValidator::postProcess(ParsedNumber& result) const { + if (result.prefix.isBogus() || result.suffix.isBogus()) { + // We saw a prefix or a suffix but not both. Fail the parse. + result.flags |= FLAG_FAIL; + } +} + +UnicodeString RequireAffixValidator::toString() const { + return u""; +} + + +void RequireCurrencyValidator::postProcess(ParsedNumber& result) const { + if (result.currencyCode[0] == 0) { + result.flags |= FLAG_FAIL; + } +} + +UnicodeString RequireCurrencyValidator::toString() const { + return u""; +} + + +RequireDecimalSeparatorValidator::RequireDecimalSeparatorValidator(bool patternHasDecimalSeparator) + : fPatternHasDecimalSeparator(patternHasDecimalSeparator) { +} + +void RequireDecimalSeparatorValidator::postProcess(ParsedNumber& result) const { + bool parseHasDecimalSeparator = 0 != (result.flags & FLAG_HAS_DECIMAL_SEPARATOR); + if (parseHasDecimalSeparator != fPatternHasDecimalSeparator) { + result.flags |= FLAG_FAIL; + } +} + +UnicodeString RequireDecimalSeparatorValidator::toString() const { + return u""; +} + + +void RequireNumberValidator::postProcess(ParsedNumber& result) const { + // Require that a number is matched. + if (!result.seenNumber()) { + result.flags |= FLAG_FAIL; + } +} + +UnicodeString RequireNumberValidator::toString() const { + return u""; +} + +MultiplierParseHandler::MultiplierParseHandler(::icu::number::Scale multiplier) + : fMultiplier(std::move(multiplier)) {} + +void MultiplierParseHandler::postProcess(ParsedNumber& result) const { + if (!result.quantity.bogus) { + fMultiplier.applyReciprocalTo(result.quantity); + // NOTE: It is okay if the multiplier was negative. + } +} + +UnicodeString MultiplierParseHandler::toString() const { + return u""; +} + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/numparse_validators.h b/deps/icu-small/source/i18n/numparse_validators.h new file mode 100644 index 00000000000000..5d43b779d0bb67 --- /dev/null +++ b/deps/icu-small/source/i18n/numparse_validators.h @@ -0,0 +1,95 @@ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING +#ifndef __SOURCE_NUMPARSE_VALIDATORS_H__ +#define __SOURCE_NUMPARSE_VALIDATORS_H__ + +#include "numparse_types.h" +#include "static_unicode_sets.h" + +U_NAMESPACE_BEGIN namespace numparse { +namespace impl { + + +class ValidationMatcher : public NumberParseMatcher { + public: + bool match(StringSegment&, ParsedNumber&, UErrorCode&) const U_OVERRIDE { + // No-op + return false; + } + + bool smokeTest(const StringSegment&) const U_OVERRIDE { + // No-op + return false; + } + + void postProcess(ParsedNumber& result) const U_OVERRIDE = 0; +}; + + +class RequireAffixValidator : public ValidationMatcher, public UMemory { + public: + void postProcess(ParsedNumber& result) const U_OVERRIDE; + + UnicodeString toString() const U_OVERRIDE; +}; + + +class RequireCurrencyValidator : public ValidationMatcher, public UMemory { + public: + void postProcess(ParsedNumber& result) const U_OVERRIDE; + + UnicodeString toString() const U_OVERRIDE; +}; + + +class RequireDecimalSeparatorValidator : public ValidationMatcher, public UMemory { + public: + RequireDecimalSeparatorValidator() = default; // leaves instance in valid but undefined state + + RequireDecimalSeparatorValidator(bool patternHasDecimalSeparator); + + void postProcess(ParsedNumber& result) const U_OVERRIDE; + + UnicodeString toString() const U_OVERRIDE; + + private: + bool fPatternHasDecimalSeparator; +}; + + +class RequireNumberValidator : public ValidationMatcher, public UMemory { + public: + void postProcess(ParsedNumber& result) const U_OVERRIDE; + + UnicodeString toString() const U_OVERRIDE; +}; + + +/** + * Wraps a {@link Multiplier} for use in the number parsing pipeline. + */ +class MultiplierParseHandler : public ValidationMatcher, public UMemory { + public: + MultiplierParseHandler() = default; // leaves instance in valid but undefined state + + MultiplierParseHandler(::icu::number::Scale multiplier); + + void postProcess(ParsedNumber& result) const U_OVERRIDE; + + UnicodeString toString() const U_OVERRIDE; + + private: + ::icu::number::Scale fMultiplier; +}; + + +} // namespace impl +} // namespace numparse +U_NAMESPACE_END + +#endif //__SOURCE_NUMPARSE_VALIDATORS_H__ +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/pluralaffix.cpp b/deps/icu-small/source/i18n/pluralaffix.cpp deleted file mode 100644 index ea400206b38b81..00000000000000 --- a/deps/icu-small/source/i18n/pluralaffix.cpp +++ /dev/null @@ -1,104 +0,0 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - * Copyright (C) 2015, International Business Machines - * Corporation and others. All Rights Reserved. - * - * file name: pluralaffix.cpp - */ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "cstring.h" -#include "digitaffix.h" -#include "pluralaffix.h" - -U_NAMESPACE_BEGIN - -UBool -PluralAffix::setVariant( - const char *variant, const UnicodeString &value, UErrorCode &status) { - DigitAffix *current = affixes.getMutable(variant, status); - if (U_FAILURE(status)) { - return FALSE; - } - current->remove(); - current->append(value); - return TRUE; -} - -void -PluralAffix::remove() { - affixes.clear(); -} - -void -PluralAffix::appendUChar( - const UChar value, int32_t fieldId) { - PluralMapBase::Category index = PluralMapBase::NONE; - for (DigitAffix *current = affixes.nextMutable(index); - current != NULL; current = affixes.nextMutable(index)) { - current->appendUChar(value, fieldId); - } -} - -void -PluralAffix::append( - const UnicodeString &value, int32_t fieldId) { - PluralMapBase::Category index = PluralMapBase::NONE; - for (DigitAffix *current = affixes.nextMutable(index); - current != NULL; current = affixes.nextMutable(index)) { - current->append(value, fieldId); - } -} - -void -PluralAffix::append( - const UChar *value, int32_t charCount, int32_t fieldId) { - PluralMapBase::Category index = PluralMapBase::NONE; - for (DigitAffix *current = affixes.nextMutable(index); - current != NULL; current = affixes.nextMutable(index)) { - current->append(value, charCount, fieldId); - } -} - -UBool -PluralAffix::append( - const PluralAffix &rhs, int32_t fieldId, UErrorCode &status) { - if (U_FAILURE(status)) { - return FALSE; - } - PluralMapBase::Category index = PluralMapBase::NONE; - while(rhs.affixes.next(index) != NULL) { - affixes.getMutableWithDefault(index, affixes.getOther(), status); - } - index = PluralMapBase::NONE; - for (DigitAffix *current = affixes.nextMutable(index); - current != NULL; current = affixes.nextMutable(index)) { - current->append(rhs.affixes.get(index).toString(), fieldId); - } - return TRUE; -} - -const DigitAffix & -PluralAffix::getByCategory(const char *category) const { - return affixes.get(category); -} - -const DigitAffix & -PluralAffix::getByCategory(const UnicodeString &category) const { - return affixes.get(category); -} - -UBool -PluralAffix::hasMultipleVariants() const { - // This works because OTHER is guaranteed to be the first enum value - PluralMapBase::Category index = PluralMapBase::OTHER; - return (affixes.next(index) != NULL); -} - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/pluralaffix.h b/deps/icu-small/source/i18n/pluralaffix.h deleted file mode 100644 index 94366ce4cf81ec..00000000000000 --- a/deps/icu-small/source/i18n/pluralaffix.h +++ /dev/null @@ -1,177 +0,0 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2015, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* pluralaffix.h -* -* created on: 2015jan06 -* created by: Travis Keep -*/ - -#ifndef __PLURALAFFIX_H__ -#define __PLURALAFFIX_H__ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/unum.h" -#include "unicode/uobject.h" - -#include "digitaffix.h" -#include "pluralmap.h" - -U_NAMESPACE_BEGIN - -class FieldPositionHandler; - -// Export an explicit template instantiation. -// -// MSVC requires this, even though it should not be necessary. -// No direct access leaks out of the i18n library. -// -// Macintosh produces duplicate definition linker errors with the explicit template -// instantiation. -// -#if !U_PLATFORM_IS_DARWIN_BASED -template class U_I18N_API PluralMap; -#endif - - -/** - * A plural aware prefix or suffix of a formatted number. - * - * PluralAffix is essentially a map of DigitAffix objects keyed by plural - * category. The 'other' category is the default and always has some - * value. The rest of the categories are optional. Querying for a category that - * is not set always returns the DigitAffix stored in the 'other' category. - * - * To use one of these objects, build it up first using append() and - * setVariant() methods. Once built, leave unchanged and let multiple threads - * safely access. - * - * The following code is sample code for building up: - * one: US Dollar - - * other: US Dollars - - * - * and storing it in "negativeCurrencyPrefix" - * - * UErrorCode status = U_ZERO_ERROR; - * - * PluralAffix negativeCurrencyPrefix; - * - * PluralAffix currencyName; - * currencyName.setVariant("one", "US Dollar", status); - * currencyName.setVariant("other", "US Dollars", status); - * - * negativeCurrencyPrefix.append(currencyName, UNUM_CURRENCY_FIELD, status); - * negativeCurrencyPrefix.append(" "); - * negativeCurrencyPrefix.append("-", UNUM_SIGN_FIELD, status); - */ -class U_I18N_API PluralAffix : public UMemory { -public: - - /** - * Create empty PluralAffix. - */ - PluralAffix() : affixes() { } - - /** - * Create a PluralAffix where the 'other' variant is otherVariant. - */ - PluralAffix(const DigitAffix &otherVariant) : affixes(otherVariant) { } - - /** - * Sets a particular variant for a plural category while overwriting - * anything that may have been previously stored for that plural - * category. The set value has no field annotations. - * @param category "one", "two", "few", ... - * @param variant the variant to store under the particular category - * @param status Any error returned here. - */ - UBool setVariant( - const char *category, - const UnicodeString &variant, - UErrorCode &status); - /** - * Make the 'other' variant be the empty string with no field annotations - * and remove the variants for the rest of the plural categories. - */ - void remove(); - - /** - * Append value to all set plural categories. If fieldId present, value - * is that field type. - */ - void appendUChar(UChar value, int32_t fieldId=UNUM_FIELD_COUNT); - - /** - * Append value to all set plural categories. If fieldId present, value - * is that field type. - */ - void append(const UnicodeString &value, int32_t fieldId=UNUM_FIELD_COUNT); - - /** - * Append value to all set plural categories. If fieldId present, value - * is that field type. - */ - void append(const UChar *value, int32_t charCount, int32_t fieldId=UNUM_FIELD_COUNT); - - /** - * Append the value for each plural category in rhs to the corresponding - * plural category in this instance. Each value appended from rhs is - * of type fieldId. - */ - UBool append( - const PluralAffix &rhs, - int32_t fieldId, - UErrorCode &status); - /** - * Get the DigitAffix for a paricular category such as "zero", "one", ... - * If the particular category is not set, returns the 'other' category - * which is always set. - */ - const DigitAffix &getByCategory(const char *category) const; - - /** - * Get the DigitAffix for a paricular category such as "zero", "one", ... - * If the particular category is not set, returns the 'other' category - * which is always set. - */ - const DigitAffix &getByCategory(const UnicodeString &category) const; - - /** - * Get the DigitAffix for the other category which is always set. - */ - const DigitAffix &getOtherVariant() const { - return affixes.getOther(); - } - - /** - * Returns TRUE if this instance has variants stored besides the "other" - * variant. - */ - UBool hasMultipleVariants() const; - - /** - * Returns TRUE if this instance equals rhs. - */ - UBool equals(const PluralAffix &rhs) const { - return affixes.equals(rhs.affixes, &eq); - } - -private: - PluralMap affixes; - - static UBool eq(const DigitAffix &x, const DigitAffix &y) { - return x.equals(y); - } -}; - - -U_NAMESPACE_END -#endif /* #if !UCONFIG_NO_FORMATTING */ -#endif // __PLURALAFFIX_H__ diff --git a/deps/icu-small/source/i18n/plurfmt.cpp b/deps/icu-small/source/i18n/plurfmt.cpp index e14ef6d831ecce..2775766d32df80 100644 --- a/deps/icu-small/source/i18n/plurfmt.cpp +++ b/deps/icu-small/source/i18n/plurfmt.cpp @@ -21,13 +21,16 @@ #include "plurrule_impl.h" #include "uassert.h" #include "uhash.h" -#include "precision.h" -#include "visibledigits.h" +#include "number_decimalquantity.h" +#include "number_utils.h" +#include "number_utypes.h" #if !UCONFIG_NO_FORMATTING U_NAMESPACE_BEGIN +using number::impl::DecimalQuantity; + static const UChar OTHER_STRING[] = { 0x6F, 0x74, 0x68, 0x65, 0x72, 0 // "other" }; @@ -258,45 +261,33 @@ PluralFormat::format(const Formattable& numberObject, double number, if (msgPattern.countParts() == 0) { return numberFormat->format(numberObject, appendTo, pos, status); } + // Get the appropriate sub-message. // Select it based on the formatted number-offset. double numberMinusOffset = number - offset; - UnicodeString numberString; - FieldPosition ignorePos; - FixedPrecision fp; - VisibleDigitsWithExponent dec; - fp.initVisibleDigitsWithExponent(numberMinusOffset, dec, status); - if (U_FAILURE(status)) { - return appendTo; - } + // Call NumberFormatter to get both the DecimalQuantity and the string. + // This call site needs to use more internal APIs than the Java equivalent. + number::impl::UFormattedNumberData data; if (offset == 0) { - DecimalFormat *decFmt = dynamic_cast(numberFormat); - if(decFmt != NULL) { - decFmt->initVisibleDigitsWithExponent( - numberObject, dec, status); - if (U_FAILURE(status)) { - return appendTo; - } - decFmt->format(dec, numberString, ignorePos, status); - } else { - numberFormat->format( - numberObject, numberString, ignorePos, status); // could be BigDecimal etc. - } + // could be BigDecimal etc. + numberObject.populateDecimalQuantity(data.quantity, status); } else { - DecimalFormat *decFmt = dynamic_cast(numberFormat); - if(decFmt != NULL) { - decFmt->initVisibleDigitsWithExponent( - numberMinusOffset, dec, status); - if (U_FAILURE(status)) { - return appendTo; - } - decFmt->format(dec, numberString, ignorePos, status); + data.quantity.setToDouble(numberMinusOffset); + } + UnicodeString numberString; + auto *decFmt = dynamic_cast(numberFormat); + if(decFmt != nullptr) { + decFmt->toNumberFormatter().formatImpl(&data, status); // mutates &data + numberString = data.string.toUnicodeString(); + } else { + if (offset == 0) { + numberFormat->format(numberObject, numberString, status); } else { - numberFormat->format( - numberMinusOffset, numberString, ignorePos, status); + numberFormat->format(numberMinusOffset, numberString, status); } } - int32_t partIndex = findSubMessage(msgPattern, 0, pluralRulesWrapper, &dec, number, status); + + int32_t partIndex = findSubMessage(msgPattern, 0, pluralRulesWrapper, &data.quantity, number, status); if (U_FAILURE(status)) { return appendTo; } // Replace syntactic # signs in the top level of this sub-message // (not in nested arguments) with the formatted number-offset. @@ -585,7 +576,7 @@ PluralFormat::PluralSelectorAdapter::~PluralSelectorAdapter() { UnicodeString PluralFormat::PluralSelectorAdapter::select(void *context, double number, UErrorCode& /*ec*/) const { (void)number; // unused except in the assertion - VisibleDigitsWithExponent *dec=static_cast(context); + IFixedDecimal *dec=static_cast(context); return pluralRules->select(*dec); } diff --git a/deps/icu-small/source/i18n/plurrule.cpp b/deps/icu-small/source/i18n/plurrule.cpp index 6733a23e00362f..9597e8eb00d023 100644 --- a/deps/icu-small/source/i18n/plurrule.cpp +++ b/deps/icu-small/source/i18n/plurrule.cpp @@ -22,7 +22,6 @@ #include "charstr.h" #include "cmemory.h" #include "cstring.h" -#include "digitlst.h" #include "hash.h" #include "locutil.h" #include "mutex.h" @@ -35,13 +34,15 @@ #include "uvectr32.h" #include "sharedpluralrules.h" #include "unifiedcache.h" -#include "digitinterval.h" -#include "visibledigits.h" +#include "number_decimalquantity.h" #if !UCONFIG_NO_FORMATTING U_NAMESPACE_BEGIN +using namespace icu::pluralimpl; +using icu::number::impl::DecimalQuantity; + static const UChar PLURAL_KEYWORD_OTHER[]={LOW_O,LOW_T,LOW_H,LOW_E,LOW_R,0}; static const UChar PLURAL_DEFAULT_RULE[]={LOW_O,LOW_T,LOW_H,LOW_E,LOW_R,COLON,SPACE,LOW_N,0}; static const UChar PK_IN[]={LOW_I,LOW_N,0}; @@ -247,26 +248,6 @@ PluralRules::select(double number) const { return select(FixedDecimal(number)); } -UnicodeString -PluralRules::select(const Formattable& obj, const NumberFormat& fmt, UErrorCode& status) const { - if (U_SUCCESS(status)) { - const DecimalFormat *decFmt = dynamic_cast(&fmt); - if (decFmt != NULL) { - VisibleDigitsWithExponent digits; - decFmt->initVisibleDigitsWithExponent(obj, digits, status); - if (U_SUCCESS(status)) { - return select(digits); - } - } else { - double number = obj.getDouble(status); - if (U_SUCCESS(status)) { - return select(number); - } - } - } - return UnicodeString(); -} - UnicodeString PluralRules::select(const IFixedDecimal &number) const { if (mRules == NULL) { @@ -277,14 +258,6 @@ PluralRules::select(const IFixedDecimal &number) const { } } -UnicodeString -PluralRules::select(const VisibleDigitsWithExponent &number) const { - if (number.getExponent() != NULL) { - return UnicodeString(TRUE, PLURAL_DEFAULT_RULE, -1); - } - return select(FixedDecimal(number.getMantissa())); -} - StringEnumeration* @@ -1425,18 +1398,6 @@ PluralOperand tokenTypeToPluralOperand(tokenType tt) { } } -IFixedDecimal::~IFixedDecimal() = default; - -FixedDecimal::FixedDecimal(const VisibleDigits &digits) { - digits.getFixedDecimal( - source, intValue, decimalDigits, - decimalDigitsWithoutTrailingZeros, - visibleDecimalDigitCount, hasIntegerValue); - isNegative = digits.isNegative(); - _isNaN = digits.isNaN(); - _isInfinite = digits.isInfinite(); -} - FixedDecimal::FixedDecimal(double n, int32_t v, int64_t f) { init(n, v, f); // check values. TODO make into unit test. @@ -1474,14 +1435,14 @@ FixedDecimal::FixedDecimal() { FixedDecimal::FixedDecimal(const UnicodeString &num, UErrorCode &status) { CharString cs; cs.appendInvariantChars(num, status); - DigitList dl; - dl.set(cs.toStringPiece(), status); + DecimalQuantity dl; + dl.setToDecNumber(cs.toStringPiece(), status); if (U_FAILURE(status)) { init(0, 0, 0); return; } int32_t decimalPoint = num.indexOf(DOT); - double n = dl.getDouble(); + double n = dl.toDouble(); if (decimalPoint == -1) { init(n, 0, 0); } else { @@ -1497,7 +1458,7 @@ FixedDecimal::FixedDecimal(const FixedDecimal &other) { decimalDigits = other.decimalDigits; decimalDigitsWithoutTrailingZeros = other.decimalDigitsWithoutTrailingZeros; intValue = other.intValue; - hasIntegerValue = other.hasIntegerValue; + _hasIntegerValue = other._hasIntegerValue; isNegative = other.isNegative; _isNaN = other._isNaN; _isInfinite = other._isInfinite; @@ -1521,10 +1482,10 @@ void FixedDecimal::init(double n, int32_t v, int64_t f) { v = 0; f = 0; intValue = 0; - hasIntegerValue = FALSE; + _hasIntegerValue = FALSE; } else { intValue = (int64_t)source; - hasIntegerValue = (source == intValue); + _hasIntegerValue = (source == intValue); } visibleDecimalDigitCount = v; @@ -1658,6 +1619,10 @@ bool FixedDecimal::isInfinite() const { return _isInfinite; } +bool FixedDecimal::hasIntegerValue() const { + return _hasIntegerValue; +} + bool FixedDecimal::isNanOrInfinity() const { return _isNaN || _isInfinite; } diff --git a/deps/icu-small/source/i18n/plurrule_impl.h b/deps/icu-small/source/i18n/plurrule_impl.h index b93fc501baced2..3ab445d5843c8b 100644 --- a/deps/icu-small/source/i18n/plurrule_impl.h +++ b/deps/icu-small/source/i18n/plurrule_impl.h @@ -40,67 +40,73 @@ class DigitInterval; class PluralRules; class VisibleDigits; -static const UChar DOT = ((UChar)0x002E); -static const UChar SINGLE_QUOTE = ((UChar)0x0027); -static const UChar SLASH = ((UChar)0x002F); -static const UChar BACKSLASH = ((UChar)0x005C); -static const UChar SPACE = ((UChar)0x0020); -static const UChar EXCLAMATION = ((UChar)0x0021); -static const UChar QUOTATION_MARK = ((UChar)0x0022); -static const UChar NUMBER_SIGN = ((UChar)0x0023); -static const UChar PERCENT_SIGN = ((UChar)0x0025); -static const UChar ASTERISK = ((UChar)0x002A); -static const UChar COMMA = ((UChar)0x002C); -static const UChar HYPHEN = ((UChar)0x002D); -static const UChar U_ZERO = ((UChar)0x0030); -static const UChar U_ONE = ((UChar)0x0031); -static const UChar U_TWO = ((UChar)0x0032); -static const UChar U_THREE = ((UChar)0x0033); -static const UChar U_FOUR = ((UChar)0x0034); -static const UChar U_FIVE = ((UChar)0x0035); -static const UChar U_SIX = ((UChar)0x0036); -static const UChar U_SEVEN = ((UChar)0x0037); -static const UChar U_EIGHT = ((UChar)0x0038); -static const UChar U_NINE = ((UChar)0x0039); -static const UChar COLON = ((UChar)0x003A); -static const UChar SEMI_COLON = ((UChar)0x003B); -static const UChar EQUALS = ((UChar)0x003D); -static const UChar AT = ((UChar)0x0040); -static const UChar CAP_A = ((UChar)0x0041); -static const UChar CAP_B = ((UChar)0x0042); -static const UChar CAP_R = ((UChar)0x0052); -static const UChar CAP_Z = ((UChar)0x005A); -static const UChar LOWLINE = ((UChar)0x005F); -static const UChar LEFTBRACE = ((UChar)0x007B); -static const UChar RIGHTBRACE = ((UChar)0x007D); -static const UChar TILDE = ((UChar)0x007E); -static const UChar ELLIPSIS = ((UChar)0x2026); - -static const UChar LOW_A = ((UChar)0x0061); -static const UChar LOW_B = ((UChar)0x0062); -static const UChar LOW_C = ((UChar)0x0063); -static const UChar LOW_D = ((UChar)0x0064); -static const UChar LOW_E = ((UChar)0x0065); -static const UChar LOW_F = ((UChar)0x0066); -static const UChar LOW_G = ((UChar)0x0067); -static const UChar LOW_H = ((UChar)0x0068); -static const UChar LOW_I = ((UChar)0x0069); -static const UChar LOW_J = ((UChar)0x006a); -static const UChar LOW_K = ((UChar)0x006B); -static const UChar LOW_L = ((UChar)0x006C); -static const UChar LOW_M = ((UChar)0x006D); -static const UChar LOW_N = ((UChar)0x006E); -static const UChar LOW_O = ((UChar)0x006F); -static const UChar LOW_P = ((UChar)0x0070); -static const UChar LOW_Q = ((UChar)0x0071); -static const UChar LOW_R = ((UChar)0x0072); -static const UChar LOW_S = ((UChar)0x0073); -static const UChar LOW_T = ((UChar)0x0074); -static const UChar LOW_U = ((UChar)0x0075); -static const UChar LOW_V = ((UChar)0x0076); -static const UChar LOW_W = ((UChar)0x0077); -static const UChar LOW_Y = ((UChar)0x0079); -static const UChar LOW_Z = ((UChar)0x007A); +namespace pluralimpl { + +// TODO: Remove this and replace with u"" literals. Was for EBCDIC compatibility. + +static const UChar DOT = ((UChar) 0x002E); +static const UChar SINGLE_QUOTE = ((UChar) 0x0027); +static const UChar SLASH = ((UChar) 0x002F); +static const UChar BACKSLASH = ((UChar) 0x005C); +static const UChar SPACE = ((UChar) 0x0020); +static const UChar EXCLAMATION = ((UChar) 0x0021); +static const UChar QUOTATION_MARK = ((UChar) 0x0022); +static const UChar NUMBER_SIGN = ((UChar) 0x0023); +static const UChar PERCENT_SIGN = ((UChar) 0x0025); +static const UChar ASTERISK = ((UChar) 0x002A); +static const UChar COMMA = ((UChar) 0x002C); +static const UChar HYPHEN = ((UChar) 0x002D); +static const UChar U_ZERO = ((UChar) 0x0030); +static const UChar U_ONE = ((UChar) 0x0031); +static const UChar U_TWO = ((UChar) 0x0032); +static const UChar U_THREE = ((UChar) 0x0033); +static const UChar U_FOUR = ((UChar) 0x0034); +static const UChar U_FIVE = ((UChar) 0x0035); +static const UChar U_SIX = ((UChar) 0x0036); +static const UChar U_SEVEN = ((UChar) 0x0037); +static const UChar U_EIGHT = ((UChar) 0x0038); +static const UChar U_NINE = ((UChar) 0x0039); +static const UChar COLON = ((UChar) 0x003A); +static const UChar SEMI_COLON = ((UChar) 0x003B); +static const UChar EQUALS = ((UChar) 0x003D); +static const UChar AT = ((UChar) 0x0040); +static const UChar CAP_A = ((UChar) 0x0041); +static const UChar CAP_B = ((UChar) 0x0042); +static const UChar CAP_R = ((UChar) 0x0052); +static const UChar CAP_Z = ((UChar) 0x005A); +static const UChar LOWLINE = ((UChar) 0x005F); +static const UChar LEFTBRACE = ((UChar) 0x007B); +static const UChar RIGHTBRACE = ((UChar) 0x007D); +static const UChar TILDE = ((UChar) 0x007E); +static const UChar ELLIPSIS = ((UChar) 0x2026); + +static const UChar LOW_A = ((UChar) 0x0061); +static const UChar LOW_B = ((UChar) 0x0062); +static const UChar LOW_C = ((UChar) 0x0063); +static const UChar LOW_D = ((UChar) 0x0064); +static const UChar LOW_E = ((UChar) 0x0065); +static const UChar LOW_F = ((UChar) 0x0066); +static const UChar LOW_G = ((UChar) 0x0067); +static const UChar LOW_H = ((UChar) 0x0068); +static const UChar LOW_I = ((UChar) 0x0069); +static const UChar LOW_J = ((UChar) 0x006a); +static const UChar LOW_K = ((UChar) 0x006B); +static const UChar LOW_L = ((UChar) 0x006C); +static const UChar LOW_M = ((UChar) 0x006D); +static const UChar LOW_N = ((UChar) 0x006E); +static const UChar LOW_O = ((UChar) 0x006F); +static const UChar LOW_P = ((UChar) 0x0070); +static const UChar LOW_Q = ((UChar) 0x0071); +static const UChar LOW_R = ((UChar) 0x0072); +static const UChar LOW_S = ((UChar) 0x0073); +static const UChar LOW_T = ((UChar) 0x0074); +static const UChar LOW_U = ((UChar) 0x0075); +static const UChar LOW_V = ((UChar) 0x0076); +static const UChar LOW_W = ((UChar) 0x0077); +static const UChar LOW_Y = ((UChar) 0x0079); +static const UChar LOW_Z = ((UChar) 0x007A); + +} static const int32_t PLURAL_RANGE_HIGH = 0x7fffffff; @@ -244,6 +250,9 @@ class U_I18N_API IFixedDecimal { virtual bool isNaN() const = 0; virtual bool isInfinite() const = 0; + + /** Whether the number has no nonzero fraction digits. */ + virtual bool hasIntegerValue() const = 0; }; /** @@ -263,7 +272,6 @@ class U_I18N_API FixedDecimal: public IFixedDecimal, public UObject { FixedDecimal(double n, int32_t v, int64_t f); FixedDecimal(double n, int32_t); explicit FixedDecimal(double n); - explicit FixedDecimal(const VisibleDigits &n); FixedDecimal(); ~FixedDecimal() U_OVERRIDE; FixedDecimal(const UnicodeString &s, UErrorCode &ec); @@ -272,6 +280,7 @@ class U_I18N_API FixedDecimal: public IFixedDecimal, public UObject { double getPluralOperand(PluralOperand operand) const U_OVERRIDE; bool isNaN() const U_OVERRIDE; bool isInfinite() const U_OVERRIDE; + bool hasIntegerValue() const U_OVERRIDE; bool isNanOrInfinity() const; // used in decimfmtimpl.cpp @@ -290,7 +299,7 @@ class U_I18N_API FixedDecimal: public IFixedDecimal, public UObject { int64_t decimalDigits; int64_t decimalDigitsWithoutTrailingZeros; int64_t intValue; - UBool hasIntegerValue; + UBool _hasIntegerValue; UBool isNegative; UBool _isNaN; UBool _isInfinite; diff --git a/deps/icu-small/source/i18n/precision.cpp b/deps/icu-small/source/i18n/precision.cpp deleted file mode 100644 index 97dc13dc385651..00000000000000 --- a/deps/icu-small/source/i18n/precision.cpp +++ /dev/null @@ -1,444 +0,0 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - * Copyright (C) 2015, International Business Machines - * Corporation and others. All Rights Reserved. - * - * file name: precisison.cpp - */ - -#include - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "digitlst.h" -#include "fmtableimp.h" -#include "precision.h" -#include "putilimp.h" -#include "visibledigits.h" - -U_NAMESPACE_BEGIN - -static const int32_t gPower10[] = {1, 10, 100, 1000}; - -FixedPrecision::FixedPrecision() - : fExactOnly(FALSE), fFailIfOverMax(FALSE), fRoundingMode(DecimalFormat::kRoundHalfEven) { - fMin.setIntDigitCount(1); - fMin.setFracDigitCount(0); -} - -UBool -FixedPrecision::isRoundingRequired( - int32_t upperExponent, int32_t lowerExponent) const { - int32_t leastSigAllowed = fMax.getLeastSignificantInclusive(); - int32_t maxSignificantDigits = fSignificant.getMax(); - int32_t roundDigit; - if (maxSignificantDigits == INT32_MAX) { - roundDigit = leastSigAllowed; - } else { - int32_t limitDigit = upperExponent - maxSignificantDigits; - roundDigit = - limitDigit > leastSigAllowed ? limitDigit : leastSigAllowed; - } - return (roundDigit > lowerExponent); -} - -DigitList & -FixedPrecision::round( - DigitList &value, int32_t exponent, UErrorCode &status) const { - if (U_FAILURE(status)) { - return value; - } - value .fContext.status &= ~DEC_Inexact; - if (!fRoundingIncrement.isZero()) { - if (exponent == 0) { - value.quantize(fRoundingIncrement, status); - } else { - DigitList adjustedIncrement(fRoundingIncrement); - adjustedIncrement.shiftDecimalRight(exponent); - value.quantize(adjustedIncrement, status); - } - if (U_FAILURE(status)) { - return value; - } - } - int32_t leastSig = fMax.getLeastSignificantInclusive(); - if (leastSig == INT32_MIN) { - value.round(fSignificant.getMax()); - } else { - value.roundAtExponent( - exponent + leastSig, - fSignificant.getMax()); - } - if (fExactOnly && (value.fContext.status & DEC_Inexact)) { - status = U_FORMAT_INEXACT_ERROR; - } else if (fFailIfOverMax) { - // Smallest interval for value stored in interval - DigitInterval interval; - value.getSmallestInterval(interval); - if (fMax.getIntDigitCount() < interval.getIntDigitCount()) { - status = U_ILLEGAL_ARGUMENT_ERROR; - } - } - return value; -} - -DigitInterval & -FixedPrecision::getIntervalForZero(DigitInterval &interval) const { - interval = fMin; - if (fSignificant.getMin() > 0) { - interval.expandToContainDigit(interval.getIntDigitCount() - fSignificant.getMin()); - } - interval.shrinkToFitWithin(fMax); - return interval; -} - -DigitInterval & -FixedPrecision::getInterval( - int32_t upperExponent, DigitInterval &interval) const { - if (fSignificant.getMin() > 0) { - interval.expandToContainDigit( - upperExponent - fSignificant.getMin()); - } - interval.expandToContain(fMin); - interval.shrinkToFitWithin(fMax); - return interval; -} - -DigitInterval & -FixedPrecision::getInterval( - const DigitList &value, DigitInterval &interval) const { - if (value.isZero()) { - interval = fMin; - if (fSignificant.getMin() > 0) { - interval.expandToContainDigit(interval.getIntDigitCount() - fSignificant.getMin()); - } - } else { - value.getSmallestInterval(interval); - if (fSignificant.getMin() > 0) { - interval.expandToContainDigit( - value.getUpperExponent() - fSignificant.getMin()); - } - interval.expandToContain(fMin); - } - interval.shrinkToFitWithin(fMax); - return interval; -} - -UBool -FixedPrecision::isFastFormattable() const { - return (fMin.getFracDigitCount() == 0 && fSignificant.isNoConstraints() && fRoundingIncrement.isZero() && !fFailIfOverMax); -} - -UBool -FixedPrecision::handleNonNumeric(DigitList &value, VisibleDigits &digits) { - if (value.isNaN()) { - digits.setNaN(); - return TRUE; - } - if (value.isInfinite()) { - digits.setInfinite(); - if (!value.isPositive()) { - digits.setNegative(); - } - return TRUE; - } - return FALSE; -} - -VisibleDigits & -FixedPrecision::initVisibleDigits( - DigitList &value, - VisibleDigits &digits, - UErrorCode &status) const { - if (U_FAILURE(status)) { - return digits; - } - digits.clear(); - if (handleNonNumeric(value, digits)) { - return digits; - } - if (!value.isPositive()) { - digits.setNegative(); - } - value.setRoundingMode(fRoundingMode); - round(value, 0, status); - getInterval(value, digits.fInterval); - digits.fExponent = value.getLowerExponent(); - value.appendDigitsTo(digits.fDigits, status); - return digits; -} - -VisibleDigits & -FixedPrecision::initVisibleDigits( - int64_t value, - VisibleDigits &digits, - UErrorCode &status) const { - if (U_FAILURE(status)) { - return digits; - } - if (!fRoundingIncrement.isZero()) { - // If we have round increment, use digit list. - DigitList digitList; - digitList.set(value); - return initVisibleDigits(digitList, digits, status); - } - // Try fast path - if (initVisibleDigits(value, 0, digits, status)) { - digits.fAbsDoubleValue = fabs((double) value); - digits.fAbsDoubleValueSet = U_SUCCESS(status) && !digits.isOverMaxDigits(); - return digits; - } - // Oops have to use digit list - DigitList digitList; - digitList.set(value); - return initVisibleDigits(digitList, digits, status); -} - -VisibleDigits & -FixedPrecision::initVisibleDigits( - double value, - VisibleDigits &digits, - UErrorCode &status) const { - if (U_FAILURE(status)) { - return digits; - } - digits.clear(); - if (uprv_isNaN(value)) { - digits.setNaN(); - return digits; - } - if (uprv_isPositiveInfinity(value)) { - digits.setInfinite(); - return digits; - } - if (uprv_isNegativeInfinity(value)) { - digits.setInfinite(); - digits.setNegative(); - return digits; - } - if (!fRoundingIncrement.isZero()) { - // If we have round increment, use digit list. - DigitList digitList; - digitList.set(value); - return initVisibleDigits(digitList, digits, status); - } - // Try to find n such that value * 10^n is an integer - int32_t n = -1; - double scaled; - for (int32_t i = 0; i < UPRV_LENGTHOF(gPower10); ++i) { - scaled = value * gPower10[i]; - if (scaled > MAX_INT64_IN_DOUBLE || scaled < -MAX_INT64_IN_DOUBLE) { - break; - } - if (scaled == floor(scaled)) { - n = i; - break; - } - } - // Try fast path - if (n >= 0 && initVisibleDigits(static_cast(scaled), -n, digits, status)) { - digits.fAbsDoubleValue = fabs(value); - digits.fAbsDoubleValueSet = U_SUCCESS(status) && !digits.isOverMaxDigits(); - // Adjust for negative 0 because when we cast to an int64, - // negative 0 becomes positive 0. - if (scaled == 0.0 && uprv_isNegative(scaled)) { - digits.setNegative(); - } - return digits; - } - - // Oops have to use digit list - DigitList digitList; - digitList.set(value); - return initVisibleDigits(digitList, digits, status); -} - -UBool -FixedPrecision::initVisibleDigits( - int64_t mantissa, - int32_t exponent, - VisibleDigits &digits, - UErrorCode &status) const { - if (U_FAILURE(status)) { - return TRUE; - } - digits.clear(); - - // Precompute fAbsIntValue if it is small enough, but we don't know yet - // if it will be valid. - UBool absIntValueComputed = FALSE; - if (mantissa > -1000000000000000000LL /* -1e18 */ - && mantissa < 1000000000000000000LL /* 1e18 */) { - digits.fAbsIntValue = mantissa; - if (digits.fAbsIntValue < 0) { - digits.fAbsIntValue = -digits.fAbsIntValue; - } - int32_t i = 0; - int32_t maxPower10Exp = UPRV_LENGTHOF(gPower10) - 1; - for (; i > exponent + maxPower10Exp; i -= maxPower10Exp) { - digits.fAbsIntValue /= gPower10[maxPower10Exp]; - } - digits.fAbsIntValue /= gPower10[i - exponent]; - absIntValueComputed = TRUE; - } - if (mantissa == 0) { - getIntervalForZero(digits.fInterval); - digits.fAbsIntValueSet = absIntValueComputed; - return TRUE; - } - // be sure least significant digit is non zero - while (mantissa % 10 == 0) { - mantissa /= 10; - ++exponent; - } - if (mantissa < 0) { - digits.fDigits.append((char) -(mantissa % -10), status); - mantissa /= -10; - digits.setNegative(); - } - while (mantissa) { - digits.fDigits.append((char) (mantissa % 10), status); - mantissa /= 10; - } - if (U_FAILURE(status)) { - return TRUE; - } - digits.fExponent = exponent; - int32_t upperExponent = exponent + digits.fDigits.length(); - if (fFailIfOverMax && upperExponent > fMax.getIntDigitCount()) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return TRUE; - } - UBool roundingRequired = - isRoundingRequired(upperExponent, exponent); - if (roundingRequired) { - if (fExactOnly) { - status = U_FORMAT_INEXACT_ERROR; - return TRUE; - } - return FALSE; - } - digits.fInterval.setLeastSignificantInclusive(exponent); - digits.fInterval.setMostSignificantExclusive(upperExponent); - getInterval(upperExponent, digits.fInterval); - - // The intValue we computed above is only valid if our visible digits - // doesn't exceed the maximum integer digits allowed. - digits.fAbsIntValueSet = absIntValueComputed && !digits.isOverMaxDigits(); - return TRUE; -} - -VisibleDigitsWithExponent & -FixedPrecision::initVisibleDigitsWithExponent( - DigitList &value, - VisibleDigitsWithExponent &digits, - UErrorCode &status) const { - digits.clear(); - initVisibleDigits(value, digits.fMantissa, status); - return digits; -} - -VisibleDigitsWithExponent & -FixedPrecision::initVisibleDigitsWithExponent( - double value, - VisibleDigitsWithExponent &digits, - UErrorCode &status) const { - digits.clear(); - initVisibleDigits(value, digits.fMantissa, status); - return digits; -} - -VisibleDigitsWithExponent & -FixedPrecision::initVisibleDigitsWithExponent( - int64_t value, - VisibleDigitsWithExponent &digits, - UErrorCode &status) const { - digits.clear(); - initVisibleDigits(value, digits.fMantissa, status); - return digits; -} - -ScientificPrecision::ScientificPrecision() : fMinExponentDigits(1) { -} - -DigitList & -ScientificPrecision::round(DigitList &value, UErrorCode &status) const { - if (U_FAILURE(status)) { - return value; - } - int32_t exponent = value.getScientificExponent( - fMantissa.fMin.getIntDigitCount(), getMultiplier()); - return fMantissa.round(value, exponent, status); -} - -int32_t -ScientificPrecision::toScientific(DigitList &value) const { - return value.toScientific( - fMantissa.fMin.getIntDigitCount(), getMultiplier()); -} - -int32_t -ScientificPrecision::getMultiplier() const { - int32_t maxIntDigitCount = fMantissa.fMax.getIntDigitCount(); - if (maxIntDigitCount == INT32_MAX) { - return 1; - } - int32_t multiplier = - maxIntDigitCount - fMantissa.fMin.getIntDigitCount() + 1; - return (multiplier < 1 ? 1 : multiplier); -} - -VisibleDigitsWithExponent & -ScientificPrecision::initVisibleDigitsWithExponent( - DigitList &value, - VisibleDigitsWithExponent &digits, - UErrorCode &status) const { - if (U_FAILURE(status)) { - return digits; - } - digits.clear(); - if (FixedPrecision::handleNonNumeric(value, digits.fMantissa)) { - return digits; - } - value.setRoundingMode(fMantissa.fRoundingMode); - int64_t exponent = toScientific(round(value, status)); - fMantissa.initVisibleDigits(value, digits.fMantissa, status); - FixedPrecision exponentPrecision; - exponentPrecision.fMin.setIntDigitCount(fMinExponentDigits); - exponentPrecision.initVisibleDigits(exponent, digits.fExponent, status); - digits.fHasExponent = TRUE; - return digits; -} - -VisibleDigitsWithExponent & -ScientificPrecision::initVisibleDigitsWithExponent( - double value, - VisibleDigitsWithExponent &digits, - UErrorCode &status) const { - if (U_FAILURE(status)) { - return digits; - } - DigitList digitList; - digitList.set(value); - return initVisibleDigitsWithExponent(digitList, digits, status); -} - -VisibleDigitsWithExponent & -ScientificPrecision::initVisibleDigitsWithExponent( - int64_t value, - VisibleDigitsWithExponent &digits, - UErrorCode &status) const { - if (U_FAILURE(status)) { - return digits; - } - DigitList digitList; - digitList.set(value); - return initVisibleDigitsWithExponent(digitList, digits, status); -} - - -U_NAMESPACE_END -#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/precision.h b/deps/icu-small/source/i18n/precision.h deleted file mode 100644 index 0598fa17d62771..00000000000000 --- a/deps/icu-small/source/i18n/precision.h +++ /dev/null @@ -1,323 +0,0 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2015, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* precision.h -* -* created on: 2015jan06 -* created by: Travis Keep -*/ - -#ifndef __PRECISION_H__ -#define __PRECISION_H__ - -#include "unicode/uobject.h" - -#if !UCONFIG_NO_FORMATTING -#include "unicode/utypes.h" - -#include "digitinterval.h" -#include "digitlst.h" -#include "significantdigitinterval.h" - -U_NAMESPACE_BEGIN - -class VisibleDigits; -class VisibleDigitsWithExponent; - - -/** - * A precision manager for values to be formatted as fixed point. - * Handles rounding of number to prepare it for formatting. - */ -class U_I18N_API FixedPrecision : public UMemory { -public: - - /** - * The smallest format interval allowed. Default is 1 integer digit and no - * fraction digits. - */ - DigitInterval fMin; - - /** - * The largest format interval allowed. Must contain fMin. - * Default is all digits. - */ - DigitInterval fMax; - - /** - * Min and max significant digits allowed. The default is no constraints. - */ - SignificantDigitInterval fSignificant; - - /** - * The rounding increment or zero if there is no rounding increment. - * Default is zero. - */ - DigitList fRoundingIncrement; - - /** - * If set, causes round() to set status to U_FORMAT_INEXACT_ERROR if - * any rounding is done. Default is FALSE. - */ - UBool fExactOnly; - - /** - * If set, causes round() to set status to U_ILLEGAL_ARGUMENT_ERROR if - * rounded number has more than maximum integer digits. Default is FALSE. - */ - UBool fFailIfOverMax; - - /** - * Controls the rounding mode that initVisibleDigits uses. - * Default is DecimalFormat::kRoundHalfEven - */ - DecimalFormat::ERoundingMode fRoundingMode; - - FixedPrecision(); - - /** - * Returns TRUE if this object equals rhs. - */ - UBool equals(const FixedPrecision &rhs) const { - return (fMin.equals(rhs.fMin) && - fMax.equals(rhs.fMax) && - fSignificant.equals(rhs.fSignificant) && - (fRoundingIncrement == rhs.fRoundingIncrement) && - fExactOnly == rhs.fExactOnly && - fFailIfOverMax == rhs.fFailIfOverMax && - fRoundingMode == rhs.fRoundingMode); - } - - /** - * Rounds value in place to prepare it for formatting. - * @param value The value to be rounded. It is rounded in place. - * @param exponent Always pass 0 for fixed decimal formatting. scientific - * precision passes the exponent value. Essentially, it divides value by - * 10^exponent, rounds and then multiplies by 10^exponent. - * @param status error returned here. - * @return reference to value. - */ - DigitList &round(DigitList &value, int32_t exponent, UErrorCode &status) const; - - /** - * Returns the interval to use to format the rounded value. - * @param roundedValue the already rounded value to format. - * @param interval modified in place to be the interval to use to format - * the rounded value. - * @return a reference to interval. - */ - DigitInterval &getInterval( - const DigitList &roundedValue, DigitInterval &interval) const; - - /** - * Returns TRUE if this instance allows for fast formatting of integers. - */ - UBool isFastFormattable() const; - - /** - * Initializes a VisibleDigits. - * @param value value for VisibleDigits - * Caller must not assume that the value of this parameter will remain - * unchanged. - * @param digits This is the value that is initialized. - * @param status any error returned here. - * @return digits - */ - VisibleDigits &initVisibleDigits( - DigitList &value, - VisibleDigits &digits, - UErrorCode &status) const; - - /** - * Initializes a VisibleDigits. - * @param value value for VisibleDigits - * @param digits This is the value that is initialized. - * @param status any error returned here. - * @return digits - */ - VisibleDigits &initVisibleDigits( - double value, - VisibleDigits &digits, - UErrorCode &status) const; - - /** - * Initializes a VisibleDigits. - * @param value value for VisibleDigits - * @param digits This is the value that is initialized. - * @param status any error returned here. - * @return digits - */ - VisibleDigits &initVisibleDigits( - int64_t value, - VisibleDigits &digits, - UErrorCode &status) const; - - /** - * Initializes a VisibleDigitsWithExponent. - * @param value value for VisibleDigits - * Caller must not assume that the value of this parameter will remain - * unchanged. - * @param digits This is the value that is initialized. - * @param status any error returned here. - * @return digits - */ - VisibleDigitsWithExponent &initVisibleDigitsWithExponent( - DigitList &value, - VisibleDigitsWithExponent &digits, - UErrorCode &status) const; - - /** - * Initializes a VisibleDigitsWithExponent. - * @param value value for VisibleDigits - * @param digits This is the value that is initialized. - * @param status any error returned here. - * @return digits - */ - VisibleDigitsWithExponent &initVisibleDigitsWithExponent( - double value, - VisibleDigitsWithExponent &digits, - UErrorCode &status) const; - - /** - * Initializes a VisibleDigitsWithExponent. - * @param value value for VisibleDigits - * @param digits This is the value that is initialized. - * @param status any error returned here. - * @return digits - */ - VisibleDigitsWithExponent &initVisibleDigitsWithExponent( - int64_t value, - VisibleDigitsWithExponent &digits, - UErrorCode &status) const; - -private: - /** - * Attempts to initialize 'digits' using simple mod 10 arithmetic. - * Returns FALSE if this is not possible such as when rounding - * would change the value. Otherwise returns TRUE. - * - * If the method returns FALSE, caller should create a DigitList - * and use it to initialize 'digits'. If this method returns TRUE, - * caller should accept the value stored in 'digits'. If this - * method returns TRUE along with a non zero error, caller must accept - * the error and not try again with a DigitList. - * - * Before calling this method, caller must verify that this object - * has no rounding increment set. - * - * The value that 'digits' is initialized to is mantissa * 10^exponent. - * For example mantissa = 54700 and exponent = -3 means 54.7. The - * properties of this object (such as min and max fraction digits), - * not the number of trailing zeros in the mantissa, determine whether or - * not the result contains any trailing 0's after the decimal point. - * - * @param mantissa the digits. May be positive or negative. May contain - * trailing zeros. - * @param exponent must always be zero or negative. An exponent > 0 - * yields undefined results! - * @param digits result stored here. - * @param status any error returned here. - */ - UBool - initVisibleDigits( - int64_t mantissa, - int32_t exponent, - VisibleDigits &digits, - UErrorCode &status) const; - UBool isRoundingRequired( - int32_t upperExponent, int32_t lowerExponent) const; - DigitInterval &getIntervalForZero(DigitInterval &interval) const; - DigitInterval &getInterval( - int32_t upperExponent, DigitInterval &interval) const; - static UBool handleNonNumeric(DigitList &value, VisibleDigits &digits); - - friend class ScientificPrecision; -}; - -/** - * A precision manager for values to be expressed as scientific notation. - */ -class U_I18N_API ScientificPrecision : public UMemory { -public: - FixedPrecision fMantissa; - int32_t fMinExponentDigits; - - ScientificPrecision(); - - /** - * rounds value in place to prepare it for formatting. - * @param value The value to be rounded. It is rounded in place. - * @param status error returned here. - * @return reference to value. - */ - DigitList &round(DigitList &value, UErrorCode &status) const; - - /** - * Converts value to a mantissa and exponent. - * - * @param value modified in place to be the mantissa. Depending on - * the precision settings, the resulting mantissa may not fall - * between 1.0 and 10.0. - * @return the exponent of value. - */ - int32_t toScientific(DigitList &value) const; - - /** - * Returns TRUE if this object equals rhs. - */ - UBool equals(const ScientificPrecision &rhs) const { - return fMantissa.equals(rhs.fMantissa) && fMinExponentDigits == rhs.fMinExponentDigits; - } - - /** - * Initializes a VisibleDigitsWithExponent. - * @param value the value - * Caller must not assume that the value of this parameter will remain - * unchanged. - * @param digits This is the value that is initialized. - * @param status any error returned here. - * @return digits - */ - VisibleDigitsWithExponent &initVisibleDigitsWithExponent( - DigitList &value, - VisibleDigitsWithExponent &digits, - UErrorCode &status) const; - - /** - * Initializes a VisibleDigitsWithExponent. - * @param value the value - * @param digits This is the value that is initialized. - * @param status any error returned here. - * @return digits - */ - VisibleDigitsWithExponent &initVisibleDigitsWithExponent( - double value, - VisibleDigitsWithExponent &digits, - UErrorCode &status) const; - - /** - * Initializes a VisibleDigitsWithExponent. - * @param value the value - * @param digits This is the value that is initialized. - * @param status any error returned here. - * @return digits - */ - VisibleDigitsWithExponent &initVisibleDigitsWithExponent( - int64_t value, - VisibleDigitsWithExponent &digits, - UErrorCode &status) const; - -private: - int32_t getMultiplier() const; - -}; - - - -U_NAMESPACE_END -#endif // #if !UCONFIG_NO_FORMATTING -#endif // __PRECISION_H__ diff --git a/deps/icu-small/source/i18n/quantityformatter.cpp b/deps/icu-small/source/i18n/quantityformatter.cpp index 208e064700ab2b..ba06ba06b97013 100644 --- a/deps/icu-small/source/i18n/quantityformatter.cpp +++ b/deps/icu-small/source/i18n/quantityformatter.cpp @@ -23,8 +23,8 @@ #include "unicode/fmtable.h" #include "unicode/fieldpos.h" #include "standardplural.h" -#include "visibledigits.h" #include "uassert.h" +#include "number_decimalquantity.h" U_NAMESPACE_BEGIN @@ -149,15 +149,15 @@ StandardPlural::Form QuantityFormatter::selectPlural( return StandardPlural::OTHER; } UnicodeString pluralKeyword; - VisibleDigitsWithExponent digits; const DecimalFormat *decFmt = dynamic_cast(&fmt); if (decFmt != NULL) { - decFmt->initVisibleDigitsWithExponent(number, digits, status); + number::impl::DecimalQuantity dq; + decFmt->formatToDecimalQuantity(number, dq, status); if (U_FAILURE(status)) { return StandardPlural::OTHER; } - pluralKeyword = rules.select(digits); - decFmt->format(digits, formattedNumber, pos, status); + pluralKeyword = rules.select(dq); + decFmt->format(number, formattedNumber, pos, status); } else { if (number.getType() == Formattable::kDouble) { pluralKeyword = rules.select(number.getDouble()); diff --git a/deps/icu-small/source/i18n/rbnf.cpp b/deps/icu-small/source/i18n/rbnf.cpp index 3385f300b11afe..ab9ad15e8c720d 100644 --- a/deps/icu-small/source/i18n/rbnf.cpp +++ b/deps/icu-small/source/i18n/rbnf.cpp @@ -34,7 +34,7 @@ #include "patternprops.h" #include "uresimp.h" #include "nfrs.h" -#include "digitlst.h" +#include "number_decimalquantity.h" // debugging // #define RBNF_DEBUG @@ -68,6 +68,8 @@ static const UChar gSemiPercent[] = U_NAMESPACE_BEGIN +using number::impl::DecimalQuantity; + UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RuleBasedNumberFormat) /* @@ -1109,21 +1111,21 @@ RuleBasedNumberFormat::findRuleSet(const UnicodeString& name, UErrorCode& status } UnicodeString& -RuleBasedNumberFormat::format(const DigitList &number, +RuleBasedNumberFormat::format(const DecimalQuantity &number, UnicodeString &appendTo, FieldPositionIterator *posIter, UErrorCode &status) const { if (U_FAILURE(status)) { return appendTo; } - DigitList copy(number); - if (copy.fitsIntoInt64(false)) { - format(((DigitList &)number).getInt64(), appendTo, posIter, status); + DecimalQuantity copy(number); + if (copy.fitsInLong()) { + format(number.toLong(), appendTo, posIter, status); } else { - copy.roundAtExponent(0); - if (copy.fitsIntoInt64(false)) { - format(number.getDouble(), appendTo, posIter, status); + copy.roundToMagnitude(0, number::impl::RoundingMode::UNUM_ROUND_HALFEVEN, status); + if (copy.fitsInLong()) { + format(number.toDouble(), appendTo, posIter, status); } else { // We're outside of our normal range that this framework can handle. @@ -1132,7 +1134,7 @@ RuleBasedNumberFormat::format(const DigitList &number, // TODO this section should probably be optimized. The DecimalFormat is shared in ICU4J. NumberFormat *decimalFormat = NumberFormat::createInstance(locale, UNUM_DECIMAL, status); Formattable f; - f.adoptDigitList(new DigitList(number)); + f.adoptDecimalQuantity(new DecimalQuantity(number)); decimalFormat->format(f, appendTo, posIter, status); delete decimalFormat; } @@ -1142,21 +1144,21 @@ RuleBasedNumberFormat::format(const DigitList &number, UnicodeString& -RuleBasedNumberFormat::format(const DigitList &number, +RuleBasedNumberFormat::format(const DecimalQuantity &number, UnicodeString& appendTo, FieldPosition& pos, UErrorCode &status) const { if (U_FAILURE(status)) { return appendTo; } - DigitList copy(number); - if (copy.fitsIntoInt64(false)) { - format(((DigitList &)number).getInt64(), appendTo, pos, status); + DecimalQuantity copy(number); + if (copy.fitsInLong()) { + format(number.toLong(), appendTo, pos, status); } else { - copy.roundAtExponent(0); - if (copy.fitsIntoInt64(false)) { - format(number.getDouble(), appendTo, pos, status); + copy.roundToMagnitude(0, number::impl::RoundingMode::UNUM_ROUND_HALFEVEN, status); + if (copy.fitsInLong()) { + format(number.toDouble(), appendTo, pos, status); } else { // We're outside of our normal range that this framework can handle. @@ -1165,7 +1167,7 @@ RuleBasedNumberFormat::format(const DigitList &number, // TODO this section should probably be optimized. The DecimalFormat is shared in ICU4J. NumberFormat *decimalFormat = NumberFormat::createInstance(locale, UNUM_DECIMAL, status); Formattable f; - f.adoptDigitList(new DigitList(number)); + f.adoptDecimalQuantity(new DecimalQuantity(number)); decimalFormat->format(f, appendTo, pos, status); delete decimalFormat; } @@ -1270,11 +1272,13 @@ RuleBasedNumberFormat::format(double number, { int32_t startPos = toAppendTo.length(); if (getRoundingMode() != DecimalFormat::ERoundingMode::kRoundUnnecessary && !uprv_isNaN(number) && !uprv_isInfinite(number)) { - DigitList digitList; - digitList.set(number); - digitList.setRoundingMode(getRoundingMode()); - digitList.roundFixedPoint(getMaximumFractionDigits()); - number = digitList.getDouble(); + DecimalQuantity digitList; + digitList.setToDouble(number); + digitList.roundToMagnitude( + -getMaximumFractionDigits(), + static_cast(getRoundingMode()), + status); + number = digitList.toDouble(); } rs.format(number, toAppendTo, toAppendTo.length(), 0, status); adjustForCapitalizationContext(startPos, toAppendTo, status); @@ -1310,9 +1314,9 @@ RuleBasedNumberFormat::format(int64_t number, NFRuleSet *ruleSet, UnicodeString& NumberFormat *decimalFormat = NumberFormat::createInstance(locale, UNUM_DECIMAL, status); Formattable f; FieldPosition pos(FieldPosition::DONT_CARE); - DigitList *digitList = new DigitList(); - digitList->set(number); - f.adoptDigitList(digitList); + DecimalQuantity *digitList = new DecimalQuantity(); + digitList->setToLong(number); + f.adoptDecimalQuantity(digitList); decimalFormat->format(f, toAppendTo, pos, status); delete decimalFormat; } diff --git a/deps/icu-small/source/i18n/reldatefmt.cpp b/deps/icu-small/source/i18n/reldatefmt.cpp index 5cf053db9a98fb..42ede7ae4d9903 100644 --- a/deps/icu-small/source/i18n/reldatefmt.cpp +++ b/deps/icu-small/source/i18n/reldatefmt.cpp @@ -14,6 +14,7 @@ #if !UCONFIG_NO_FORMATTING && !UCONFIG_NO_BREAK_ITERATION +#include #include "unicode/dtfmtsym.h" #include "unicode/ucasemap.h" #include "unicode/ureldatefmt.h" @@ -849,7 +850,7 @@ UnicodeString& RelativeDateTimeFormatter::formatNumeric( return appendTo; } UDateDirection direction = UDAT_DIRECTION_NEXT; - if (offset < 0) { + if (std::signbit(offset)) { // needed to handle -0.0 direction = UDAT_DIRECTION_LAST; offset = -offset; } diff --git a/deps/icu-small/source/i18n/scientificnumberformatter.cpp b/deps/icu-small/source/i18n/scientificnumberformatter.cpp index adf032d989dd90..03d98dd6e100e2 100644 --- a/deps/icu-small/source/i18n/scientificnumberformatter.cpp +++ b/deps/icu-small/source/i18n/scientificnumberformatter.cpp @@ -15,8 +15,8 @@ #include "unicode/fpositer.h" #include "unicode/utf16.h" #include "unicode/uniset.h" -#include "decfmtst.h" #include "unicode/decimfmt.h" +#include "static_unicode_sets.h" U_NAMESPACE_BEGIN @@ -129,7 +129,6 @@ UnicodeString &ScientificNumberFormatter::SuperscriptStyle::format( const UnicodeString &original, FieldPositionIterator &fpi, const UnicodeString &preExponent, - const DecimalFormatStaticSets &staticSets, UnicodeString &appendTo, UErrorCode &status) const { if (U_FAILURE(status)) { @@ -149,16 +148,17 @@ UnicodeString &ScientificNumberFormatter::SuperscriptStyle::format( break; case UNUM_EXPONENT_SIGN_FIELD: { + using namespace icu::numparse::impl; int32_t beginIndex = fp.getBeginIndex(); int32_t endIndex = fp.getEndIndex(); UChar32 aChar = original.char32At(beginIndex); - if (staticSets.fMinusSigns->contains(aChar)) { + if (unisets::get(unisets::MINUS_SIGN)->contains(aChar)) { appendTo.append( original, copyFromOffset, beginIndex - copyFromOffset); appendTo.append(kSuperscriptMinusSign); - } else if (staticSets.fPlusSigns->contains(aChar)) { + } else if (unisets::get(unisets::PLUS_SIGN)->contains(aChar)) { appendTo.append( original, copyFromOffset, @@ -203,7 +203,6 @@ UnicodeString &ScientificNumberFormatter::MarkupStyle::format( const UnicodeString &original, FieldPositionIterator &fpi, const UnicodeString &preExponent, - const DecimalFormatStaticSets & /*unusedDecimalFormatSets*/, UnicodeString &appendTo, UErrorCode &status) const { if (U_FAILURE(status)) { @@ -243,8 +242,7 @@ ScientificNumberFormatter::ScientificNumberFormatter( DecimalFormat *fmtToAdopt, Style *styleToAdopt, UErrorCode &status) : fPreExponent(), fDecimalFormat(fmtToAdopt), - fStyle(styleToAdopt), - fStaticSets(NULL) { + fStyle(styleToAdopt) { if (U_FAILURE(status)) { return; } @@ -258,7 +256,6 @@ ScientificNumberFormatter::ScientificNumberFormatter( return; } getPreExponent(*sym, fPreExponent); - fStaticSets = DecimalFormatStaticSets::getStaticSets(status); } ScientificNumberFormatter::ScientificNumberFormatter( @@ -266,8 +263,7 @@ ScientificNumberFormatter::ScientificNumberFormatter( : UObject(other), fPreExponent(other.fPreExponent), fDecimalFormat(NULL), - fStyle(NULL), - fStaticSets(other.fStaticSets) { + fStyle(NULL) { fDecimalFormat = static_cast( other.fDecimalFormat->clone()); fStyle = other.fStyle->clone(); @@ -292,7 +288,6 @@ UnicodeString &ScientificNumberFormatter::format( original, fpi, fPreExponent, - *fStaticSets, appendTo, status); } diff --git a/deps/icu-small/source/i18n/significantdigitinterval.h b/deps/icu-small/source/i18n/significantdigitinterval.h deleted file mode 100644 index fc23370de5b6fd..00000000000000 --- a/deps/icu-small/source/i18n/significantdigitinterval.h +++ /dev/null @@ -1,92 +0,0 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2015, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* significantdigitinterval.h -* -* created on: 2015jan6 -* created by: Travis Keep -*/ - -#ifndef __SIGNIFICANTDIGITINTERVAL_H__ -#define __SIGNIFICANTDIGITINTERVAL_H__ - -#include "unicode/uobject.h" -#include "unicode/utypes.h" - -U_NAMESPACE_BEGIN - -/** - * An interval of allowed significant digit counts. - */ -class U_I18N_API SignificantDigitInterval : public UMemory { -public: - - /** - * No limits on significant digits. - */ - SignificantDigitInterval() - : fMax(INT32_MAX), fMin(0) { } - - /** - * Make this instance have no limit on significant digits. - */ - void clear() { - fMin = 0; - fMax = INT32_MAX; - } - - /** - * Returns TRUE if this object is equal to rhs. - */ - UBool equals(const SignificantDigitInterval &rhs) const { - return ((fMax == rhs.fMax) && (fMin == rhs.fMin)); - } - - /** - * Sets maximum significant digits. 0 or negative means no maximum. - */ - void setMax(int32_t count) { - fMax = count <= 0 ? INT32_MAX : count; - } - - /** - * Get maximum significant digits. INT32_MAX means no maximum. - */ - int32_t getMax() const { - return fMax; - } - - /** - * Sets minimum significant digits. 0 or negative means no minimum. - */ - void setMin(int32_t count) { - fMin = count <= 0 ? 0 : count; - } - - /** - * Get maximum significant digits. 0 means no minimum. - */ - int32_t getMin() const { - return fMin; - } - - /** - * Returns TRUE if this instance represents no constraints on significant - * digits. - */ - UBool isNoConstraints() const { - return fMin == 0 && fMax == INT32_MAX; - } - -private: - int32_t fMax; - int32_t fMin; -}; - -U_NAMESPACE_END - -#endif // __SIGNIFICANTDIGITINTERVAL_H__ diff --git a/deps/icu-small/source/i18n/smallintformatter.cpp b/deps/icu-small/source/i18n/smallintformatter.cpp deleted file mode 100644 index 0c56e38bd69560..00000000000000 --- a/deps/icu-small/source/i18n/smallintformatter.cpp +++ /dev/null @@ -1,2622 +0,0 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - * Copyright (C) 2015, International Business Machines - * Corporation and others. All Rights Reserved. - * - * file name: smallintformatter.cpp - */ - -#include "unicode/unistr.h" - -#include "smallintformatter.h" - -static const int32_t gMaxFastInt = 4096; - -static const UChar gDigits[] = { - 0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x31, - 0x30,0x30,0x30,0x32,0x30,0x30,0x30,0x33, - 0x30,0x30,0x30,0x34,0x30,0x30,0x30,0x35, - 0x30,0x30,0x30,0x36,0x30,0x30,0x30,0x37, - 0x30,0x30,0x30,0x38,0x30,0x30,0x30,0x39, - 0x30,0x30,0x31,0x30,0x30,0x30,0x31,0x31, - 0x30,0x30,0x31,0x32,0x30,0x30,0x31,0x33, - 0x30,0x30,0x31,0x34,0x30,0x30,0x31,0x35, - 0x30,0x30,0x31,0x36,0x30,0x30,0x31,0x37, - 0x30,0x30,0x31,0x38,0x30,0x30,0x31,0x39, - 0x30,0x30,0x32,0x30,0x30,0x30,0x32,0x31, - 0x30,0x30,0x32,0x32,0x30,0x30,0x32,0x33, - 0x30,0x30,0x32,0x34,0x30,0x30,0x32,0x35, - 0x30,0x30,0x32,0x36,0x30,0x30,0x32,0x37, - 0x30,0x30,0x32,0x38,0x30,0x30,0x32,0x39, - 0x30,0x30,0x33,0x30,0x30,0x30,0x33,0x31, - 0x30,0x30,0x33,0x32,0x30,0x30,0x33,0x33, - 0x30,0x30,0x33,0x34,0x30,0x30,0x33,0x35, - 0x30,0x30,0x33,0x36,0x30,0x30,0x33,0x37, - 0x30,0x30,0x33,0x38,0x30,0x30,0x33,0x39, - 0x30,0x30,0x34,0x30,0x30,0x30,0x34,0x31, - 0x30,0x30,0x34,0x32,0x30,0x30,0x34,0x33, - 0x30,0x30,0x34,0x34,0x30,0x30,0x34,0x35, - 0x30,0x30,0x34,0x36,0x30,0x30,0x34,0x37, - 0x30,0x30,0x34,0x38,0x30,0x30,0x34,0x39, - 0x30,0x30,0x35,0x30,0x30,0x30,0x35,0x31, - 0x30,0x30,0x35,0x32,0x30,0x30,0x35,0x33, - 0x30,0x30,0x35,0x34,0x30,0x30,0x35,0x35, - 0x30,0x30,0x35,0x36,0x30,0x30,0x35,0x37, - 0x30,0x30,0x35,0x38,0x30,0x30,0x35,0x39, - 0x30,0x30,0x36,0x30,0x30,0x30,0x36,0x31, - 0x30,0x30,0x36,0x32,0x30,0x30,0x36,0x33, - 0x30,0x30,0x36,0x34,0x30,0x30,0x36,0x35, - 0x30,0x30,0x36,0x36,0x30,0x30,0x36,0x37, - 0x30,0x30,0x36,0x38,0x30,0x30,0x36,0x39, - 0x30,0x30,0x37,0x30,0x30,0x30,0x37,0x31, - 0x30,0x30,0x37,0x32,0x30,0x30,0x37,0x33, - 0x30,0x30,0x37,0x34,0x30,0x30,0x37,0x35, - 0x30,0x30,0x37,0x36,0x30,0x30,0x37,0x37, - 0x30,0x30,0x37,0x38,0x30,0x30,0x37,0x39, - 0x30,0x30,0x38,0x30,0x30,0x30,0x38,0x31, - 0x30,0x30,0x38,0x32,0x30,0x30,0x38,0x33, - 0x30,0x30,0x38,0x34,0x30,0x30,0x38,0x35, - 0x30,0x30,0x38,0x36,0x30,0x30,0x38,0x37, - 0x30,0x30,0x38,0x38,0x30,0x30,0x38,0x39, - 0x30,0x30,0x39,0x30,0x30,0x30,0x39,0x31, - 0x30,0x30,0x39,0x32,0x30,0x30,0x39,0x33, - 0x30,0x30,0x39,0x34,0x30,0x30,0x39,0x35, - 0x30,0x30,0x39,0x36,0x30,0x30,0x39,0x37, - 0x30,0x30,0x39,0x38,0x30,0x30,0x39,0x39, - 0x30,0x31,0x30,0x30,0x30,0x31,0x30,0x31, - 0x30,0x31,0x30,0x32,0x30,0x31,0x30,0x33, - 0x30,0x31,0x30,0x34,0x30,0x31,0x30,0x35, - 0x30,0x31,0x30,0x36,0x30,0x31,0x30,0x37, - 0x30,0x31,0x30,0x38,0x30,0x31,0x30,0x39, - 0x30,0x31,0x31,0x30,0x30,0x31,0x31,0x31, - 0x30,0x31,0x31,0x32,0x30,0x31,0x31,0x33, - 0x30,0x31,0x31,0x34,0x30,0x31,0x31,0x35, - 0x30,0x31,0x31,0x36,0x30,0x31,0x31,0x37, - 0x30,0x31,0x31,0x38,0x30,0x31,0x31,0x39, - 0x30,0x31,0x32,0x30,0x30,0x31,0x32,0x31, - 0x30,0x31,0x32,0x32,0x30,0x31,0x32,0x33, - 0x30,0x31,0x32,0x34,0x30,0x31,0x32,0x35, - 0x30,0x31,0x32,0x36,0x30,0x31,0x32,0x37, - 0x30,0x31,0x32,0x38,0x30,0x31,0x32,0x39, - 0x30,0x31,0x33,0x30,0x30,0x31,0x33,0x31, - 0x30,0x31,0x33,0x32,0x30,0x31,0x33,0x33, - 0x30,0x31,0x33,0x34,0x30,0x31,0x33,0x35, - 0x30,0x31,0x33,0x36,0x30,0x31,0x33,0x37, - 0x30,0x31,0x33,0x38,0x30,0x31,0x33,0x39, - 0x30,0x31,0x34,0x30,0x30,0x31,0x34,0x31, - 0x30,0x31,0x34,0x32,0x30,0x31,0x34,0x33, - 0x30,0x31,0x34,0x34,0x30,0x31,0x34,0x35, - 0x30,0x31,0x34,0x36,0x30,0x31,0x34,0x37, - 0x30,0x31,0x34,0x38,0x30,0x31,0x34,0x39, - 0x30,0x31,0x35,0x30,0x30,0x31,0x35,0x31, - 0x30,0x31,0x35,0x32,0x30,0x31,0x35,0x33, - 0x30,0x31,0x35,0x34,0x30,0x31,0x35,0x35, - 0x30,0x31,0x35,0x36,0x30,0x31,0x35,0x37, - 0x30,0x31,0x35,0x38,0x30,0x31,0x35,0x39, - 0x30,0x31,0x36,0x30,0x30,0x31,0x36,0x31, - 0x30,0x31,0x36,0x32,0x30,0x31,0x36,0x33, - 0x30,0x31,0x36,0x34,0x30,0x31,0x36,0x35, - 0x30,0x31,0x36,0x36,0x30,0x31,0x36,0x37, - 0x30,0x31,0x36,0x38,0x30,0x31,0x36,0x39, - 0x30,0x31,0x37,0x30,0x30,0x31,0x37,0x31, - 0x30,0x31,0x37,0x32,0x30,0x31,0x37,0x33, - 0x30,0x31,0x37,0x34,0x30,0x31,0x37,0x35, - 0x30,0x31,0x37,0x36,0x30,0x31,0x37,0x37, - 0x30,0x31,0x37,0x38,0x30,0x31,0x37,0x39, - 0x30,0x31,0x38,0x30,0x30,0x31,0x38,0x31, - 0x30,0x31,0x38,0x32,0x30,0x31,0x38,0x33, - 0x30,0x31,0x38,0x34,0x30,0x31,0x38,0x35, - 0x30,0x31,0x38,0x36,0x30,0x31,0x38,0x37, - 0x30,0x31,0x38,0x38,0x30,0x31,0x38,0x39, - 0x30,0x31,0x39,0x30,0x30,0x31,0x39,0x31, - 0x30,0x31,0x39,0x32,0x30,0x31,0x39,0x33, - 0x30,0x31,0x39,0x34,0x30,0x31,0x39,0x35, - 0x30,0x31,0x39,0x36,0x30,0x31,0x39,0x37, - 0x30,0x31,0x39,0x38,0x30,0x31,0x39,0x39, - 0x30,0x32,0x30,0x30,0x30,0x32,0x30,0x31, - 0x30,0x32,0x30,0x32,0x30,0x32,0x30,0x33, - 0x30,0x32,0x30,0x34,0x30,0x32,0x30,0x35, - 0x30,0x32,0x30,0x36,0x30,0x32,0x30,0x37, - 0x30,0x32,0x30,0x38,0x30,0x32,0x30,0x39, - 0x30,0x32,0x31,0x30,0x30,0x32,0x31,0x31, - 0x30,0x32,0x31,0x32,0x30,0x32,0x31,0x33, - 0x30,0x32,0x31,0x34,0x30,0x32,0x31,0x35, - 0x30,0x32,0x31,0x36,0x30,0x32,0x31,0x37, - 0x30,0x32,0x31,0x38,0x30,0x32,0x31,0x39, - 0x30,0x32,0x32,0x30,0x30,0x32,0x32,0x31, - 0x30,0x32,0x32,0x32,0x30,0x32,0x32,0x33, - 0x30,0x32,0x32,0x34,0x30,0x32,0x32,0x35, - 0x30,0x32,0x32,0x36,0x30,0x32,0x32,0x37, - 0x30,0x32,0x32,0x38,0x30,0x32,0x32,0x39, - 0x30,0x32,0x33,0x30,0x30,0x32,0x33,0x31, - 0x30,0x32,0x33,0x32,0x30,0x32,0x33,0x33, - 0x30,0x32,0x33,0x34,0x30,0x32,0x33,0x35, - 0x30,0x32,0x33,0x36,0x30,0x32,0x33,0x37, - 0x30,0x32,0x33,0x38,0x30,0x32,0x33,0x39, - 0x30,0x32,0x34,0x30,0x30,0x32,0x34,0x31, - 0x30,0x32,0x34,0x32,0x30,0x32,0x34,0x33, - 0x30,0x32,0x34,0x34,0x30,0x32,0x34,0x35, - 0x30,0x32,0x34,0x36,0x30,0x32,0x34,0x37, - 0x30,0x32,0x34,0x38,0x30,0x32,0x34,0x39, - 0x30,0x32,0x35,0x30,0x30,0x32,0x35,0x31, - 0x30,0x32,0x35,0x32,0x30,0x32,0x35,0x33, - 0x30,0x32,0x35,0x34,0x30,0x32,0x35,0x35, - 0x30,0x32,0x35,0x36,0x30,0x32,0x35,0x37, - 0x30,0x32,0x35,0x38,0x30,0x32,0x35,0x39, - 0x30,0x32,0x36,0x30,0x30,0x32,0x36,0x31, - 0x30,0x32,0x36,0x32,0x30,0x32,0x36,0x33, - 0x30,0x32,0x36,0x34,0x30,0x32,0x36,0x35, - 0x30,0x32,0x36,0x36,0x30,0x32,0x36,0x37, - 0x30,0x32,0x36,0x38,0x30,0x32,0x36,0x39, - 0x30,0x32,0x37,0x30,0x30,0x32,0x37,0x31, - 0x30,0x32,0x37,0x32,0x30,0x32,0x37,0x33, - 0x30,0x32,0x37,0x34,0x30,0x32,0x37,0x35, - 0x30,0x32,0x37,0x36,0x30,0x32,0x37,0x37, - 0x30,0x32,0x37,0x38,0x30,0x32,0x37,0x39, - 0x30,0x32,0x38,0x30,0x30,0x32,0x38,0x31, - 0x30,0x32,0x38,0x32,0x30,0x32,0x38,0x33, - 0x30,0x32,0x38,0x34,0x30,0x32,0x38,0x35, - 0x30,0x32,0x38,0x36,0x30,0x32,0x38,0x37, - 0x30,0x32,0x38,0x38,0x30,0x32,0x38,0x39, - 0x30,0x32,0x39,0x30,0x30,0x32,0x39,0x31, - 0x30,0x32,0x39,0x32,0x30,0x32,0x39,0x33, - 0x30,0x32,0x39,0x34,0x30,0x32,0x39,0x35, - 0x30,0x32,0x39,0x36,0x30,0x32,0x39,0x37, - 0x30,0x32,0x39,0x38,0x30,0x32,0x39,0x39, - 0x30,0x33,0x30,0x30,0x30,0x33,0x30,0x31, - 0x30,0x33,0x30,0x32,0x30,0x33,0x30,0x33, - 0x30,0x33,0x30,0x34,0x30,0x33,0x30,0x35, - 0x30,0x33,0x30,0x36,0x30,0x33,0x30,0x37, - 0x30,0x33,0x30,0x38,0x30,0x33,0x30,0x39, - 0x30,0x33,0x31,0x30,0x30,0x33,0x31,0x31, - 0x30,0x33,0x31,0x32,0x30,0x33,0x31,0x33, - 0x30,0x33,0x31,0x34,0x30,0x33,0x31,0x35, - 0x30,0x33,0x31,0x36,0x30,0x33,0x31,0x37, - 0x30,0x33,0x31,0x38,0x30,0x33,0x31,0x39, - 0x30,0x33,0x32,0x30,0x30,0x33,0x32,0x31, - 0x30,0x33,0x32,0x32,0x30,0x33,0x32,0x33, - 0x30,0x33,0x32,0x34,0x30,0x33,0x32,0x35, - 0x30,0x33,0x32,0x36,0x30,0x33,0x32,0x37, - 0x30,0x33,0x32,0x38,0x30,0x33,0x32,0x39, - 0x30,0x33,0x33,0x30,0x30,0x33,0x33,0x31, - 0x30,0x33,0x33,0x32,0x30,0x33,0x33,0x33, - 0x30,0x33,0x33,0x34,0x30,0x33,0x33,0x35, - 0x30,0x33,0x33,0x36,0x30,0x33,0x33,0x37, - 0x30,0x33,0x33,0x38,0x30,0x33,0x33,0x39, - 0x30,0x33,0x34,0x30,0x30,0x33,0x34,0x31, - 0x30,0x33,0x34,0x32,0x30,0x33,0x34,0x33, - 0x30,0x33,0x34,0x34,0x30,0x33,0x34,0x35, - 0x30,0x33,0x34,0x36,0x30,0x33,0x34,0x37, - 0x30,0x33,0x34,0x38,0x30,0x33,0x34,0x39, - 0x30,0x33,0x35,0x30,0x30,0x33,0x35,0x31, - 0x30,0x33,0x35,0x32,0x30,0x33,0x35,0x33, - 0x30,0x33,0x35,0x34,0x30,0x33,0x35,0x35, - 0x30,0x33,0x35,0x36,0x30,0x33,0x35,0x37, - 0x30,0x33,0x35,0x38,0x30,0x33,0x35,0x39, - 0x30,0x33,0x36,0x30,0x30,0x33,0x36,0x31, - 0x30,0x33,0x36,0x32,0x30,0x33,0x36,0x33, - 0x30,0x33,0x36,0x34,0x30,0x33,0x36,0x35, - 0x30,0x33,0x36,0x36,0x30,0x33,0x36,0x37, - 0x30,0x33,0x36,0x38,0x30,0x33,0x36,0x39, - 0x30,0x33,0x37,0x30,0x30,0x33,0x37,0x31, - 0x30,0x33,0x37,0x32,0x30,0x33,0x37,0x33, - 0x30,0x33,0x37,0x34,0x30,0x33,0x37,0x35, - 0x30,0x33,0x37,0x36,0x30,0x33,0x37,0x37, - 0x30,0x33,0x37,0x38,0x30,0x33,0x37,0x39, - 0x30,0x33,0x38,0x30,0x30,0x33,0x38,0x31, - 0x30,0x33,0x38,0x32,0x30,0x33,0x38,0x33, - 0x30,0x33,0x38,0x34,0x30,0x33,0x38,0x35, - 0x30,0x33,0x38,0x36,0x30,0x33,0x38,0x37, - 0x30,0x33,0x38,0x38,0x30,0x33,0x38,0x39, - 0x30,0x33,0x39,0x30,0x30,0x33,0x39,0x31, - 0x30,0x33,0x39,0x32,0x30,0x33,0x39,0x33, - 0x30,0x33,0x39,0x34,0x30,0x33,0x39,0x35, - 0x30,0x33,0x39,0x36,0x30,0x33,0x39,0x37, - 0x30,0x33,0x39,0x38,0x30,0x33,0x39,0x39, - 0x30,0x34,0x30,0x30,0x30,0x34,0x30,0x31, - 0x30,0x34,0x30,0x32,0x30,0x34,0x30,0x33, - 0x30,0x34,0x30,0x34,0x30,0x34,0x30,0x35, - 0x30,0x34,0x30,0x36,0x30,0x34,0x30,0x37, - 0x30,0x34,0x30,0x38,0x30,0x34,0x30,0x39, - 0x30,0x34,0x31,0x30,0x30,0x34,0x31,0x31, - 0x30,0x34,0x31,0x32,0x30,0x34,0x31,0x33, - 0x30,0x34,0x31,0x34,0x30,0x34,0x31,0x35, - 0x30,0x34,0x31,0x36,0x30,0x34,0x31,0x37, - 0x30,0x34,0x31,0x38,0x30,0x34,0x31,0x39, - 0x30,0x34,0x32,0x30,0x30,0x34,0x32,0x31, - 0x30,0x34,0x32,0x32,0x30,0x34,0x32,0x33, - 0x30,0x34,0x32,0x34,0x30,0x34,0x32,0x35, - 0x30,0x34,0x32,0x36,0x30,0x34,0x32,0x37, - 0x30,0x34,0x32,0x38,0x30,0x34,0x32,0x39, - 0x30,0x34,0x33,0x30,0x30,0x34,0x33,0x31, - 0x30,0x34,0x33,0x32,0x30,0x34,0x33,0x33, - 0x30,0x34,0x33,0x34,0x30,0x34,0x33,0x35, - 0x30,0x34,0x33,0x36,0x30,0x34,0x33,0x37, - 0x30,0x34,0x33,0x38,0x30,0x34,0x33,0x39, - 0x30,0x34,0x34,0x30,0x30,0x34,0x34,0x31, - 0x30,0x34,0x34,0x32,0x30,0x34,0x34,0x33, - 0x30,0x34,0x34,0x34,0x30,0x34,0x34,0x35, - 0x30,0x34,0x34,0x36,0x30,0x34,0x34,0x37, - 0x30,0x34,0x34,0x38,0x30,0x34,0x34,0x39, - 0x30,0x34,0x35,0x30,0x30,0x34,0x35,0x31, - 0x30,0x34,0x35,0x32,0x30,0x34,0x35,0x33, - 0x30,0x34,0x35,0x34,0x30,0x34,0x35,0x35, - 0x30,0x34,0x35,0x36,0x30,0x34,0x35,0x37, - 0x30,0x34,0x35,0x38,0x30,0x34,0x35,0x39, - 0x30,0x34,0x36,0x30,0x30,0x34,0x36,0x31, - 0x30,0x34,0x36,0x32,0x30,0x34,0x36,0x33, - 0x30,0x34,0x36,0x34,0x30,0x34,0x36,0x35, - 0x30,0x34,0x36,0x36,0x30,0x34,0x36,0x37, - 0x30,0x34,0x36,0x38,0x30,0x34,0x36,0x39, - 0x30,0x34,0x37,0x30,0x30,0x34,0x37,0x31, - 0x30,0x34,0x37,0x32,0x30,0x34,0x37,0x33, - 0x30,0x34,0x37,0x34,0x30,0x34,0x37,0x35, - 0x30,0x34,0x37,0x36,0x30,0x34,0x37,0x37, - 0x30,0x34,0x37,0x38,0x30,0x34,0x37,0x39, - 0x30,0x34,0x38,0x30,0x30,0x34,0x38,0x31, - 0x30,0x34,0x38,0x32,0x30,0x34,0x38,0x33, - 0x30,0x34,0x38,0x34,0x30,0x34,0x38,0x35, - 0x30,0x34,0x38,0x36,0x30,0x34,0x38,0x37, - 0x30,0x34,0x38,0x38,0x30,0x34,0x38,0x39, - 0x30,0x34,0x39,0x30,0x30,0x34,0x39,0x31, - 0x30,0x34,0x39,0x32,0x30,0x34,0x39,0x33, - 0x30,0x34,0x39,0x34,0x30,0x34,0x39,0x35, - 0x30,0x34,0x39,0x36,0x30,0x34,0x39,0x37, - 0x30,0x34,0x39,0x38,0x30,0x34,0x39,0x39, - 0x30,0x35,0x30,0x30,0x30,0x35,0x30,0x31, - 0x30,0x35,0x30,0x32,0x30,0x35,0x30,0x33, - 0x30,0x35,0x30,0x34,0x30,0x35,0x30,0x35, - 0x30,0x35,0x30,0x36,0x30,0x35,0x30,0x37, - 0x30,0x35,0x30,0x38,0x30,0x35,0x30,0x39, - 0x30,0x35,0x31,0x30,0x30,0x35,0x31,0x31, - 0x30,0x35,0x31,0x32,0x30,0x35,0x31,0x33, - 0x30,0x35,0x31,0x34,0x30,0x35,0x31,0x35, - 0x30,0x35,0x31,0x36,0x30,0x35,0x31,0x37, - 0x30,0x35,0x31,0x38,0x30,0x35,0x31,0x39, - 0x30,0x35,0x32,0x30,0x30,0x35,0x32,0x31, - 0x30,0x35,0x32,0x32,0x30,0x35,0x32,0x33, - 0x30,0x35,0x32,0x34,0x30,0x35,0x32,0x35, - 0x30,0x35,0x32,0x36,0x30,0x35,0x32,0x37, - 0x30,0x35,0x32,0x38,0x30,0x35,0x32,0x39, - 0x30,0x35,0x33,0x30,0x30,0x35,0x33,0x31, - 0x30,0x35,0x33,0x32,0x30,0x35,0x33,0x33, - 0x30,0x35,0x33,0x34,0x30,0x35,0x33,0x35, - 0x30,0x35,0x33,0x36,0x30,0x35,0x33,0x37, - 0x30,0x35,0x33,0x38,0x30,0x35,0x33,0x39, - 0x30,0x35,0x34,0x30,0x30,0x35,0x34,0x31, - 0x30,0x35,0x34,0x32,0x30,0x35,0x34,0x33, - 0x30,0x35,0x34,0x34,0x30,0x35,0x34,0x35, - 0x30,0x35,0x34,0x36,0x30,0x35,0x34,0x37, - 0x30,0x35,0x34,0x38,0x30,0x35,0x34,0x39, - 0x30,0x35,0x35,0x30,0x30,0x35,0x35,0x31, - 0x30,0x35,0x35,0x32,0x30,0x35,0x35,0x33, - 0x30,0x35,0x35,0x34,0x30,0x35,0x35,0x35, - 0x30,0x35,0x35,0x36,0x30,0x35,0x35,0x37, - 0x30,0x35,0x35,0x38,0x30,0x35,0x35,0x39, - 0x30,0x35,0x36,0x30,0x30,0x35,0x36,0x31, - 0x30,0x35,0x36,0x32,0x30,0x35,0x36,0x33, - 0x30,0x35,0x36,0x34,0x30,0x35,0x36,0x35, - 0x30,0x35,0x36,0x36,0x30,0x35,0x36,0x37, - 0x30,0x35,0x36,0x38,0x30,0x35,0x36,0x39, - 0x30,0x35,0x37,0x30,0x30,0x35,0x37,0x31, - 0x30,0x35,0x37,0x32,0x30,0x35,0x37,0x33, - 0x30,0x35,0x37,0x34,0x30,0x35,0x37,0x35, - 0x30,0x35,0x37,0x36,0x30,0x35,0x37,0x37, - 0x30,0x35,0x37,0x38,0x30,0x35,0x37,0x39, - 0x30,0x35,0x38,0x30,0x30,0x35,0x38,0x31, - 0x30,0x35,0x38,0x32,0x30,0x35,0x38,0x33, - 0x30,0x35,0x38,0x34,0x30,0x35,0x38,0x35, - 0x30,0x35,0x38,0x36,0x30,0x35,0x38,0x37, - 0x30,0x35,0x38,0x38,0x30,0x35,0x38,0x39, - 0x30,0x35,0x39,0x30,0x30,0x35,0x39,0x31, - 0x30,0x35,0x39,0x32,0x30,0x35,0x39,0x33, - 0x30,0x35,0x39,0x34,0x30,0x35,0x39,0x35, - 0x30,0x35,0x39,0x36,0x30,0x35,0x39,0x37, - 0x30,0x35,0x39,0x38,0x30,0x35,0x39,0x39, - 0x30,0x36,0x30,0x30,0x30,0x36,0x30,0x31, - 0x30,0x36,0x30,0x32,0x30,0x36,0x30,0x33, - 0x30,0x36,0x30,0x34,0x30,0x36,0x30,0x35, - 0x30,0x36,0x30,0x36,0x30,0x36,0x30,0x37, - 0x30,0x36,0x30,0x38,0x30,0x36,0x30,0x39, - 0x30,0x36,0x31,0x30,0x30,0x36,0x31,0x31, - 0x30,0x36,0x31,0x32,0x30,0x36,0x31,0x33, - 0x30,0x36,0x31,0x34,0x30,0x36,0x31,0x35, - 0x30,0x36,0x31,0x36,0x30,0x36,0x31,0x37, - 0x30,0x36,0x31,0x38,0x30,0x36,0x31,0x39, - 0x30,0x36,0x32,0x30,0x30,0x36,0x32,0x31, - 0x30,0x36,0x32,0x32,0x30,0x36,0x32,0x33, - 0x30,0x36,0x32,0x34,0x30,0x36,0x32,0x35, - 0x30,0x36,0x32,0x36,0x30,0x36,0x32,0x37, - 0x30,0x36,0x32,0x38,0x30,0x36,0x32,0x39, - 0x30,0x36,0x33,0x30,0x30,0x36,0x33,0x31, - 0x30,0x36,0x33,0x32,0x30,0x36,0x33,0x33, - 0x30,0x36,0x33,0x34,0x30,0x36,0x33,0x35, - 0x30,0x36,0x33,0x36,0x30,0x36,0x33,0x37, - 0x30,0x36,0x33,0x38,0x30,0x36,0x33,0x39, - 0x30,0x36,0x34,0x30,0x30,0x36,0x34,0x31, - 0x30,0x36,0x34,0x32,0x30,0x36,0x34,0x33, - 0x30,0x36,0x34,0x34,0x30,0x36,0x34,0x35, - 0x30,0x36,0x34,0x36,0x30,0x36,0x34,0x37, - 0x30,0x36,0x34,0x38,0x30,0x36,0x34,0x39, - 0x30,0x36,0x35,0x30,0x30,0x36,0x35,0x31, - 0x30,0x36,0x35,0x32,0x30,0x36,0x35,0x33, - 0x30,0x36,0x35,0x34,0x30,0x36,0x35,0x35, - 0x30,0x36,0x35,0x36,0x30,0x36,0x35,0x37, - 0x30,0x36,0x35,0x38,0x30,0x36,0x35,0x39, - 0x30,0x36,0x36,0x30,0x30,0x36,0x36,0x31, - 0x30,0x36,0x36,0x32,0x30,0x36,0x36,0x33, - 0x30,0x36,0x36,0x34,0x30,0x36,0x36,0x35, - 0x30,0x36,0x36,0x36,0x30,0x36,0x36,0x37, - 0x30,0x36,0x36,0x38,0x30,0x36,0x36,0x39, - 0x30,0x36,0x37,0x30,0x30,0x36,0x37,0x31, - 0x30,0x36,0x37,0x32,0x30,0x36,0x37,0x33, - 0x30,0x36,0x37,0x34,0x30,0x36,0x37,0x35, - 0x30,0x36,0x37,0x36,0x30,0x36,0x37,0x37, - 0x30,0x36,0x37,0x38,0x30,0x36,0x37,0x39, - 0x30,0x36,0x38,0x30,0x30,0x36,0x38,0x31, - 0x30,0x36,0x38,0x32,0x30,0x36,0x38,0x33, - 0x30,0x36,0x38,0x34,0x30,0x36,0x38,0x35, - 0x30,0x36,0x38,0x36,0x30,0x36,0x38,0x37, - 0x30,0x36,0x38,0x38,0x30,0x36,0x38,0x39, - 0x30,0x36,0x39,0x30,0x30,0x36,0x39,0x31, - 0x30,0x36,0x39,0x32,0x30,0x36,0x39,0x33, - 0x30,0x36,0x39,0x34,0x30,0x36,0x39,0x35, - 0x30,0x36,0x39,0x36,0x30,0x36,0x39,0x37, - 0x30,0x36,0x39,0x38,0x30,0x36,0x39,0x39, - 0x30,0x37,0x30,0x30,0x30,0x37,0x30,0x31, - 0x30,0x37,0x30,0x32,0x30,0x37,0x30,0x33, - 0x30,0x37,0x30,0x34,0x30,0x37,0x30,0x35, - 0x30,0x37,0x30,0x36,0x30,0x37,0x30,0x37, - 0x30,0x37,0x30,0x38,0x30,0x37,0x30,0x39, - 0x30,0x37,0x31,0x30,0x30,0x37,0x31,0x31, - 0x30,0x37,0x31,0x32,0x30,0x37,0x31,0x33, - 0x30,0x37,0x31,0x34,0x30,0x37,0x31,0x35, - 0x30,0x37,0x31,0x36,0x30,0x37,0x31,0x37, - 0x30,0x37,0x31,0x38,0x30,0x37,0x31,0x39, - 0x30,0x37,0x32,0x30,0x30,0x37,0x32,0x31, - 0x30,0x37,0x32,0x32,0x30,0x37,0x32,0x33, - 0x30,0x37,0x32,0x34,0x30,0x37,0x32,0x35, - 0x30,0x37,0x32,0x36,0x30,0x37,0x32,0x37, - 0x30,0x37,0x32,0x38,0x30,0x37,0x32,0x39, - 0x30,0x37,0x33,0x30,0x30,0x37,0x33,0x31, - 0x30,0x37,0x33,0x32,0x30,0x37,0x33,0x33, - 0x30,0x37,0x33,0x34,0x30,0x37,0x33,0x35, - 0x30,0x37,0x33,0x36,0x30,0x37,0x33,0x37, - 0x30,0x37,0x33,0x38,0x30,0x37,0x33,0x39, - 0x30,0x37,0x34,0x30,0x30,0x37,0x34,0x31, - 0x30,0x37,0x34,0x32,0x30,0x37,0x34,0x33, - 0x30,0x37,0x34,0x34,0x30,0x37,0x34,0x35, - 0x30,0x37,0x34,0x36,0x30,0x37,0x34,0x37, - 0x30,0x37,0x34,0x38,0x30,0x37,0x34,0x39, - 0x30,0x37,0x35,0x30,0x30,0x37,0x35,0x31, - 0x30,0x37,0x35,0x32,0x30,0x37,0x35,0x33, - 0x30,0x37,0x35,0x34,0x30,0x37,0x35,0x35, - 0x30,0x37,0x35,0x36,0x30,0x37,0x35,0x37, - 0x30,0x37,0x35,0x38,0x30,0x37,0x35,0x39, - 0x30,0x37,0x36,0x30,0x30,0x37,0x36,0x31, - 0x30,0x37,0x36,0x32,0x30,0x37,0x36,0x33, - 0x30,0x37,0x36,0x34,0x30,0x37,0x36,0x35, - 0x30,0x37,0x36,0x36,0x30,0x37,0x36,0x37, - 0x30,0x37,0x36,0x38,0x30,0x37,0x36,0x39, - 0x30,0x37,0x37,0x30,0x30,0x37,0x37,0x31, - 0x30,0x37,0x37,0x32,0x30,0x37,0x37,0x33, - 0x30,0x37,0x37,0x34,0x30,0x37,0x37,0x35, - 0x30,0x37,0x37,0x36,0x30,0x37,0x37,0x37, - 0x30,0x37,0x37,0x38,0x30,0x37,0x37,0x39, - 0x30,0x37,0x38,0x30,0x30,0x37,0x38,0x31, - 0x30,0x37,0x38,0x32,0x30,0x37,0x38,0x33, - 0x30,0x37,0x38,0x34,0x30,0x37,0x38,0x35, - 0x30,0x37,0x38,0x36,0x30,0x37,0x38,0x37, - 0x30,0x37,0x38,0x38,0x30,0x37,0x38,0x39, - 0x30,0x37,0x39,0x30,0x30,0x37,0x39,0x31, - 0x30,0x37,0x39,0x32,0x30,0x37,0x39,0x33, - 0x30,0x37,0x39,0x34,0x30,0x37,0x39,0x35, - 0x30,0x37,0x39,0x36,0x30,0x37,0x39,0x37, - 0x30,0x37,0x39,0x38,0x30,0x37,0x39,0x39, - 0x30,0x38,0x30,0x30,0x30,0x38,0x30,0x31, - 0x30,0x38,0x30,0x32,0x30,0x38,0x30,0x33, - 0x30,0x38,0x30,0x34,0x30,0x38,0x30,0x35, - 0x30,0x38,0x30,0x36,0x30,0x38,0x30,0x37, - 0x30,0x38,0x30,0x38,0x30,0x38,0x30,0x39, - 0x30,0x38,0x31,0x30,0x30,0x38,0x31,0x31, - 0x30,0x38,0x31,0x32,0x30,0x38,0x31,0x33, - 0x30,0x38,0x31,0x34,0x30,0x38,0x31,0x35, - 0x30,0x38,0x31,0x36,0x30,0x38,0x31,0x37, - 0x30,0x38,0x31,0x38,0x30,0x38,0x31,0x39, - 0x30,0x38,0x32,0x30,0x30,0x38,0x32,0x31, - 0x30,0x38,0x32,0x32,0x30,0x38,0x32,0x33, - 0x30,0x38,0x32,0x34,0x30,0x38,0x32,0x35, - 0x30,0x38,0x32,0x36,0x30,0x38,0x32,0x37, - 0x30,0x38,0x32,0x38,0x30,0x38,0x32,0x39, - 0x30,0x38,0x33,0x30,0x30,0x38,0x33,0x31, - 0x30,0x38,0x33,0x32,0x30,0x38,0x33,0x33, - 0x30,0x38,0x33,0x34,0x30,0x38,0x33,0x35, - 0x30,0x38,0x33,0x36,0x30,0x38,0x33,0x37, - 0x30,0x38,0x33,0x38,0x30,0x38,0x33,0x39, - 0x30,0x38,0x34,0x30,0x30,0x38,0x34,0x31, - 0x30,0x38,0x34,0x32,0x30,0x38,0x34,0x33, - 0x30,0x38,0x34,0x34,0x30,0x38,0x34,0x35, - 0x30,0x38,0x34,0x36,0x30,0x38,0x34,0x37, - 0x30,0x38,0x34,0x38,0x30,0x38,0x34,0x39, - 0x30,0x38,0x35,0x30,0x30,0x38,0x35,0x31, - 0x30,0x38,0x35,0x32,0x30,0x38,0x35,0x33, - 0x30,0x38,0x35,0x34,0x30,0x38,0x35,0x35, - 0x30,0x38,0x35,0x36,0x30,0x38,0x35,0x37, - 0x30,0x38,0x35,0x38,0x30,0x38,0x35,0x39, - 0x30,0x38,0x36,0x30,0x30,0x38,0x36,0x31, - 0x30,0x38,0x36,0x32,0x30,0x38,0x36,0x33, - 0x30,0x38,0x36,0x34,0x30,0x38,0x36,0x35, - 0x30,0x38,0x36,0x36,0x30,0x38,0x36,0x37, - 0x30,0x38,0x36,0x38,0x30,0x38,0x36,0x39, - 0x30,0x38,0x37,0x30,0x30,0x38,0x37,0x31, - 0x30,0x38,0x37,0x32,0x30,0x38,0x37,0x33, - 0x30,0x38,0x37,0x34,0x30,0x38,0x37,0x35, - 0x30,0x38,0x37,0x36,0x30,0x38,0x37,0x37, - 0x30,0x38,0x37,0x38,0x30,0x38,0x37,0x39, - 0x30,0x38,0x38,0x30,0x30,0x38,0x38,0x31, - 0x30,0x38,0x38,0x32,0x30,0x38,0x38,0x33, - 0x30,0x38,0x38,0x34,0x30,0x38,0x38,0x35, - 0x30,0x38,0x38,0x36,0x30,0x38,0x38,0x37, - 0x30,0x38,0x38,0x38,0x30,0x38,0x38,0x39, - 0x30,0x38,0x39,0x30,0x30,0x38,0x39,0x31, - 0x30,0x38,0x39,0x32,0x30,0x38,0x39,0x33, - 0x30,0x38,0x39,0x34,0x30,0x38,0x39,0x35, - 0x30,0x38,0x39,0x36,0x30,0x38,0x39,0x37, - 0x30,0x38,0x39,0x38,0x30,0x38,0x39,0x39, - 0x30,0x39,0x30,0x30,0x30,0x39,0x30,0x31, - 0x30,0x39,0x30,0x32,0x30,0x39,0x30,0x33, - 0x30,0x39,0x30,0x34,0x30,0x39,0x30,0x35, - 0x30,0x39,0x30,0x36,0x30,0x39,0x30,0x37, - 0x30,0x39,0x30,0x38,0x30,0x39,0x30,0x39, - 0x30,0x39,0x31,0x30,0x30,0x39,0x31,0x31, - 0x30,0x39,0x31,0x32,0x30,0x39,0x31,0x33, - 0x30,0x39,0x31,0x34,0x30,0x39,0x31,0x35, - 0x30,0x39,0x31,0x36,0x30,0x39,0x31,0x37, - 0x30,0x39,0x31,0x38,0x30,0x39,0x31,0x39, - 0x30,0x39,0x32,0x30,0x30,0x39,0x32,0x31, - 0x30,0x39,0x32,0x32,0x30,0x39,0x32,0x33, - 0x30,0x39,0x32,0x34,0x30,0x39,0x32,0x35, - 0x30,0x39,0x32,0x36,0x30,0x39,0x32,0x37, - 0x30,0x39,0x32,0x38,0x30,0x39,0x32,0x39, - 0x30,0x39,0x33,0x30,0x30,0x39,0x33,0x31, - 0x30,0x39,0x33,0x32,0x30,0x39,0x33,0x33, - 0x30,0x39,0x33,0x34,0x30,0x39,0x33,0x35, - 0x30,0x39,0x33,0x36,0x30,0x39,0x33,0x37, - 0x30,0x39,0x33,0x38,0x30,0x39,0x33,0x39, - 0x30,0x39,0x34,0x30,0x30,0x39,0x34,0x31, - 0x30,0x39,0x34,0x32,0x30,0x39,0x34,0x33, - 0x30,0x39,0x34,0x34,0x30,0x39,0x34,0x35, - 0x30,0x39,0x34,0x36,0x30,0x39,0x34,0x37, - 0x30,0x39,0x34,0x38,0x30,0x39,0x34,0x39, - 0x30,0x39,0x35,0x30,0x30,0x39,0x35,0x31, - 0x30,0x39,0x35,0x32,0x30,0x39,0x35,0x33, - 0x30,0x39,0x35,0x34,0x30,0x39,0x35,0x35, - 0x30,0x39,0x35,0x36,0x30,0x39,0x35,0x37, - 0x30,0x39,0x35,0x38,0x30,0x39,0x35,0x39, - 0x30,0x39,0x36,0x30,0x30,0x39,0x36,0x31, - 0x30,0x39,0x36,0x32,0x30,0x39,0x36,0x33, - 0x30,0x39,0x36,0x34,0x30,0x39,0x36,0x35, - 0x30,0x39,0x36,0x36,0x30,0x39,0x36,0x37, - 0x30,0x39,0x36,0x38,0x30,0x39,0x36,0x39, - 0x30,0x39,0x37,0x30,0x30,0x39,0x37,0x31, - 0x30,0x39,0x37,0x32,0x30,0x39,0x37,0x33, - 0x30,0x39,0x37,0x34,0x30,0x39,0x37,0x35, - 0x30,0x39,0x37,0x36,0x30,0x39,0x37,0x37, - 0x30,0x39,0x37,0x38,0x30,0x39,0x37,0x39, - 0x30,0x39,0x38,0x30,0x30,0x39,0x38,0x31, - 0x30,0x39,0x38,0x32,0x30,0x39,0x38,0x33, - 0x30,0x39,0x38,0x34,0x30,0x39,0x38,0x35, - 0x30,0x39,0x38,0x36,0x30,0x39,0x38,0x37, - 0x30,0x39,0x38,0x38,0x30,0x39,0x38,0x39, - 0x30,0x39,0x39,0x30,0x30,0x39,0x39,0x31, - 0x30,0x39,0x39,0x32,0x30,0x39,0x39,0x33, - 0x30,0x39,0x39,0x34,0x30,0x39,0x39,0x35, - 0x30,0x39,0x39,0x36,0x30,0x39,0x39,0x37, - 0x30,0x39,0x39,0x38,0x30,0x39,0x39,0x39, - 0x31,0x30,0x30,0x30,0x31,0x30,0x30,0x31, - 0x31,0x30,0x30,0x32,0x31,0x30,0x30,0x33, - 0x31,0x30,0x30,0x34,0x31,0x30,0x30,0x35, - 0x31,0x30,0x30,0x36,0x31,0x30,0x30,0x37, - 0x31,0x30,0x30,0x38,0x31,0x30,0x30,0x39, - 0x31,0x30,0x31,0x30,0x31,0x30,0x31,0x31, - 0x31,0x30,0x31,0x32,0x31,0x30,0x31,0x33, - 0x31,0x30,0x31,0x34,0x31,0x30,0x31,0x35, - 0x31,0x30,0x31,0x36,0x31,0x30,0x31,0x37, - 0x31,0x30,0x31,0x38,0x31,0x30,0x31,0x39, - 0x31,0x30,0x32,0x30,0x31,0x30,0x32,0x31, - 0x31,0x30,0x32,0x32,0x31,0x30,0x32,0x33, - 0x31,0x30,0x32,0x34,0x31,0x30,0x32,0x35, - 0x31,0x30,0x32,0x36,0x31,0x30,0x32,0x37, - 0x31,0x30,0x32,0x38,0x31,0x30,0x32,0x39, - 0x31,0x30,0x33,0x30,0x31,0x30,0x33,0x31, - 0x31,0x30,0x33,0x32,0x31,0x30,0x33,0x33, - 0x31,0x30,0x33,0x34,0x31,0x30,0x33,0x35, - 0x31,0x30,0x33,0x36,0x31,0x30,0x33,0x37, - 0x31,0x30,0x33,0x38,0x31,0x30,0x33,0x39, - 0x31,0x30,0x34,0x30,0x31,0x30,0x34,0x31, - 0x31,0x30,0x34,0x32,0x31,0x30,0x34,0x33, - 0x31,0x30,0x34,0x34,0x31,0x30,0x34,0x35, - 0x31,0x30,0x34,0x36,0x31,0x30,0x34,0x37, - 0x31,0x30,0x34,0x38,0x31,0x30,0x34,0x39, - 0x31,0x30,0x35,0x30,0x31,0x30,0x35,0x31, - 0x31,0x30,0x35,0x32,0x31,0x30,0x35,0x33, - 0x31,0x30,0x35,0x34,0x31,0x30,0x35,0x35, - 0x31,0x30,0x35,0x36,0x31,0x30,0x35,0x37, - 0x31,0x30,0x35,0x38,0x31,0x30,0x35,0x39, - 0x31,0x30,0x36,0x30,0x31,0x30,0x36,0x31, - 0x31,0x30,0x36,0x32,0x31,0x30,0x36,0x33, - 0x31,0x30,0x36,0x34,0x31,0x30,0x36,0x35, - 0x31,0x30,0x36,0x36,0x31,0x30,0x36,0x37, - 0x31,0x30,0x36,0x38,0x31,0x30,0x36,0x39, - 0x31,0x30,0x37,0x30,0x31,0x30,0x37,0x31, - 0x31,0x30,0x37,0x32,0x31,0x30,0x37,0x33, - 0x31,0x30,0x37,0x34,0x31,0x30,0x37,0x35, - 0x31,0x30,0x37,0x36,0x31,0x30,0x37,0x37, - 0x31,0x30,0x37,0x38,0x31,0x30,0x37,0x39, - 0x31,0x30,0x38,0x30,0x31,0x30,0x38,0x31, - 0x31,0x30,0x38,0x32,0x31,0x30,0x38,0x33, - 0x31,0x30,0x38,0x34,0x31,0x30,0x38,0x35, - 0x31,0x30,0x38,0x36,0x31,0x30,0x38,0x37, - 0x31,0x30,0x38,0x38,0x31,0x30,0x38,0x39, - 0x31,0x30,0x39,0x30,0x31,0x30,0x39,0x31, - 0x31,0x30,0x39,0x32,0x31,0x30,0x39,0x33, - 0x31,0x30,0x39,0x34,0x31,0x30,0x39,0x35, - 0x31,0x30,0x39,0x36,0x31,0x30,0x39,0x37, - 0x31,0x30,0x39,0x38,0x31,0x30,0x39,0x39, - 0x31,0x31,0x30,0x30,0x31,0x31,0x30,0x31, - 0x31,0x31,0x30,0x32,0x31,0x31,0x30,0x33, - 0x31,0x31,0x30,0x34,0x31,0x31,0x30,0x35, - 0x31,0x31,0x30,0x36,0x31,0x31,0x30,0x37, - 0x31,0x31,0x30,0x38,0x31,0x31,0x30,0x39, - 0x31,0x31,0x31,0x30,0x31,0x31,0x31,0x31, - 0x31,0x31,0x31,0x32,0x31,0x31,0x31,0x33, - 0x31,0x31,0x31,0x34,0x31,0x31,0x31,0x35, - 0x31,0x31,0x31,0x36,0x31,0x31,0x31,0x37, - 0x31,0x31,0x31,0x38,0x31,0x31,0x31,0x39, - 0x31,0x31,0x32,0x30,0x31,0x31,0x32,0x31, - 0x31,0x31,0x32,0x32,0x31,0x31,0x32,0x33, - 0x31,0x31,0x32,0x34,0x31,0x31,0x32,0x35, - 0x31,0x31,0x32,0x36,0x31,0x31,0x32,0x37, - 0x31,0x31,0x32,0x38,0x31,0x31,0x32,0x39, - 0x31,0x31,0x33,0x30,0x31,0x31,0x33,0x31, - 0x31,0x31,0x33,0x32,0x31,0x31,0x33,0x33, - 0x31,0x31,0x33,0x34,0x31,0x31,0x33,0x35, - 0x31,0x31,0x33,0x36,0x31,0x31,0x33,0x37, - 0x31,0x31,0x33,0x38,0x31,0x31,0x33,0x39, - 0x31,0x31,0x34,0x30,0x31,0x31,0x34,0x31, - 0x31,0x31,0x34,0x32,0x31,0x31,0x34,0x33, - 0x31,0x31,0x34,0x34,0x31,0x31,0x34,0x35, - 0x31,0x31,0x34,0x36,0x31,0x31,0x34,0x37, - 0x31,0x31,0x34,0x38,0x31,0x31,0x34,0x39, - 0x31,0x31,0x35,0x30,0x31,0x31,0x35,0x31, - 0x31,0x31,0x35,0x32,0x31,0x31,0x35,0x33, - 0x31,0x31,0x35,0x34,0x31,0x31,0x35,0x35, - 0x31,0x31,0x35,0x36,0x31,0x31,0x35,0x37, - 0x31,0x31,0x35,0x38,0x31,0x31,0x35,0x39, - 0x31,0x31,0x36,0x30,0x31,0x31,0x36,0x31, - 0x31,0x31,0x36,0x32,0x31,0x31,0x36,0x33, - 0x31,0x31,0x36,0x34,0x31,0x31,0x36,0x35, - 0x31,0x31,0x36,0x36,0x31,0x31,0x36,0x37, - 0x31,0x31,0x36,0x38,0x31,0x31,0x36,0x39, - 0x31,0x31,0x37,0x30,0x31,0x31,0x37,0x31, - 0x31,0x31,0x37,0x32,0x31,0x31,0x37,0x33, - 0x31,0x31,0x37,0x34,0x31,0x31,0x37,0x35, - 0x31,0x31,0x37,0x36,0x31,0x31,0x37,0x37, - 0x31,0x31,0x37,0x38,0x31,0x31,0x37,0x39, - 0x31,0x31,0x38,0x30,0x31,0x31,0x38,0x31, - 0x31,0x31,0x38,0x32,0x31,0x31,0x38,0x33, - 0x31,0x31,0x38,0x34,0x31,0x31,0x38,0x35, - 0x31,0x31,0x38,0x36,0x31,0x31,0x38,0x37, - 0x31,0x31,0x38,0x38,0x31,0x31,0x38,0x39, - 0x31,0x31,0x39,0x30,0x31,0x31,0x39,0x31, - 0x31,0x31,0x39,0x32,0x31,0x31,0x39,0x33, - 0x31,0x31,0x39,0x34,0x31,0x31,0x39,0x35, - 0x31,0x31,0x39,0x36,0x31,0x31,0x39,0x37, - 0x31,0x31,0x39,0x38,0x31,0x31,0x39,0x39, - 0x31,0x32,0x30,0x30,0x31,0x32,0x30,0x31, - 0x31,0x32,0x30,0x32,0x31,0x32,0x30,0x33, - 0x31,0x32,0x30,0x34,0x31,0x32,0x30,0x35, - 0x31,0x32,0x30,0x36,0x31,0x32,0x30,0x37, - 0x31,0x32,0x30,0x38,0x31,0x32,0x30,0x39, - 0x31,0x32,0x31,0x30,0x31,0x32,0x31,0x31, - 0x31,0x32,0x31,0x32,0x31,0x32,0x31,0x33, - 0x31,0x32,0x31,0x34,0x31,0x32,0x31,0x35, - 0x31,0x32,0x31,0x36,0x31,0x32,0x31,0x37, - 0x31,0x32,0x31,0x38,0x31,0x32,0x31,0x39, - 0x31,0x32,0x32,0x30,0x31,0x32,0x32,0x31, - 0x31,0x32,0x32,0x32,0x31,0x32,0x32,0x33, - 0x31,0x32,0x32,0x34,0x31,0x32,0x32,0x35, - 0x31,0x32,0x32,0x36,0x31,0x32,0x32,0x37, - 0x31,0x32,0x32,0x38,0x31,0x32,0x32,0x39, - 0x31,0x32,0x33,0x30,0x31,0x32,0x33,0x31, - 0x31,0x32,0x33,0x32,0x31,0x32,0x33,0x33, - 0x31,0x32,0x33,0x34,0x31,0x32,0x33,0x35, - 0x31,0x32,0x33,0x36,0x31,0x32,0x33,0x37, - 0x31,0x32,0x33,0x38,0x31,0x32,0x33,0x39, - 0x31,0x32,0x34,0x30,0x31,0x32,0x34,0x31, - 0x31,0x32,0x34,0x32,0x31,0x32,0x34,0x33, - 0x31,0x32,0x34,0x34,0x31,0x32,0x34,0x35, - 0x31,0x32,0x34,0x36,0x31,0x32,0x34,0x37, - 0x31,0x32,0x34,0x38,0x31,0x32,0x34,0x39, - 0x31,0x32,0x35,0x30,0x31,0x32,0x35,0x31, - 0x31,0x32,0x35,0x32,0x31,0x32,0x35,0x33, - 0x31,0x32,0x35,0x34,0x31,0x32,0x35,0x35, - 0x31,0x32,0x35,0x36,0x31,0x32,0x35,0x37, - 0x31,0x32,0x35,0x38,0x31,0x32,0x35,0x39, - 0x31,0x32,0x36,0x30,0x31,0x32,0x36,0x31, - 0x31,0x32,0x36,0x32,0x31,0x32,0x36,0x33, - 0x31,0x32,0x36,0x34,0x31,0x32,0x36,0x35, - 0x31,0x32,0x36,0x36,0x31,0x32,0x36,0x37, - 0x31,0x32,0x36,0x38,0x31,0x32,0x36,0x39, - 0x31,0x32,0x37,0x30,0x31,0x32,0x37,0x31, - 0x31,0x32,0x37,0x32,0x31,0x32,0x37,0x33, - 0x31,0x32,0x37,0x34,0x31,0x32,0x37,0x35, - 0x31,0x32,0x37,0x36,0x31,0x32,0x37,0x37, - 0x31,0x32,0x37,0x38,0x31,0x32,0x37,0x39, - 0x31,0x32,0x38,0x30,0x31,0x32,0x38,0x31, - 0x31,0x32,0x38,0x32,0x31,0x32,0x38,0x33, - 0x31,0x32,0x38,0x34,0x31,0x32,0x38,0x35, - 0x31,0x32,0x38,0x36,0x31,0x32,0x38,0x37, - 0x31,0x32,0x38,0x38,0x31,0x32,0x38,0x39, - 0x31,0x32,0x39,0x30,0x31,0x32,0x39,0x31, - 0x31,0x32,0x39,0x32,0x31,0x32,0x39,0x33, - 0x31,0x32,0x39,0x34,0x31,0x32,0x39,0x35, - 0x31,0x32,0x39,0x36,0x31,0x32,0x39,0x37, - 0x31,0x32,0x39,0x38,0x31,0x32,0x39,0x39, - 0x31,0x33,0x30,0x30,0x31,0x33,0x30,0x31, - 0x31,0x33,0x30,0x32,0x31,0x33,0x30,0x33, - 0x31,0x33,0x30,0x34,0x31,0x33,0x30,0x35, - 0x31,0x33,0x30,0x36,0x31,0x33,0x30,0x37, - 0x31,0x33,0x30,0x38,0x31,0x33,0x30,0x39, - 0x31,0x33,0x31,0x30,0x31,0x33,0x31,0x31, - 0x31,0x33,0x31,0x32,0x31,0x33,0x31,0x33, - 0x31,0x33,0x31,0x34,0x31,0x33,0x31,0x35, - 0x31,0x33,0x31,0x36,0x31,0x33,0x31,0x37, - 0x31,0x33,0x31,0x38,0x31,0x33,0x31,0x39, - 0x31,0x33,0x32,0x30,0x31,0x33,0x32,0x31, - 0x31,0x33,0x32,0x32,0x31,0x33,0x32,0x33, - 0x31,0x33,0x32,0x34,0x31,0x33,0x32,0x35, - 0x31,0x33,0x32,0x36,0x31,0x33,0x32,0x37, - 0x31,0x33,0x32,0x38,0x31,0x33,0x32,0x39, - 0x31,0x33,0x33,0x30,0x31,0x33,0x33,0x31, - 0x31,0x33,0x33,0x32,0x31,0x33,0x33,0x33, - 0x31,0x33,0x33,0x34,0x31,0x33,0x33,0x35, - 0x31,0x33,0x33,0x36,0x31,0x33,0x33,0x37, - 0x31,0x33,0x33,0x38,0x31,0x33,0x33,0x39, - 0x31,0x33,0x34,0x30,0x31,0x33,0x34,0x31, - 0x31,0x33,0x34,0x32,0x31,0x33,0x34,0x33, - 0x31,0x33,0x34,0x34,0x31,0x33,0x34,0x35, - 0x31,0x33,0x34,0x36,0x31,0x33,0x34,0x37, - 0x31,0x33,0x34,0x38,0x31,0x33,0x34,0x39, - 0x31,0x33,0x35,0x30,0x31,0x33,0x35,0x31, - 0x31,0x33,0x35,0x32,0x31,0x33,0x35,0x33, - 0x31,0x33,0x35,0x34,0x31,0x33,0x35,0x35, - 0x31,0x33,0x35,0x36,0x31,0x33,0x35,0x37, - 0x31,0x33,0x35,0x38,0x31,0x33,0x35,0x39, - 0x31,0x33,0x36,0x30,0x31,0x33,0x36,0x31, - 0x31,0x33,0x36,0x32,0x31,0x33,0x36,0x33, - 0x31,0x33,0x36,0x34,0x31,0x33,0x36,0x35, - 0x31,0x33,0x36,0x36,0x31,0x33,0x36,0x37, - 0x31,0x33,0x36,0x38,0x31,0x33,0x36,0x39, - 0x31,0x33,0x37,0x30,0x31,0x33,0x37,0x31, - 0x31,0x33,0x37,0x32,0x31,0x33,0x37,0x33, - 0x31,0x33,0x37,0x34,0x31,0x33,0x37,0x35, - 0x31,0x33,0x37,0x36,0x31,0x33,0x37,0x37, - 0x31,0x33,0x37,0x38,0x31,0x33,0x37,0x39, - 0x31,0x33,0x38,0x30,0x31,0x33,0x38,0x31, - 0x31,0x33,0x38,0x32,0x31,0x33,0x38,0x33, - 0x31,0x33,0x38,0x34,0x31,0x33,0x38,0x35, - 0x31,0x33,0x38,0x36,0x31,0x33,0x38,0x37, - 0x31,0x33,0x38,0x38,0x31,0x33,0x38,0x39, - 0x31,0x33,0x39,0x30,0x31,0x33,0x39,0x31, - 0x31,0x33,0x39,0x32,0x31,0x33,0x39,0x33, - 0x31,0x33,0x39,0x34,0x31,0x33,0x39,0x35, - 0x31,0x33,0x39,0x36,0x31,0x33,0x39,0x37, - 0x31,0x33,0x39,0x38,0x31,0x33,0x39,0x39, - 0x31,0x34,0x30,0x30,0x31,0x34,0x30,0x31, - 0x31,0x34,0x30,0x32,0x31,0x34,0x30,0x33, - 0x31,0x34,0x30,0x34,0x31,0x34,0x30,0x35, - 0x31,0x34,0x30,0x36,0x31,0x34,0x30,0x37, - 0x31,0x34,0x30,0x38,0x31,0x34,0x30,0x39, - 0x31,0x34,0x31,0x30,0x31,0x34,0x31,0x31, - 0x31,0x34,0x31,0x32,0x31,0x34,0x31,0x33, - 0x31,0x34,0x31,0x34,0x31,0x34,0x31,0x35, - 0x31,0x34,0x31,0x36,0x31,0x34,0x31,0x37, - 0x31,0x34,0x31,0x38,0x31,0x34,0x31,0x39, - 0x31,0x34,0x32,0x30,0x31,0x34,0x32,0x31, - 0x31,0x34,0x32,0x32,0x31,0x34,0x32,0x33, - 0x31,0x34,0x32,0x34,0x31,0x34,0x32,0x35, - 0x31,0x34,0x32,0x36,0x31,0x34,0x32,0x37, - 0x31,0x34,0x32,0x38,0x31,0x34,0x32,0x39, - 0x31,0x34,0x33,0x30,0x31,0x34,0x33,0x31, - 0x31,0x34,0x33,0x32,0x31,0x34,0x33,0x33, - 0x31,0x34,0x33,0x34,0x31,0x34,0x33,0x35, - 0x31,0x34,0x33,0x36,0x31,0x34,0x33,0x37, - 0x31,0x34,0x33,0x38,0x31,0x34,0x33,0x39, - 0x31,0x34,0x34,0x30,0x31,0x34,0x34,0x31, - 0x31,0x34,0x34,0x32,0x31,0x34,0x34,0x33, - 0x31,0x34,0x34,0x34,0x31,0x34,0x34,0x35, - 0x31,0x34,0x34,0x36,0x31,0x34,0x34,0x37, - 0x31,0x34,0x34,0x38,0x31,0x34,0x34,0x39, - 0x31,0x34,0x35,0x30,0x31,0x34,0x35,0x31, - 0x31,0x34,0x35,0x32,0x31,0x34,0x35,0x33, - 0x31,0x34,0x35,0x34,0x31,0x34,0x35,0x35, - 0x31,0x34,0x35,0x36,0x31,0x34,0x35,0x37, - 0x31,0x34,0x35,0x38,0x31,0x34,0x35,0x39, - 0x31,0x34,0x36,0x30,0x31,0x34,0x36,0x31, - 0x31,0x34,0x36,0x32,0x31,0x34,0x36,0x33, - 0x31,0x34,0x36,0x34,0x31,0x34,0x36,0x35, - 0x31,0x34,0x36,0x36,0x31,0x34,0x36,0x37, - 0x31,0x34,0x36,0x38,0x31,0x34,0x36,0x39, - 0x31,0x34,0x37,0x30,0x31,0x34,0x37,0x31, - 0x31,0x34,0x37,0x32,0x31,0x34,0x37,0x33, - 0x31,0x34,0x37,0x34,0x31,0x34,0x37,0x35, - 0x31,0x34,0x37,0x36,0x31,0x34,0x37,0x37, - 0x31,0x34,0x37,0x38,0x31,0x34,0x37,0x39, - 0x31,0x34,0x38,0x30,0x31,0x34,0x38,0x31, - 0x31,0x34,0x38,0x32,0x31,0x34,0x38,0x33, - 0x31,0x34,0x38,0x34,0x31,0x34,0x38,0x35, - 0x31,0x34,0x38,0x36,0x31,0x34,0x38,0x37, - 0x31,0x34,0x38,0x38,0x31,0x34,0x38,0x39, - 0x31,0x34,0x39,0x30,0x31,0x34,0x39,0x31, - 0x31,0x34,0x39,0x32,0x31,0x34,0x39,0x33, - 0x31,0x34,0x39,0x34,0x31,0x34,0x39,0x35, - 0x31,0x34,0x39,0x36,0x31,0x34,0x39,0x37, - 0x31,0x34,0x39,0x38,0x31,0x34,0x39,0x39, - 0x31,0x35,0x30,0x30,0x31,0x35,0x30,0x31, - 0x31,0x35,0x30,0x32,0x31,0x35,0x30,0x33, - 0x31,0x35,0x30,0x34,0x31,0x35,0x30,0x35, - 0x31,0x35,0x30,0x36,0x31,0x35,0x30,0x37, - 0x31,0x35,0x30,0x38,0x31,0x35,0x30,0x39, - 0x31,0x35,0x31,0x30,0x31,0x35,0x31,0x31, - 0x31,0x35,0x31,0x32,0x31,0x35,0x31,0x33, - 0x31,0x35,0x31,0x34,0x31,0x35,0x31,0x35, - 0x31,0x35,0x31,0x36,0x31,0x35,0x31,0x37, - 0x31,0x35,0x31,0x38,0x31,0x35,0x31,0x39, - 0x31,0x35,0x32,0x30,0x31,0x35,0x32,0x31, - 0x31,0x35,0x32,0x32,0x31,0x35,0x32,0x33, - 0x31,0x35,0x32,0x34,0x31,0x35,0x32,0x35, - 0x31,0x35,0x32,0x36,0x31,0x35,0x32,0x37, - 0x31,0x35,0x32,0x38,0x31,0x35,0x32,0x39, - 0x31,0x35,0x33,0x30,0x31,0x35,0x33,0x31, - 0x31,0x35,0x33,0x32,0x31,0x35,0x33,0x33, - 0x31,0x35,0x33,0x34,0x31,0x35,0x33,0x35, - 0x31,0x35,0x33,0x36,0x31,0x35,0x33,0x37, - 0x31,0x35,0x33,0x38,0x31,0x35,0x33,0x39, - 0x31,0x35,0x34,0x30,0x31,0x35,0x34,0x31, - 0x31,0x35,0x34,0x32,0x31,0x35,0x34,0x33, - 0x31,0x35,0x34,0x34,0x31,0x35,0x34,0x35, - 0x31,0x35,0x34,0x36,0x31,0x35,0x34,0x37, - 0x31,0x35,0x34,0x38,0x31,0x35,0x34,0x39, - 0x31,0x35,0x35,0x30,0x31,0x35,0x35,0x31, - 0x31,0x35,0x35,0x32,0x31,0x35,0x35,0x33, - 0x31,0x35,0x35,0x34,0x31,0x35,0x35,0x35, - 0x31,0x35,0x35,0x36,0x31,0x35,0x35,0x37, - 0x31,0x35,0x35,0x38,0x31,0x35,0x35,0x39, - 0x31,0x35,0x36,0x30,0x31,0x35,0x36,0x31, - 0x31,0x35,0x36,0x32,0x31,0x35,0x36,0x33, - 0x31,0x35,0x36,0x34,0x31,0x35,0x36,0x35, - 0x31,0x35,0x36,0x36,0x31,0x35,0x36,0x37, - 0x31,0x35,0x36,0x38,0x31,0x35,0x36,0x39, - 0x31,0x35,0x37,0x30,0x31,0x35,0x37,0x31, - 0x31,0x35,0x37,0x32,0x31,0x35,0x37,0x33, - 0x31,0x35,0x37,0x34,0x31,0x35,0x37,0x35, - 0x31,0x35,0x37,0x36,0x31,0x35,0x37,0x37, - 0x31,0x35,0x37,0x38,0x31,0x35,0x37,0x39, - 0x31,0x35,0x38,0x30,0x31,0x35,0x38,0x31, - 0x31,0x35,0x38,0x32,0x31,0x35,0x38,0x33, - 0x31,0x35,0x38,0x34,0x31,0x35,0x38,0x35, - 0x31,0x35,0x38,0x36,0x31,0x35,0x38,0x37, - 0x31,0x35,0x38,0x38,0x31,0x35,0x38,0x39, - 0x31,0x35,0x39,0x30,0x31,0x35,0x39,0x31, - 0x31,0x35,0x39,0x32,0x31,0x35,0x39,0x33, - 0x31,0x35,0x39,0x34,0x31,0x35,0x39,0x35, - 0x31,0x35,0x39,0x36,0x31,0x35,0x39,0x37, - 0x31,0x35,0x39,0x38,0x31,0x35,0x39,0x39, - 0x31,0x36,0x30,0x30,0x31,0x36,0x30,0x31, - 0x31,0x36,0x30,0x32,0x31,0x36,0x30,0x33, - 0x31,0x36,0x30,0x34,0x31,0x36,0x30,0x35, - 0x31,0x36,0x30,0x36,0x31,0x36,0x30,0x37, - 0x31,0x36,0x30,0x38,0x31,0x36,0x30,0x39, - 0x31,0x36,0x31,0x30,0x31,0x36,0x31,0x31, - 0x31,0x36,0x31,0x32,0x31,0x36,0x31,0x33, - 0x31,0x36,0x31,0x34,0x31,0x36,0x31,0x35, - 0x31,0x36,0x31,0x36,0x31,0x36,0x31,0x37, - 0x31,0x36,0x31,0x38,0x31,0x36,0x31,0x39, - 0x31,0x36,0x32,0x30,0x31,0x36,0x32,0x31, - 0x31,0x36,0x32,0x32,0x31,0x36,0x32,0x33, - 0x31,0x36,0x32,0x34,0x31,0x36,0x32,0x35, - 0x31,0x36,0x32,0x36,0x31,0x36,0x32,0x37, - 0x31,0x36,0x32,0x38,0x31,0x36,0x32,0x39, - 0x31,0x36,0x33,0x30,0x31,0x36,0x33,0x31, - 0x31,0x36,0x33,0x32,0x31,0x36,0x33,0x33, - 0x31,0x36,0x33,0x34,0x31,0x36,0x33,0x35, - 0x31,0x36,0x33,0x36,0x31,0x36,0x33,0x37, - 0x31,0x36,0x33,0x38,0x31,0x36,0x33,0x39, - 0x31,0x36,0x34,0x30,0x31,0x36,0x34,0x31, - 0x31,0x36,0x34,0x32,0x31,0x36,0x34,0x33, - 0x31,0x36,0x34,0x34,0x31,0x36,0x34,0x35, - 0x31,0x36,0x34,0x36,0x31,0x36,0x34,0x37, - 0x31,0x36,0x34,0x38,0x31,0x36,0x34,0x39, - 0x31,0x36,0x35,0x30,0x31,0x36,0x35,0x31, - 0x31,0x36,0x35,0x32,0x31,0x36,0x35,0x33, - 0x31,0x36,0x35,0x34,0x31,0x36,0x35,0x35, - 0x31,0x36,0x35,0x36,0x31,0x36,0x35,0x37, - 0x31,0x36,0x35,0x38,0x31,0x36,0x35,0x39, - 0x31,0x36,0x36,0x30,0x31,0x36,0x36,0x31, - 0x31,0x36,0x36,0x32,0x31,0x36,0x36,0x33, - 0x31,0x36,0x36,0x34,0x31,0x36,0x36,0x35, - 0x31,0x36,0x36,0x36,0x31,0x36,0x36,0x37, - 0x31,0x36,0x36,0x38,0x31,0x36,0x36,0x39, - 0x31,0x36,0x37,0x30,0x31,0x36,0x37,0x31, - 0x31,0x36,0x37,0x32,0x31,0x36,0x37,0x33, - 0x31,0x36,0x37,0x34,0x31,0x36,0x37,0x35, - 0x31,0x36,0x37,0x36,0x31,0x36,0x37,0x37, - 0x31,0x36,0x37,0x38,0x31,0x36,0x37,0x39, - 0x31,0x36,0x38,0x30,0x31,0x36,0x38,0x31, - 0x31,0x36,0x38,0x32,0x31,0x36,0x38,0x33, - 0x31,0x36,0x38,0x34,0x31,0x36,0x38,0x35, - 0x31,0x36,0x38,0x36,0x31,0x36,0x38,0x37, - 0x31,0x36,0x38,0x38,0x31,0x36,0x38,0x39, - 0x31,0x36,0x39,0x30,0x31,0x36,0x39,0x31, - 0x31,0x36,0x39,0x32,0x31,0x36,0x39,0x33, - 0x31,0x36,0x39,0x34,0x31,0x36,0x39,0x35, - 0x31,0x36,0x39,0x36,0x31,0x36,0x39,0x37, - 0x31,0x36,0x39,0x38,0x31,0x36,0x39,0x39, - 0x31,0x37,0x30,0x30,0x31,0x37,0x30,0x31, - 0x31,0x37,0x30,0x32,0x31,0x37,0x30,0x33, - 0x31,0x37,0x30,0x34,0x31,0x37,0x30,0x35, - 0x31,0x37,0x30,0x36,0x31,0x37,0x30,0x37, - 0x31,0x37,0x30,0x38,0x31,0x37,0x30,0x39, - 0x31,0x37,0x31,0x30,0x31,0x37,0x31,0x31, - 0x31,0x37,0x31,0x32,0x31,0x37,0x31,0x33, - 0x31,0x37,0x31,0x34,0x31,0x37,0x31,0x35, - 0x31,0x37,0x31,0x36,0x31,0x37,0x31,0x37, - 0x31,0x37,0x31,0x38,0x31,0x37,0x31,0x39, - 0x31,0x37,0x32,0x30,0x31,0x37,0x32,0x31, - 0x31,0x37,0x32,0x32,0x31,0x37,0x32,0x33, - 0x31,0x37,0x32,0x34,0x31,0x37,0x32,0x35, - 0x31,0x37,0x32,0x36,0x31,0x37,0x32,0x37, - 0x31,0x37,0x32,0x38,0x31,0x37,0x32,0x39, - 0x31,0x37,0x33,0x30,0x31,0x37,0x33,0x31, - 0x31,0x37,0x33,0x32,0x31,0x37,0x33,0x33, - 0x31,0x37,0x33,0x34,0x31,0x37,0x33,0x35, - 0x31,0x37,0x33,0x36,0x31,0x37,0x33,0x37, - 0x31,0x37,0x33,0x38,0x31,0x37,0x33,0x39, - 0x31,0x37,0x34,0x30,0x31,0x37,0x34,0x31, - 0x31,0x37,0x34,0x32,0x31,0x37,0x34,0x33, - 0x31,0x37,0x34,0x34,0x31,0x37,0x34,0x35, - 0x31,0x37,0x34,0x36,0x31,0x37,0x34,0x37, - 0x31,0x37,0x34,0x38,0x31,0x37,0x34,0x39, - 0x31,0x37,0x35,0x30,0x31,0x37,0x35,0x31, - 0x31,0x37,0x35,0x32,0x31,0x37,0x35,0x33, - 0x31,0x37,0x35,0x34,0x31,0x37,0x35,0x35, - 0x31,0x37,0x35,0x36,0x31,0x37,0x35,0x37, - 0x31,0x37,0x35,0x38,0x31,0x37,0x35,0x39, - 0x31,0x37,0x36,0x30,0x31,0x37,0x36,0x31, - 0x31,0x37,0x36,0x32,0x31,0x37,0x36,0x33, - 0x31,0x37,0x36,0x34,0x31,0x37,0x36,0x35, - 0x31,0x37,0x36,0x36,0x31,0x37,0x36,0x37, - 0x31,0x37,0x36,0x38,0x31,0x37,0x36,0x39, - 0x31,0x37,0x37,0x30,0x31,0x37,0x37,0x31, - 0x31,0x37,0x37,0x32,0x31,0x37,0x37,0x33, - 0x31,0x37,0x37,0x34,0x31,0x37,0x37,0x35, - 0x31,0x37,0x37,0x36,0x31,0x37,0x37,0x37, - 0x31,0x37,0x37,0x38,0x31,0x37,0x37,0x39, - 0x31,0x37,0x38,0x30,0x31,0x37,0x38,0x31, - 0x31,0x37,0x38,0x32,0x31,0x37,0x38,0x33, - 0x31,0x37,0x38,0x34,0x31,0x37,0x38,0x35, - 0x31,0x37,0x38,0x36,0x31,0x37,0x38,0x37, - 0x31,0x37,0x38,0x38,0x31,0x37,0x38,0x39, - 0x31,0x37,0x39,0x30,0x31,0x37,0x39,0x31, - 0x31,0x37,0x39,0x32,0x31,0x37,0x39,0x33, - 0x31,0x37,0x39,0x34,0x31,0x37,0x39,0x35, - 0x31,0x37,0x39,0x36,0x31,0x37,0x39,0x37, - 0x31,0x37,0x39,0x38,0x31,0x37,0x39,0x39, - 0x31,0x38,0x30,0x30,0x31,0x38,0x30,0x31, - 0x31,0x38,0x30,0x32,0x31,0x38,0x30,0x33, - 0x31,0x38,0x30,0x34,0x31,0x38,0x30,0x35, - 0x31,0x38,0x30,0x36,0x31,0x38,0x30,0x37, - 0x31,0x38,0x30,0x38,0x31,0x38,0x30,0x39, - 0x31,0x38,0x31,0x30,0x31,0x38,0x31,0x31, - 0x31,0x38,0x31,0x32,0x31,0x38,0x31,0x33, - 0x31,0x38,0x31,0x34,0x31,0x38,0x31,0x35, - 0x31,0x38,0x31,0x36,0x31,0x38,0x31,0x37, - 0x31,0x38,0x31,0x38,0x31,0x38,0x31,0x39, - 0x31,0x38,0x32,0x30,0x31,0x38,0x32,0x31, - 0x31,0x38,0x32,0x32,0x31,0x38,0x32,0x33, - 0x31,0x38,0x32,0x34,0x31,0x38,0x32,0x35, - 0x31,0x38,0x32,0x36,0x31,0x38,0x32,0x37, - 0x31,0x38,0x32,0x38,0x31,0x38,0x32,0x39, - 0x31,0x38,0x33,0x30,0x31,0x38,0x33,0x31, - 0x31,0x38,0x33,0x32,0x31,0x38,0x33,0x33, - 0x31,0x38,0x33,0x34,0x31,0x38,0x33,0x35, - 0x31,0x38,0x33,0x36,0x31,0x38,0x33,0x37, - 0x31,0x38,0x33,0x38,0x31,0x38,0x33,0x39, - 0x31,0x38,0x34,0x30,0x31,0x38,0x34,0x31, - 0x31,0x38,0x34,0x32,0x31,0x38,0x34,0x33, - 0x31,0x38,0x34,0x34,0x31,0x38,0x34,0x35, - 0x31,0x38,0x34,0x36,0x31,0x38,0x34,0x37, - 0x31,0x38,0x34,0x38,0x31,0x38,0x34,0x39, - 0x31,0x38,0x35,0x30,0x31,0x38,0x35,0x31, - 0x31,0x38,0x35,0x32,0x31,0x38,0x35,0x33, - 0x31,0x38,0x35,0x34,0x31,0x38,0x35,0x35, - 0x31,0x38,0x35,0x36,0x31,0x38,0x35,0x37, - 0x31,0x38,0x35,0x38,0x31,0x38,0x35,0x39, - 0x31,0x38,0x36,0x30,0x31,0x38,0x36,0x31, - 0x31,0x38,0x36,0x32,0x31,0x38,0x36,0x33, - 0x31,0x38,0x36,0x34,0x31,0x38,0x36,0x35, - 0x31,0x38,0x36,0x36,0x31,0x38,0x36,0x37, - 0x31,0x38,0x36,0x38,0x31,0x38,0x36,0x39, - 0x31,0x38,0x37,0x30,0x31,0x38,0x37,0x31, - 0x31,0x38,0x37,0x32,0x31,0x38,0x37,0x33, - 0x31,0x38,0x37,0x34,0x31,0x38,0x37,0x35, - 0x31,0x38,0x37,0x36,0x31,0x38,0x37,0x37, - 0x31,0x38,0x37,0x38,0x31,0x38,0x37,0x39, - 0x31,0x38,0x38,0x30,0x31,0x38,0x38,0x31, - 0x31,0x38,0x38,0x32,0x31,0x38,0x38,0x33, - 0x31,0x38,0x38,0x34,0x31,0x38,0x38,0x35, - 0x31,0x38,0x38,0x36,0x31,0x38,0x38,0x37, - 0x31,0x38,0x38,0x38,0x31,0x38,0x38,0x39, - 0x31,0x38,0x39,0x30,0x31,0x38,0x39,0x31, - 0x31,0x38,0x39,0x32,0x31,0x38,0x39,0x33, - 0x31,0x38,0x39,0x34,0x31,0x38,0x39,0x35, - 0x31,0x38,0x39,0x36,0x31,0x38,0x39,0x37, - 0x31,0x38,0x39,0x38,0x31,0x38,0x39,0x39, - 0x31,0x39,0x30,0x30,0x31,0x39,0x30,0x31, - 0x31,0x39,0x30,0x32,0x31,0x39,0x30,0x33, - 0x31,0x39,0x30,0x34,0x31,0x39,0x30,0x35, - 0x31,0x39,0x30,0x36,0x31,0x39,0x30,0x37, - 0x31,0x39,0x30,0x38,0x31,0x39,0x30,0x39, - 0x31,0x39,0x31,0x30,0x31,0x39,0x31,0x31, - 0x31,0x39,0x31,0x32,0x31,0x39,0x31,0x33, - 0x31,0x39,0x31,0x34,0x31,0x39,0x31,0x35, - 0x31,0x39,0x31,0x36,0x31,0x39,0x31,0x37, - 0x31,0x39,0x31,0x38,0x31,0x39,0x31,0x39, - 0x31,0x39,0x32,0x30,0x31,0x39,0x32,0x31, - 0x31,0x39,0x32,0x32,0x31,0x39,0x32,0x33, - 0x31,0x39,0x32,0x34,0x31,0x39,0x32,0x35, - 0x31,0x39,0x32,0x36,0x31,0x39,0x32,0x37, - 0x31,0x39,0x32,0x38,0x31,0x39,0x32,0x39, - 0x31,0x39,0x33,0x30,0x31,0x39,0x33,0x31, - 0x31,0x39,0x33,0x32,0x31,0x39,0x33,0x33, - 0x31,0x39,0x33,0x34,0x31,0x39,0x33,0x35, - 0x31,0x39,0x33,0x36,0x31,0x39,0x33,0x37, - 0x31,0x39,0x33,0x38,0x31,0x39,0x33,0x39, - 0x31,0x39,0x34,0x30,0x31,0x39,0x34,0x31, - 0x31,0x39,0x34,0x32,0x31,0x39,0x34,0x33, - 0x31,0x39,0x34,0x34,0x31,0x39,0x34,0x35, - 0x31,0x39,0x34,0x36,0x31,0x39,0x34,0x37, - 0x31,0x39,0x34,0x38,0x31,0x39,0x34,0x39, - 0x31,0x39,0x35,0x30,0x31,0x39,0x35,0x31, - 0x31,0x39,0x35,0x32,0x31,0x39,0x35,0x33, - 0x31,0x39,0x35,0x34,0x31,0x39,0x35,0x35, - 0x31,0x39,0x35,0x36,0x31,0x39,0x35,0x37, - 0x31,0x39,0x35,0x38,0x31,0x39,0x35,0x39, - 0x31,0x39,0x36,0x30,0x31,0x39,0x36,0x31, - 0x31,0x39,0x36,0x32,0x31,0x39,0x36,0x33, - 0x31,0x39,0x36,0x34,0x31,0x39,0x36,0x35, - 0x31,0x39,0x36,0x36,0x31,0x39,0x36,0x37, - 0x31,0x39,0x36,0x38,0x31,0x39,0x36,0x39, - 0x31,0x39,0x37,0x30,0x31,0x39,0x37,0x31, - 0x31,0x39,0x37,0x32,0x31,0x39,0x37,0x33, - 0x31,0x39,0x37,0x34,0x31,0x39,0x37,0x35, - 0x31,0x39,0x37,0x36,0x31,0x39,0x37,0x37, - 0x31,0x39,0x37,0x38,0x31,0x39,0x37,0x39, - 0x31,0x39,0x38,0x30,0x31,0x39,0x38,0x31, - 0x31,0x39,0x38,0x32,0x31,0x39,0x38,0x33, - 0x31,0x39,0x38,0x34,0x31,0x39,0x38,0x35, - 0x31,0x39,0x38,0x36,0x31,0x39,0x38,0x37, - 0x31,0x39,0x38,0x38,0x31,0x39,0x38,0x39, - 0x31,0x39,0x39,0x30,0x31,0x39,0x39,0x31, - 0x31,0x39,0x39,0x32,0x31,0x39,0x39,0x33, - 0x31,0x39,0x39,0x34,0x31,0x39,0x39,0x35, - 0x31,0x39,0x39,0x36,0x31,0x39,0x39,0x37, - 0x31,0x39,0x39,0x38,0x31,0x39,0x39,0x39, - 0x32,0x30,0x30,0x30,0x32,0x30,0x30,0x31, - 0x32,0x30,0x30,0x32,0x32,0x30,0x30,0x33, - 0x32,0x30,0x30,0x34,0x32,0x30,0x30,0x35, - 0x32,0x30,0x30,0x36,0x32,0x30,0x30,0x37, - 0x32,0x30,0x30,0x38,0x32,0x30,0x30,0x39, - 0x32,0x30,0x31,0x30,0x32,0x30,0x31,0x31, - 0x32,0x30,0x31,0x32,0x32,0x30,0x31,0x33, - 0x32,0x30,0x31,0x34,0x32,0x30,0x31,0x35, - 0x32,0x30,0x31,0x36,0x32,0x30,0x31,0x37, - 0x32,0x30,0x31,0x38,0x32,0x30,0x31,0x39, - 0x32,0x30,0x32,0x30,0x32,0x30,0x32,0x31, - 0x32,0x30,0x32,0x32,0x32,0x30,0x32,0x33, - 0x32,0x30,0x32,0x34,0x32,0x30,0x32,0x35, - 0x32,0x30,0x32,0x36,0x32,0x30,0x32,0x37, - 0x32,0x30,0x32,0x38,0x32,0x30,0x32,0x39, - 0x32,0x30,0x33,0x30,0x32,0x30,0x33,0x31, - 0x32,0x30,0x33,0x32,0x32,0x30,0x33,0x33, - 0x32,0x30,0x33,0x34,0x32,0x30,0x33,0x35, - 0x32,0x30,0x33,0x36,0x32,0x30,0x33,0x37, - 0x32,0x30,0x33,0x38,0x32,0x30,0x33,0x39, - 0x32,0x30,0x34,0x30,0x32,0x30,0x34,0x31, - 0x32,0x30,0x34,0x32,0x32,0x30,0x34,0x33, - 0x32,0x30,0x34,0x34,0x32,0x30,0x34,0x35, - 0x32,0x30,0x34,0x36,0x32,0x30,0x34,0x37, - 0x32,0x30,0x34,0x38,0x32,0x30,0x34,0x39, - 0x32,0x30,0x35,0x30,0x32,0x30,0x35,0x31, - 0x32,0x30,0x35,0x32,0x32,0x30,0x35,0x33, - 0x32,0x30,0x35,0x34,0x32,0x30,0x35,0x35, - 0x32,0x30,0x35,0x36,0x32,0x30,0x35,0x37, - 0x32,0x30,0x35,0x38,0x32,0x30,0x35,0x39, - 0x32,0x30,0x36,0x30,0x32,0x30,0x36,0x31, - 0x32,0x30,0x36,0x32,0x32,0x30,0x36,0x33, - 0x32,0x30,0x36,0x34,0x32,0x30,0x36,0x35, - 0x32,0x30,0x36,0x36,0x32,0x30,0x36,0x37, - 0x32,0x30,0x36,0x38,0x32,0x30,0x36,0x39, - 0x32,0x30,0x37,0x30,0x32,0x30,0x37,0x31, - 0x32,0x30,0x37,0x32,0x32,0x30,0x37,0x33, - 0x32,0x30,0x37,0x34,0x32,0x30,0x37,0x35, - 0x32,0x30,0x37,0x36,0x32,0x30,0x37,0x37, - 0x32,0x30,0x37,0x38,0x32,0x30,0x37,0x39, - 0x32,0x30,0x38,0x30,0x32,0x30,0x38,0x31, - 0x32,0x30,0x38,0x32,0x32,0x30,0x38,0x33, - 0x32,0x30,0x38,0x34,0x32,0x30,0x38,0x35, - 0x32,0x30,0x38,0x36,0x32,0x30,0x38,0x37, - 0x32,0x30,0x38,0x38,0x32,0x30,0x38,0x39, - 0x32,0x30,0x39,0x30,0x32,0x30,0x39,0x31, - 0x32,0x30,0x39,0x32,0x32,0x30,0x39,0x33, - 0x32,0x30,0x39,0x34,0x32,0x30,0x39,0x35, - 0x32,0x30,0x39,0x36,0x32,0x30,0x39,0x37, - 0x32,0x30,0x39,0x38,0x32,0x30,0x39,0x39, - 0x32,0x31,0x30,0x30,0x32,0x31,0x30,0x31, - 0x32,0x31,0x30,0x32,0x32,0x31,0x30,0x33, - 0x32,0x31,0x30,0x34,0x32,0x31,0x30,0x35, - 0x32,0x31,0x30,0x36,0x32,0x31,0x30,0x37, - 0x32,0x31,0x30,0x38,0x32,0x31,0x30,0x39, - 0x32,0x31,0x31,0x30,0x32,0x31,0x31,0x31, - 0x32,0x31,0x31,0x32,0x32,0x31,0x31,0x33, - 0x32,0x31,0x31,0x34,0x32,0x31,0x31,0x35, - 0x32,0x31,0x31,0x36,0x32,0x31,0x31,0x37, - 0x32,0x31,0x31,0x38,0x32,0x31,0x31,0x39, - 0x32,0x31,0x32,0x30,0x32,0x31,0x32,0x31, - 0x32,0x31,0x32,0x32,0x32,0x31,0x32,0x33, - 0x32,0x31,0x32,0x34,0x32,0x31,0x32,0x35, - 0x32,0x31,0x32,0x36,0x32,0x31,0x32,0x37, - 0x32,0x31,0x32,0x38,0x32,0x31,0x32,0x39, - 0x32,0x31,0x33,0x30,0x32,0x31,0x33,0x31, - 0x32,0x31,0x33,0x32,0x32,0x31,0x33,0x33, - 0x32,0x31,0x33,0x34,0x32,0x31,0x33,0x35, - 0x32,0x31,0x33,0x36,0x32,0x31,0x33,0x37, - 0x32,0x31,0x33,0x38,0x32,0x31,0x33,0x39, - 0x32,0x31,0x34,0x30,0x32,0x31,0x34,0x31, - 0x32,0x31,0x34,0x32,0x32,0x31,0x34,0x33, - 0x32,0x31,0x34,0x34,0x32,0x31,0x34,0x35, - 0x32,0x31,0x34,0x36,0x32,0x31,0x34,0x37, - 0x32,0x31,0x34,0x38,0x32,0x31,0x34,0x39, - 0x32,0x31,0x35,0x30,0x32,0x31,0x35,0x31, - 0x32,0x31,0x35,0x32,0x32,0x31,0x35,0x33, - 0x32,0x31,0x35,0x34,0x32,0x31,0x35,0x35, - 0x32,0x31,0x35,0x36,0x32,0x31,0x35,0x37, - 0x32,0x31,0x35,0x38,0x32,0x31,0x35,0x39, - 0x32,0x31,0x36,0x30,0x32,0x31,0x36,0x31, - 0x32,0x31,0x36,0x32,0x32,0x31,0x36,0x33, - 0x32,0x31,0x36,0x34,0x32,0x31,0x36,0x35, - 0x32,0x31,0x36,0x36,0x32,0x31,0x36,0x37, - 0x32,0x31,0x36,0x38,0x32,0x31,0x36,0x39, - 0x32,0x31,0x37,0x30,0x32,0x31,0x37,0x31, - 0x32,0x31,0x37,0x32,0x32,0x31,0x37,0x33, - 0x32,0x31,0x37,0x34,0x32,0x31,0x37,0x35, - 0x32,0x31,0x37,0x36,0x32,0x31,0x37,0x37, - 0x32,0x31,0x37,0x38,0x32,0x31,0x37,0x39, - 0x32,0x31,0x38,0x30,0x32,0x31,0x38,0x31, - 0x32,0x31,0x38,0x32,0x32,0x31,0x38,0x33, - 0x32,0x31,0x38,0x34,0x32,0x31,0x38,0x35, - 0x32,0x31,0x38,0x36,0x32,0x31,0x38,0x37, - 0x32,0x31,0x38,0x38,0x32,0x31,0x38,0x39, - 0x32,0x31,0x39,0x30,0x32,0x31,0x39,0x31, - 0x32,0x31,0x39,0x32,0x32,0x31,0x39,0x33, - 0x32,0x31,0x39,0x34,0x32,0x31,0x39,0x35, - 0x32,0x31,0x39,0x36,0x32,0x31,0x39,0x37, - 0x32,0x31,0x39,0x38,0x32,0x31,0x39,0x39, - 0x32,0x32,0x30,0x30,0x32,0x32,0x30,0x31, - 0x32,0x32,0x30,0x32,0x32,0x32,0x30,0x33, - 0x32,0x32,0x30,0x34,0x32,0x32,0x30,0x35, - 0x32,0x32,0x30,0x36,0x32,0x32,0x30,0x37, - 0x32,0x32,0x30,0x38,0x32,0x32,0x30,0x39, - 0x32,0x32,0x31,0x30,0x32,0x32,0x31,0x31, - 0x32,0x32,0x31,0x32,0x32,0x32,0x31,0x33, - 0x32,0x32,0x31,0x34,0x32,0x32,0x31,0x35, - 0x32,0x32,0x31,0x36,0x32,0x32,0x31,0x37, - 0x32,0x32,0x31,0x38,0x32,0x32,0x31,0x39, - 0x32,0x32,0x32,0x30,0x32,0x32,0x32,0x31, - 0x32,0x32,0x32,0x32,0x32,0x32,0x32,0x33, - 0x32,0x32,0x32,0x34,0x32,0x32,0x32,0x35, - 0x32,0x32,0x32,0x36,0x32,0x32,0x32,0x37, - 0x32,0x32,0x32,0x38,0x32,0x32,0x32,0x39, - 0x32,0x32,0x33,0x30,0x32,0x32,0x33,0x31, - 0x32,0x32,0x33,0x32,0x32,0x32,0x33,0x33, - 0x32,0x32,0x33,0x34,0x32,0x32,0x33,0x35, - 0x32,0x32,0x33,0x36,0x32,0x32,0x33,0x37, - 0x32,0x32,0x33,0x38,0x32,0x32,0x33,0x39, - 0x32,0x32,0x34,0x30,0x32,0x32,0x34,0x31, - 0x32,0x32,0x34,0x32,0x32,0x32,0x34,0x33, - 0x32,0x32,0x34,0x34,0x32,0x32,0x34,0x35, - 0x32,0x32,0x34,0x36,0x32,0x32,0x34,0x37, - 0x32,0x32,0x34,0x38,0x32,0x32,0x34,0x39, - 0x32,0x32,0x35,0x30,0x32,0x32,0x35,0x31, - 0x32,0x32,0x35,0x32,0x32,0x32,0x35,0x33, - 0x32,0x32,0x35,0x34,0x32,0x32,0x35,0x35, - 0x32,0x32,0x35,0x36,0x32,0x32,0x35,0x37, - 0x32,0x32,0x35,0x38,0x32,0x32,0x35,0x39, - 0x32,0x32,0x36,0x30,0x32,0x32,0x36,0x31, - 0x32,0x32,0x36,0x32,0x32,0x32,0x36,0x33, - 0x32,0x32,0x36,0x34,0x32,0x32,0x36,0x35, - 0x32,0x32,0x36,0x36,0x32,0x32,0x36,0x37, - 0x32,0x32,0x36,0x38,0x32,0x32,0x36,0x39, - 0x32,0x32,0x37,0x30,0x32,0x32,0x37,0x31, - 0x32,0x32,0x37,0x32,0x32,0x32,0x37,0x33, - 0x32,0x32,0x37,0x34,0x32,0x32,0x37,0x35, - 0x32,0x32,0x37,0x36,0x32,0x32,0x37,0x37, - 0x32,0x32,0x37,0x38,0x32,0x32,0x37,0x39, - 0x32,0x32,0x38,0x30,0x32,0x32,0x38,0x31, - 0x32,0x32,0x38,0x32,0x32,0x32,0x38,0x33, - 0x32,0x32,0x38,0x34,0x32,0x32,0x38,0x35, - 0x32,0x32,0x38,0x36,0x32,0x32,0x38,0x37, - 0x32,0x32,0x38,0x38,0x32,0x32,0x38,0x39, - 0x32,0x32,0x39,0x30,0x32,0x32,0x39,0x31, - 0x32,0x32,0x39,0x32,0x32,0x32,0x39,0x33, - 0x32,0x32,0x39,0x34,0x32,0x32,0x39,0x35, - 0x32,0x32,0x39,0x36,0x32,0x32,0x39,0x37, - 0x32,0x32,0x39,0x38,0x32,0x32,0x39,0x39, - 0x32,0x33,0x30,0x30,0x32,0x33,0x30,0x31, - 0x32,0x33,0x30,0x32,0x32,0x33,0x30,0x33, - 0x32,0x33,0x30,0x34,0x32,0x33,0x30,0x35, - 0x32,0x33,0x30,0x36,0x32,0x33,0x30,0x37, - 0x32,0x33,0x30,0x38,0x32,0x33,0x30,0x39, - 0x32,0x33,0x31,0x30,0x32,0x33,0x31,0x31, - 0x32,0x33,0x31,0x32,0x32,0x33,0x31,0x33, - 0x32,0x33,0x31,0x34,0x32,0x33,0x31,0x35, - 0x32,0x33,0x31,0x36,0x32,0x33,0x31,0x37, - 0x32,0x33,0x31,0x38,0x32,0x33,0x31,0x39, - 0x32,0x33,0x32,0x30,0x32,0x33,0x32,0x31, - 0x32,0x33,0x32,0x32,0x32,0x33,0x32,0x33, - 0x32,0x33,0x32,0x34,0x32,0x33,0x32,0x35, - 0x32,0x33,0x32,0x36,0x32,0x33,0x32,0x37, - 0x32,0x33,0x32,0x38,0x32,0x33,0x32,0x39, - 0x32,0x33,0x33,0x30,0x32,0x33,0x33,0x31, - 0x32,0x33,0x33,0x32,0x32,0x33,0x33,0x33, - 0x32,0x33,0x33,0x34,0x32,0x33,0x33,0x35, - 0x32,0x33,0x33,0x36,0x32,0x33,0x33,0x37, - 0x32,0x33,0x33,0x38,0x32,0x33,0x33,0x39, - 0x32,0x33,0x34,0x30,0x32,0x33,0x34,0x31, - 0x32,0x33,0x34,0x32,0x32,0x33,0x34,0x33, - 0x32,0x33,0x34,0x34,0x32,0x33,0x34,0x35, - 0x32,0x33,0x34,0x36,0x32,0x33,0x34,0x37, - 0x32,0x33,0x34,0x38,0x32,0x33,0x34,0x39, - 0x32,0x33,0x35,0x30,0x32,0x33,0x35,0x31, - 0x32,0x33,0x35,0x32,0x32,0x33,0x35,0x33, - 0x32,0x33,0x35,0x34,0x32,0x33,0x35,0x35, - 0x32,0x33,0x35,0x36,0x32,0x33,0x35,0x37, - 0x32,0x33,0x35,0x38,0x32,0x33,0x35,0x39, - 0x32,0x33,0x36,0x30,0x32,0x33,0x36,0x31, - 0x32,0x33,0x36,0x32,0x32,0x33,0x36,0x33, - 0x32,0x33,0x36,0x34,0x32,0x33,0x36,0x35, - 0x32,0x33,0x36,0x36,0x32,0x33,0x36,0x37, - 0x32,0x33,0x36,0x38,0x32,0x33,0x36,0x39, - 0x32,0x33,0x37,0x30,0x32,0x33,0x37,0x31, - 0x32,0x33,0x37,0x32,0x32,0x33,0x37,0x33, - 0x32,0x33,0x37,0x34,0x32,0x33,0x37,0x35, - 0x32,0x33,0x37,0x36,0x32,0x33,0x37,0x37, - 0x32,0x33,0x37,0x38,0x32,0x33,0x37,0x39, - 0x32,0x33,0x38,0x30,0x32,0x33,0x38,0x31, - 0x32,0x33,0x38,0x32,0x32,0x33,0x38,0x33, - 0x32,0x33,0x38,0x34,0x32,0x33,0x38,0x35, - 0x32,0x33,0x38,0x36,0x32,0x33,0x38,0x37, - 0x32,0x33,0x38,0x38,0x32,0x33,0x38,0x39, - 0x32,0x33,0x39,0x30,0x32,0x33,0x39,0x31, - 0x32,0x33,0x39,0x32,0x32,0x33,0x39,0x33, - 0x32,0x33,0x39,0x34,0x32,0x33,0x39,0x35, - 0x32,0x33,0x39,0x36,0x32,0x33,0x39,0x37, - 0x32,0x33,0x39,0x38,0x32,0x33,0x39,0x39, - 0x32,0x34,0x30,0x30,0x32,0x34,0x30,0x31, - 0x32,0x34,0x30,0x32,0x32,0x34,0x30,0x33, - 0x32,0x34,0x30,0x34,0x32,0x34,0x30,0x35, - 0x32,0x34,0x30,0x36,0x32,0x34,0x30,0x37, - 0x32,0x34,0x30,0x38,0x32,0x34,0x30,0x39, - 0x32,0x34,0x31,0x30,0x32,0x34,0x31,0x31, - 0x32,0x34,0x31,0x32,0x32,0x34,0x31,0x33, - 0x32,0x34,0x31,0x34,0x32,0x34,0x31,0x35, - 0x32,0x34,0x31,0x36,0x32,0x34,0x31,0x37, - 0x32,0x34,0x31,0x38,0x32,0x34,0x31,0x39, - 0x32,0x34,0x32,0x30,0x32,0x34,0x32,0x31, - 0x32,0x34,0x32,0x32,0x32,0x34,0x32,0x33, - 0x32,0x34,0x32,0x34,0x32,0x34,0x32,0x35, - 0x32,0x34,0x32,0x36,0x32,0x34,0x32,0x37, - 0x32,0x34,0x32,0x38,0x32,0x34,0x32,0x39, - 0x32,0x34,0x33,0x30,0x32,0x34,0x33,0x31, - 0x32,0x34,0x33,0x32,0x32,0x34,0x33,0x33, - 0x32,0x34,0x33,0x34,0x32,0x34,0x33,0x35, - 0x32,0x34,0x33,0x36,0x32,0x34,0x33,0x37, - 0x32,0x34,0x33,0x38,0x32,0x34,0x33,0x39, - 0x32,0x34,0x34,0x30,0x32,0x34,0x34,0x31, - 0x32,0x34,0x34,0x32,0x32,0x34,0x34,0x33, - 0x32,0x34,0x34,0x34,0x32,0x34,0x34,0x35, - 0x32,0x34,0x34,0x36,0x32,0x34,0x34,0x37, - 0x32,0x34,0x34,0x38,0x32,0x34,0x34,0x39, - 0x32,0x34,0x35,0x30,0x32,0x34,0x35,0x31, - 0x32,0x34,0x35,0x32,0x32,0x34,0x35,0x33, - 0x32,0x34,0x35,0x34,0x32,0x34,0x35,0x35, - 0x32,0x34,0x35,0x36,0x32,0x34,0x35,0x37, - 0x32,0x34,0x35,0x38,0x32,0x34,0x35,0x39, - 0x32,0x34,0x36,0x30,0x32,0x34,0x36,0x31, - 0x32,0x34,0x36,0x32,0x32,0x34,0x36,0x33, - 0x32,0x34,0x36,0x34,0x32,0x34,0x36,0x35, - 0x32,0x34,0x36,0x36,0x32,0x34,0x36,0x37, - 0x32,0x34,0x36,0x38,0x32,0x34,0x36,0x39, - 0x32,0x34,0x37,0x30,0x32,0x34,0x37,0x31, - 0x32,0x34,0x37,0x32,0x32,0x34,0x37,0x33, - 0x32,0x34,0x37,0x34,0x32,0x34,0x37,0x35, - 0x32,0x34,0x37,0x36,0x32,0x34,0x37,0x37, - 0x32,0x34,0x37,0x38,0x32,0x34,0x37,0x39, - 0x32,0x34,0x38,0x30,0x32,0x34,0x38,0x31, - 0x32,0x34,0x38,0x32,0x32,0x34,0x38,0x33, - 0x32,0x34,0x38,0x34,0x32,0x34,0x38,0x35, - 0x32,0x34,0x38,0x36,0x32,0x34,0x38,0x37, - 0x32,0x34,0x38,0x38,0x32,0x34,0x38,0x39, - 0x32,0x34,0x39,0x30,0x32,0x34,0x39,0x31, - 0x32,0x34,0x39,0x32,0x32,0x34,0x39,0x33, - 0x32,0x34,0x39,0x34,0x32,0x34,0x39,0x35, - 0x32,0x34,0x39,0x36,0x32,0x34,0x39,0x37, - 0x32,0x34,0x39,0x38,0x32,0x34,0x39,0x39, - 0x32,0x35,0x30,0x30,0x32,0x35,0x30,0x31, - 0x32,0x35,0x30,0x32,0x32,0x35,0x30,0x33, - 0x32,0x35,0x30,0x34,0x32,0x35,0x30,0x35, - 0x32,0x35,0x30,0x36,0x32,0x35,0x30,0x37, - 0x32,0x35,0x30,0x38,0x32,0x35,0x30,0x39, - 0x32,0x35,0x31,0x30,0x32,0x35,0x31,0x31, - 0x32,0x35,0x31,0x32,0x32,0x35,0x31,0x33, - 0x32,0x35,0x31,0x34,0x32,0x35,0x31,0x35, - 0x32,0x35,0x31,0x36,0x32,0x35,0x31,0x37, - 0x32,0x35,0x31,0x38,0x32,0x35,0x31,0x39, - 0x32,0x35,0x32,0x30,0x32,0x35,0x32,0x31, - 0x32,0x35,0x32,0x32,0x32,0x35,0x32,0x33, - 0x32,0x35,0x32,0x34,0x32,0x35,0x32,0x35, - 0x32,0x35,0x32,0x36,0x32,0x35,0x32,0x37, - 0x32,0x35,0x32,0x38,0x32,0x35,0x32,0x39, - 0x32,0x35,0x33,0x30,0x32,0x35,0x33,0x31, - 0x32,0x35,0x33,0x32,0x32,0x35,0x33,0x33, - 0x32,0x35,0x33,0x34,0x32,0x35,0x33,0x35, - 0x32,0x35,0x33,0x36,0x32,0x35,0x33,0x37, - 0x32,0x35,0x33,0x38,0x32,0x35,0x33,0x39, - 0x32,0x35,0x34,0x30,0x32,0x35,0x34,0x31, - 0x32,0x35,0x34,0x32,0x32,0x35,0x34,0x33, - 0x32,0x35,0x34,0x34,0x32,0x35,0x34,0x35, - 0x32,0x35,0x34,0x36,0x32,0x35,0x34,0x37, - 0x32,0x35,0x34,0x38,0x32,0x35,0x34,0x39, - 0x32,0x35,0x35,0x30,0x32,0x35,0x35,0x31, - 0x32,0x35,0x35,0x32,0x32,0x35,0x35,0x33, - 0x32,0x35,0x35,0x34,0x32,0x35,0x35,0x35, - 0x32,0x35,0x35,0x36,0x32,0x35,0x35,0x37, - 0x32,0x35,0x35,0x38,0x32,0x35,0x35,0x39, - 0x32,0x35,0x36,0x30,0x32,0x35,0x36,0x31, - 0x32,0x35,0x36,0x32,0x32,0x35,0x36,0x33, - 0x32,0x35,0x36,0x34,0x32,0x35,0x36,0x35, - 0x32,0x35,0x36,0x36,0x32,0x35,0x36,0x37, - 0x32,0x35,0x36,0x38,0x32,0x35,0x36,0x39, - 0x32,0x35,0x37,0x30,0x32,0x35,0x37,0x31, - 0x32,0x35,0x37,0x32,0x32,0x35,0x37,0x33, - 0x32,0x35,0x37,0x34,0x32,0x35,0x37,0x35, - 0x32,0x35,0x37,0x36,0x32,0x35,0x37,0x37, - 0x32,0x35,0x37,0x38,0x32,0x35,0x37,0x39, - 0x32,0x35,0x38,0x30,0x32,0x35,0x38,0x31, - 0x32,0x35,0x38,0x32,0x32,0x35,0x38,0x33, - 0x32,0x35,0x38,0x34,0x32,0x35,0x38,0x35, - 0x32,0x35,0x38,0x36,0x32,0x35,0x38,0x37, - 0x32,0x35,0x38,0x38,0x32,0x35,0x38,0x39, - 0x32,0x35,0x39,0x30,0x32,0x35,0x39,0x31, - 0x32,0x35,0x39,0x32,0x32,0x35,0x39,0x33, - 0x32,0x35,0x39,0x34,0x32,0x35,0x39,0x35, - 0x32,0x35,0x39,0x36,0x32,0x35,0x39,0x37, - 0x32,0x35,0x39,0x38,0x32,0x35,0x39,0x39, - 0x32,0x36,0x30,0x30,0x32,0x36,0x30,0x31, - 0x32,0x36,0x30,0x32,0x32,0x36,0x30,0x33, - 0x32,0x36,0x30,0x34,0x32,0x36,0x30,0x35, - 0x32,0x36,0x30,0x36,0x32,0x36,0x30,0x37, - 0x32,0x36,0x30,0x38,0x32,0x36,0x30,0x39, - 0x32,0x36,0x31,0x30,0x32,0x36,0x31,0x31, - 0x32,0x36,0x31,0x32,0x32,0x36,0x31,0x33, - 0x32,0x36,0x31,0x34,0x32,0x36,0x31,0x35, - 0x32,0x36,0x31,0x36,0x32,0x36,0x31,0x37, - 0x32,0x36,0x31,0x38,0x32,0x36,0x31,0x39, - 0x32,0x36,0x32,0x30,0x32,0x36,0x32,0x31, - 0x32,0x36,0x32,0x32,0x32,0x36,0x32,0x33, - 0x32,0x36,0x32,0x34,0x32,0x36,0x32,0x35, - 0x32,0x36,0x32,0x36,0x32,0x36,0x32,0x37, - 0x32,0x36,0x32,0x38,0x32,0x36,0x32,0x39, - 0x32,0x36,0x33,0x30,0x32,0x36,0x33,0x31, - 0x32,0x36,0x33,0x32,0x32,0x36,0x33,0x33, - 0x32,0x36,0x33,0x34,0x32,0x36,0x33,0x35, - 0x32,0x36,0x33,0x36,0x32,0x36,0x33,0x37, - 0x32,0x36,0x33,0x38,0x32,0x36,0x33,0x39, - 0x32,0x36,0x34,0x30,0x32,0x36,0x34,0x31, - 0x32,0x36,0x34,0x32,0x32,0x36,0x34,0x33, - 0x32,0x36,0x34,0x34,0x32,0x36,0x34,0x35, - 0x32,0x36,0x34,0x36,0x32,0x36,0x34,0x37, - 0x32,0x36,0x34,0x38,0x32,0x36,0x34,0x39, - 0x32,0x36,0x35,0x30,0x32,0x36,0x35,0x31, - 0x32,0x36,0x35,0x32,0x32,0x36,0x35,0x33, - 0x32,0x36,0x35,0x34,0x32,0x36,0x35,0x35, - 0x32,0x36,0x35,0x36,0x32,0x36,0x35,0x37, - 0x32,0x36,0x35,0x38,0x32,0x36,0x35,0x39, - 0x32,0x36,0x36,0x30,0x32,0x36,0x36,0x31, - 0x32,0x36,0x36,0x32,0x32,0x36,0x36,0x33, - 0x32,0x36,0x36,0x34,0x32,0x36,0x36,0x35, - 0x32,0x36,0x36,0x36,0x32,0x36,0x36,0x37, - 0x32,0x36,0x36,0x38,0x32,0x36,0x36,0x39, - 0x32,0x36,0x37,0x30,0x32,0x36,0x37,0x31, - 0x32,0x36,0x37,0x32,0x32,0x36,0x37,0x33, - 0x32,0x36,0x37,0x34,0x32,0x36,0x37,0x35, - 0x32,0x36,0x37,0x36,0x32,0x36,0x37,0x37, - 0x32,0x36,0x37,0x38,0x32,0x36,0x37,0x39, - 0x32,0x36,0x38,0x30,0x32,0x36,0x38,0x31, - 0x32,0x36,0x38,0x32,0x32,0x36,0x38,0x33, - 0x32,0x36,0x38,0x34,0x32,0x36,0x38,0x35, - 0x32,0x36,0x38,0x36,0x32,0x36,0x38,0x37, - 0x32,0x36,0x38,0x38,0x32,0x36,0x38,0x39, - 0x32,0x36,0x39,0x30,0x32,0x36,0x39,0x31, - 0x32,0x36,0x39,0x32,0x32,0x36,0x39,0x33, - 0x32,0x36,0x39,0x34,0x32,0x36,0x39,0x35, - 0x32,0x36,0x39,0x36,0x32,0x36,0x39,0x37, - 0x32,0x36,0x39,0x38,0x32,0x36,0x39,0x39, - 0x32,0x37,0x30,0x30,0x32,0x37,0x30,0x31, - 0x32,0x37,0x30,0x32,0x32,0x37,0x30,0x33, - 0x32,0x37,0x30,0x34,0x32,0x37,0x30,0x35, - 0x32,0x37,0x30,0x36,0x32,0x37,0x30,0x37, - 0x32,0x37,0x30,0x38,0x32,0x37,0x30,0x39, - 0x32,0x37,0x31,0x30,0x32,0x37,0x31,0x31, - 0x32,0x37,0x31,0x32,0x32,0x37,0x31,0x33, - 0x32,0x37,0x31,0x34,0x32,0x37,0x31,0x35, - 0x32,0x37,0x31,0x36,0x32,0x37,0x31,0x37, - 0x32,0x37,0x31,0x38,0x32,0x37,0x31,0x39, - 0x32,0x37,0x32,0x30,0x32,0x37,0x32,0x31, - 0x32,0x37,0x32,0x32,0x32,0x37,0x32,0x33, - 0x32,0x37,0x32,0x34,0x32,0x37,0x32,0x35, - 0x32,0x37,0x32,0x36,0x32,0x37,0x32,0x37, - 0x32,0x37,0x32,0x38,0x32,0x37,0x32,0x39, - 0x32,0x37,0x33,0x30,0x32,0x37,0x33,0x31, - 0x32,0x37,0x33,0x32,0x32,0x37,0x33,0x33, - 0x32,0x37,0x33,0x34,0x32,0x37,0x33,0x35, - 0x32,0x37,0x33,0x36,0x32,0x37,0x33,0x37, - 0x32,0x37,0x33,0x38,0x32,0x37,0x33,0x39, - 0x32,0x37,0x34,0x30,0x32,0x37,0x34,0x31, - 0x32,0x37,0x34,0x32,0x32,0x37,0x34,0x33, - 0x32,0x37,0x34,0x34,0x32,0x37,0x34,0x35, - 0x32,0x37,0x34,0x36,0x32,0x37,0x34,0x37, - 0x32,0x37,0x34,0x38,0x32,0x37,0x34,0x39, - 0x32,0x37,0x35,0x30,0x32,0x37,0x35,0x31, - 0x32,0x37,0x35,0x32,0x32,0x37,0x35,0x33, - 0x32,0x37,0x35,0x34,0x32,0x37,0x35,0x35, - 0x32,0x37,0x35,0x36,0x32,0x37,0x35,0x37, - 0x32,0x37,0x35,0x38,0x32,0x37,0x35,0x39, - 0x32,0x37,0x36,0x30,0x32,0x37,0x36,0x31, - 0x32,0x37,0x36,0x32,0x32,0x37,0x36,0x33, - 0x32,0x37,0x36,0x34,0x32,0x37,0x36,0x35, - 0x32,0x37,0x36,0x36,0x32,0x37,0x36,0x37, - 0x32,0x37,0x36,0x38,0x32,0x37,0x36,0x39, - 0x32,0x37,0x37,0x30,0x32,0x37,0x37,0x31, - 0x32,0x37,0x37,0x32,0x32,0x37,0x37,0x33, - 0x32,0x37,0x37,0x34,0x32,0x37,0x37,0x35, - 0x32,0x37,0x37,0x36,0x32,0x37,0x37,0x37, - 0x32,0x37,0x37,0x38,0x32,0x37,0x37,0x39, - 0x32,0x37,0x38,0x30,0x32,0x37,0x38,0x31, - 0x32,0x37,0x38,0x32,0x32,0x37,0x38,0x33, - 0x32,0x37,0x38,0x34,0x32,0x37,0x38,0x35, - 0x32,0x37,0x38,0x36,0x32,0x37,0x38,0x37, - 0x32,0x37,0x38,0x38,0x32,0x37,0x38,0x39, - 0x32,0x37,0x39,0x30,0x32,0x37,0x39,0x31, - 0x32,0x37,0x39,0x32,0x32,0x37,0x39,0x33, - 0x32,0x37,0x39,0x34,0x32,0x37,0x39,0x35, - 0x32,0x37,0x39,0x36,0x32,0x37,0x39,0x37, - 0x32,0x37,0x39,0x38,0x32,0x37,0x39,0x39, - 0x32,0x38,0x30,0x30,0x32,0x38,0x30,0x31, - 0x32,0x38,0x30,0x32,0x32,0x38,0x30,0x33, - 0x32,0x38,0x30,0x34,0x32,0x38,0x30,0x35, - 0x32,0x38,0x30,0x36,0x32,0x38,0x30,0x37, - 0x32,0x38,0x30,0x38,0x32,0x38,0x30,0x39, - 0x32,0x38,0x31,0x30,0x32,0x38,0x31,0x31, - 0x32,0x38,0x31,0x32,0x32,0x38,0x31,0x33, - 0x32,0x38,0x31,0x34,0x32,0x38,0x31,0x35, - 0x32,0x38,0x31,0x36,0x32,0x38,0x31,0x37, - 0x32,0x38,0x31,0x38,0x32,0x38,0x31,0x39, - 0x32,0x38,0x32,0x30,0x32,0x38,0x32,0x31, - 0x32,0x38,0x32,0x32,0x32,0x38,0x32,0x33, - 0x32,0x38,0x32,0x34,0x32,0x38,0x32,0x35, - 0x32,0x38,0x32,0x36,0x32,0x38,0x32,0x37, - 0x32,0x38,0x32,0x38,0x32,0x38,0x32,0x39, - 0x32,0x38,0x33,0x30,0x32,0x38,0x33,0x31, - 0x32,0x38,0x33,0x32,0x32,0x38,0x33,0x33, - 0x32,0x38,0x33,0x34,0x32,0x38,0x33,0x35, - 0x32,0x38,0x33,0x36,0x32,0x38,0x33,0x37, - 0x32,0x38,0x33,0x38,0x32,0x38,0x33,0x39, - 0x32,0x38,0x34,0x30,0x32,0x38,0x34,0x31, - 0x32,0x38,0x34,0x32,0x32,0x38,0x34,0x33, - 0x32,0x38,0x34,0x34,0x32,0x38,0x34,0x35, - 0x32,0x38,0x34,0x36,0x32,0x38,0x34,0x37, - 0x32,0x38,0x34,0x38,0x32,0x38,0x34,0x39, - 0x32,0x38,0x35,0x30,0x32,0x38,0x35,0x31, - 0x32,0x38,0x35,0x32,0x32,0x38,0x35,0x33, - 0x32,0x38,0x35,0x34,0x32,0x38,0x35,0x35, - 0x32,0x38,0x35,0x36,0x32,0x38,0x35,0x37, - 0x32,0x38,0x35,0x38,0x32,0x38,0x35,0x39, - 0x32,0x38,0x36,0x30,0x32,0x38,0x36,0x31, - 0x32,0x38,0x36,0x32,0x32,0x38,0x36,0x33, - 0x32,0x38,0x36,0x34,0x32,0x38,0x36,0x35, - 0x32,0x38,0x36,0x36,0x32,0x38,0x36,0x37, - 0x32,0x38,0x36,0x38,0x32,0x38,0x36,0x39, - 0x32,0x38,0x37,0x30,0x32,0x38,0x37,0x31, - 0x32,0x38,0x37,0x32,0x32,0x38,0x37,0x33, - 0x32,0x38,0x37,0x34,0x32,0x38,0x37,0x35, - 0x32,0x38,0x37,0x36,0x32,0x38,0x37,0x37, - 0x32,0x38,0x37,0x38,0x32,0x38,0x37,0x39, - 0x32,0x38,0x38,0x30,0x32,0x38,0x38,0x31, - 0x32,0x38,0x38,0x32,0x32,0x38,0x38,0x33, - 0x32,0x38,0x38,0x34,0x32,0x38,0x38,0x35, - 0x32,0x38,0x38,0x36,0x32,0x38,0x38,0x37, - 0x32,0x38,0x38,0x38,0x32,0x38,0x38,0x39, - 0x32,0x38,0x39,0x30,0x32,0x38,0x39,0x31, - 0x32,0x38,0x39,0x32,0x32,0x38,0x39,0x33, - 0x32,0x38,0x39,0x34,0x32,0x38,0x39,0x35, - 0x32,0x38,0x39,0x36,0x32,0x38,0x39,0x37, - 0x32,0x38,0x39,0x38,0x32,0x38,0x39,0x39, - 0x32,0x39,0x30,0x30,0x32,0x39,0x30,0x31, - 0x32,0x39,0x30,0x32,0x32,0x39,0x30,0x33, - 0x32,0x39,0x30,0x34,0x32,0x39,0x30,0x35, - 0x32,0x39,0x30,0x36,0x32,0x39,0x30,0x37, - 0x32,0x39,0x30,0x38,0x32,0x39,0x30,0x39, - 0x32,0x39,0x31,0x30,0x32,0x39,0x31,0x31, - 0x32,0x39,0x31,0x32,0x32,0x39,0x31,0x33, - 0x32,0x39,0x31,0x34,0x32,0x39,0x31,0x35, - 0x32,0x39,0x31,0x36,0x32,0x39,0x31,0x37, - 0x32,0x39,0x31,0x38,0x32,0x39,0x31,0x39, - 0x32,0x39,0x32,0x30,0x32,0x39,0x32,0x31, - 0x32,0x39,0x32,0x32,0x32,0x39,0x32,0x33, - 0x32,0x39,0x32,0x34,0x32,0x39,0x32,0x35, - 0x32,0x39,0x32,0x36,0x32,0x39,0x32,0x37, - 0x32,0x39,0x32,0x38,0x32,0x39,0x32,0x39, - 0x32,0x39,0x33,0x30,0x32,0x39,0x33,0x31, - 0x32,0x39,0x33,0x32,0x32,0x39,0x33,0x33, - 0x32,0x39,0x33,0x34,0x32,0x39,0x33,0x35, - 0x32,0x39,0x33,0x36,0x32,0x39,0x33,0x37, - 0x32,0x39,0x33,0x38,0x32,0x39,0x33,0x39, - 0x32,0x39,0x34,0x30,0x32,0x39,0x34,0x31, - 0x32,0x39,0x34,0x32,0x32,0x39,0x34,0x33, - 0x32,0x39,0x34,0x34,0x32,0x39,0x34,0x35, - 0x32,0x39,0x34,0x36,0x32,0x39,0x34,0x37, - 0x32,0x39,0x34,0x38,0x32,0x39,0x34,0x39, - 0x32,0x39,0x35,0x30,0x32,0x39,0x35,0x31, - 0x32,0x39,0x35,0x32,0x32,0x39,0x35,0x33, - 0x32,0x39,0x35,0x34,0x32,0x39,0x35,0x35, - 0x32,0x39,0x35,0x36,0x32,0x39,0x35,0x37, - 0x32,0x39,0x35,0x38,0x32,0x39,0x35,0x39, - 0x32,0x39,0x36,0x30,0x32,0x39,0x36,0x31, - 0x32,0x39,0x36,0x32,0x32,0x39,0x36,0x33, - 0x32,0x39,0x36,0x34,0x32,0x39,0x36,0x35, - 0x32,0x39,0x36,0x36,0x32,0x39,0x36,0x37, - 0x32,0x39,0x36,0x38,0x32,0x39,0x36,0x39, - 0x32,0x39,0x37,0x30,0x32,0x39,0x37,0x31, - 0x32,0x39,0x37,0x32,0x32,0x39,0x37,0x33, - 0x32,0x39,0x37,0x34,0x32,0x39,0x37,0x35, - 0x32,0x39,0x37,0x36,0x32,0x39,0x37,0x37, - 0x32,0x39,0x37,0x38,0x32,0x39,0x37,0x39, - 0x32,0x39,0x38,0x30,0x32,0x39,0x38,0x31, - 0x32,0x39,0x38,0x32,0x32,0x39,0x38,0x33, - 0x32,0x39,0x38,0x34,0x32,0x39,0x38,0x35, - 0x32,0x39,0x38,0x36,0x32,0x39,0x38,0x37, - 0x32,0x39,0x38,0x38,0x32,0x39,0x38,0x39, - 0x32,0x39,0x39,0x30,0x32,0x39,0x39,0x31, - 0x32,0x39,0x39,0x32,0x32,0x39,0x39,0x33, - 0x32,0x39,0x39,0x34,0x32,0x39,0x39,0x35, - 0x32,0x39,0x39,0x36,0x32,0x39,0x39,0x37, - 0x32,0x39,0x39,0x38,0x32,0x39,0x39,0x39, - 0x33,0x30,0x30,0x30,0x33,0x30,0x30,0x31, - 0x33,0x30,0x30,0x32,0x33,0x30,0x30,0x33, - 0x33,0x30,0x30,0x34,0x33,0x30,0x30,0x35, - 0x33,0x30,0x30,0x36,0x33,0x30,0x30,0x37, - 0x33,0x30,0x30,0x38,0x33,0x30,0x30,0x39, - 0x33,0x30,0x31,0x30,0x33,0x30,0x31,0x31, - 0x33,0x30,0x31,0x32,0x33,0x30,0x31,0x33, - 0x33,0x30,0x31,0x34,0x33,0x30,0x31,0x35, - 0x33,0x30,0x31,0x36,0x33,0x30,0x31,0x37, - 0x33,0x30,0x31,0x38,0x33,0x30,0x31,0x39, - 0x33,0x30,0x32,0x30,0x33,0x30,0x32,0x31, - 0x33,0x30,0x32,0x32,0x33,0x30,0x32,0x33, - 0x33,0x30,0x32,0x34,0x33,0x30,0x32,0x35, - 0x33,0x30,0x32,0x36,0x33,0x30,0x32,0x37, - 0x33,0x30,0x32,0x38,0x33,0x30,0x32,0x39, - 0x33,0x30,0x33,0x30,0x33,0x30,0x33,0x31, - 0x33,0x30,0x33,0x32,0x33,0x30,0x33,0x33, - 0x33,0x30,0x33,0x34,0x33,0x30,0x33,0x35, - 0x33,0x30,0x33,0x36,0x33,0x30,0x33,0x37, - 0x33,0x30,0x33,0x38,0x33,0x30,0x33,0x39, - 0x33,0x30,0x34,0x30,0x33,0x30,0x34,0x31, - 0x33,0x30,0x34,0x32,0x33,0x30,0x34,0x33, - 0x33,0x30,0x34,0x34,0x33,0x30,0x34,0x35, - 0x33,0x30,0x34,0x36,0x33,0x30,0x34,0x37, - 0x33,0x30,0x34,0x38,0x33,0x30,0x34,0x39, - 0x33,0x30,0x35,0x30,0x33,0x30,0x35,0x31, - 0x33,0x30,0x35,0x32,0x33,0x30,0x35,0x33, - 0x33,0x30,0x35,0x34,0x33,0x30,0x35,0x35, - 0x33,0x30,0x35,0x36,0x33,0x30,0x35,0x37, - 0x33,0x30,0x35,0x38,0x33,0x30,0x35,0x39, - 0x33,0x30,0x36,0x30,0x33,0x30,0x36,0x31, - 0x33,0x30,0x36,0x32,0x33,0x30,0x36,0x33, - 0x33,0x30,0x36,0x34,0x33,0x30,0x36,0x35, - 0x33,0x30,0x36,0x36,0x33,0x30,0x36,0x37, - 0x33,0x30,0x36,0x38,0x33,0x30,0x36,0x39, - 0x33,0x30,0x37,0x30,0x33,0x30,0x37,0x31, - 0x33,0x30,0x37,0x32,0x33,0x30,0x37,0x33, - 0x33,0x30,0x37,0x34,0x33,0x30,0x37,0x35, - 0x33,0x30,0x37,0x36,0x33,0x30,0x37,0x37, - 0x33,0x30,0x37,0x38,0x33,0x30,0x37,0x39, - 0x33,0x30,0x38,0x30,0x33,0x30,0x38,0x31, - 0x33,0x30,0x38,0x32,0x33,0x30,0x38,0x33, - 0x33,0x30,0x38,0x34,0x33,0x30,0x38,0x35, - 0x33,0x30,0x38,0x36,0x33,0x30,0x38,0x37, - 0x33,0x30,0x38,0x38,0x33,0x30,0x38,0x39, - 0x33,0x30,0x39,0x30,0x33,0x30,0x39,0x31, - 0x33,0x30,0x39,0x32,0x33,0x30,0x39,0x33, - 0x33,0x30,0x39,0x34,0x33,0x30,0x39,0x35, - 0x33,0x30,0x39,0x36,0x33,0x30,0x39,0x37, - 0x33,0x30,0x39,0x38,0x33,0x30,0x39,0x39, - 0x33,0x31,0x30,0x30,0x33,0x31,0x30,0x31, - 0x33,0x31,0x30,0x32,0x33,0x31,0x30,0x33, - 0x33,0x31,0x30,0x34,0x33,0x31,0x30,0x35, - 0x33,0x31,0x30,0x36,0x33,0x31,0x30,0x37, - 0x33,0x31,0x30,0x38,0x33,0x31,0x30,0x39, - 0x33,0x31,0x31,0x30,0x33,0x31,0x31,0x31, - 0x33,0x31,0x31,0x32,0x33,0x31,0x31,0x33, - 0x33,0x31,0x31,0x34,0x33,0x31,0x31,0x35, - 0x33,0x31,0x31,0x36,0x33,0x31,0x31,0x37, - 0x33,0x31,0x31,0x38,0x33,0x31,0x31,0x39, - 0x33,0x31,0x32,0x30,0x33,0x31,0x32,0x31, - 0x33,0x31,0x32,0x32,0x33,0x31,0x32,0x33, - 0x33,0x31,0x32,0x34,0x33,0x31,0x32,0x35, - 0x33,0x31,0x32,0x36,0x33,0x31,0x32,0x37, - 0x33,0x31,0x32,0x38,0x33,0x31,0x32,0x39, - 0x33,0x31,0x33,0x30,0x33,0x31,0x33,0x31, - 0x33,0x31,0x33,0x32,0x33,0x31,0x33,0x33, - 0x33,0x31,0x33,0x34,0x33,0x31,0x33,0x35, - 0x33,0x31,0x33,0x36,0x33,0x31,0x33,0x37, - 0x33,0x31,0x33,0x38,0x33,0x31,0x33,0x39, - 0x33,0x31,0x34,0x30,0x33,0x31,0x34,0x31, - 0x33,0x31,0x34,0x32,0x33,0x31,0x34,0x33, - 0x33,0x31,0x34,0x34,0x33,0x31,0x34,0x35, - 0x33,0x31,0x34,0x36,0x33,0x31,0x34,0x37, - 0x33,0x31,0x34,0x38,0x33,0x31,0x34,0x39, - 0x33,0x31,0x35,0x30,0x33,0x31,0x35,0x31, - 0x33,0x31,0x35,0x32,0x33,0x31,0x35,0x33, - 0x33,0x31,0x35,0x34,0x33,0x31,0x35,0x35, - 0x33,0x31,0x35,0x36,0x33,0x31,0x35,0x37, - 0x33,0x31,0x35,0x38,0x33,0x31,0x35,0x39, - 0x33,0x31,0x36,0x30,0x33,0x31,0x36,0x31, - 0x33,0x31,0x36,0x32,0x33,0x31,0x36,0x33, - 0x33,0x31,0x36,0x34,0x33,0x31,0x36,0x35, - 0x33,0x31,0x36,0x36,0x33,0x31,0x36,0x37, - 0x33,0x31,0x36,0x38,0x33,0x31,0x36,0x39, - 0x33,0x31,0x37,0x30,0x33,0x31,0x37,0x31, - 0x33,0x31,0x37,0x32,0x33,0x31,0x37,0x33, - 0x33,0x31,0x37,0x34,0x33,0x31,0x37,0x35, - 0x33,0x31,0x37,0x36,0x33,0x31,0x37,0x37, - 0x33,0x31,0x37,0x38,0x33,0x31,0x37,0x39, - 0x33,0x31,0x38,0x30,0x33,0x31,0x38,0x31, - 0x33,0x31,0x38,0x32,0x33,0x31,0x38,0x33, - 0x33,0x31,0x38,0x34,0x33,0x31,0x38,0x35, - 0x33,0x31,0x38,0x36,0x33,0x31,0x38,0x37, - 0x33,0x31,0x38,0x38,0x33,0x31,0x38,0x39, - 0x33,0x31,0x39,0x30,0x33,0x31,0x39,0x31, - 0x33,0x31,0x39,0x32,0x33,0x31,0x39,0x33, - 0x33,0x31,0x39,0x34,0x33,0x31,0x39,0x35, - 0x33,0x31,0x39,0x36,0x33,0x31,0x39,0x37, - 0x33,0x31,0x39,0x38,0x33,0x31,0x39,0x39, - 0x33,0x32,0x30,0x30,0x33,0x32,0x30,0x31, - 0x33,0x32,0x30,0x32,0x33,0x32,0x30,0x33, - 0x33,0x32,0x30,0x34,0x33,0x32,0x30,0x35, - 0x33,0x32,0x30,0x36,0x33,0x32,0x30,0x37, - 0x33,0x32,0x30,0x38,0x33,0x32,0x30,0x39, - 0x33,0x32,0x31,0x30,0x33,0x32,0x31,0x31, - 0x33,0x32,0x31,0x32,0x33,0x32,0x31,0x33, - 0x33,0x32,0x31,0x34,0x33,0x32,0x31,0x35, - 0x33,0x32,0x31,0x36,0x33,0x32,0x31,0x37, - 0x33,0x32,0x31,0x38,0x33,0x32,0x31,0x39, - 0x33,0x32,0x32,0x30,0x33,0x32,0x32,0x31, - 0x33,0x32,0x32,0x32,0x33,0x32,0x32,0x33, - 0x33,0x32,0x32,0x34,0x33,0x32,0x32,0x35, - 0x33,0x32,0x32,0x36,0x33,0x32,0x32,0x37, - 0x33,0x32,0x32,0x38,0x33,0x32,0x32,0x39, - 0x33,0x32,0x33,0x30,0x33,0x32,0x33,0x31, - 0x33,0x32,0x33,0x32,0x33,0x32,0x33,0x33, - 0x33,0x32,0x33,0x34,0x33,0x32,0x33,0x35, - 0x33,0x32,0x33,0x36,0x33,0x32,0x33,0x37, - 0x33,0x32,0x33,0x38,0x33,0x32,0x33,0x39, - 0x33,0x32,0x34,0x30,0x33,0x32,0x34,0x31, - 0x33,0x32,0x34,0x32,0x33,0x32,0x34,0x33, - 0x33,0x32,0x34,0x34,0x33,0x32,0x34,0x35, - 0x33,0x32,0x34,0x36,0x33,0x32,0x34,0x37, - 0x33,0x32,0x34,0x38,0x33,0x32,0x34,0x39, - 0x33,0x32,0x35,0x30,0x33,0x32,0x35,0x31, - 0x33,0x32,0x35,0x32,0x33,0x32,0x35,0x33, - 0x33,0x32,0x35,0x34,0x33,0x32,0x35,0x35, - 0x33,0x32,0x35,0x36,0x33,0x32,0x35,0x37, - 0x33,0x32,0x35,0x38,0x33,0x32,0x35,0x39, - 0x33,0x32,0x36,0x30,0x33,0x32,0x36,0x31, - 0x33,0x32,0x36,0x32,0x33,0x32,0x36,0x33, - 0x33,0x32,0x36,0x34,0x33,0x32,0x36,0x35, - 0x33,0x32,0x36,0x36,0x33,0x32,0x36,0x37, - 0x33,0x32,0x36,0x38,0x33,0x32,0x36,0x39, - 0x33,0x32,0x37,0x30,0x33,0x32,0x37,0x31, - 0x33,0x32,0x37,0x32,0x33,0x32,0x37,0x33, - 0x33,0x32,0x37,0x34,0x33,0x32,0x37,0x35, - 0x33,0x32,0x37,0x36,0x33,0x32,0x37,0x37, - 0x33,0x32,0x37,0x38,0x33,0x32,0x37,0x39, - 0x33,0x32,0x38,0x30,0x33,0x32,0x38,0x31, - 0x33,0x32,0x38,0x32,0x33,0x32,0x38,0x33, - 0x33,0x32,0x38,0x34,0x33,0x32,0x38,0x35, - 0x33,0x32,0x38,0x36,0x33,0x32,0x38,0x37, - 0x33,0x32,0x38,0x38,0x33,0x32,0x38,0x39, - 0x33,0x32,0x39,0x30,0x33,0x32,0x39,0x31, - 0x33,0x32,0x39,0x32,0x33,0x32,0x39,0x33, - 0x33,0x32,0x39,0x34,0x33,0x32,0x39,0x35, - 0x33,0x32,0x39,0x36,0x33,0x32,0x39,0x37, - 0x33,0x32,0x39,0x38,0x33,0x32,0x39,0x39, - 0x33,0x33,0x30,0x30,0x33,0x33,0x30,0x31, - 0x33,0x33,0x30,0x32,0x33,0x33,0x30,0x33, - 0x33,0x33,0x30,0x34,0x33,0x33,0x30,0x35, - 0x33,0x33,0x30,0x36,0x33,0x33,0x30,0x37, - 0x33,0x33,0x30,0x38,0x33,0x33,0x30,0x39, - 0x33,0x33,0x31,0x30,0x33,0x33,0x31,0x31, - 0x33,0x33,0x31,0x32,0x33,0x33,0x31,0x33, - 0x33,0x33,0x31,0x34,0x33,0x33,0x31,0x35, - 0x33,0x33,0x31,0x36,0x33,0x33,0x31,0x37, - 0x33,0x33,0x31,0x38,0x33,0x33,0x31,0x39, - 0x33,0x33,0x32,0x30,0x33,0x33,0x32,0x31, - 0x33,0x33,0x32,0x32,0x33,0x33,0x32,0x33, - 0x33,0x33,0x32,0x34,0x33,0x33,0x32,0x35, - 0x33,0x33,0x32,0x36,0x33,0x33,0x32,0x37, - 0x33,0x33,0x32,0x38,0x33,0x33,0x32,0x39, - 0x33,0x33,0x33,0x30,0x33,0x33,0x33,0x31, - 0x33,0x33,0x33,0x32,0x33,0x33,0x33,0x33, - 0x33,0x33,0x33,0x34,0x33,0x33,0x33,0x35, - 0x33,0x33,0x33,0x36,0x33,0x33,0x33,0x37, - 0x33,0x33,0x33,0x38,0x33,0x33,0x33,0x39, - 0x33,0x33,0x34,0x30,0x33,0x33,0x34,0x31, - 0x33,0x33,0x34,0x32,0x33,0x33,0x34,0x33, - 0x33,0x33,0x34,0x34,0x33,0x33,0x34,0x35, - 0x33,0x33,0x34,0x36,0x33,0x33,0x34,0x37, - 0x33,0x33,0x34,0x38,0x33,0x33,0x34,0x39, - 0x33,0x33,0x35,0x30,0x33,0x33,0x35,0x31, - 0x33,0x33,0x35,0x32,0x33,0x33,0x35,0x33, - 0x33,0x33,0x35,0x34,0x33,0x33,0x35,0x35, - 0x33,0x33,0x35,0x36,0x33,0x33,0x35,0x37, - 0x33,0x33,0x35,0x38,0x33,0x33,0x35,0x39, - 0x33,0x33,0x36,0x30,0x33,0x33,0x36,0x31, - 0x33,0x33,0x36,0x32,0x33,0x33,0x36,0x33, - 0x33,0x33,0x36,0x34,0x33,0x33,0x36,0x35, - 0x33,0x33,0x36,0x36,0x33,0x33,0x36,0x37, - 0x33,0x33,0x36,0x38,0x33,0x33,0x36,0x39, - 0x33,0x33,0x37,0x30,0x33,0x33,0x37,0x31, - 0x33,0x33,0x37,0x32,0x33,0x33,0x37,0x33, - 0x33,0x33,0x37,0x34,0x33,0x33,0x37,0x35, - 0x33,0x33,0x37,0x36,0x33,0x33,0x37,0x37, - 0x33,0x33,0x37,0x38,0x33,0x33,0x37,0x39, - 0x33,0x33,0x38,0x30,0x33,0x33,0x38,0x31, - 0x33,0x33,0x38,0x32,0x33,0x33,0x38,0x33, - 0x33,0x33,0x38,0x34,0x33,0x33,0x38,0x35, - 0x33,0x33,0x38,0x36,0x33,0x33,0x38,0x37, - 0x33,0x33,0x38,0x38,0x33,0x33,0x38,0x39, - 0x33,0x33,0x39,0x30,0x33,0x33,0x39,0x31, - 0x33,0x33,0x39,0x32,0x33,0x33,0x39,0x33, - 0x33,0x33,0x39,0x34,0x33,0x33,0x39,0x35, - 0x33,0x33,0x39,0x36,0x33,0x33,0x39,0x37, - 0x33,0x33,0x39,0x38,0x33,0x33,0x39,0x39, - 0x33,0x34,0x30,0x30,0x33,0x34,0x30,0x31, - 0x33,0x34,0x30,0x32,0x33,0x34,0x30,0x33, - 0x33,0x34,0x30,0x34,0x33,0x34,0x30,0x35, - 0x33,0x34,0x30,0x36,0x33,0x34,0x30,0x37, - 0x33,0x34,0x30,0x38,0x33,0x34,0x30,0x39, - 0x33,0x34,0x31,0x30,0x33,0x34,0x31,0x31, - 0x33,0x34,0x31,0x32,0x33,0x34,0x31,0x33, - 0x33,0x34,0x31,0x34,0x33,0x34,0x31,0x35, - 0x33,0x34,0x31,0x36,0x33,0x34,0x31,0x37, - 0x33,0x34,0x31,0x38,0x33,0x34,0x31,0x39, - 0x33,0x34,0x32,0x30,0x33,0x34,0x32,0x31, - 0x33,0x34,0x32,0x32,0x33,0x34,0x32,0x33, - 0x33,0x34,0x32,0x34,0x33,0x34,0x32,0x35, - 0x33,0x34,0x32,0x36,0x33,0x34,0x32,0x37, - 0x33,0x34,0x32,0x38,0x33,0x34,0x32,0x39, - 0x33,0x34,0x33,0x30,0x33,0x34,0x33,0x31, - 0x33,0x34,0x33,0x32,0x33,0x34,0x33,0x33, - 0x33,0x34,0x33,0x34,0x33,0x34,0x33,0x35, - 0x33,0x34,0x33,0x36,0x33,0x34,0x33,0x37, - 0x33,0x34,0x33,0x38,0x33,0x34,0x33,0x39, - 0x33,0x34,0x34,0x30,0x33,0x34,0x34,0x31, - 0x33,0x34,0x34,0x32,0x33,0x34,0x34,0x33, - 0x33,0x34,0x34,0x34,0x33,0x34,0x34,0x35, - 0x33,0x34,0x34,0x36,0x33,0x34,0x34,0x37, - 0x33,0x34,0x34,0x38,0x33,0x34,0x34,0x39, - 0x33,0x34,0x35,0x30,0x33,0x34,0x35,0x31, - 0x33,0x34,0x35,0x32,0x33,0x34,0x35,0x33, - 0x33,0x34,0x35,0x34,0x33,0x34,0x35,0x35, - 0x33,0x34,0x35,0x36,0x33,0x34,0x35,0x37, - 0x33,0x34,0x35,0x38,0x33,0x34,0x35,0x39, - 0x33,0x34,0x36,0x30,0x33,0x34,0x36,0x31, - 0x33,0x34,0x36,0x32,0x33,0x34,0x36,0x33, - 0x33,0x34,0x36,0x34,0x33,0x34,0x36,0x35, - 0x33,0x34,0x36,0x36,0x33,0x34,0x36,0x37, - 0x33,0x34,0x36,0x38,0x33,0x34,0x36,0x39, - 0x33,0x34,0x37,0x30,0x33,0x34,0x37,0x31, - 0x33,0x34,0x37,0x32,0x33,0x34,0x37,0x33, - 0x33,0x34,0x37,0x34,0x33,0x34,0x37,0x35, - 0x33,0x34,0x37,0x36,0x33,0x34,0x37,0x37, - 0x33,0x34,0x37,0x38,0x33,0x34,0x37,0x39, - 0x33,0x34,0x38,0x30,0x33,0x34,0x38,0x31, - 0x33,0x34,0x38,0x32,0x33,0x34,0x38,0x33, - 0x33,0x34,0x38,0x34,0x33,0x34,0x38,0x35, - 0x33,0x34,0x38,0x36,0x33,0x34,0x38,0x37, - 0x33,0x34,0x38,0x38,0x33,0x34,0x38,0x39, - 0x33,0x34,0x39,0x30,0x33,0x34,0x39,0x31, - 0x33,0x34,0x39,0x32,0x33,0x34,0x39,0x33, - 0x33,0x34,0x39,0x34,0x33,0x34,0x39,0x35, - 0x33,0x34,0x39,0x36,0x33,0x34,0x39,0x37, - 0x33,0x34,0x39,0x38,0x33,0x34,0x39,0x39, - 0x33,0x35,0x30,0x30,0x33,0x35,0x30,0x31, - 0x33,0x35,0x30,0x32,0x33,0x35,0x30,0x33, - 0x33,0x35,0x30,0x34,0x33,0x35,0x30,0x35, - 0x33,0x35,0x30,0x36,0x33,0x35,0x30,0x37, - 0x33,0x35,0x30,0x38,0x33,0x35,0x30,0x39, - 0x33,0x35,0x31,0x30,0x33,0x35,0x31,0x31, - 0x33,0x35,0x31,0x32,0x33,0x35,0x31,0x33, - 0x33,0x35,0x31,0x34,0x33,0x35,0x31,0x35, - 0x33,0x35,0x31,0x36,0x33,0x35,0x31,0x37, - 0x33,0x35,0x31,0x38,0x33,0x35,0x31,0x39, - 0x33,0x35,0x32,0x30,0x33,0x35,0x32,0x31, - 0x33,0x35,0x32,0x32,0x33,0x35,0x32,0x33, - 0x33,0x35,0x32,0x34,0x33,0x35,0x32,0x35, - 0x33,0x35,0x32,0x36,0x33,0x35,0x32,0x37, - 0x33,0x35,0x32,0x38,0x33,0x35,0x32,0x39, - 0x33,0x35,0x33,0x30,0x33,0x35,0x33,0x31, - 0x33,0x35,0x33,0x32,0x33,0x35,0x33,0x33, - 0x33,0x35,0x33,0x34,0x33,0x35,0x33,0x35, - 0x33,0x35,0x33,0x36,0x33,0x35,0x33,0x37, - 0x33,0x35,0x33,0x38,0x33,0x35,0x33,0x39, - 0x33,0x35,0x34,0x30,0x33,0x35,0x34,0x31, - 0x33,0x35,0x34,0x32,0x33,0x35,0x34,0x33, - 0x33,0x35,0x34,0x34,0x33,0x35,0x34,0x35, - 0x33,0x35,0x34,0x36,0x33,0x35,0x34,0x37, - 0x33,0x35,0x34,0x38,0x33,0x35,0x34,0x39, - 0x33,0x35,0x35,0x30,0x33,0x35,0x35,0x31, - 0x33,0x35,0x35,0x32,0x33,0x35,0x35,0x33, - 0x33,0x35,0x35,0x34,0x33,0x35,0x35,0x35, - 0x33,0x35,0x35,0x36,0x33,0x35,0x35,0x37, - 0x33,0x35,0x35,0x38,0x33,0x35,0x35,0x39, - 0x33,0x35,0x36,0x30,0x33,0x35,0x36,0x31, - 0x33,0x35,0x36,0x32,0x33,0x35,0x36,0x33, - 0x33,0x35,0x36,0x34,0x33,0x35,0x36,0x35, - 0x33,0x35,0x36,0x36,0x33,0x35,0x36,0x37, - 0x33,0x35,0x36,0x38,0x33,0x35,0x36,0x39, - 0x33,0x35,0x37,0x30,0x33,0x35,0x37,0x31, - 0x33,0x35,0x37,0x32,0x33,0x35,0x37,0x33, - 0x33,0x35,0x37,0x34,0x33,0x35,0x37,0x35, - 0x33,0x35,0x37,0x36,0x33,0x35,0x37,0x37, - 0x33,0x35,0x37,0x38,0x33,0x35,0x37,0x39, - 0x33,0x35,0x38,0x30,0x33,0x35,0x38,0x31, - 0x33,0x35,0x38,0x32,0x33,0x35,0x38,0x33, - 0x33,0x35,0x38,0x34,0x33,0x35,0x38,0x35, - 0x33,0x35,0x38,0x36,0x33,0x35,0x38,0x37, - 0x33,0x35,0x38,0x38,0x33,0x35,0x38,0x39, - 0x33,0x35,0x39,0x30,0x33,0x35,0x39,0x31, - 0x33,0x35,0x39,0x32,0x33,0x35,0x39,0x33, - 0x33,0x35,0x39,0x34,0x33,0x35,0x39,0x35, - 0x33,0x35,0x39,0x36,0x33,0x35,0x39,0x37, - 0x33,0x35,0x39,0x38,0x33,0x35,0x39,0x39, - 0x33,0x36,0x30,0x30,0x33,0x36,0x30,0x31, - 0x33,0x36,0x30,0x32,0x33,0x36,0x30,0x33, - 0x33,0x36,0x30,0x34,0x33,0x36,0x30,0x35, - 0x33,0x36,0x30,0x36,0x33,0x36,0x30,0x37, - 0x33,0x36,0x30,0x38,0x33,0x36,0x30,0x39, - 0x33,0x36,0x31,0x30,0x33,0x36,0x31,0x31, - 0x33,0x36,0x31,0x32,0x33,0x36,0x31,0x33, - 0x33,0x36,0x31,0x34,0x33,0x36,0x31,0x35, - 0x33,0x36,0x31,0x36,0x33,0x36,0x31,0x37, - 0x33,0x36,0x31,0x38,0x33,0x36,0x31,0x39, - 0x33,0x36,0x32,0x30,0x33,0x36,0x32,0x31, - 0x33,0x36,0x32,0x32,0x33,0x36,0x32,0x33, - 0x33,0x36,0x32,0x34,0x33,0x36,0x32,0x35, - 0x33,0x36,0x32,0x36,0x33,0x36,0x32,0x37, - 0x33,0x36,0x32,0x38,0x33,0x36,0x32,0x39, - 0x33,0x36,0x33,0x30,0x33,0x36,0x33,0x31, - 0x33,0x36,0x33,0x32,0x33,0x36,0x33,0x33, - 0x33,0x36,0x33,0x34,0x33,0x36,0x33,0x35, - 0x33,0x36,0x33,0x36,0x33,0x36,0x33,0x37, - 0x33,0x36,0x33,0x38,0x33,0x36,0x33,0x39, - 0x33,0x36,0x34,0x30,0x33,0x36,0x34,0x31, - 0x33,0x36,0x34,0x32,0x33,0x36,0x34,0x33, - 0x33,0x36,0x34,0x34,0x33,0x36,0x34,0x35, - 0x33,0x36,0x34,0x36,0x33,0x36,0x34,0x37, - 0x33,0x36,0x34,0x38,0x33,0x36,0x34,0x39, - 0x33,0x36,0x35,0x30,0x33,0x36,0x35,0x31, - 0x33,0x36,0x35,0x32,0x33,0x36,0x35,0x33, - 0x33,0x36,0x35,0x34,0x33,0x36,0x35,0x35, - 0x33,0x36,0x35,0x36,0x33,0x36,0x35,0x37, - 0x33,0x36,0x35,0x38,0x33,0x36,0x35,0x39, - 0x33,0x36,0x36,0x30,0x33,0x36,0x36,0x31, - 0x33,0x36,0x36,0x32,0x33,0x36,0x36,0x33, - 0x33,0x36,0x36,0x34,0x33,0x36,0x36,0x35, - 0x33,0x36,0x36,0x36,0x33,0x36,0x36,0x37, - 0x33,0x36,0x36,0x38,0x33,0x36,0x36,0x39, - 0x33,0x36,0x37,0x30,0x33,0x36,0x37,0x31, - 0x33,0x36,0x37,0x32,0x33,0x36,0x37,0x33, - 0x33,0x36,0x37,0x34,0x33,0x36,0x37,0x35, - 0x33,0x36,0x37,0x36,0x33,0x36,0x37,0x37, - 0x33,0x36,0x37,0x38,0x33,0x36,0x37,0x39, - 0x33,0x36,0x38,0x30,0x33,0x36,0x38,0x31, - 0x33,0x36,0x38,0x32,0x33,0x36,0x38,0x33, - 0x33,0x36,0x38,0x34,0x33,0x36,0x38,0x35, - 0x33,0x36,0x38,0x36,0x33,0x36,0x38,0x37, - 0x33,0x36,0x38,0x38,0x33,0x36,0x38,0x39, - 0x33,0x36,0x39,0x30,0x33,0x36,0x39,0x31, - 0x33,0x36,0x39,0x32,0x33,0x36,0x39,0x33, - 0x33,0x36,0x39,0x34,0x33,0x36,0x39,0x35, - 0x33,0x36,0x39,0x36,0x33,0x36,0x39,0x37, - 0x33,0x36,0x39,0x38,0x33,0x36,0x39,0x39, - 0x33,0x37,0x30,0x30,0x33,0x37,0x30,0x31, - 0x33,0x37,0x30,0x32,0x33,0x37,0x30,0x33, - 0x33,0x37,0x30,0x34,0x33,0x37,0x30,0x35, - 0x33,0x37,0x30,0x36,0x33,0x37,0x30,0x37, - 0x33,0x37,0x30,0x38,0x33,0x37,0x30,0x39, - 0x33,0x37,0x31,0x30,0x33,0x37,0x31,0x31, - 0x33,0x37,0x31,0x32,0x33,0x37,0x31,0x33, - 0x33,0x37,0x31,0x34,0x33,0x37,0x31,0x35, - 0x33,0x37,0x31,0x36,0x33,0x37,0x31,0x37, - 0x33,0x37,0x31,0x38,0x33,0x37,0x31,0x39, - 0x33,0x37,0x32,0x30,0x33,0x37,0x32,0x31, - 0x33,0x37,0x32,0x32,0x33,0x37,0x32,0x33, - 0x33,0x37,0x32,0x34,0x33,0x37,0x32,0x35, - 0x33,0x37,0x32,0x36,0x33,0x37,0x32,0x37, - 0x33,0x37,0x32,0x38,0x33,0x37,0x32,0x39, - 0x33,0x37,0x33,0x30,0x33,0x37,0x33,0x31, - 0x33,0x37,0x33,0x32,0x33,0x37,0x33,0x33, - 0x33,0x37,0x33,0x34,0x33,0x37,0x33,0x35, - 0x33,0x37,0x33,0x36,0x33,0x37,0x33,0x37, - 0x33,0x37,0x33,0x38,0x33,0x37,0x33,0x39, - 0x33,0x37,0x34,0x30,0x33,0x37,0x34,0x31, - 0x33,0x37,0x34,0x32,0x33,0x37,0x34,0x33, - 0x33,0x37,0x34,0x34,0x33,0x37,0x34,0x35, - 0x33,0x37,0x34,0x36,0x33,0x37,0x34,0x37, - 0x33,0x37,0x34,0x38,0x33,0x37,0x34,0x39, - 0x33,0x37,0x35,0x30,0x33,0x37,0x35,0x31, - 0x33,0x37,0x35,0x32,0x33,0x37,0x35,0x33, - 0x33,0x37,0x35,0x34,0x33,0x37,0x35,0x35, - 0x33,0x37,0x35,0x36,0x33,0x37,0x35,0x37, - 0x33,0x37,0x35,0x38,0x33,0x37,0x35,0x39, - 0x33,0x37,0x36,0x30,0x33,0x37,0x36,0x31, - 0x33,0x37,0x36,0x32,0x33,0x37,0x36,0x33, - 0x33,0x37,0x36,0x34,0x33,0x37,0x36,0x35, - 0x33,0x37,0x36,0x36,0x33,0x37,0x36,0x37, - 0x33,0x37,0x36,0x38,0x33,0x37,0x36,0x39, - 0x33,0x37,0x37,0x30,0x33,0x37,0x37,0x31, - 0x33,0x37,0x37,0x32,0x33,0x37,0x37,0x33, - 0x33,0x37,0x37,0x34,0x33,0x37,0x37,0x35, - 0x33,0x37,0x37,0x36,0x33,0x37,0x37,0x37, - 0x33,0x37,0x37,0x38,0x33,0x37,0x37,0x39, - 0x33,0x37,0x38,0x30,0x33,0x37,0x38,0x31, - 0x33,0x37,0x38,0x32,0x33,0x37,0x38,0x33, - 0x33,0x37,0x38,0x34,0x33,0x37,0x38,0x35, - 0x33,0x37,0x38,0x36,0x33,0x37,0x38,0x37, - 0x33,0x37,0x38,0x38,0x33,0x37,0x38,0x39, - 0x33,0x37,0x39,0x30,0x33,0x37,0x39,0x31, - 0x33,0x37,0x39,0x32,0x33,0x37,0x39,0x33, - 0x33,0x37,0x39,0x34,0x33,0x37,0x39,0x35, - 0x33,0x37,0x39,0x36,0x33,0x37,0x39,0x37, - 0x33,0x37,0x39,0x38,0x33,0x37,0x39,0x39, - 0x33,0x38,0x30,0x30,0x33,0x38,0x30,0x31, - 0x33,0x38,0x30,0x32,0x33,0x38,0x30,0x33, - 0x33,0x38,0x30,0x34,0x33,0x38,0x30,0x35, - 0x33,0x38,0x30,0x36,0x33,0x38,0x30,0x37, - 0x33,0x38,0x30,0x38,0x33,0x38,0x30,0x39, - 0x33,0x38,0x31,0x30,0x33,0x38,0x31,0x31, - 0x33,0x38,0x31,0x32,0x33,0x38,0x31,0x33, - 0x33,0x38,0x31,0x34,0x33,0x38,0x31,0x35, - 0x33,0x38,0x31,0x36,0x33,0x38,0x31,0x37, - 0x33,0x38,0x31,0x38,0x33,0x38,0x31,0x39, - 0x33,0x38,0x32,0x30,0x33,0x38,0x32,0x31, - 0x33,0x38,0x32,0x32,0x33,0x38,0x32,0x33, - 0x33,0x38,0x32,0x34,0x33,0x38,0x32,0x35, - 0x33,0x38,0x32,0x36,0x33,0x38,0x32,0x37, - 0x33,0x38,0x32,0x38,0x33,0x38,0x32,0x39, - 0x33,0x38,0x33,0x30,0x33,0x38,0x33,0x31, - 0x33,0x38,0x33,0x32,0x33,0x38,0x33,0x33, - 0x33,0x38,0x33,0x34,0x33,0x38,0x33,0x35, - 0x33,0x38,0x33,0x36,0x33,0x38,0x33,0x37, - 0x33,0x38,0x33,0x38,0x33,0x38,0x33,0x39, - 0x33,0x38,0x34,0x30,0x33,0x38,0x34,0x31, - 0x33,0x38,0x34,0x32,0x33,0x38,0x34,0x33, - 0x33,0x38,0x34,0x34,0x33,0x38,0x34,0x35, - 0x33,0x38,0x34,0x36,0x33,0x38,0x34,0x37, - 0x33,0x38,0x34,0x38,0x33,0x38,0x34,0x39, - 0x33,0x38,0x35,0x30,0x33,0x38,0x35,0x31, - 0x33,0x38,0x35,0x32,0x33,0x38,0x35,0x33, - 0x33,0x38,0x35,0x34,0x33,0x38,0x35,0x35, - 0x33,0x38,0x35,0x36,0x33,0x38,0x35,0x37, - 0x33,0x38,0x35,0x38,0x33,0x38,0x35,0x39, - 0x33,0x38,0x36,0x30,0x33,0x38,0x36,0x31, - 0x33,0x38,0x36,0x32,0x33,0x38,0x36,0x33, - 0x33,0x38,0x36,0x34,0x33,0x38,0x36,0x35, - 0x33,0x38,0x36,0x36,0x33,0x38,0x36,0x37, - 0x33,0x38,0x36,0x38,0x33,0x38,0x36,0x39, - 0x33,0x38,0x37,0x30,0x33,0x38,0x37,0x31, - 0x33,0x38,0x37,0x32,0x33,0x38,0x37,0x33, - 0x33,0x38,0x37,0x34,0x33,0x38,0x37,0x35, - 0x33,0x38,0x37,0x36,0x33,0x38,0x37,0x37, - 0x33,0x38,0x37,0x38,0x33,0x38,0x37,0x39, - 0x33,0x38,0x38,0x30,0x33,0x38,0x38,0x31, - 0x33,0x38,0x38,0x32,0x33,0x38,0x38,0x33, - 0x33,0x38,0x38,0x34,0x33,0x38,0x38,0x35, - 0x33,0x38,0x38,0x36,0x33,0x38,0x38,0x37, - 0x33,0x38,0x38,0x38,0x33,0x38,0x38,0x39, - 0x33,0x38,0x39,0x30,0x33,0x38,0x39,0x31, - 0x33,0x38,0x39,0x32,0x33,0x38,0x39,0x33, - 0x33,0x38,0x39,0x34,0x33,0x38,0x39,0x35, - 0x33,0x38,0x39,0x36,0x33,0x38,0x39,0x37, - 0x33,0x38,0x39,0x38,0x33,0x38,0x39,0x39, - 0x33,0x39,0x30,0x30,0x33,0x39,0x30,0x31, - 0x33,0x39,0x30,0x32,0x33,0x39,0x30,0x33, - 0x33,0x39,0x30,0x34,0x33,0x39,0x30,0x35, - 0x33,0x39,0x30,0x36,0x33,0x39,0x30,0x37, - 0x33,0x39,0x30,0x38,0x33,0x39,0x30,0x39, - 0x33,0x39,0x31,0x30,0x33,0x39,0x31,0x31, - 0x33,0x39,0x31,0x32,0x33,0x39,0x31,0x33, - 0x33,0x39,0x31,0x34,0x33,0x39,0x31,0x35, - 0x33,0x39,0x31,0x36,0x33,0x39,0x31,0x37, - 0x33,0x39,0x31,0x38,0x33,0x39,0x31,0x39, - 0x33,0x39,0x32,0x30,0x33,0x39,0x32,0x31, - 0x33,0x39,0x32,0x32,0x33,0x39,0x32,0x33, - 0x33,0x39,0x32,0x34,0x33,0x39,0x32,0x35, - 0x33,0x39,0x32,0x36,0x33,0x39,0x32,0x37, - 0x33,0x39,0x32,0x38,0x33,0x39,0x32,0x39, - 0x33,0x39,0x33,0x30,0x33,0x39,0x33,0x31, - 0x33,0x39,0x33,0x32,0x33,0x39,0x33,0x33, - 0x33,0x39,0x33,0x34,0x33,0x39,0x33,0x35, - 0x33,0x39,0x33,0x36,0x33,0x39,0x33,0x37, - 0x33,0x39,0x33,0x38,0x33,0x39,0x33,0x39, - 0x33,0x39,0x34,0x30,0x33,0x39,0x34,0x31, - 0x33,0x39,0x34,0x32,0x33,0x39,0x34,0x33, - 0x33,0x39,0x34,0x34,0x33,0x39,0x34,0x35, - 0x33,0x39,0x34,0x36,0x33,0x39,0x34,0x37, - 0x33,0x39,0x34,0x38,0x33,0x39,0x34,0x39, - 0x33,0x39,0x35,0x30,0x33,0x39,0x35,0x31, - 0x33,0x39,0x35,0x32,0x33,0x39,0x35,0x33, - 0x33,0x39,0x35,0x34,0x33,0x39,0x35,0x35, - 0x33,0x39,0x35,0x36,0x33,0x39,0x35,0x37, - 0x33,0x39,0x35,0x38,0x33,0x39,0x35,0x39, - 0x33,0x39,0x36,0x30,0x33,0x39,0x36,0x31, - 0x33,0x39,0x36,0x32,0x33,0x39,0x36,0x33, - 0x33,0x39,0x36,0x34,0x33,0x39,0x36,0x35, - 0x33,0x39,0x36,0x36,0x33,0x39,0x36,0x37, - 0x33,0x39,0x36,0x38,0x33,0x39,0x36,0x39, - 0x33,0x39,0x37,0x30,0x33,0x39,0x37,0x31, - 0x33,0x39,0x37,0x32,0x33,0x39,0x37,0x33, - 0x33,0x39,0x37,0x34,0x33,0x39,0x37,0x35, - 0x33,0x39,0x37,0x36,0x33,0x39,0x37,0x37, - 0x33,0x39,0x37,0x38,0x33,0x39,0x37,0x39, - 0x33,0x39,0x38,0x30,0x33,0x39,0x38,0x31, - 0x33,0x39,0x38,0x32,0x33,0x39,0x38,0x33, - 0x33,0x39,0x38,0x34,0x33,0x39,0x38,0x35, - 0x33,0x39,0x38,0x36,0x33,0x39,0x38,0x37, - 0x33,0x39,0x38,0x38,0x33,0x39,0x38,0x39, - 0x33,0x39,0x39,0x30,0x33,0x39,0x39,0x31, - 0x33,0x39,0x39,0x32,0x33,0x39,0x39,0x33, - 0x33,0x39,0x39,0x34,0x33,0x39,0x39,0x35, - 0x33,0x39,0x39,0x36,0x33,0x39,0x39,0x37, - 0x33,0x39,0x39,0x38,0x33,0x39,0x39,0x39, - 0x34,0x30,0x30,0x30,0x34,0x30,0x30,0x31, - 0x34,0x30,0x30,0x32,0x34,0x30,0x30,0x33, - 0x34,0x30,0x30,0x34,0x34,0x30,0x30,0x35, - 0x34,0x30,0x30,0x36,0x34,0x30,0x30,0x37, - 0x34,0x30,0x30,0x38,0x34,0x30,0x30,0x39, - 0x34,0x30,0x31,0x30,0x34,0x30,0x31,0x31, - 0x34,0x30,0x31,0x32,0x34,0x30,0x31,0x33, - 0x34,0x30,0x31,0x34,0x34,0x30,0x31,0x35, - 0x34,0x30,0x31,0x36,0x34,0x30,0x31,0x37, - 0x34,0x30,0x31,0x38,0x34,0x30,0x31,0x39, - 0x34,0x30,0x32,0x30,0x34,0x30,0x32,0x31, - 0x34,0x30,0x32,0x32,0x34,0x30,0x32,0x33, - 0x34,0x30,0x32,0x34,0x34,0x30,0x32,0x35, - 0x34,0x30,0x32,0x36,0x34,0x30,0x32,0x37, - 0x34,0x30,0x32,0x38,0x34,0x30,0x32,0x39, - 0x34,0x30,0x33,0x30,0x34,0x30,0x33,0x31, - 0x34,0x30,0x33,0x32,0x34,0x30,0x33,0x33, - 0x34,0x30,0x33,0x34,0x34,0x30,0x33,0x35, - 0x34,0x30,0x33,0x36,0x34,0x30,0x33,0x37, - 0x34,0x30,0x33,0x38,0x34,0x30,0x33,0x39, - 0x34,0x30,0x34,0x30,0x34,0x30,0x34,0x31, - 0x34,0x30,0x34,0x32,0x34,0x30,0x34,0x33, - 0x34,0x30,0x34,0x34,0x34,0x30,0x34,0x35, - 0x34,0x30,0x34,0x36,0x34,0x30,0x34,0x37, - 0x34,0x30,0x34,0x38,0x34,0x30,0x34,0x39, - 0x34,0x30,0x35,0x30,0x34,0x30,0x35,0x31, - 0x34,0x30,0x35,0x32,0x34,0x30,0x35,0x33, - 0x34,0x30,0x35,0x34,0x34,0x30,0x35,0x35, - 0x34,0x30,0x35,0x36,0x34,0x30,0x35,0x37, - 0x34,0x30,0x35,0x38,0x34,0x30,0x35,0x39, - 0x34,0x30,0x36,0x30,0x34,0x30,0x36,0x31, - 0x34,0x30,0x36,0x32,0x34,0x30,0x36,0x33, - 0x34,0x30,0x36,0x34,0x34,0x30,0x36,0x35, - 0x34,0x30,0x36,0x36,0x34,0x30,0x36,0x37, - 0x34,0x30,0x36,0x38,0x34,0x30,0x36,0x39, - 0x34,0x30,0x37,0x30,0x34,0x30,0x37,0x31, - 0x34,0x30,0x37,0x32,0x34,0x30,0x37,0x33, - 0x34,0x30,0x37,0x34,0x34,0x30,0x37,0x35, - 0x34,0x30,0x37,0x36,0x34,0x30,0x37,0x37, - 0x34,0x30,0x37,0x38,0x34,0x30,0x37,0x39, - 0x34,0x30,0x38,0x30,0x34,0x30,0x38,0x31, - 0x34,0x30,0x38,0x32,0x34,0x30,0x38,0x33, - 0x34,0x30,0x38,0x34,0x34,0x30,0x38,0x35, - 0x34,0x30,0x38,0x36,0x34,0x30,0x38,0x37, - 0x34,0x30,0x38,0x38,0x34,0x30,0x38,0x39, - 0x34,0x30,0x39,0x30,0x34,0x30,0x39,0x31, - 0x34,0x30,0x39,0x32,0x34,0x30,0x39,0x33, - 0x34,0x30,0x39,0x34,0x34,0x30,0x39,0x35}; - - static const int32_t gDigitCount[] = { - 1,1,1,1,1,1,1,1, - 1,1,2,2,2,2,2,2, - 2,2,2,2,2,2,2,2, - 2,2,2,2,2,2,2,2, - 2,2,2,2,2,2,2,2, - 2,2,2,2,2,2,2,2, - 2,2,2,2,2,2,2,2, - 2,2,2,2,2,2,2,2, - 2,2,2,2,2,2,2,2, - 2,2,2,2,2,2,2,2, - 2,2,2,2,2,2,2,2, - 2,2,2,2,2,2,2,2, - 2,2,2,2,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 3,3,3,3,3,3,3,3, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4, - 4,4,4,4,4,4,4,4}; - -U_NAMESPACE_BEGIN - - -IntDigitCountRange::IntDigitCountRange(int32_t min, int32_t max) { - fMin = min < 0 ? 0 : min; - fMax = max < fMin ? fMin : max; -} - -int32_t -IntDigitCountRange::pin(int32_t digitCount) const { - return digitCount < fMin ? fMin : (digitCount < fMax ? digitCount : fMax); -} - -int32_t -SmallIntFormatter::estimateDigitCount( - int32_t positiveValue, const IntDigitCountRange &range) { - if (positiveValue >= gMaxFastInt) { - return range.getMax(); - } - return range.pin(gDigitCount[positiveValue]); -} - -UBool -SmallIntFormatter::canFormat( - int32_t positiveValue, const IntDigitCountRange &range) { - return (positiveValue < gMaxFastInt && range.getMin() <= 4); -} - -UnicodeString & -SmallIntFormatter::format( - int32_t smallPositiveValue, - const IntDigitCountRange &range, - UnicodeString &appendTo) { - int32_t digits = range.pin(gDigitCount[smallPositiveValue]); - - // Always emit at least '0' - if (digits == 0) { - return appendTo.append((UChar) 0x30); - } - return appendTo.append(gDigits, ((smallPositiveValue + 1) << 2) - digits, digits); -} - -U_NAMESPACE_END diff --git a/deps/icu-small/source/i18n/smallintformatter.h b/deps/icu-small/source/i18n/smallintformatter.h deleted file mode 100644 index 3373a9c35ffcec..00000000000000 --- a/deps/icu-small/source/i18n/smallintformatter.h +++ /dev/null @@ -1,90 +0,0 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2015, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* smallintformatter.h -* -* created on: 2015jan06 -* created by: Travis Keep -*/ - -#ifndef __SMALLINTFORMATTER_H__ -#define __SMALLINTFORMATTER_H__ - -#include "unicode/uobject.h" -#include "unicode/utypes.h" - -U_NAMESPACE_BEGIN - -class UnicodeString; - -/** - * A representation an acceptable range of digit counts for integers. - */ -class U_I18N_API IntDigitCountRange : public UMemory { -public: - /** - * No constraints: 0 up to INT32_MAX - */ - IntDigitCountRange() : fMin(0), fMax(INT32_MAX) { } - IntDigitCountRange(int32_t min, int32_t max); - int32_t pin(int32_t digitCount) const; - int32_t getMax() const { return fMax; } - int32_t getMin() const { return fMin; } -private: - int32_t fMin; - int32_t fMax; -}; - - -/** - * A formatter for small, positive integers. - */ -class U_I18N_API SmallIntFormatter : public UMemory { -public: - /** - * Estimates the actual digit count needed to format positiveValue - * using the given range of digit counts. - * Returns a value that is at least the actual digit count needed. - * - * @param positiveValue the value to format - * @param range the acceptable range of digit counts. - */ - static int32_t estimateDigitCount( - int32_t positiveValue, const IntDigitCountRange &range); - - /** - * Returns TRUE if this class can format positiveValue using - * the given range of digit counts. - * - * @param positiveValue the value to format - * @param range the acceptable range of digit counts. - */ - static UBool canFormat( - int32_t positiveValue, const IntDigitCountRange &range); - - /** - * Formats positiveValue using the given range of digit counts. - * Always uses standard digits '0' through '9'. Formatted value is - * left padded with '0' as necessary to achieve minimum digit count. - * Does not produce any grouping separators or trailing decimal point. - * Calling format to format a value with a particular digit count range - * when canFormat indicates that the same value and digit count range - * cannot be formatted results in undefined behavior. - * - * @param positiveValue the value to format - * @param range the acceptable range of digit counts. - */ - static UnicodeString &format( - int32_t positiveValue, - const IntDigitCountRange &range, - UnicodeString &appendTo); - -}; - -U_NAMESPACE_END - -#endif // __SMALLINTFORMATTER_H__ diff --git a/deps/icu-small/source/i18n/smpdtfmt.cpp b/deps/icu-small/source/i18n/smpdtfmt.cpp index 27fbbd8f7a9ef5..b1b90882fceead 100644 --- a/deps/icu-small/source/i18n/smpdtfmt.cpp +++ b/deps/icu-small/source/i18n/smpdtfmt.cpp @@ -53,6 +53,7 @@ #include "unicode/vtzone.h" #include "unicode/udisplaycontext.h" #include "unicode/brkiter.h" +#include "unicode/rbnf.h" #include "uresimp.h" #include "olsontz.h" #include "patternprops.h" @@ -72,6 +73,7 @@ #include "cstr.h" #include "dayperiodrules.h" #include "tznames_impl.h" // ZONE_NAME_U16_MAX +#include "number_utypes.h" #if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL) #include @@ -312,57 +314,6 @@ const NumberFormat *SimpleDateFormat::getNumberFormatByIndex( return &(**fSharedNumberFormatters[index]); } -class SimpleDateFormatMutableNFNode { - public: - const NumberFormat *key; - NumberFormat *value; - SimpleDateFormatMutableNFNode() - : key(NULL), value(NULL) { } - ~SimpleDateFormatMutableNFNode() { - delete value; - } - private: - SimpleDateFormatMutableNFNode(const SimpleDateFormatMutableNFNode &); - SimpleDateFormatMutableNFNode &operator=(const SimpleDateFormatMutableNFNode &); -}; - -// Single threaded cache of non const NumberFormats. Designed to be stack -// allocated and used for a single format call. -class SimpleDateFormatMutableNFs : public UMemory { - public: - SimpleDateFormatMutableNFs() { - } - - // Returns a non-const clone of nf which can be safely modified. - // Subsequent calls with same nf will return the same non-const clone. - // This object maintains ownership of all returned non-const - // NumberFormat objects. On memory allocation error returns NULL. - // Caller must check for NULL return value. - NumberFormat *get(const NumberFormat *nf) { - if (nf == NULL) { - return NULL; - } - int32_t idx = 0; - while (nodes[idx].value) { - if (nf == nodes[idx].key) { - return nodes[idx].value; - } - ++idx; - } - U_ASSERT(idx < UDAT_FIELD_COUNT); - nodes[idx].key = nf; - nodes[idx].value = (NumberFormat *) nf->clone(); - return nodes[idx].value; - } - private: - // +1 extra for sentinel. If each field had its own NumberFormat, this - // cache would have to allocate UDAT_FIELD_COUNT mutable versions worst - // case. - SimpleDateFormatMutableNFNode nodes[UDAT_FIELD_COUNT + 1]; - SimpleDateFormatMutableNFs(const SimpleDateFormatMutableNFs &); - SimpleDateFormatMutableNFs &operator=(const SimpleDateFormatMutableNFs &); -}; - //---------------------------------------------------------------------- SimpleDateFormat::~SimpleDateFormat() @@ -374,6 +325,7 @@ SimpleDateFormat::~SimpleDateFormat() if (fTimeZoneFormat) { delete fTimeZoneFormat; } + freeFastNumberFormatters(); #if !UCONFIG_NO_BREAK_ITERATION delete fCapitalizationBrkIter; @@ -659,6 +611,10 @@ SimpleDateFormat& SimpleDateFormat::operator=(const SimpleDateFormat& other) } } + UErrorCode localStatus = U_ZERO_ERROR; + freeFastNumberFormatters(); + initFastNumberFormatters(localStatus); + return *this; } @@ -908,7 +864,8 @@ SimpleDateFormat::initialize(const Locale& locale, fixNumberFormatForDates(*fNumberFormat); //fNumberFormat->setLenient(TRUE); // Java uses a custom DateNumberFormat to format/parse - initNumberFormatters(locale,status); + initNumberFormatters(locale, status); + initFastNumberFormatters(status); } else if (U_SUCCESS(status)) @@ -1023,11 +980,6 @@ SimpleDateFormat::_format(Calendar& cal, UnicodeString& appendTo, int32_t fieldNum = 0; UDisplayContext capitalizationContext = getContext(UDISPCTX_TYPE_CAPITALIZATION, status); - // Create temporary cache of mutable number format objects. This way - // subFormat won't have to clone the const NumberFormat for each field. - // if several fields share the same NumberFormat, which will almost - // always be the case, this is a big save. - SimpleDateFormatMutableNFs mutableNFs; // loop through the pattern string character by character for (int32_t i = 0; i < fPattern.length() && U_SUCCESS(status); ++i) { UChar ch = fPattern[i]; @@ -1035,7 +987,7 @@ SimpleDateFormat::_format(Calendar& cal, UnicodeString& appendTo, // Use subFormat() to format a repeated pattern character // when a different pattern or non-pattern character is seen if (ch != prevCh && count > 0) { - subFormat(appendTo, prevCh, count, capitalizationContext, fieldNum++, handler, *workCal, mutableNFs, status); + subFormat(appendTo, prevCh, count, capitalizationContext, fieldNum++, handler, *workCal, status); count = 0; } if (ch == QUOTE) { @@ -1062,7 +1014,7 @@ SimpleDateFormat::_format(Calendar& cal, UnicodeString& appendTo, // Format the last item in the pattern, if any if (count > 0) { - subFormat(appendTo, prevCh, count, capitalizationContext, fieldNum++, handler, *workCal, mutableNFs, status); + subFormat(appendTo, prevCh, count, capitalizationContext, fieldNum++, handler, *workCal, status); } if (calClone != NULL) { @@ -1257,6 +1209,43 @@ _appendSymbolWithMonthPattern(UnicodeString& dst, int32_t value, const UnicodeSt } //---------------------------------------------------------------------- + +static number::LocalizedNumberFormatter* +createFastFormatter(const DecimalFormat* df, int32_t minInt, int32_t maxInt) { + return new number::LocalizedNumberFormatter( + df->toNumberFormatter() + .integerWidth(number::IntegerWidth::zeroFillTo(minInt).truncateAt(maxInt))); +} + +void SimpleDateFormat::initFastNumberFormatters(UErrorCode& status) { + if (U_FAILURE(status)) { + return; + } + auto* df = dynamic_cast(fNumberFormat); + if (df == nullptr) { + return; + } + fFastNumberFormatters[SMPDTFMT_NF_1x10] = createFastFormatter(df, 1, 10); + fFastNumberFormatters[SMPDTFMT_NF_2x10] = createFastFormatter(df, 2, 10); + fFastNumberFormatters[SMPDTFMT_NF_3x10] = createFastFormatter(df, 3, 10); + fFastNumberFormatters[SMPDTFMT_NF_4x10] = createFastFormatter(df, 4, 10); + fFastNumberFormatters[SMPDTFMT_NF_2x2] = createFastFormatter(df, 2, 2); +} + +void SimpleDateFormat::freeFastNumberFormatters() { + delete fFastNumberFormatters[SMPDTFMT_NF_1x10]; + delete fFastNumberFormatters[SMPDTFMT_NF_2x10]; + delete fFastNumberFormatters[SMPDTFMT_NF_3x10]; + delete fFastNumberFormatters[SMPDTFMT_NF_4x10]; + delete fFastNumberFormatters[SMPDTFMT_NF_2x2]; + fFastNumberFormatters[SMPDTFMT_NF_1x10] = nullptr; + fFastNumberFormatters[SMPDTFMT_NF_2x10] = nullptr; + fFastNumberFormatters[SMPDTFMT_NF_3x10] = nullptr; + fFastNumberFormatters[SMPDTFMT_NF_4x10] = nullptr; + fFastNumberFormatters[SMPDTFMT_NF_2x2] = nullptr; +} + + void SimpleDateFormat::initNumberFormatters(const Locale &locale,UErrorCode &status) { if (U_FAILURE(status)) { @@ -1406,7 +1395,6 @@ SimpleDateFormat::subFormat(UnicodeString &appendTo, int32_t fieldNum, FieldPositionHandler& handler, Calendar& cal, - SimpleDateFormatMutableNFs &mutableNFs, UErrorCode& status) const { if (U_FAILURE(status)) { @@ -1419,7 +1407,7 @@ SimpleDateFormat::subFormat(UnicodeString &appendTo, UDateFormatField patternCharIndex = DateFormatSymbols::getPatternCharIndex(ch); const int32_t maxIntCount = 10; int32_t beginOffset = appendTo.length(); - NumberFormat *currentNumberFormat; + const NumberFormat *currentNumberFormat; DateFormatSymbols::ECapitalizationContextUsageType capContextUsageType = DateFormatSymbols::kCapContextUsageOther; UBool isHebrewCalendar = (uprv_strcmp(cal.getType(),"hebrew") == 0); @@ -1444,9 +1432,9 @@ SimpleDateFormat::subFormat(UnicodeString &appendTo, return; } - currentNumberFormat = mutableNFs.get(getNumberFormatByIndex(patternCharIndex)); + currentNumberFormat = getNumberFormatByIndex(patternCharIndex); if (currentNumberFormat == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; + status = U_INTERNAL_PROGRAM_ERROR; return; } UnicodeString hebr("hebr", 4, US_INV); @@ -1566,18 +1554,15 @@ SimpleDateFormat::subFormat(UnicodeString &appendTo, case UDAT_FRACTIONAL_SECOND_FIELD: // Fractional seconds left-justify { - currentNumberFormat->setMinimumIntegerDigits((count > 3) ? 3 : count); - currentNumberFormat->setMaximumIntegerDigits(maxIntCount); + int32_t minDigits = (count > 3) ? 3 : count; if (count == 1) { value /= 100; } else if (count == 2) { value /= 10; } - FieldPosition p(FieldPosition::DONT_CARE); - currentNumberFormat->format(value, appendTo, p); + zeroPaddingNumber(currentNumberFormat, appendTo, value, minDigits, maxIntCount); if (count > 3) { - currentNumberFormat->setMinimumIntegerDigits(count - 3); - currentNumberFormat->format((int32_t)0, appendTo, p); + zeroPaddingNumber(currentNumberFormat, appendTo, 0, count - 3, maxIntCount); } } break; @@ -1695,100 +1680,101 @@ SimpleDateFormat::subFormat(UnicodeString &appendTo, UnicodeString zoneString(zsbuf, 0, UPRV_LENGTHOF(zsbuf)); const TimeZone& tz = cal.getTimeZone(); UDate date = cal.getTime(status); + const TimeZoneFormat *tzfmt = tzFormat(status); if (U_SUCCESS(status)) { if (patternCharIndex == UDAT_TIMEZONE_FIELD) { if (count < 4) { // "z", "zz", "zzz" - tzFormat()->format(UTZFMT_STYLE_SPECIFIC_SHORT, tz, date, zoneString); + tzfmt->format(UTZFMT_STYLE_SPECIFIC_SHORT, tz, date, zoneString); capContextUsageType = DateFormatSymbols::kCapContextUsageMetazoneShort; } else { // "zzzz" or longer - tzFormat()->format(UTZFMT_STYLE_SPECIFIC_LONG, tz, date, zoneString); + tzfmt->format(UTZFMT_STYLE_SPECIFIC_LONG, tz, date, zoneString); capContextUsageType = DateFormatSymbols::kCapContextUsageMetazoneLong; } } else if (patternCharIndex == UDAT_TIMEZONE_RFC_FIELD) { if (count < 4) { // "Z" - tzFormat()->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL, tz, date, zoneString); + tzfmt->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL, tz, date, zoneString); } else if (count == 5) { // "ZZZZZ" - tzFormat()->format(UTZFMT_STYLE_ISO_EXTENDED_FULL, tz, date, zoneString); + tzfmt->format(UTZFMT_STYLE_ISO_EXTENDED_FULL, tz, date, zoneString); } else { // "ZZ", "ZZZ", "ZZZZ" - tzFormat()->format(UTZFMT_STYLE_LOCALIZED_GMT, tz, date, zoneString); + tzfmt->format(UTZFMT_STYLE_LOCALIZED_GMT, tz, date, zoneString); } } else if (patternCharIndex == UDAT_TIMEZONE_GENERIC_FIELD) { if (count == 1) { // "v" - tzFormat()->format(UTZFMT_STYLE_GENERIC_SHORT, tz, date, zoneString); + tzfmt->format(UTZFMT_STYLE_GENERIC_SHORT, tz, date, zoneString); capContextUsageType = DateFormatSymbols::kCapContextUsageMetazoneShort; } else if (count == 4) { // "vvvv" - tzFormat()->format(UTZFMT_STYLE_GENERIC_LONG, tz, date, zoneString); + tzfmt->format(UTZFMT_STYLE_GENERIC_LONG, tz, date, zoneString); capContextUsageType = DateFormatSymbols::kCapContextUsageMetazoneLong; } } else if (patternCharIndex == UDAT_TIMEZONE_SPECIAL_FIELD) { if (count == 1) { // "V" - tzFormat()->format(UTZFMT_STYLE_ZONE_ID_SHORT, tz, date, zoneString); + tzfmt->format(UTZFMT_STYLE_ZONE_ID_SHORT, tz, date, zoneString); } else if (count == 2) { // "VV" - tzFormat()->format(UTZFMT_STYLE_ZONE_ID, tz, date, zoneString); + tzfmt->format(UTZFMT_STYLE_ZONE_ID, tz, date, zoneString); } else if (count == 3) { // "VVV" - tzFormat()->format(UTZFMT_STYLE_EXEMPLAR_LOCATION, tz, date, zoneString); + tzfmt->format(UTZFMT_STYLE_EXEMPLAR_LOCATION, tz, date, zoneString); } else if (count == 4) { // "VVVV" - tzFormat()->format(UTZFMT_STYLE_GENERIC_LOCATION, tz, date, zoneString); + tzfmt->format(UTZFMT_STYLE_GENERIC_LOCATION, tz, date, zoneString); capContextUsageType = DateFormatSymbols::kCapContextUsageZoneLong; } } else if (patternCharIndex == UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD) { if (count == 1) { // "O" - tzFormat()->format(UTZFMT_STYLE_LOCALIZED_GMT_SHORT, tz, date, zoneString); + tzfmt->format(UTZFMT_STYLE_LOCALIZED_GMT_SHORT, tz, date, zoneString); } else if (count == 4) { // "OOOO" - tzFormat()->format(UTZFMT_STYLE_LOCALIZED_GMT, tz, date, zoneString); + tzfmt->format(UTZFMT_STYLE_LOCALIZED_GMT, tz, date, zoneString); } } else if (patternCharIndex == UDAT_TIMEZONE_ISO_FIELD) { if (count == 1) { // "X" - tzFormat()->format(UTZFMT_STYLE_ISO_BASIC_SHORT, tz, date, zoneString); + tzfmt->format(UTZFMT_STYLE_ISO_BASIC_SHORT, tz, date, zoneString); } else if (count == 2) { // "XX" - tzFormat()->format(UTZFMT_STYLE_ISO_BASIC_FIXED, tz, date, zoneString); + tzfmt->format(UTZFMT_STYLE_ISO_BASIC_FIXED, tz, date, zoneString); } else if (count == 3) { // "XXX" - tzFormat()->format(UTZFMT_STYLE_ISO_EXTENDED_FIXED, tz, date, zoneString); + tzfmt->format(UTZFMT_STYLE_ISO_EXTENDED_FIXED, tz, date, zoneString); } else if (count == 4) { // "XXXX" - tzFormat()->format(UTZFMT_STYLE_ISO_BASIC_FULL, tz, date, zoneString); + tzfmt->format(UTZFMT_STYLE_ISO_BASIC_FULL, tz, date, zoneString); } else if (count == 5) { // "XXXXX" - tzFormat()->format(UTZFMT_STYLE_ISO_EXTENDED_FULL, tz, date, zoneString); + tzfmt->format(UTZFMT_STYLE_ISO_EXTENDED_FULL, tz, date, zoneString); } } else if (patternCharIndex == UDAT_TIMEZONE_ISO_LOCAL_FIELD) { if (count == 1) { // "x" - tzFormat()->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT, tz, date, zoneString); + tzfmt->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_SHORT, tz, date, zoneString); } else if (count == 2) { // "xx" - tzFormat()->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED, tz, date, zoneString); + tzfmt->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_FIXED, tz, date, zoneString); } else if (count == 3) { // "xxx" - tzFormat()->format(UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED, tz, date, zoneString); + tzfmt->format(UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FIXED, tz, date, zoneString); } else if (count == 4) { // "xxxx" - tzFormat()->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL, tz, date, zoneString); + tzfmt->format(UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL, tz, date, zoneString); } else if (count == 5) { // "xxxxx" - tzFormat()->format(UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL, tz, date, zoneString); + tzfmt->format(UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL, tz, date, zoneString); } } else { @@ -1855,7 +1841,7 @@ SimpleDateFormat::subFormat(UnicodeString &appendTo, if (toAppend == NULL || toAppend->isBogus()) { // Reformat with identical arguments except ch, now changed to 'a'. subFormat(appendTo, 0x61, count, capitalizationContext, fieldNum, - handler, cal, mutableNFs, status); + handler, cal, status); } else { appendTo += *toAppend; } @@ -1876,7 +1862,7 @@ SimpleDateFormat::subFormat(UnicodeString &appendTo, // Data doesn't exist for the locale we're looking for. // Falling back to am/pm. subFormat(appendTo, 0x61, count, capitalizationContext, fieldNum, - handler, cal, mutableNFs, status); + handler, cal, status); break; } @@ -1947,7 +1933,7 @@ SimpleDateFormat::subFormat(UnicodeString &appendTo, periodType == DayPeriodRules::DAYPERIOD_PM || toAppend->isBogus()) { subFormat(appendTo, 0x61, count, capitalizationContext, fieldNum, - handler, cal, mutableNFs, status); + handler, cal, status); } else { appendTo += *toAppend; @@ -2004,6 +1990,11 @@ void SimpleDateFormat::adoptNumberFormat(NumberFormat *formatToAdopt) { freeSharedNumberFormatters(fSharedNumberFormatters); fSharedNumberFormatters = NULL; } + + // Also re-compute the fast formatters. + UErrorCode localStatus = U_ZERO_ERROR; + freeFastNumberFormatters(); + initFastNumberFormatters(localStatus); } void SimpleDateFormat::adoptNumberFormat(const UnicodeString& fields, NumberFormat *formatToAdopt, UErrorCode &status){ @@ -2055,16 +2046,58 @@ SimpleDateFormat::getNumberFormatForField(UChar field) const { //---------------------------------------------------------------------- void SimpleDateFormat::zeroPaddingNumber( - NumberFormat *currentNumberFormat, + const NumberFormat *currentNumberFormat, UnicodeString &appendTo, int32_t value, int32_t minDigits, int32_t maxDigits) const { - if (currentNumberFormat!=NULL) { + const number::LocalizedNumberFormatter* fastFormatter = nullptr; + // NOTE: This uses the heuristic that these five min/max int settings account for the vast majority + // of SimpleDateFormat number formatting cases at the time of writing (ICU 62). + if (currentNumberFormat == fNumberFormat) { + if (maxDigits == 10) { + if (minDigits == 1) { + fastFormatter = fFastNumberFormatters[SMPDTFMT_NF_1x10]; + } else if (minDigits == 2) { + fastFormatter = fFastNumberFormatters[SMPDTFMT_NF_2x10]; + } else if (minDigits == 3) { + fastFormatter = fFastNumberFormatters[SMPDTFMT_NF_3x10]; + } else if (minDigits == 4) { + fastFormatter = fFastNumberFormatters[SMPDTFMT_NF_4x10]; + } + } else if (maxDigits == 2) { + if (minDigits == 2) { + fastFormatter = fFastNumberFormatters[SMPDTFMT_NF_2x2]; + } + } + } + if (fastFormatter != nullptr) { + // Can use fast path + number::impl::UFormattedNumberData result; + result.quantity.setToInt(value); + UErrorCode localStatus = U_ZERO_ERROR; + fastFormatter->formatImpl(&result, localStatus); + if (U_FAILURE(localStatus)) { + return; + } + appendTo.append(result.string.toTempUnicodeString()); + return; + } + + // Check for RBNF (no clone necessary) + auto* rbnf = dynamic_cast(currentNumberFormat); + if (rbnf != nullptr) { FieldPosition pos(FieldPosition::DONT_CARE); + rbnf->format(value, appendTo, pos); // 3rd arg is there to speed up processing + return; + } - currentNumberFormat->setMinimumIntegerDigits(minDigits); - currentNumberFormat->setMaximumIntegerDigits(maxDigits); - currentNumberFormat->format(value, appendTo, pos); // 3rd arg is there to speed up processing + // Fall back to slow path (clone and mutate the NumberFormat) + if (currentNumberFormat != nullptr) { + FieldPosition pos(FieldPosition::DONT_CARE); + LocalPointer nf(dynamic_cast(currentNumberFormat->clone())); + nf->setMinimumIntegerDigits(minDigits); + nf->setMaximumIntegerDigits(maxDigits); + nf->format(value, appendTo, pos); // 3rd arg is there to speed up processing } } @@ -2131,7 +2164,6 @@ SimpleDateFormat::parse(const UnicodeString& text, Calendar& cal, ParsePosition& int32_t saveHebrewMonth = -1; int32_t count = 0; UTimeZoneFormatTimeType tzTimeType = UTZFMT_TIME_TYPE_UNKNOWN; - SimpleDateFormatMutableNFs mutableNFs; // For parsing abutting numeric fields. 'abutPat' is the // offset into 'pattern' of the first of 2 or more abutting @@ -2225,7 +2257,7 @@ SimpleDateFormat::parse(const UnicodeString& text, Calendar& cal, ParsePosition& } pos = subParse(text, pos, ch, count, - TRUE, FALSE, ambiguousYear, saveHebrewMonth, *workCal, i, numericLeapMonthFormatter, &tzTimeType, mutableNFs); + TRUE, FALSE, ambiguousYear, saveHebrewMonth, *workCal, i, numericLeapMonthFormatter, &tzTimeType); // If the parse fails anywhere in the run, back up to the // start of the run and retry. @@ -2240,7 +2272,7 @@ SimpleDateFormat::parse(const UnicodeString& text, Calendar& cal, ParsePosition& // fields. else if (ch != 0x6C) { // pattern char 'l' (SMALL LETTER L) just gets ignored int32_t s = subParse(text, pos, ch, count, - FALSE, TRUE, ambiguousYear, saveHebrewMonth, *workCal, i, numericLeapMonthFormatter, &tzTimeType, mutableNFs, &dayPeriodInt); + FALSE, TRUE, ambiguousYear, saveHebrewMonth, *workCal, i, numericLeapMonthFormatter, &tzTimeType, &dayPeriodInt); if (s == -pos-1) { // era not present, in special cases allow this to continue @@ -2858,7 +2890,7 @@ SimpleDateFormat::set2DigitYearStart(UDate d, UErrorCode& status) */ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UChar ch, int32_t count, UBool obeyCount, UBool allowNegative, UBool ambiguousYear[], int32_t& saveHebrewMonth, Calendar& cal, - int32_t patLoc, MessageFormat * numericLeapMonthFormatter, UTimeZoneFormatTimeType *tzTimeType, SimpleDateFormatMutableNFs &mutableNFs, + int32_t patLoc, MessageFormat * numericLeapMonthFormatter, UTimeZoneFormatTimeType *tzTimeType, int32_t *dayPeriod) const { Formattable number; @@ -2868,7 +2900,7 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UC UErrorCode status = U_ZERO_ERROR; ParsePosition pos(0); UDateFormatField patternCharIndex = DateFormatSymbols::getPatternCharIndex(ch); - NumberFormat *currentNumberFormat; + const NumberFormat *currentNumberFormat; UnicodeString temp; UBool gotNumber = FALSE; @@ -2880,7 +2912,7 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UC return -start; } - currentNumberFormat = mutableNFs.get(getNumberFormatByIndex(patternCharIndex)); + currentNumberFormat = getNumberFormatByIndex(patternCharIndex); if (currentNumberFormat == NULL) { return -start; } @@ -3393,31 +3425,41 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UC case UDAT_TIMEZONE_FIELD: // 'z' { UTimeZoneFormatStyle style = (count < 4) ? UTZFMT_STYLE_SPECIFIC_SHORT : UTZFMT_STYLE_SPECIFIC_LONG; - TimeZone *tz = tzFormat()->parse(style, text, pos, tzTimeType); - if (tz != NULL) { - cal.adoptTimeZone(tz); - return pos.getIndex(); + const TimeZoneFormat *tzfmt = tzFormat(status); + if (U_SUCCESS(status)) { + TimeZone *tz = tzfmt->parse(style, text, pos, tzTimeType); + if (tz != NULL) { + cal.adoptTimeZone(tz); + return pos.getIndex(); + } } - } + return -start; + } break; case UDAT_TIMEZONE_RFC_FIELD: // 'Z' { UTimeZoneFormatStyle style = (count < 4) ? UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL : ((count == 5) ? UTZFMT_STYLE_ISO_EXTENDED_FULL: UTZFMT_STYLE_LOCALIZED_GMT); - TimeZone *tz = tzFormat()->parse(style, text, pos, tzTimeType); - if (tz != NULL) { - cal.adoptTimeZone(tz); - return pos.getIndex(); + const TimeZoneFormat *tzfmt = tzFormat(status); + if (U_SUCCESS(status)) { + TimeZone *tz = tzfmt->parse(style, text, pos, tzTimeType); + if (tz != NULL) { + cal.adoptTimeZone(tz); + return pos.getIndex(); + } } return -start; } case UDAT_TIMEZONE_GENERIC_FIELD: // 'v' { UTimeZoneFormatStyle style = (count < 4) ? UTZFMT_STYLE_GENERIC_SHORT : UTZFMT_STYLE_GENERIC_LONG; - TimeZone *tz = tzFormat()->parse(style, text, pos, tzTimeType); - if (tz != NULL) { - cal.adoptTimeZone(tz); - return pos.getIndex(); + const TimeZoneFormat *tzfmt = tzFormat(status); + if (U_SUCCESS(status)) { + TimeZone *tz = tzfmt->parse(style, text, pos, tzTimeType); + if (tz != NULL) { + cal.adoptTimeZone(tz); + return pos.getIndex(); + } } return -start; } @@ -3438,20 +3480,26 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UC style = UTZFMT_STYLE_GENERIC_LOCATION; break; } - TimeZone *tz = tzFormat()->parse(style, text, pos, tzTimeType); - if (tz != NULL) { - cal.adoptTimeZone(tz); - return pos.getIndex(); + const TimeZoneFormat *tzfmt = tzFormat(status); + if (U_SUCCESS(status)) { + TimeZone *tz = tzfmt->parse(style, text, pos, tzTimeType); + if (tz != NULL) { + cal.adoptTimeZone(tz); + return pos.getIndex(); + } } return -start; } case UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD: // 'O' { UTimeZoneFormatStyle style = (count < 4) ? UTZFMT_STYLE_LOCALIZED_GMT_SHORT : UTZFMT_STYLE_LOCALIZED_GMT; - TimeZone *tz = tzFormat()->parse(style, text, pos, tzTimeType); - if (tz != NULL) { - cal.adoptTimeZone(tz); - return pos.getIndex(); + const TimeZoneFormat *tzfmt = tzFormat(status); + if (U_SUCCESS(status)) { + TimeZone *tz = tzfmt->parse(style, text, pos, tzTimeType); + if (tz != NULL) { + cal.adoptTimeZone(tz); + return pos.getIndex(); + } } return -start; } @@ -3475,10 +3523,13 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UC style = UTZFMT_STYLE_ISO_EXTENDED_FULL; break; } - TimeZone *tz = tzFormat()->parse(style, text, pos, tzTimeType); - if (tz != NULL) { - cal.adoptTimeZone(tz); - return pos.getIndex(); + const TimeZoneFormat *tzfmt = tzFormat(status); + if (U_SUCCESS(status)) { + TimeZone *tz = tzfmt->parse(style, text, pos, tzTimeType); + if (tz != NULL) { + cal.adoptTimeZone(tz); + return pos.getIndex(); + } } return -start; } @@ -3502,10 +3553,13 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UC style = UTZFMT_STYLE_ISO_EXTENDED_LOCAL_FULL; break; } - TimeZone *tz = tzFormat()->parse(style, text, pos, tzTimeType); - if (tz != NULL) { - cal.adoptTimeZone(tz); - return pos.getIndex(); + const TimeZoneFormat *tzfmt = tzFormat(status); + if (U_SUCCESS(status)) { + TimeZone *tz = tzfmt->parse(style, text, pos, tzTimeType); + if (tz != NULL) { + cal.adoptTimeZone(tz); + return pos.getIndex(); + } } return -start; } @@ -3539,7 +3593,7 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UC U_ASSERT(dayPeriod != NULL); int32_t ampmStart = subParse(text, start, 0x61, count, obeyCount, allowNegative, ambiguousYear, saveHebrewMonth, cal, - patLoc, numericLeapMonthFormatter, tzTimeType, mutableNFs); + patLoc, numericLeapMonthFormatter, tzTimeType); if (ampmStart > 0) { return ampmStart; @@ -3686,7 +3740,7 @@ void SimpleDateFormat::parseInt(const UnicodeString& text, Formattable& number, ParsePosition& pos, UBool allowNegative, - NumberFormat *fmt) const { + const NumberFormat *fmt) const { parseInt(text, number, -1, pos, allowNegative,fmt); } @@ -3698,18 +3752,21 @@ void SimpleDateFormat::parseInt(const UnicodeString& text, int32_t maxDigits, ParsePosition& pos, UBool allowNegative, - NumberFormat *fmt) const { + const NumberFormat *fmt) const { UnicodeString oldPrefix; - DecimalFormat* df = NULL; - if (!allowNegative && (df = dynamic_cast(fmt)) != NULL) { - df->getNegativePrefix(oldPrefix); + auto* fmtAsDF = dynamic_cast(fmt); + LocalPointer df; + if (!allowNegative && fmtAsDF != nullptr) { + df.adoptInstead(dynamic_cast(fmtAsDF->clone())); + if (df.isNull()) { + // Memory allocation error + return; + } df->setNegativePrefix(UnicodeString(TRUE, SUPPRESS_NEGATIVE_PREFIX, -1)); + fmt = df.getAlias(); } int32_t oldPos = pos.getIndex(); fmt->parse(text, number, pos); - if (df != NULL) { - df->setNegativePrefix(oldPrefix); - } if (maxDigits > 0) { // adjust the result to fit into @@ -3856,7 +3913,13 @@ SimpleDateFormat::setDateFormatSymbols(const DateFormatSymbols& newFormatSymbols //---------------------------------------------------------------------- const TimeZoneFormat* SimpleDateFormat::getTimeZoneFormat(void) const { - return (const TimeZoneFormat*)tzFormat(); + // TimeZoneFormat initialization might fail when out of memory. + // If we always initialize TimeZoneFormat instance, we can return + // such status there. For now, this implementation lazily instantiates + // a TimeZoneFormat for performance optimization reasons, but cannot + // propagate such error (probably just out of memory case) to the caller. + UErrorCode status = U_ZERO_ERROR; + return (const TimeZoneFormat*)tzFormat(status); } //---------------------------------------------------------------------- @@ -4123,12 +4186,11 @@ SimpleDateFormat::skipUWhiteSpace(const UnicodeString& text, int32_t pos) const // Lazy TimeZoneFormat instantiation, semantically const. TimeZoneFormat * -SimpleDateFormat::tzFormat() const { +SimpleDateFormat::tzFormat(UErrorCode &status) const { if (fTimeZoneFormat == NULL) { umtx_lock(&LOCK); { if (fTimeZoneFormat == NULL) { - UErrorCode status = U_ZERO_ERROR; TimeZoneFormat *tzfmt = TimeZoneFormat::createInstance(fLocale, status); if (U_FAILURE(status)) { return NULL; diff --git a/deps/icu-small/source/i18n/timezone.cpp b/deps/icu-small/source/i18n/timezone.cpp index e662bf7674b9f4..f7f45c7d3eeffd 100644 --- a/deps/icu-small/source/i18n/timezone.cpp +++ b/deps/icu-small/source/i18n/timezone.cpp @@ -739,8 +739,7 @@ class TZEnumeration : public StringEnumeration { len = mapLen; } - UBool getID(int32_t i) { - UErrorCode ec = U_ZERO_ERROR; + UBool getID(int32_t i, UErrorCode& ec) { int32_t idLen = 0; const UChar* id = NULL; UResourceBundle *top = ures_openDirect(0, kZONEINFO, &ec); @@ -930,7 +929,7 @@ class TZEnumeration : public StringEnumeration { virtual const UnicodeString* snext(UErrorCode& status) { if (U_SUCCESS(status) && map != NULL && pos < len) { - getID(map[pos]); + getID(map[pos], status); ++pos; return &unistr; } diff --git a/deps/icu-small/source/i18n/ucln_in.h b/deps/icu-small/source/i18n/ucln_in.h index 40a5c36d87a9f7..318eafc143c968 100644 --- a/deps/icu-small/source/i18n/ucln_in.h +++ b/deps/icu-small/source/i18n/ucln_in.h @@ -26,6 +26,7 @@ as the functions are suppose to be called. It's usually best to have child dependencies called first. */ typedef enum ECleanupI18NType { UCLN_I18N_START = -1, + UCLN_I18N_NUMBER_SKELETONS, UCLN_I18N_CURRENCY_SPACING, UCLN_I18N_SPOOF, UCLN_I18N_SPOOFDATA, diff --git a/deps/icu-small/source/i18n/unicode/compactdecimalformat.h b/deps/icu-small/source/i18n/unicode/compactdecimalformat.h index d682d2d0e74278..7dc92f610062f4 100644 --- a/deps/icu-small/source/i18n/unicode/compactdecimalformat.h +++ b/deps/icu-small/source/i18n/unicode/compactdecimalformat.h @@ -84,7 +84,7 @@ class U_I18N_API CompactDecimalFormat : public DecimalFormat { * Destructor. * @stable ICU 51 */ - virtual ~CompactDecimalFormat(); + ~CompactDecimalFormat() U_OVERRIDE; /** * Assignment operator. @@ -101,245 +101,21 @@ class U_I18N_API CompactDecimalFormat : public DecimalFormat { * @return a polymorphic copy of this CompactDecimalFormat. * @stable ICU 51 */ - virtual Format* clone() const; - - /** - * Return TRUE if the given Format objects are semantically equal. - * Objects of different subclasses are considered unequal. - * - * @param other the object to be compared with. - * @return TRUE if the given Format objects are semantically equal. - * @stable ICU 51 - */ - virtual UBool operator==(const Format& other) const; - + Format* clone() const U_OVERRIDE; using DecimalFormat::format; /** - * Format a double or long number using base-10 representation. - * - * @param number The value to be formatted. - * @param appendTo Output parameter to receive result. - * Result is appended to existing contents. - * @param pos On input: an alignment field, if desired. - * On output: the offsets of the alignment field. - * @return Reference to 'appendTo' parameter. - * @stable ICU 51 - */ - virtual UnicodeString& format(double number, - UnicodeString& appendTo, - FieldPosition& pos) const; - - /** - * Format a double or long number using base-10 representation. - * - * @param number The value to be formatted. - * @param appendTo Output parameter to receive result. - * Result is appended to existing contents. - * @param pos On input: an alignment field, if desired. - * On output: the offsets of the alignment field. - * @param status - * @return Reference to 'appendTo' parameter. - * @internal - */ - virtual UnicodeString& format(double number, - UnicodeString& appendTo, - FieldPosition& pos, - UErrorCode &status) const; - - /** - * Format a double or long number using base-10 representation. - * Currently sets status to U_UNSUPPORTED_ERROR. - * - * @param number The value to be formatted. - * @param appendTo Output parameter to receive result. - * Result is appended to existing contents. - * @param posIter On return, can be used to iterate over positions - * of fields generated by this format call. - * Can be NULL. - * @param status Output param filled with success/failure status. - * @return Reference to 'appendTo' parameter. - * @internal - */ - virtual UnicodeString& format(double number, - UnicodeString& appendTo, - FieldPositionIterator* posIter, - UErrorCode& status) const; - - /** - * Format a long number using base-10 representation. - * - * @param number The value to be formatted. - * @param appendTo Output parameter to receive result. - * Result is appended to existing contents. - * @param pos On input: an alignment field, if desired. - * On output: the offsets of the alignment field. - * @return Reference to 'appendTo' parameter. - * @stable ICU 56 - */ - virtual UnicodeString& format(int32_t number, - UnicodeString& appendTo, - FieldPosition& pos) const; - - /** - * Format a long number using base-10 representation. - * - * @param number The value to be formatted. - * @param appendTo Output parameter to receive result. - * Result is appended to existing contents. - * @param pos On input: an alignment field, if desired. - * On output: the offsets of the alignment field. - * @return Reference to 'appendTo' parameter. - * @internal - */ - virtual UnicodeString& format(int32_t number, - UnicodeString& appendTo, - FieldPosition& pos, - UErrorCode &status) const; - - /** - * Format a long number using base-10 representation. - * Currently sets status to U_UNSUPPORTED_ERROR - * - * @param number The value to be formatted. - * @param appendTo Output parameter to receive result. - * Result is appended to existing contents. - * @param posIter On return, can be used to iterate over positions - * of fields generated by this format call. - * Can be NULL. - * @param status Output param filled with success/failure status. - * @return Reference to 'appendTo' parameter. - * @internal - */ - virtual UnicodeString& format(int32_t number, - UnicodeString& appendTo, - FieldPositionIterator* posIter, - UErrorCode& status) const; - - /** - * Format an int64 number using base-10 representation. - * - * @param number The value to be formatted. - * @param appendTo Output parameter to receive result. - * Result is appended to existing contents. - * @param pos On input: an alignment field, if desired. - * On output: the offsets of the alignment field. - * @return Reference to 'appendTo' parameter. + * CompactDecimalFormat does not support parsing. This implementation + * does nothing. + * @param text Unused. + * @param result Does not change. + * @param parsePosition Does not change. + * @see Formattable * @stable ICU 51 */ - virtual UnicodeString& format(int64_t number, - UnicodeString& appendTo, - FieldPosition& pos) const; - - /** - * Format an int64 number using base-10 representation. - * - * @param number The value to be formatted. - * @param appendTo Output parameter to receive result. - * Result is appended to existing contents. - * @param pos On input: an alignment field, if desired. - * On output: the offsets of the alignment field. - * @return Reference to 'appendTo' parameter. - * @internal - */ - virtual UnicodeString& format(int64_t number, - UnicodeString& appendTo, - FieldPosition& pos, - UErrorCode &status) const; - - /** - * Format an int64 number using base-10 representation. - * Currently sets status to U_UNSUPPORTED_ERROR - * - * @param number The value to be formatted. - * @param appendTo Output parameter to receive result. - * Result is appended to existing contents. - * @param posIter On return, can be used to iterate over positions - * of fields generated by this format call. - * Can be NULL. - * @param status Output param filled with success/failure status. - * @return Reference to 'appendTo' parameter. - * @internal - */ - virtual UnicodeString& format(int64_t number, - UnicodeString& appendTo, - FieldPositionIterator* posIter, - UErrorCode& status) const; - - /** - * Format a decimal number. Currently sets status to U_UNSUPPORTED_ERROR - * The syntax of the unformatted number is a "numeric string" - * as defined in the Decimal Arithmetic Specification, available at - * http://speleotrove.com/decimal - * - * @param number The unformatted number, as a string. - * @param appendTo Output parameter to receive result. - * Result is appended to existing contents. - * @param posIter On return, can be used to iterate over positions - * of fields generated by this format call. - * Can be NULL. - * @param status Output param filled with success/failure status. - * @return Reference to 'appendTo' parameter. - * @internal - */ - virtual UnicodeString& format(StringPiece number, - UnicodeString& appendTo, - FieldPositionIterator* posIter, - UErrorCode& status) const; - - /** - * Format a decimal number. Currently sets status to U_UNSUPPORTED_ERROR - * The number is a DigitList wrapper onto a floating point decimal number. - * The default implementation in NumberFormat converts the decimal number - * to a double and formats that. - * - * @param number The number, a DigitList format Decimal Floating Point. - * @param appendTo Output parameter to receive result. - * Result is appended to existing contents. - * @param posIter On return, can be used to iterate over positions - * of fields generated by this format call. - * @param status Output param filled with success/failure status. - * @return Reference to 'appendTo' parameter. - * @internal - */ - virtual UnicodeString& format(const DigitList &number, - UnicodeString& appendTo, - FieldPositionIterator* posIter, - UErrorCode& status) const; - - /** - * Format a decimal number. Currently sets status to U_UNSUPPORTED_ERROR. - * The number is a DigitList wrapper onto a floating point decimal number. - * The default implementation in NumberFormat converts the decimal number - * to a double and formats that. - * - * @param number The number, a DigitList format Decimal Floating Point. - * @param appendTo Output parameter to receive result. - * Result is appended to existing contents. - * @param pos On input: an alignment field, if desired. - * On output: the offsets of the alignment field. - * @param status Output param filled with success/failure status. - * @return Reference to 'appendTo' parameter. - * @internal - */ - virtual UnicodeString& format(const DigitList &number, - UnicodeString& appendTo, - FieldPosition& pos, - UErrorCode& status) const; - - /** - * CompactDecimalFormat does not support parsing. This implementation - * does nothing. - * @param text Unused. - * @param result Does not change. - * @param parsePosition Does not change. - * @see Formattable - * @stable ICU 51 - */ - virtual void parse(const UnicodeString& text, - Formattable& result, - ParsePosition& parsePosition) const; + void parse(const UnicodeString& text, Formattable& result, + ParsePosition& parsePosition) const U_OVERRIDE; /** * CompactDecimalFormat does not support parsing. This implementation @@ -350,10 +126,9 @@ class U_I18N_API CompactDecimalFormat : public DecimalFormat { * @param status Always set to U_UNSUPPORTED_ERROR. * @stable ICU 51 */ - virtual void parse(const UnicodeString& text, - Formattable& result, - UErrorCode& status) const; + void parse(const UnicodeString& text, Formattable& result, UErrorCode& status) const U_OVERRIDE; +#ifndef U_HIDE_INTERNAL_API /** * Parses text from the given string as a currency amount. Unlike * the parse() method, this method will attempt to parse a generic @@ -374,8 +149,8 @@ class U_I18N_API CompactDecimalFormat : public DecimalFormat { * the parsed currency; if parse fails, this is NULL. * @internal */ - virtual CurrencyAmount* parseCurrency(const UnicodeString& text, - ParsePosition& pos) const; + CurrencyAmount* parseCurrency(const UnicodeString& text, ParsePosition& pos) const U_OVERRIDE; +#endif /* U_HIDE_INTERNAL_API */ /** * Return the class ID for this class. This is useful only for @@ -401,18 +176,10 @@ class U_I18N_API CompactDecimalFormat : public DecimalFormat { * other classes have different class IDs. * @stable ICU 51 */ - virtual UClassID getDynamicClassID() const; - -private: - - const UHashtable* _unitsByVariant; - const double* _divisors; - PluralRules* _pluralRules; - - // Default constructor not implemented. - CompactDecimalFormat(const DecimalFormat &, const UHashtable* unitsByVariant, const double* divisors, PluralRules* pluralRules); + UClassID getDynamicClassID() const U_OVERRIDE; - UBool eqHelper(const CompactDecimalFormat& that) const; + private: + CompactDecimalFormat(const Locale& inLocale, UNumberCompactStyle style, UErrorCode& status); }; U_NAMESPACE_END diff --git a/deps/icu-small/source/i18n/unicode/currunit.h b/deps/icu-small/source/i18n/unicode/currunit.h index e7e0dc72da8292..d5bc4aa6d6d482 100644 --- a/deps/icu-small/source/i18n/unicode/currunit.h +++ b/deps/icu-small/source/i18n/unicode/currunit.h @@ -44,8 +44,9 @@ class U_I18N_API CurrencyUnit: public MeasureUnit { /** * Construct an object with the given ISO currency code. - * @param isoCode the 3-letter ISO 4217 currency code; must not be - * NULL and must have length 3 + * @param isoCode the 3-letter ISO 4217 currency code; must have + * length 3 and need not be NUL-terminated. If NULL, the currency + * is initialized to the unknown currency XXX. * @param ec input-output error code. If the isoCode is invalid, * then this will be set to a failing value. * @stable ICU 3.0 diff --git a/deps/icu-small/source/i18n/unicode/dcfmtsym.h b/deps/icu-small/source/i18n/unicode/dcfmtsym.h index e58befa31bda88..2f824cec3087f5 100644 --- a/deps/icu-small/source/i18n/unicode/dcfmtsym.h +++ b/deps/icu-small/source/i18n/unicode/dcfmtsym.h @@ -406,9 +406,12 @@ class U_I18N_API DecimalFormatSymbols : public UObject { * returning a const reference to one of the symbol strings. * The returned reference becomes invalid when the symbol is changed * or when the DecimalFormatSymbols are destroyed. - * ### TODO markus 2002oct11: Consider proposing getConstSymbol() to be really public. * Note: moved #ifndef U_HIDE_INTERNAL_API after this, since this is needed for inline in DecimalFormat * + * This is not currently stable API, but if you think it should be stable, + * post a comment on the following ticket and the ICU team will take a look: + * http://bugs.icu-project.org/trac/ticket/13580 + * * @param symbol Constant to indicate a number format symbol. * @return the format symbol by the param 'symbol' * @internal @@ -422,6 +425,10 @@ class U_I18N_API DecimalFormatSymbols : public UObject { * to accessing the symbol from getConstSymbol with the corresponding * key, such as kZeroDigitSymbol or kOneDigitSymbol. * + * This is not currently stable API, but if you think it should be stable, + * post a comment on the following ticket and the ICU team will take a look: + * http://bugs.icu-project.org/trac/ticket/13580 + * * @param digit The digit, an integer between 0 and 9 inclusive. * If outside the range 0 to 9, the zero digit is returned. * @return the format symbol for the given digit. diff --git a/deps/icu-small/source/i18n/unicode/decimfmt.h b/deps/icu-small/source/i18n/unicode/decimfmt.h index b062208d9b1f93..3747f510f79c79 100644 --- a/deps/icu-small/source/i18n/unicode/decimfmt.h +++ b/deps/icu-small/source/i18n/unicode/decimfmt.h @@ -43,28 +43,25 @@ #include "unicode/curramt.h" #include "unicode/enumset.h" -#ifndef U_HIDE_INTERNAL_API -/** - * \def UNUM_DECIMALFORMAT_INTERNAL_SIZE - * @internal - */ -#if UCONFIG_FORMAT_FASTPATHS_49 -#define UNUM_DECIMALFORMAT_INTERNAL_SIZE 16 -#endif -#endif /* U_HIDE_INTERNAL_API */ - U_NAMESPACE_BEGIN -class DigitList; class CurrencyPluralInfo; -class Hashtable; -class UnicodeSet; -class FieldPositionHandler; -class DecimalFormatStaticSets; -class FixedDecimal; -class DecimalFormatImpl; -class PluralRules; -class VisibleDigitsWithExponent; +class CompactDecimalFormat; + +namespace number { +class LocalizedNumberFormatter; +class FormattedNumber; +namespace impl { +class DecimalQuantity; +struct DecimalFormatFields; +} +} + +namespace numparse { +namespace impl { +class NumberParserImpl; +} +} // explicit template instantiation. see digitlst.h // (When building DLLs for Windows this is required.) @@ -672,17 +669,14 @@ template class U_I18N_API EnumSet "123", and "123" -> 1.23 * @stable ICU 2.0 @@ -1362,12 +1320,52 @@ class U_I18N_API DecimalFormat: public NumberFormat { * (For Arabic, use arabic percent symbol). * For a permill, set the suffixes to have "\\u2031" and the multiplier to be 1000. * + * This method only supports integer multipliers. To multiply by a non-integer, pair this + * method with setMultiplierScale(). + * * @param newValue the new value of the multiplier for use in percent, permill, etc. * Examples: with 100, 1.23 -> "123", and "123" -> 1.23 * @stable ICU 2.0 */ virtual void setMultiplier(int32_t newValue); +#ifndef U_HIDE_DRAFT_API + /** + * Gets the power of ten by which number should be multiplied before formatting, which + * can be combined with setMultiplier() to multiply by any arbitrary decimal value. + * + * A multiplier scale of 2 corresponds to multiplication by 100, and a multiplier scale + * of -2 corresponds to multiplication by 0.01. + * + * This method is analogous to UNUM_SCALE in getAttribute. + * + * @return the current value of the power-of-ten multiplier. + * @draft ICU 62 + */ + int32_t getMultiplierScale(void) const; +#endif /* U_HIDE_DRAFT_API */ + + /** + * Sets a power of ten by which number should be multiplied before formatting, which + * can be combined with setMultiplier() to multiply by any arbitrary decimal value. + * + * A multiplier scale of 2 corresponds to multiplication by 100, and a multiplier scale + * of -2 corresponds to multiplication by 0.01. + * + * For example, to multiply numbers by 0.5 before formatting, you can do: + * + *

+     * df.setMultiplier(5);
+     * df.setMultiplierScale(-1);
+     * 
+ * + * This method is analogous to UNUM_SCALE in setAttribute. + * + * @param newValue the new value of the power-of-ten multiplier. + * @draft ICU 62 + */ + virtual void setMultiplierScale(int32_t newValue); + /** * Get the rounding increment. * @return A positive rounding increment, or 0.0 if a custom rounding @@ -1400,7 +1398,7 @@ class U_I18N_API DecimalFormat: public NumberFormat { * @see #setRoundingMode * @stable ICU 2.0 */ - virtual ERoundingMode getRoundingMode(void) const; + virtual ERoundingMode getRoundingMode(void) const U_OVERRIDE; /** * Set the rounding mode. @@ -1410,7 +1408,7 @@ class U_I18N_API DecimalFormat: public NumberFormat { * @see #getRoundingMode * @stable ICU 2.0 */ - virtual void setRoundingMode(ERoundingMode roundingMode); + virtual void setRoundingMode(ERoundingMode roundingMode) U_OVERRIDE; /** * Get the width to which the output of format() is padded. @@ -1469,7 +1467,7 @@ class U_I18N_API DecimalFormat: public NumberFormat { * @see #setPadPosition * @stable ICU 2.0 */ - virtual void setPadCharacter(const UnicodeString &padChar); + virtual void setPadCharacter(const UnicodeString& padChar); /** * Get the position at which padding will take place. This is the location @@ -1676,7 +1674,7 @@ class U_I18N_API DecimalFormat: public NumberFormat { #endif /* U_HIDE_INTERNAL_API */ - /* Cannot use #ifndef U_HIDE_INTERNAL_API for the following draft method since it is virtual. */ + /* Cannot use #ifndef U_HIDE_INTERNAL_API for the following draft method since it is virtual. */ /** * Sets the minimum grouping digits. Setting to a value less than or * equal to 1 turns off minimum grouping digits. @@ -1717,7 +1715,7 @@ class U_I18N_API DecimalFormat: public NumberFormat { UBool isDecimalPatternMatchRequired(void) const; /** - * Allows you to set the behavior of the pattern decimal mark. + * Allows you to set the parse behavior of the pattern decimal mark. * * if TRUE, the input must have a decimal mark if one was specified in the pattern. When * FALSE the decimal mark may be omitted from the input. @@ -1727,6 +1725,60 @@ class U_I18N_API DecimalFormat: public NumberFormat { */ virtual void setDecimalPatternMatchRequired(UBool newValue); + /** + * {@icu} Returns whether to ignore exponents when parsing. + * + * @see #setParseNoExponent + * @internal This API is a technical preview. It may change in an upcoming release. + */ + virtual UBool isParseNoExponent() const; + + /** + * {@icu} Specifies whether to stop parsing when an exponent separator is encountered. For + * example, parses "123E4" to 123 (with parse position 3) instead of 1230000 (with parse position + * 5). + * + * @param value true to prevent exponents from being parsed; false to allow them to be parsed. + * @internal This API is a technical preview. It may change in an upcoming release. + */ + virtual void setParseNoExponent(UBool value); + + /** + * {@icu} Returns whether parsing is sensitive to case (lowercase/uppercase). + * + * @see #setParseCaseSensitive + * @internal This API is a technical preview. It may change in an upcoming release. + */ + virtual UBool isParseCaseSensitive() const; + + /** + * {@icu} Whether to pay attention to case when parsing; default is to ignore case (perform + * case-folding). For example, "A" == "a" in case-insensitive but not case-sensitive mode. + * + * Currency symbols are never case-folded. For example, "us$1.00" will not parse in case-insensitive + * mode, even though "US$1.00" parses. + * + * @internal This API is a technical preview. It may change in an upcoming release. + */ + virtual void setParseCaseSensitive(UBool value); + + /** + * {@icu} Returns whether truncation of high-order integer digits should result in an error. + * By default, setMaximumIntegerDigits truncates high-order digits silently. + * + * @see setFormatFailIfMoreThanMaxDigits + * @internal This API is a technical preview. It may change in an upcoming release. + */ + virtual UBool isFormatFailIfMoreThanMaxDigits() const; + + /** + * {@icu} Sets whether truncation of high-order integer digits should result in an error. + * By default, setMaximumIntegerDigits truncates high-order digits silently. + * + * @internal This API is a technical preview. It may change in an upcoming release. + */ + virtual void setFormatFailIfMoreThanMaxDigits(UBool value); + /** * Synthesizes a pattern string that represents the current state @@ -1781,9 +1833,8 @@ class U_I18N_API DecimalFormat: public NumberFormat { * set to a failure result. * @stable ICU 2.0 */ - virtual void applyPattern(const UnicodeString& pattern, - UParseError& parseError, - UErrorCode& status); + virtual void applyPattern(const UnicodeString& pattern, UParseError& parseError, UErrorCode& status); + /** * Sets the pattern. * @param pattern The pattern to be applied. @@ -1792,8 +1843,7 @@ class U_I18N_API DecimalFormat: public NumberFormat { * set to a failure result. * @stable ICU 2.0 */ - virtual void applyPattern(const UnicodeString& pattern, - UErrorCode& status); + virtual void applyPattern(const UnicodeString& pattern, UErrorCode& status); /** * Apply the given pattern to this Format object. The pattern @@ -1825,8 +1875,7 @@ class U_I18N_API DecimalFormat: public NumberFormat { * set to a failure result. * @stable ICU 2.0 */ - virtual void applyLocalizedPattern(const UnicodeString& pattern, - UParseError& parseError, + virtual void applyLocalizedPattern(const UnicodeString& pattern, UParseError& parseError, UErrorCode& status); /** @@ -1838,8 +1887,7 @@ class U_I18N_API DecimalFormat: public NumberFormat { * set to a failure result. * @stable ICU 2.0 */ - virtual void applyLocalizedPattern(const UnicodeString& pattern, - UErrorCode& status); + virtual void applyLocalizedPattern(const UnicodeString& pattern, UErrorCode& status); /** @@ -1851,7 +1899,7 @@ class U_I18N_API DecimalFormat: public NumberFormat { * @see NumberFormat#setMaximumIntegerDigits * @stable ICU 2.0 */ - virtual void setMaximumIntegerDigits(int32_t newValue); + void setMaximumIntegerDigits(int32_t newValue) U_OVERRIDE; /** * Sets the minimum number of digits allowed in the integer portion of a @@ -1862,7 +1910,7 @@ class U_I18N_API DecimalFormat: public NumberFormat { * @see NumberFormat#setMinimumIntegerDigits * @stable ICU 2.0 */ - virtual void setMinimumIntegerDigits(int32_t newValue); + void setMinimumIntegerDigits(int32_t newValue) U_OVERRIDE; /** * Sets the maximum number of digits allowed in the fraction portion of a @@ -1873,7 +1921,7 @@ class U_I18N_API DecimalFormat: public NumberFormat { * @see NumberFormat#setMaximumFractionDigits * @stable ICU 2.0 */ - virtual void setMaximumFractionDigits(int32_t newValue); + void setMaximumFractionDigits(int32_t newValue) U_OVERRIDE; /** * Sets the minimum number of digits allowed in the fraction portion of a @@ -1884,7 +1932,7 @@ class U_I18N_API DecimalFormat: public NumberFormat { * @see NumberFormat#setMinimumFractionDigits * @stable ICU 2.0 */ - virtual void setMinimumFractionDigits(int32_t newValue); + void setMinimumFractionDigits(int32_t newValue) U_OVERRIDE; /** * Returns the minimum number of significant digits that will be @@ -1947,7 +1995,6 @@ class U_I18N_API DecimalFormat: public NumberFormat { */ void setSignificantDigitsUsed(UBool useSignificantDigits); - public: /** * Sets the currency used to display currency * amounts. This takes effect immediately, if this format is a @@ -1960,7 +2007,7 @@ class U_I18N_API DecimalFormat: public NumberFormat { * @param ec input-output error code * @stable ICU 3.0 */ - virtual void setCurrency(const char16_t* theCurrency, UErrorCode& ec); + void setCurrency(const char16_t* theCurrency, UErrorCode& ec) U_OVERRIDE; /** * Sets the currency used to display currency amounts. See @@ -1984,78 +2031,53 @@ class U_I18N_API DecimalFormat: public NumberFormat { */ UCurrencyUsage getCurrencyUsage() const; - -#ifndef U_HIDE_DEPRECATED_API - /** - * The resource tags we use to retrieve decimal format data from - * locale resource bundles. - * @deprecated ICU 3.4. This string has no public purpose. Please don't use it. - */ - static const char fgNumberPatterns[]; -#endif // U_HIDE_DEPRECATED_API - #ifndef U_HIDE_INTERNAL_API - /** - * Get a FixedDecimal corresponding to a double as it would be - * formatted by this DecimalFormat. - * Internal, not intended for public use. - * @internal - */ - FixedDecimal getFixedDecimal(double number, UErrorCode &status) const; - - /** - * Get a FixedDecimal corresponding to a formattable as it would be - * formatted by this DecimalFormat. - * Internal, not intended for public use. - * @internal - */ - FixedDecimal getFixedDecimal(const Formattable &number, UErrorCode &status) const; /** - * Get a FixedDecimal corresponding to a DigitList as it would be - * formatted by this DecimalFormat. Note: the DigitList may be modified. + * Format a number and save it into the given DecimalQuantity. * Internal, not intended for public use. * @internal */ - FixedDecimal getFixedDecimal(DigitList &number, UErrorCode &status) const; + void formatToDecimalQuantity(double number, number::impl::DecimalQuantity& output, + UErrorCode& status) const; /** - * Get a VisibleDigitsWithExponent corresponding to a double - * as it would be formatted by this DecimalFormat. + * Get a DecimalQuantity corresponding to a formattable as it would be + * formatted by this DecimalFormat. * Internal, not intended for public use. * @internal */ - VisibleDigitsWithExponent &initVisibleDigitsWithExponent( - double number, - VisibleDigitsWithExponent &digits, - UErrorCode &status) const; + void formatToDecimalQuantity(const Formattable& number, number::impl::DecimalQuantity& output, + UErrorCode& status) const; - /** - * Get a VisibleDigitsWithExponent corresponding to a formattable - * as it would be formatted by this DecimalFormat. - * Internal, not intended for public use. - * @internal - */ - VisibleDigitsWithExponent &initVisibleDigitsWithExponent( - const Formattable &number, - VisibleDigitsWithExponent &digits, - UErrorCode &status) const; +#endif +#ifndef U_HIDE_DRAFT_API /** - * Get a VisibleDigitsWithExponent corresponding to a DigitList - * as it would be formatted by this DecimalFormat. - * Note: the DigitList may be modified. - * Internal, not intended for public use. - * @internal + * Converts this DecimalFormat to a NumberFormatter. Starting in ICU 60, + * NumberFormatter is the recommended way to format numbers. + * + * NOTE: The returned LocalizedNumberFormatter is owned by this DecimalFormat. + * If a non-const method is called on the DecimalFormat, or if the DecimalFormat + * is deleted, the object becomes invalid. If you plan to keep the return value + * beyond the lifetime of the DecimalFormat, copy it to a local variable: + * + *
+     * LocalizedNumberFormatter f = df->toNumberFormatter();
+     * 
+ * + * It is, however, safe to use the return value for chaining: + * + *
+     * FormattedNumber result = df->toNumberFormatter().formatDouble(123, status);
+     * 
+ * + * @param output The variable into which to store the LocalizedNumberFormatter. + * @return The output variable, for chaining. + * @draft ICU 62 */ - VisibleDigitsWithExponent &initVisibleDigitsWithExponent( - DigitList &number, - VisibleDigitsWithExponent &digits, - UErrorCode &status) const; - -#endif /* U_HIDE_INTERNAL_API */ - -public: + const number::LocalizedNumberFormatter& toNumberFormatter() const; +#endif /* U_HIDE_DRAFT_API */ /** * Return the class ID for this class. This is useful only for @@ -2081,217 +2103,57 @@ class U_I18N_API DecimalFormat: public NumberFormat { * other classes have different class IDs. * @stable ICU 2.0 */ - virtual UClassID getDynamicClassID(void) const; - -private: - - DecimalFormat(); // default constructor not implemented - - /** - * Initialize all fields of a new DecimalFormatter to a safe default value. - * Common code for use by constructors. - */ - void init(); - - /** - * Do real work of constructing a new DecimalFormat. - */ - void construct(UErrorCode& status, - UParseError& parseErr, - const UnicodeString* pattern = 0, - DecimalFormatSymbols* symbolsToAdopt = 0 - ); - - void handleCurrencySignInPattern(UErrorCode& status); - - void parse(const UnicodeString& text, - Formattable& result, - ParsePosition& pos, - char16_t* currency) const; - - enum { - fgStatusInfinite, - fgStatusLength // Leave last in list. - } StatusFlags; - - UBool subparse(const UnicodeString& text, - const UnicodeString* negPrefix, - const UnicodeString* negSuffix, - const UnicodeString* posPrefix, - const UnicodeString* posSuffix, - UBool complexCurrencyParsing, - int8_t type, - ParsePosition& parsePosition, - DigitList& digits, UBool* status, - char16_t* currency) const; - - // Mixed style parsing for currency. - // It parses against the current currency pattern - // using complex affix comparison - // parses against the currency plural patterns using complex affix comparison, - // and parses against the current pattern using simple affix comparison. - UBool parseForCurrency(const UnicodeString& text, - ParsePosition& parsePosition, - DigitList& digits, - UBool* status, - char16_t* currency) const; - - int32_t skipPadding(const UnicodeString& text, int32_t position) const; - - int32_t compareAffix(const UnicodeString& input, - int32_t pos, - UBool isNegative, - UBool isPrefix, - const UnicodeString* affixPat, - UBool complexCurrencyParsing, - int8_t type, - char16_t* currency) const; - - static UnicodeString& trimMarksFromAffix(const UnicodeString& affix, UnicodeString& trimmedAffix); - - UBool equalWithSignCompatibility(UChar32 lhs, UChar32 rhs) const; - - int32_t compareSimpleAffix(const UnicodeString& affix, - const UnicodeString& input, - int32_t pos, - UBool lenient) const; - - static int32_t skipPatternWhiteSpace(const UnicodeString& text, int32_t pos); - - static int32_t skipUWhiteSpace(const UnicodeString& text, int32_t pos); - - static int32_t skipUWhiteSpaceAndMarks(const UnicodeString& text, int32_t pos); - - static int32_t skipBidiMarks(const UnicodeString& text, int32_t pos); - - int32_t compareComplexAffix(const UnicodeString& affixPat, - const UnicodeString& input, - int32_t pos, - int8_t type, - char16_t* currency) const; - - static int32_t match(const UnicodeString& text, int32_t pos, UChar32 ch); + UClassID getDynamicClassID(void) const U_OVERRIDE; - static int32_t match(const UnicodeString& text, int32_t pos, const UnicodeString& str); + private: - static UBool matchSymbol(const UnicodeString &text, int32_t position, int32_t length, const UnicodeString &symbol, - UnicodeSet *sset, UChar32 schar); + /** Rebuilds the formatter object from the property bag. */ + void touch(UErrorCode& status); - static UBool matchDecimal(UChar32 symbolChar, - UBool sawDecimal, UChar32 sawDecimalChar, - const UnicodeSet *sset, UChar32 schar); - - static UBool matchGrouping(UChar32 groupingChar, - UBool sawGrouping, UChar32 sawGroupingChar, - const UnicodeSet *sset, - UChar32 decimalChar, const UnicodeSet *decimalSet, - UChar32 schar); - - // set up currency affix patterns for mix parsing. - // The patterns saved here are the affix patterns of default currency - // pattern and the unique affix patterns of the plural currency patterns. - // Those patterns are used by parseForCurrency(). - void setupCurrencyAffixPatterns(UErrorCode& status); - - // get the currency rounding with respect to currency usage - double getCurrencyRounding(const char16_t* currency, - UErrorCode* ec) const; - - // get the currency fraction with respect to currency usage - int getCurrencyFractionDigits(const char16_t* currency, - UErrorCode* ec) const; - - // hashtable operations - Hashtable* initHashForAffixPattern(UErrorCode& status); - - void deleteHashForAffixPattern(); - - void copyHashForAffixPattern(const Hashtable* source, - Hashtable* target, UErrorCode& status); - - DecimalFormatImpl *fImpl; + /** Rebuilds the formatter object, hiding the error code. */ + void touchNoError(); /** - * Constants. + * Updates the property bag with settings from the given pattern. + * + * @param pattern The pattern string to parse. + * @param ignoreRounding Whether to leave out rounding information (minFrac, maxFrac, and rounding + * increment) when parsing the pattern. This may be desirable if a custom rounding mode, such + * as CurrencyUsage, is to be used instead. One of {@link + * PatternStringParser#IGNORE_ROUNDING_ALWAYS}, {@link PatternStringParser#IGNORE_ROUNDING_IF_CURRENCY}, + * or {@link PatternStringParser#IGNORE_ROUNDING_NEVER}. + * @see PatternAndPropertyUtils#parseToExistingProperties */ + void setPropertiesFromPattern(const UnicodeString& pattern, int32_t ignoreRounding, + UErrorCode& status); + const numparse::impl::NumberParserImpl* getParser(UErrorCode& status) const; - EnumSet - fBoolFlags; + const numparse::impl::NumberParserImpl* getCurrencyParser(UErrorCode& status) const; + static void fieldPositionHelper(const number::FormattedNumber& formatted, FieldPosition& fieldPosition, + int32_t offset, UErrorCode& status); - // style is only valid when decimal formatter is constructed by - // DecimalFormat(pattern, decimalFormatSymbol, style) - int fStyle; + static void fieldPositionIteratorHelper(const number::FormattedNumber& formatted, + FieldPositionIterator* fpi, int32_t offset, UErrorCode& status); + void setupFastFormat(); - // Affix pattern set for currency. - // It is a set of AffixPatternsForCurrency, - // each element of the set saves the negative prefix pattern, - // negative suffix pattern, positive prefix pattern, - // and positive suffix pattern of a pattern. - // It is used for currency mixed style parsing. - // It is actually is a set. - // The set contains the default currency pattern from the locale, - // and the currency plural patterns. - // Since it is a set, it does not contain duplicated items. - // For example, if 2 currency plural patterns are the same, only one pattern - // is included in the set. When parsing, we do not check whether the plural - // count match or not. - Hashtable* fAffixPatternsForCurrency; + bool fastFormatDouble(double input, UnicodeString& output) const; - // Information needed for DecimalFormat to format/parse currency plural. - CurrencyPluralInfo* fCurrencyPluralInfo; + bool fastFormatInt64(int64_t input, UnicodeString& output) const; -#if UCONFIG_HAVE_PARSEALLINPUT - UNumberFormatAttributeValue fParseAllInput; -#endif + void doFastFormatInt32(int32_t input, bool isNegative, UnicodeString& output) const; - // Decimal Format Static Sets singleton. - const DecimalFormatStaticSets *fStaticSets; + //=====================================================================================// + // INSTANCE FIELDS // + //=====================================================================================// -protected: + // Only one instance field: keep all fields inside of an implementation class defined in number_mapper.h + number::impl::DecimalFormatFields* fields; -#ifndef U_HIDE_INTERNAL_API - /** - * Rounds a value according to the rules of this object. - * @internal - */ - DigitList& _round(const DigitList& number, DigitList& adjustedNum, UBool& isNegative, UErrorCode& status) const; -#endif /* U_HIDE_INTERNAL_API */ - - /** - * Returns the currency in effect for this formatter. Subclasses - * should override this method as needed. Unlike getCurrency(), - * this method should never return "". - * @result output parameter for null-terminated result, which must - * have a capacity of at least 4 - * @internal - */ - virtual void getEffectiveCurrency(char16_t* result, UErrorCode& ec) const; - - /** number of integer digits - * @stable ICU 2.4 - */ - static const int32_t kDoubleIntegerDigits; - /** number of fraction digits - * @stable ICU 2.4 - */ - static const int32_t kDoubleFractionDigits; - - /** - * When someone turns on scientific mode, we assume that more than this - * number of digits is due to flipping from some other mode that didn't - * restrict the maximum, and so we force 1 integer digit. We don't bother - * to track and see if someone is using exponential notation with more than - * this number, it wouldn't make sense anyway, and this is just to make sure - * that someone turning on scientific mode with default settings doesn't - * end up with lots of zeroes. - * @stable ICU 2.8 - */ - static const int32_t kMaxScientificIntegerDigits; + // Allow child class CompactDecimalFormat to access fProperties: + friend class CompactDecimalFormat; }; diff --git a/deps/icu-small/source/i18n/unicode/fmtable.h b/deps/icu-small/source/i18n/unicode/fmtable.h index 766a71969deadc..2359b61d46186e 100644 --- a/deps/icu-small/source/i18n/unicode/fmtable.h +++ b/deps/icu-small/source/i18n/unicode/fmtable.h @@ -33,17 +33,11 @@ U_NAMESPACE_BEGIN class CharString; -class DigitList; - -/** - * \def UNUM_INTERNAL_STACKARRAY_SIZE - * @internal - */ -#if U_PLATFORM == U_PF_OS400 -#define UNUM_INTERNAL_STACKARRAY_SIZE 144 -#else -#define UNUM_INTERNAL_STACKARRAY_SIZE 128 -#endif +namespace number { +namespace impl { +class DecimalQuantity; +} +} /** * Formattable objects can be passed to the Format class or @@ -649,24 +643,25 @@ class U_I18N_API Formattable : public UObject { * Internal function, do not use. * TODO: figure out how to make this be non-public. * NumberFormat::format(Formattable, ... - * needs to get at the DigitList, if it exists, for + * needs to get at the DecimalQuantity, if it exists, for * big decimal formatting. * @internal */ - DigitList *getDigitList() const { return fDecimalNum;} + number::impl::DecimalQuantity *getDecimalQuantity() const { return fDecimalQuantity;} /** - * @internal + * Export the value of this Formattable to a DecimalQuantity. + * @internal */ - DigitList *getInternalDigitList(); + void populateDecimalQuantity(number::impl::DecimalQuantity& output, UErrorCode& status) const; /** - * Adopt, and set value from, a DigitList + * Adopt, and set value from, a DecimalQuantity * Internal Function, do not use. - * @param dl the Digit List to be adopted + * @param dl the DecimalQuantity to be adopted * @internal */ - void adoptDigitList(DigitList *dl); + void adoptDecimalQuantity(number::impl::DecimalQuantity *dq); /** * Internal function to return the CharString pointer. @@ -706,9 +701,7 @@ class U_I18N_API Formattable : public UObject { CharString *fDecimalStr; - DigitList *fDecimalNum; - - char fStackData[UNUM_INTERNAL_STACKARRAY_SIZE]; // must be big enough for DigitList + number::impl::DecimalQuantity *fDecimalQuantity; Type fType; UnicodeString fBogus; // Bogus string when it's needed. diff --git a/deps/icu-small/source/i18n/unicode/fpositer.h b/deps/icu-small/source/i18n/unicode/fpositer.h index 8e9d69c547f849..81091f0ffad6b6 100644 --- a/deps/icu-small/source/i18n/unicode/fpositer.h +++ b/deps/icu-small/source/i18n/unicode/fpositer.h @@ -47,13 +47,6 @@ U_NAMESPACE_BEGIN class UVector32; -// Forward declaration for number formatting: -namespace number { -namespace impl { -class NumberStringBuilder; -} -} - /** * FieldPositionIterator returns the field ids and their start/limit positions generated * by a call to Format::format. See Format, NumberFormat, DecimalFormat. @@ -114,7 +107,6 @@ class U_I18N_API FieldPositionIterator : public UObject { void setData(UVector32 *adopt, UErrorCode& status); friend class FieldPositionIteratorHandler; - friend class number::impl::NumberStringBuilder; UVector32 *data; int32_t pos; diff --git a/deps/icu-small/source/i18n/unicode/measunit.h b/deps/icu-small/source/i18n/unicode/measunit.h index f7ddb4e20c559f..f552253544f890 100644 --- a/deps/icu-small/source/i18n/unicode/measunit.h +++ b/deps/icu-small/source/i18n/unicode/measunit.h @@ -400,14 +400,6 @@ class U_I18N_API MeasureUnit: public UObject { */ static MeasureUnit *createMilePerGallonImperial(UErrorCode &status); - /* - * The following were draft ICU 58, but have been withdrawn: - * static MeasureUnit *createEast(UErrorCode &status); - * static MeasureUnit *createNorth(UErrorCode &status); - * static MeasureUnit *createSouth(UErrorCode &status); - * static MeasureUnit *createWest(UErrorCode &status); - */ - /** * Returns unit of digital: bit. * Caller owns returned value and must free it. diff --git a/deps/icu-small/source/i18n/unicode/nounit.h b/deps/icu-small/source/i18n/unicode/nounit.h index 290e77e8806040..288f268d66d658 100644 --- a/deps/icu-small/source/i18n/unicode/nounit.h +++ b/deps/icu-small/source/i18n/unicode/nounit.h @@ -61,6 +61,12 @@ class U_I18N_API NoUnit: public MeasureUnit { */ NoUnit(const NoUnit& other); + /** + * Destructor. + * @draft ICU 60 + */ + virtual ~NoUnit(); + /** * Return a polymorphic clone of this object. The result will * have the same class as returned by getDynamicClassID(). @@ -86,12 +92,6 @@ class U_I18N_API NoUnit: public MeasureUnit { */ static UClassID U_EXPORT2 getStaticClassID(); - /** - * Destructor. - * @draft ICU 60 - */ - virtual ~NoUnit(); - private: /** * Constructor diff --git a/deps/icu-small/source/i18n/unicode/numberformatter.h b/deps/icu-small/source/i18n/unicode/numberformatter.h index 3fbb33cceeabf7..3ab08319f73bc7 100644 --- a/deps/icu-small/source/i18n/unicode/numberformatter.h +++ b/deps/icu-small/source/i18n/unicode/numberformatter.h @@ -17,6 +17,8 @@ #include "unicode/plurrule.h" #include "unicode/ucurr.h" #include "unicode/unum.h" +#include "unicode/unumberformatter.h" +#include "unicode/uobject.h" #ifndef U_HIDE_DRAFT_API @@ -31,11 +33,11 @@ * // Most basic usage: * NumberFormatter::withLocale(...).format(123).toString(); // 1,234 in en-US * - * // Custom notation, unit, and rounding strategy: + * // Custom notation, unit, and rounding precision: * NumberFormatter::with() * .notation(Notation::compactShort()) * .unit(CurrencyUnit("EUR", status)) - * .rounding(Rounder::maxDigits(2)) + * .precision(Precision::maxDigits(2)) * .locale(...) * .format(1234) * .toString(); // €1.2K in en-US @@ -43,7 +45,7 @@ * // Create a formatter in a singleton for use later: * static const LocalizedNumberFormatter formatter = NumberFormatter::withLocale(...) * .unit(NoUnit::percent()) - * .rounding(Rounder::fixedFraction(3)); + * .precision(Precision::fixedFraction(3)); * formatter.format(5.9831).toString(); // 5.983% in en-US * * // Create a "template" in a singleton but without setting a locale until the call site: @@ -63,7 +65,7 @@ * *
  * UnlocalizedNumberFormatter formatter = UnlocalizedNumberFormatter::with().notation(Notation::scientific());
- * formatter.rounding(Rounder.maxFraction(2)); // does nothing!
+ * formatter.precision(Precision.maxFraction(2)); // does nothing!
  * formatter.locale(Locale.getEnglish()).format(9.8765).toString(); // prints "9.8765E0", not "9.88E0"
  * 
* @@ -74,321 +76,23 @@ * @author Shane Carr */ -/** - * An enum declaring how to render units, including currencies. Example outputs when formatting 123 USD and 123 - * meters in en-CA: - * - *

- *

    - *
  • NARROW*: "$123.00" and "123 m" - *
  • SHORT: "US$ 123.00" and "123 m" - *
  • FULL_NAME: "123.00 US dollars" and "123 meters" - *
  • ISO_CODE: "USD 123.00" and undefined behavior - *
  • HIDDEN: "123.00" and "123" - *
- * - *

- * This enum is similar to {@link com.ibm.icu.text.MeasureFormat.FormatWidth}. - * - * @draft ICU 60 - */ -typedef enum UNumberUnitWidth { - /** - * Print an abbreviated version of the unit name. Similar to SHORT, but always use the shortest available - * abbreviation or symbol. This option can be used when the context hints at the identity of the unit. For more - * information on the difference between NARROW and SHORT, see SHORT. - * - *

- * In CLDR, this option corresponds to the "Narrow" format for measure units and the "¤¤¤¤¤" placeholder for - * currencies. - * - * @draft ICU 60 - */ - UNUM_UNIT_WIDTH_NARROW, - - /** - * Print an abbreviated version of the unit name. Similar to NARROW, but use a slightly wider abbreviation or - * symbol when there may be ambiguity. This is the default behavior. - * - *

- * For example, in es-US, the SHORT form for Fahrenheit is "{0} °F", but the NARROW form is "{0}°", - * since Fahrenheit is the customary unit for temperature in that locale. - * - *

- * In CLDR, this option corresponds to the "Short" format for measure units and the "¤" placeholder for - * currencies. - * - * @draft ICU 60 - */ - UNUM_UNIT_WIDTH_SHORT, - - /** - * Print the full name of the unit, without any abbreviations. - * - *

- * In CLDR, this option corresponds to the default format for measure units and the "¤¤¤" placeholder for - * currencies. - * - * @draft ICU 60 - */ - UNUM_UNIT_WIDTH_FULL_NAME, - - /** - * Use the three-digit ISO XXX code in place of the symbol for displaying currencies. The behavior of this - * option is currently undefined for use with measure units. - * - *

- * In CLDR, this option corresponds to the "¤¤" placeholder for currencies. - * - * @draft ICU 60 - */ - UNUM_UNIT_WIDTH_ISO_CODE, - - /** - * Format the number according to the specified unit, but do not display the unit. For currencies, apply - * monetary symbols and formats as with SHORT, but omit the currency symbol. For measure units, the behavior is - * equivalent to not specifying the unit at all. - * - * @draft ICU 60 - */ - UNUM_UNIT_WIDTH_HIDDEN - -#ifndef U_HIDE_INTERNAL_API - , - /** - * One more than the highest UNumberUnitWidth value. - * - * @internal ICU 60: The numeric value may change over time; see ICU ticket #12420. - */ - UNUM_UNIT_WIDTH_COUNT -#endif // U_HIDE_INTERNAL_API -} UNumberUnitWidth; - -/** - * An enum declaring the strategy for when and how to display grouping separators (i.e., the - * separator, often a comma or period, after every 2-3 powers of ten). The choices are several - * pre-built strategies for different use cases that employ locale data whenever possible. Example - * outputs for 1234 and 1234567 in en-IN: - * - *

    - *
  • OFF: 1234 and 12345 - *
  • MIN2: 1234 and 12,34,567 - *
  • AUTO: 1,234 and 12,34,567 - *
  • ON_ALIGNED: 1,234 and 12,34,567 - *
  • THOUSANDS: 1,234 and 1,234,567 - *
- * - *

- * The default is AUTO, which displays grouping separators unless the locale data says that grouping - * is not customary. To force grouping for all numbers greater than 1000 consistently across locales, - * use ON_ALIGNED. On the other hand, to display grouping less frequently than the default, use MIN2 - * or OFF. See the docs of each option for details. - * - *

- * Note: This enum specifies the strategy for grouping sizes. To set which character to use as the - * grouping separator, use the "symbols" setter. - * - * @draft ICU 61 - */ -typedef enum UGroupingStrategy { - /** - * Do not display grouping separators in any locale. - * - * @draft ICU 61 - */ - UNUM_GROUPING_OFF, - - /** - * Display grouping using locale defaults, except do not show grouping on values smaller than - * 10000 (such that there is a minimum of two digits before the first separator). - * - *

- * Note that locales may restrict grouping separators to be displayed only on 1 million or - * greater (for example, ee and hu) or disable grouping altogether (for example, bg currency). - * - *

- * Locale data is used to determine whether to separate larger numbers into groups of 2 - * (customary in South Asia) or groups of 3 (customary in Europe and the Americas). - * - * @draft ICU 61 - */ - UNUM_GROUPING_MIN2, - - /** - * Display grouping using the default strategy for all locales. This is the default behavior. - * - *

- * Note that locales may restrict grouping separators to be displayed only on 1 million or - * greater (for example, ee and hu) or disable grouping altogether (for example, bg currency). - * - *

- * Locale data is used to determine whether to separate larger numbers into groups of 2 - * (customary in South Asia) or groups of 3 (customary in Europe and the Americas). - * - * @draft ICU 61 - */ - UNUM_GROUPING_AUTO, - - /** - * Always display the grouping separator on values of at least 1000. - * - *

- * This option ignores the locale data that restricts or disables grouping, described in MIN2 and - * AUTO. This option may be useful to normalize the alignment of numbers, such as in a - * spreadsheet. - * - *

- * Locale data is used to determine whether to separate larger numbers into groups of 2 - * (customary in South Asia) or groups of 3 (customary in Europe and the Americas). - * - * @draft ICU 61 - */ - UNUM_GROUPING_ON_ALIGNED, - - /** - * Use the Western defaults: groups of 3 and enabled for all numbers 1000 or greater. Do not use - * locale data for determining the grouping strategy. - * - * @draft ICU 61 - */ - UNUM_GROUPING_THOUSANDS - -} UGroupingStrategy; - -/** - * An enum declaring how to denote positive and negative numbers. Example outputs when formatting - * 123, 0, and -123 in en-US: - * - *

    - *
  • AUTO: "123", "0", and "-123" - *
  • ALWAYS: "+123", "+0", and "-123" - *
  • NEVER: "123", "0", and "123" - *
  • ACCOUNTING: "$123", "$0", and "($123)" - *
  • ACCOUNTING_ALWAYS: "+$123", "+$0", and "($123)" - *
  • EXCEPT_ZERO: "+123", "0", and "-123" - *
  • ACCOUNTING_EXCEPT_ZERO: "+$123", "$0", and "($123)" - *
- * - *

- * The exact format, including the position and the code point of the sign, differ by locale. - * - * @draft ICU 60 - */ -typedef enum UNumberSignDisplay { - /** - * Show the minus sign on negative numbers, and do not show the sign on positive numbers. This is the default - * behavior. - * - * @draft ICU 60 - */ - UNUM_SIGN_AUTO, - - /** - * Show the minus sign on negative numbers and the plus sign on positive numbers, including zero. - * To hide the sign on zero, see {@link UNUM_SIGN_EXCEPT_ZERO}. - * - * @draft ICU 60 - */ - UNUM_SIGN_ALWAYS, - - /** - * Do not show the sign on positive or negative numbers. - * - * @draft ICU 60 - */ - UNUM_SIGN_NEVER, - - /** - * Use the locale-dependent accounting format on negative numbers, and do not show the sign on positive numbers. - * - *

- * The accounting format is defined in CLDR and varies by locale; in many Western locales, the format is a pair - * of parentheses around the number. - * - *

- * Note: Since CLDR defines the accounting format in the monetary context only, this option falls back to the - * AUTO sign display strategy when formatting without a currency unit. This limitation may be lifted in the - * future. - * - * @draft ICU 60 - */ - UNUM_SIGN_ACCOUNTING, - - /** - * Use the locale-dependent accounting format on negative numbers, and show the plus sign on - * positive numbers, including zero. For more information on the accounting format, see the - * ACCOUNTING sign display strategy. To hide the sign on zero, see - * {@link UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO}. - * - * @draft ICU 60 - */ - UNUM_SIGN_ACCOUNTING_ALWAYS, - - /** - * Show the minus sign on negative numbers and the plus sign on positive numbers. Do not show a - * sign on zero. - * - * @draft ICU 61 - */ - UNUM_SIGN_EXCEPT_ZERO, - - /** - * Use the locale-dependent accounting format on negative numbers, and show the plus sign on - * positive numbers. Do not show a sign on zero. For more information on the accounting format, - * see the ACCOUNTING sign display strategy. - * - * @draft ICU 61 - */ - UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO +U_NAMESPACE_BEGIN -#ifndef U_HIDE_INTERNAL_API - , - /** - * One more than the highest UNumberSignDisplay value. - * - * @internal ICU 60: The numeric value may change over time; see ICU ticket #12420. - */ - UNUM_SIGN_COUNT -#endif // U_HIDE_INTERNAL_API -} UNumberSignDisplay; +// Forward declarations: +class IFixedDecimal; +class FieldPositionIteratorHandler; -/** - * An enum declaring how to render the decimal separator. - * - *

- *

    - *
  • UNUM_DECIMAL_SEPARATOR_AUTO: "1", "1.1" - *
  • UNUM_DECIMAL_SEPARATOR_ALWAYS: "1.", "1.1" - *
- */ -typedef enum UNumberDecimalSeparatorDisplay { - /** - * Show the decimal separator when there are one or more digits to display after the separator, and do not show - * it otherwise. This is the default behavior. - * - * @draft ICU 60 - */ - UNUM_DECIMAL_SEPARATOR_AUTO, +namespace numparse { +namespace impl { - /** - * Always show the decimal separator, even if there are no digits to display after the separator. - * - * @draft ICU 60 - */ - UNUM_DECIMAL_SEPARATOR_ALWAYS +// Forward declarations: +class NumberParserImpl; +class MultiplierParseHandler; -#ifndef U_HIDE_INTERNAL_API - , - /** - * One more than the highest UNumberDecimalSeparatorDisplay value. - * - * @internal ICU 60: The numeric value may change over time; see ICU ticket #12420. - */ - UNUM_DECIMAL_SEPARATOR_COUNT -#endif // U_HIDE_INTERNAL_API -} UNumberDecimalMarkDisplay; +} +} -U_NAMESPACE_BEGIN namespace number { // icu::number +namespace number { // icu::number // Forward declarations: class UnlocalizedNumberFormatter; @@ -396,15 +100,14 @@ class LocalizedNumberFormatter; class FormattedNumber; class Notation; class ScientificNotation; -class Rounder; -class FractionRounder; -class CurrencyRounder; -class IncrementRounder; +class Precision; +class FractionPrecision; +class CurrencyPrecision; +class IncrementPrecision; class IntegerWidth; namespace impl { -#ifndef U_HIDE_INTERNAL_API /** * Datatype for minimum/maximum fraction digits. Must be able to hold kMaxIntFracSig. * @@ -419,24 +122,28 @@ typedef int16_t digits_t; * @internal */ static constexpr int32_t DEFAULT_THRESHOLD = 3; -#endif // U_HIDE_INTERNAL_API // Forward declarations: class Padder; struct MacroProps; struct MicroProps; class DecimalQuantity; -struct NumberFormatterResults; +struct UFormattedNumberData; class NumberFormatterImpl; struct ParsedPatternInfo; class ScientificModifier; class MultiplierProducer; -class MutablePatternModifier; -class LongNameHandler; +class RoundingImpl; class ScientificHandler; -class CompactHandler; class Modifier; class NumberStringBuilder; +class AffixPatternProvider; +class NumberPropertyMapper; +struct DecimalFormatProperties; +class MultiplierFormatHandler; +class CurrencySymbols; +class GeneratorHelpers; +class DecNum; } // namespace impl @@ -530,13 +237,13 @@ class U_I18N_API Notation : public UMemory { * * *

- * When compact notation is specified without an explicit rounding strategy, numbers are rounded off to the closest + * When compact notation is specified without an explicit rounding precision, numbers are rounded off to the closest * integer after scaling the number by the corresponding power of 10, but with a digit shown after the decimal - * separator if there is only one digit before the decimal separator. The default compact notation rounding strategy + * separator if there is only one digit before the decimal separator. The default compact notation rounding precision * is equivalent to: * *

-     * Rounder.integer().withMinDigits(2)
+     * Precision::integer().withMinDigits(2)
      * 
* * @return A CompactNotation for passing to the NumberFormatter notation() setter. @@ -641,6 +348,9 @@ class U_I18N_API Notation : public UMemory { friend class impl::NumberFormatterImpl; friend class impl::ScientificModifier; friend class impl::ScientificHandler; + + // To allow access to the skeleton generation code: + friend class impl::GeneratorHelpers; }; /** @@ -687,21 +397,36 @@ class U_I18N_API ScientificNotation : public Notation { // Inherit constructor using Notation::Notation; + // Raw constructor for NumberPropertyMapper + ScientificNotation(int8_t fEngineeringInterval, bool fRequireMinInt, impl::digits_t fMinExponentDigits, + UNumberSignDisplay fExponentSignDisplay); + friend class Notation; + + // So that NumberPropertyMapper can create instances + friend class impl::NumberPropertyMapper; }; // Reserve extra names in case they are added as classes in the future: -typedef Rounder DigitRounder; +typedef Precision SignificantDigitsPrecision; + +// Typedefs for ICU 60/61 compatibility. +// These will be removed in ICU 64. +// See http://bugs.icu-project.org/trac/ticket/13746 +typedef Precision Rounder; +typedef FractionPrecision FractionRounder; +typedef IncrementPrecision IncrementRounder; +typedef CurrencyPrecision CurrencyRounder; /** - * A class that defines the rounding strategy to be used when formatting numbers in NumberFormatter. + * A class that defines the rounding precision to be used when formatting numbers in NumberFormatter. * *

- * To create a Rounder, use one of the factory methods. + * To create a Precision, use one of the factory methods. * * @draft ICU 60 */ -class U_I18N_API Rounder : public UMemory { +class U_I18N_API Precision : public UMemory { public: /** @@ -717,18 +442,18 @@ class U_I18N_API Rounder : public UMemory { *

* http://www.serpentine.com/blog/2011/06/29/here-be-dragons-advances-in-problems-you-didnt-even-know-you-had/ * - * @return A Rounder for chaining or passing to the NumberFormatter rounding() setter. + * @return A Precision for chaining or passing to the NumberFormatter precision() setter. * @draft ICU 60 */ - static Rounder unlimited(); + static Precision unlimited(); /** * Show numbers rounded if necessary to the nearest integer. * - * @return A FractionRounder for chaining or passing to the NumberFormatter rounding() setter. + * @return A FractionPrecision for chaining or passing to the NumberFormatter precision() setter. * @draft ICU 60 */ - static FractionRounder integer(); + static FractionPrecision integer(); /** * Show numbers rounded if necessary to a certain number of fraction places (numerals after the decimal separator). @@ -754,10 +479,10 @@ class U_I18N_API Rounder : public UMemory { * @param minMaxFractionPlaces * The minimum and maximum number of numerals to display after the decimal separator (rounding if too * long or padding with zeros if too short). - * @return A FractionRounder for chaining or passing to the NumberFormatter rounding() setter. + * @return A FractionPrecision for chaining or passing to the NumberFormatter precision() setter. * @draft ICU 60 */ - static FractionRounder fixedFraction(int32_t minMaxFractionPlaces); + static FractionPrecision fixedFraction(int32_t minMaxFractionPlaces); /** * Always show at least a certain number of fraction places after the decimal separator, padding with zeros if @@ -769,10 +494,10 @@ class U_I18N_API Rounder : public UMemory { * @param minFractionPlaces * The minimum number of numerals to display after the decimal separator (padding with zeros if * necessary). - * @return A FractionRounder for chaining or passing to the NumberFormatter rounding() setter. + * @return A FractionPrecision for chaining or passing to the NumberFormatter precision() setter. * @draft ICU 60 */ - static FractionRounder minFraction(int32_t minFractionPlaces); + static FractionPrecision minFraction(int32_t minFractionPlaces); /** * Show numbers rounded if necessary to a certain number of fraction places (numerals after the decimal separator). @@ -781,10 +506,10 @@ class U_I18N_API Rounder : public UMemory { * * @param maxFractionPlaces * The maximum number of numerals to display after the decimal mark (rounding if necessary). - * @return A FractionRounder for chaining or passing to the NumberFormatter rounding() setter. + * @return A FractionPrecision for chaining or passing to the NumberFormatter precision() setter. * @draft ICU 60 */ - static FractionRounder maxFraction(int32_t maxFractionPlaces); + static FractionPrecision maxFraction(int32_t maxFractionPlaces); /** * Show numbers rounded if necessary to a certain number of fraction places (numerals after the decimal separator); @@ -796,10 +521,10 @@ class U_I18N_API Rounder : public UMemory { * necessary). * @param maxFractionPlaces * The maximum number of numerals to display after the decimal separator (rounding if necessary). - * @return A FractionRounder for chaining or passing to the NumberFormatter rounding() setter. + * @return A FractionPrecision for chaining or passing to the NumberFormatter precision() setter. * @draft ICU 60 */ - static FractionRounder minMaxFraction(int32_t minFractionPlaces, int32_t maxFractionPlaces); + static FractionPrecision minMaxFraction(int32_t minFractionPlaces, int32_t maxFractionPlaces); /** * Show numbers rounded if necessary to a certain number of significant digits or significant figures. Additionally, @@ -811,10 +536,10 @@ class U_I18N_API Rounder : public UMemory { * @param minMaxSignificantDigits * The minimum and maximum number of significant digits to display (rounding if too long or padding with * zeros if too short). - * @return A Rounder for chaining or passing to the NumberFormatter rounding() setter. - * @draft ICU 60 + * @return A precision for chaining or passing to the NumberFormatter precision() setter. + * @draft ICU 62 */ - static DigitRounder fixedDigits(int32_t minMaxSignificantDigits); + static SignificantDigitsPrecision fixedSignificantDigits(int32_t minMaxSignificantDigits); /** * Always show at least a certain number of significant digits/figures, padding with zeros if necessary. Do not @@ -825,20 +550,20 @@ class U_I18N_API Rounder : public UMemory { * * @param minSignificantDigits * The minimum number of significant digits to display (padding with zeros if too short). - * @return A Rounder for chaining or passing to the NumberFormatter rounding() setter. - * @draft ICU 60 + * @return A precision for chaining or passing to the NumberFormatter precision() setter. + * @draft ICU 62 */ - static DigitRounder minDigits(int32_t minSignificantDigits); + static SignificantDigitsPrecision minSignificantDigits(int32_t minSignificantDigits); /** * Show numbers rounded if necessary to a certain number of significant digits/figures. * * @param maxSignificantDigits * The maximum number of significant digits to display (rounding if too long). - * @return A Rounder for chaining or passing to the NumberFormatter rounding() setter. - * @draft ICU 60 + * @return A precision for chaining or passing to the NumberFormatter precision() setter. + * @draft ICU 62 */ - static DigitRounder maxDigits(int32_t maxSignificantDigits); + static SignificantDigitsPrecision maxSignificantDigits(int32_t maxSignificantDigits); /** * Show numbers rounded if necessary to a certain number of significant digits/figures; in addition, always show at @@ -848,10 +573,36 @@ class U_I18N_API Rounder : public UMemory { * The minimum number of significant digits to display (padding with zeros if necessary). * @param maxSignificantDigits * The maximum number of significant digits to display (rounding if necessary). - * @return A Rounder for chaining or passing to the NumberFormatter rounding() setter. - * @draft ICU 60 + * @return A precision for chaining or passing to the NumberFormatter precision() setter. + * @draft ICU 62 */ - static DigitRounder minMaxDigits(int32_t minSignificantDigits, int32_t maxSignificantDigits); + static SignificantDigitsPrecision minMaxSignificantDigits(int32_t minSignificantDigits, + int32_t maxSignificantDigits); + +#ifndef U_HIDE_DEPRECATED_API + // Compatiblity methods that will be removed in ICU 64. + // See http://bugs.icu-project.org/trac/ticket/13746 + + /** @deprecated ICU 62 */ + static inline SignificantDigitsPrecision fixedDigits(int32_t a) { + return fixedSignificantDigits(a); + } + + /** @deprecated ICU 62 */ + static inline SignificantDigitsPrecision minDigits(int32_t a) { + return minSignificantDigits(a); + } + + /** @deprecated ICU 62 */ + static inline SignificantDigitsPrecision maxDigits(int32_t a) { + return maxSignificantDigits(a); + } + + /** @deprecated ICU 62 */ + static inline SignificantDigitsPrecision minMaxDigits(int32_t a, int32_t b) { + return minMaxSignificantDigits(a, b); + } +#endif /* U_HIDE_DEPRECATED_API */ /** * Show numbers rounded if necessary to the closest multiple of a certain rounding increment. For example, if the @@ -864,20 +615,21 @@ class U_I18N_API Rounder : public UMemory { * decimal separator (to display 1.2 as "1.00" and 1.3 as "1.50"), you can run: * *

-     * Rounder::increment(0.5).withMinFraction(2)
+     * Precision::increment(0.5).withMinFraction(2)
      * 
* * @param roundingIncrement * The increment to which to round numbers. - * @return A Rounder for chaining or passing to the NumberFormatter rounding() setter. + * @return A precision for chaining or passing to the NumberFormatter precision() setter. * @draft ICU 60 */ - static IncrementRounder increment(double roundingIncrement); + static IncrementPrecision increment(double roundingIncrement); /** - * Show numbers rounded and padded according to the rules for the currency unit. The most common rounding settings - * for currencies include Rounder.fixedFraction(2), Rounder.integer(), and - * Rounder.increment(0.05) for cash transactions ("nickel rounding"). + * Show numbers rounded and padded according to the rules for the currency unit. The most common + * rounding precision settings for currencies include Precision::fixedFraction(2), + * Precision::integer(), and Precision::increment(0.05) for cash transactions + * ("nickel rounding"). * *

* The exact rounding details will be resolved at runtime based on the currency unit specified in the @@ -887,24 +639,28 @@ class U_I18N_API Rounder : public UMemory { * @param currencyUsage * Either STANDARD (for digital transactions) or CASH (for transactions where the rounding increment may * be limited by the available denominations of cash or coins). - * @return A CurrencyRounder for chaining or passing to the NumberFormatter rounding() setter. + * @return A CurrencyPrecision for chaining or passing to the NumberFormatter precision() setter. * @draft ICU 60 */ - static CurrencyRounder currency(UCurrencyUsage currencyUsage); + static CurrencyPrecision currency(UCurrencyUsage currencyUsage); +#ifndef U_HIDE_DEPRECATED_API /** * Sets the rounding mode to use when picking the direction to round (up or down). Common values * include HALF_EVEN, HALF_UP, and FLOOR. The default is HALF_EVEN. * * @param roundingMode * The RoundingMode to use. - * @return A Rounder for passing to the NumberFormatter rounding() setter. - * @draft ICU 60 + * @return A Precision for passing to the NumberFormatter precision() setter. + * @deprecated ICU 62 Use the top-level roundingMode() setting instead. + * This method will be removed in ICU 64. + * See http://bugs.icu-project.org/trac/ticket/13746 */ - Rounder withMode(UNumberFormatRoundingMode roundingMode) const; + Precision withMode(UNumberFormatRoundingMode roundingMode) const; +#endif /* U_HIDE_DEPRECATED_API */ private: - enum RounderType { + enum PrecisionType { RND_BOGUS, RND_NONE, RND_FRACTION, @@ -912,11 +668,10 @@ class U_I18N_API Rounder : public UMemory { RND_FRACTION_SIGNIFICANT, RND_INCREMENT, RND_CURRENCY, - RND_PASS_THROUGH, RND_ERROR } fType; - union RounderUnion { + union PrecisionUnion { struct FractionSignificantSettings { // For RND_FRACTION, RND_SIGNIFICANT, and RND_FRACTION_SIGNIFICANT impl::digits_t fMinFrac; @@ -927,24 +682,27 @@ class U_I18N_API Rounder : public UMemory { struct IncrementSettings { double fIncrement; impl::digits_t fMinFrac; + impl::digits_t fMaxFrac; } increment; // For RND_INCREMENT UCurrencyUsage currencyUsage; // For RND_CURRENCY UErrorCode errorCode; // For RND_ERROR } fUnion; - typedef RounderUnion::FractionSignificantSettings FractionSignificantSettings; - typedef RounderUnion::IncrementSettings IncrementSettings; + typedef PrecisionUnion::FractionSignificantSettings FractionSignificantSettings; + typedef PrecisionUnion::IncrementSettings IncrementSettings; + /** The Precision encapsulates the RoundingMode when used within the implementation. */ UNumberFormatRoundingMode fRoundingMode; - Rounder(const RounderType &type, const RounderUnion &union_, UNumberFormatRoundingMode roundingMode) + Precision(const PrecisionType& type, const PrecisionUnion& union_, + UNumberFormatRoundingMode roundingMode) : fType(type), fUnion(union_), fRoundingMode(roundingMode) {} - Rounder(UErrorCode errorCode) : fType(RND_ERROR) { + Precision(UErrorCode errorCode) : fType(RND_ERROR) { fUnion.errorCode = errorCode; } - Rounder() : fType(RND_BOGUS) {} + Precision() : fType(RND_BOGUS) {} bool isBogus() const { return fType == RND_BOGUS; @@ -958,47 +716,21 @@ class U_I18N_API Rounder : public UMemory { return FALSE; } - // On the parent type so that this method can be called internally on Rounder instances. - Rounder withCurrency(const CurrencyUnit ¤cy, UErrorCode &status) const; - - /** NON-CONST: mutates the current instance. */ - void setLocaleData(const CurrencyUnit ¤cy, UErrorCode &status); - - void apply(impl::DecimalQuantity &value, UErrorCode &status) const; - - /** Version of {@link #apply} that obeys minInt constraints. Used for scientific notation compatibility mode. */ - void apply(impl::DecimalQuantity &value, int32_t minInt, UErrorCode status); - - /** - * Rounding endpoint used by Engineering and Compact notation. Chooses the most appropriate multiplier (magnitude - * adjustment), applies the adjustment, rounds, and returns the chosen multiplier. - * - *

- * In most cases, this is simple. However, when rounding the number causes it to cross a multiplier boundary, we - * need to re-do the rounding. For example, to display 999,999 in Engineering notation with 2 sigfigs, first you - * guess the multiplier to be -3. However, then you end up getting 1000E3, which is not the correct output. You then - * change your multiplier to be -6, and you get 1.0E6, which is correct. - * - * @param input The quantity to process. - * @param producer Function to call to return a multiplier based on a magnitude. - * @return The number of orders of magnitude the input was adjusted by this method. - */ - int32_t - chooseMultiplierAndApply(impl::DecimalQuantity &input, const impl::MultiplierProducer &producer, - UErrorCode &status); + // On the parent type so that this method can be called internally on Precision instances. + Precision withCurrency(const CurrencyUnit ¤cy, UErrorCode &status) const; - static FractionRounder constructFraction(int32_t minFrac, int32_t maxFrac); + static FractionPrecision constructFraction(int32_t minFrac, int32_t maxFrac); - static Rounder constructSignificant(int32_t minSig, int32_t maxSig); + static Precision constructSignificant(int32_t minSig, int32_t maxSig); - static Rounder - constructFractionSignificant(const FractionRounder &base, int32_t minSig, int32_t maxSig); + static Precision + constructFractionSignificant(const FractionPrecision &base, int32_t minSig, int32_t maxSig); - static IncrementRounder constructIncrement(double increment, int32_t minFrac); + static IncrementPrecision constructIncrement(double increment, int32_t minFrac); - static CurrencyRounder constructCurrency(UCurrencyUsage usage); + static CurrencyPrecision constructCurrency(UCurrencyUsage usage); - static Rounder constructPassThrough(); + static Precision constructPassThrough(); // To allow MacroProps/MicroProps to initialize bogus instances: friend struct impl::MacroProps; @@ -1007,28 +739,31 @@ class U_I18N_API Rounder : public UMemory { // To allow NumberFormatterImpl to access isBogus() and other internal methods: friend class impl::NumberFormatterImpl; - // To give access to apply() and chooseMultiplierAndApply(): - friend class impl::MutablePatternModifier; - friend class impl::LongNameHandler; - friend class impl::ScientificHandler; - friend class impl::CompactHandler; + // To allow NumberPropertyMapper to create instances from DecimalFormatProperties: + friend class impl::NumberPropertyMapper; + + // To allow access to the main implementation class: + friend class impl::RoundingImpl; // To allow child classes to call private methods: - friend class FractionRounder; - friend class CurrencyRounder; - friend class IncrementRounder; + friend class FractionPrecision; + friend class CurrencyPrecision; + friend class IncrementPrecision; + + // To allow access to the skeleton generation code: + friend class impl::GeneratorHelpers; }; /** - * A class that defines a rounding strategy based on a number of fraction places and optionally significant digits to be + * A class that defines a rounding precision based on a number of fraction places and optionally significant digits to be * used when formatting numbers in NumberFormatter. * *

- * To create a FractionRounder, use one of the factory methods on Rounder. + * To create a FractionPrecision, use one of the factory methods on Precision. * * @draft ICU 60 */ -class U_I18N_API FractionRounder : public Rounder { +class U_I18N_API FractionPrecision : public Precision { public: /** * Ensure that no less than this number of significant digits are retained when rounding according to fraction @@ -1043,10 +778,10 @@ class U_I18N_API FractionRounder : public Rounder { * * @param minSignificantDigits * The number of significant figures to guarantee. - * @return A Rounder for chaining or passing to the NumberFormatter rounding() setter. + * @return A precision for chaining or passing to the NumberFormatter precision() setter. * @draft ICU 60 */ - Rounder withMinDigits(int32_t minSignificantDigits) const; + Precision withMinDigits(int32_t minSignificantDigits) const; /** * Ensure that no more than this number of significant digits are retained when rounding according to fraction @@ -1062,36 +797,36 @@ class U_I18N_API FractionRounder : public Rounder { * * @param maxSignificantDigits * Round the number to no more than this number of significant figures. - * @return A Rounder for chaining or passing to the NumberFormatter rounding() setter. + * @return A precision for chaining or passing to the NumberFormatter precision() setter. * @draft ICU 60 */ - Rounder withMaxDigits(int32_t maxSignificantDigits) const; + Precision withMaxDigits(int32_t maxSignificantDigits) const; private: // Inherit constructor - using Rounder::Rounder; + using Precision::Precision; // To allow parent class to call this class's constructor: - friend class Rounder; + friend class Precision; }; /** - * A class that defines a rounding strategy parameterized by a currency to be used when formatting numbers in + * A class that defines a rounding precision parameterized by a currency to be used when formatting numbers in * NumberFormatter. * *

- * To create a CurrencyRounder, use one of the factory methods on Rounder. + * To create a CurrencyPrecision, use one of the factory methods on Precision. * * @draft ICU 60 */ -class U_I18N_API CurrencyRounder : public Rounder { +class U_I18N_API CurrencyPrecision : public Precision { public: /** - * Associates a currency with this rounding strategy. + * Associates a currency with this rounding precision. * *

* Calling this method is not required, because the currency specified in unit() - * is automatically applied to currency rounding strategies. However, + * is automatically applied to currency rounding precisions. However, * this method enables you to override that automatic association. * *

@@ -1099,30 +834,30 @@ class U_I18N_API CurrencyRounder : public Rounder { * currency format. * * @param currency - * The currency to associate with this rounding strategy. - * @return A Rounder for chaining or passing to the NumberFormatter rounding() setter. + * The currency to associate with this rounding precision. + * @return A precision for chaining or passing to the NumberFormatter precision() setter. * @draft ICU 60 */ - Rounder withCurrency(const CurrencyUnit ¤cy) const; + Precision withCurrency(const CurrencyUnit ¤cy) const; private: // Inherit constructor - using Rounder::Rounder; + using Precision::Precision; // To allow parent class to call this class's constructor: - friend class Rounder; + friend class Precision; }; /** - * A class that defines a rounding strategy parameterized by a rounding increment to be used when formatting numbers in + * A class that defines a rounding precision parameterized by a rounding increment to be used when formatting numbers in * NumberFormatter. * *

- * To create an IncrementRounder, use one of the factory methods on Rounder. + * To create an IncrementPrecision, use one of the factory methods on Precision. * * @draft ICU 60 */ -class U_I18N_API IncrementRounder : public Rounder { +class U_I18N_API IncrementPrecision : public Precision { public: /** * Specifies the minimum number of fraction digits to render after the decimal separator, padding with zeros if @@ -1136,17 +871,17 @@ class U_I18N_API IncrementRounder : public Rounder { * Note: In ICU4J, this functionality is accomplished via the scale of the BigDecimal rounding increment. * * @param minFrac The minimum number of digits after the decimal separator. - * @return A Rounder for chaining or passing to the NumberFormatter rounding() setter. + * @return A precision for chaining or passing to the NumberFormatter precision() setter. * @draft ICU 60 */ - Rounder withMinFraction(int32_t minFrac) const; + Precision withMinFraction(int32_t minFrac) const; private: // Inherit constructor - using Rounder::Rounder; + using Precision::Precision; // To allow parent class to call this class's constructor: - friend class Rounder; + friend class Precision; }; /** @@ -1170,7 +905,6 @@ class U_I18N_API IntegerWidth : public UMemory { * The minimum number of places before the decimal separator. * @return An IntegerWidth for chaining or passing to the NumberFormatter integerWidth() setter. * @draft ICU 60 - * @see NumberFormatter */ static IntegerWidth zeroFillTo(int32_t minInt); @@ -1184,7 +918,6 @@ class U_I18N_API IntegerWidth : public UMemory { * truncation. * @return An IntegerWidth for passing to the NumberFormatter integerWidth() setter. * @draft ICU 60 - * @see NumberFormatter */ IntegerWidth truncateAt(int32_t maxInt); @@ -1193,12 +926,13 @@ class U_I18N_API IntegerWidth : public UMemory { struct { impl::digits_t fMinInt; impl::digits_t fMaxInt; + bool fFormatFailIfMoreThanMaxDigits; } minMaxInt; UErrorCode errorCode; } fUnion; bool fHasError = false; - IntegerWidth(impl::digits_t minInt, impl::digits_t maxInt); + IntegerWidth(impl::digits_t minInt, impl::digits_t maxInt, bool formatFailIfMoreThanMaxDigits); IntegerWidth(UErrorCode errorCode) { // NOLINT fUnion.errorCode = errorCode; @@ -1209,6 +943,11 @@ class U_I18N_API IntegerWidth : public UMemory { fUnion.minMaxInt.fMinInt = -1; } + /** Returns the default instance. */ + static IntegerWidth standard() { + return IntegerWidth::zeroFillTo(1); + } + bool isBogus() const { return !fHasError && fUnion.minMaxInt.fMinInt == -1; } @@ -1223,69 +962,214 @@ class U_I18N_API IntegerWidth : public UMemory { void apply(impl::DecimalQuantity &quantity, UErrorCode &status) const; + bool operator==(const IntegerWidth& other) const; + // To allow MacroProps/MicroProps to initialize empty instances: friend struct impl::MacroProps; friend struct impl::MicroProps; // To allow NumberFormatterImpl to access isBogus() and perform other operations: friend class impl::NumberFormatterImpl; -}; - -namespace impl { - -// Do not enclose entire SymbolsWrapper with #ifndef U_HIDE_INTERNAL_API, needed for a protected field -/** @internal */ -class U_I18N_API SymbolsWrapper : public UMemory { - public: - /** @internal */ - SymbolsWrapper() : fType(SYMPTR_NONE), fPtr{nullptr} {} - /** @internal */ - SymbolsWrapper(const SymbolsWrapper &other); - - /** @internal */ - ~SymbolsWrapper(); + // So that NumberPropertyMapper can create instances + friend class impl::NumberPropertyMapper; - /** @internal */ - SymbolsWrapper &operator=(const SymbolsWrapper &other); + // To allow access to the skeleton generation code: + friend class impl::GeneratorHelpers; +}; -#ifndef U_HIDE_INTERNAL_API +/** + * A class that defines a quantity by which a number should be multiplied when formatting. + * + *

+ * To create a Scale, use one of the factory methods. + * + * @draft ICU 62 + */ +class U_I18N_API Scale : public UMemory { + public: /** - * The provided object is copied, but we do not adopt it. - * @internal + * Do not change the value of numbers when formatting or parsing. + * + * @return A Scale to prevent any multiplication. + * @draft ICU 62 */ - void setTo(const DecimalFormatSymbols &dfs); + static Scale none(); /** - * Adopt the provided object. - * @internal + * Multiply numbers by a power of ten before formatting. Useful for combining with a percent unit: + * + *

+     * NumberFormatter::with().unit(NoUnit::percent()).multiplier(Scale::powerOfTen(2))
+     * 
+ * + * @return A Scale for passing to the setter in NumberFormatter. + * @draft ICU 62 */ - void setTo(const NumberingSystem *ns); + static Scale powerOfTen(int32_t power); /** - * Whether the object is currently holding a DecimalFormatSymbols. - * @internal + * Multiply numbers by an arbitrary value before formatting. Useful for unit conversions. + * + * This method takes a string in a decimal number format with syntax + * as defined in the Decimal Arithmetic Specification, available at + * http://speleotrove.com/decimal + * + * Also see the version of this method that takes a double. + * + * @return A Scale for passing to the setter in NumberFormatter. + * @draft ICU 62 */ - bool isDecimalFormatSymbols() const; + static Scale byDecimal(StringPiece multiplicand); /** - * Whether the object is currently holding a NumberingSystem. - * @internal + * Multiply numbers by an arbitrary value before formatting. Useful for unit conversions. + * + * This method takes a double; also see the version of this method that takes an exact decimal. + * + * @return A Scale for passing to the setter in NumberFormatter. + * @draft ICU 62 */ - bool isNumberingSystem() const; + static Scale byDouble(double multiplicand); /** - * Get the DecimalFormatSymbols pointer. No ownership change. - * @internal + * Multiply a number by both a power of ten and by an arbitrary double value. + * + * @return A Scale for passing to the setter in NumberFormatter. + * @draft ICU 62 */ - const DecimalFormatSymbols *getDecimalFormatSymbols() const; + static Scale byDoubleAndPowerOfTen(double multiplicand, int32_t power); - /** + // We need a custom destructor for the DecNum, which means we need to declare + // the copy/move constructor/assignment quartet. + + /** @draft ICU 62 */ + Scale(const Scale& other); + + /** @draft ICU 62 */ + Scale& operator=(const Scale& other); + + /** @draft ICU 62 */ + Scale(Scale&& src) U_NOEXCEPT; + + /** @draft ICU 62 */ + Scale& operator=(Scale&& src) U_NOEXCEPT; + + /** @draft ICU 62 */ + ~Scale(); + +#ifndef U_HIDE_INTERNAL_API + /** @internal */ + Scale(int32_t magnitude, impl::DecNum* arbitraryToAdopt); +#endif /* U_HIDE_INTERNAL_API */ + + private: + int32_t fMagnitude; + impl::DecNum* fArbitrary; + UErrorCode fError; + + Scale(UErrorCode error) : fMagnitude(0), fArbitrary(nullptr), fError(error) {} + + Scale() : fMagnitude(0), fArbitrary(nullptr), fError(U_ZERO_ERROR) {} + + bool isValid() const { + return fMagnitude != 0 || fArbitrary != nullptr; + } + + UBool copyErrorTo(UErrorCode &status) const { + if (fError != U_ZERO_ERROR) { + status = fError; + return TRUE; + } + return FALSE; + } + + void applyTo(impl::DecimalQuantity& quantity) const; + + void applyReciprocalTo(impl::DecimalQuantity& quantity) const; + + // To allow MacroProps/MicroProps to initialize empty instances: + friend struct impl::MacroProps; + friend struct impl::MicroProps; + + // To allow NumberFormatterImpl to access isBogus() and perform other operations: + friend class impl::NumberFormatterImpl; + + // To allow the helper class MultiplierFormatHandler access to private fields: + friend class impl::MultiplierFormatHandler; + + // To allow access to the skeleton generation code: + friend class impl::GeneratorHelpers; + + // To allow access to parsing code: + friend class ::icu::numparse::impl::NumberParserImpl; + friend class ::icu::numparse::impl::MultiplierParseHandler; +}; + +namespace impl { + +// Do not enclose entire SymbolsWrapper with #ifndef U_HIDE_INTERNAL_API, needed for a protected field +/** @internal */ +class U_I18N_API SymbolsWrapper : public UMemory { + public: + /** @internal */ + SymbolsWrapper() : fType(SYMPTR_NONE), fPtr{nullptr} {} + + /** @internal */ + SymbolsWrapper(const SymbolsWrapper &other); + + /** @internal */ + SymbolsWrapper &operator=(const SymbolsWrapper &other); + + /** @internal */ + SymbolsWrapper(SymbolsWrapper&& src) U_NOEXCEPT; + + /** @internal */ + SymbolsWrapper &operator=(SymbolsWrapper&& src) U_NOEXCEPT; + + /** @internal */ + ~SymbolsWrapper(); + +#ifndef U_HIDE_INTERNAL_API + + /** + * The provided object is copied, but we do not adopt it. + * @internal + */ + void setTo(const DecimalFormatSymbols &dfs); + + /** + * Adopt the provided object. + * @internal + */ + void setTo(const NumberingSystem *ns); + + /** + * Whether the object is currently holding a DecimalFormatSymbols. + * @internal + */ + bool isDecimalFormatSymbols() const; + + /** + * Whether the object is currently holding a NumberingSystem. + * @internal + */ + bool isNumberingSystem() const; + + /** + * Get the DecimalFormatSymbols pointer. No ownership change. + * @internal + */ + const DecimalFormatSymbols *getDecimalFormatSymbols() const; + + /** * Get the NumberingSystem pointer. No ownership change. * @internal */ const NumberingSystem *getNumberingSystem() const; +#endif // U_HIDE_INTERNAL_API + /** @internal */ UBool copyErrorTo(UErrorCode &status) const { if (fType == SYMPTR_DFS && fPtr.dfs == nullptr) { @@ -1297,7 +1181,6 @@ class U_I18N_API SymbolsWrapper : public UMemory { } return FALSE; } -#endif // U_HIDE_INTERNAL_API private: enum SymbolsPointerType { @@ -1311,6 +1194,8 @@ class U_I18N_API SymbolsWrapper : public UMemory { void doCopyFrom(const SymbolsWrapper &other); + void doMoveFrom(SymbolsWrapper&& src); + void doCleanup(); }; @@ -1322,13 +1207,28 @@ class U_I18N_API Grouper : public UMemory { /** @internal */ static Grouper forStrategy(UGroupingStrategy grouping); + /** + * Resolve the values in Properties to a Grouper object. + * @internal + */ + static Grouper forProperties(const DecimalFormatProperties& properties); + // Future: static Grouper forProperties(DecimalFormatProperties& properties); /** @internal */ - Grouper(int16_t grouping1, int16_t grouping2, int16_t minGrouping) - : fGrouping1(grouping1), fGrouping2(grouping2), fMinGrouping(minGrouping) {} + Grouper(int16_t grouping1, int16_t grouping2, int16_t minGrouping, UGroupingStrategy strategy) + : fGrouping1(grouping1), + fGrouping2(grouping2), + fMinGrouping(minGrouping), + fStrategy(strategy) {} #endif // U_HIDE_INTERNAL_API + /** @internal */ + int16_t getPrimary() const; + + /** @internal */ + int16_t getSecondary() const; + private: /** * The grouping sizes, with the following special values: @@ -1342,7 +1242,7 @@ class U_I18N_API Grouper : public UMemory { int16_t fGrouping2; /** - * The minimum gropuing size, with the following special values: + * The minimum grouping size, with the following special values: *
    *
  • -2 = needs locale data *
  • -3 = no less than 2 @@ -1350,6 +1250,12 @@ class U_I18N_API Grouper : public UMemory { */ int16_t fMinGrouping; + /** + * The UGroupingStrategy that was used to create this Grouper, or UNUM_GROUPING_COUNT if this + * was not created from a UGroupingStrategy. + */ + UGroupingStrategy fStrategy; + Grouper() : fGrouping1(-3) {}; bool isBogus() const { @@ -1367,6 +1273,12 @@ class U_I18N_API Grouper : public UMemory { // To allow NumberFormatterImpl to access isBogus() and perform other operations: friend class NumberFormatterImpl; + + // To allow NumberParserImpl to perform setLocaleData(): + friend class ::icu::numparse::impl::NumberParserImpl; + + // To allow access to the skeleton generation code: + friend class impl::GeneratorHelpers; }; // Do not enclose entire Padder with #ifndef U_HIDE_INTERNAL_API, needed for a protected field @@ -1381,6 +1293,9 @@ class U_I18N_API Padder : public UMemory { static Padder codePoints(UChar32 cp, int32_t targetWidth, UNumberFormatPadPosition position); #endif // U_HIDE_INTERNAL_API + /** @internal */ + static Padder forProperties(const DecimalFormatProperties& properties); + private: UChar32 fWidth; // -3 = error; -2 = bogus; -1 = no padding union { @@ -1427,6 +1342,9 @@ class U_I18N_API Padder : public UMemory { // To allow NumberFormatterImpl to access isBogus() and perform other operations: friend class impl::NumberFormatterImpl; + + // To allow access to the skeleton generation code: + friend class impl::GeneratorHelpers; }; // Do not enclose entire MacroProps with #ifndef U_HIDE_INTERNAL_API, needed for a protected field @@ -1442,7 +1360,10 @@ struct U_I18N_API MacroProps : public UMemory { MeasureUnit perUnit; // = NoUnit::base(); /** @internal */ - Rounder rounder; // = Rounder(); (bogus) + Precision precision; // = Precision(); (bogus) + + /** @internal */ + UNumberFormatRoundingMode roundingMode = UNUM_ROUND_HALFEVEN; /** @internal */ Grouper grouper; // = Grouper(); (bogus) @@ -1468,20 +1389,33 @@ struct U_I18N_API MacroProps : public UMemory { UNumberDecimalSeparatorDisplay decimal = UNUM_DECIMAL_SEPARATOR_COUNT; /** @internal */ - PluralRules *rules = nullptr; // no ownership + Scale scale; // = Scale(); (benign value) + + /** @internal */ + const AffixPatternProvider* affixProvider = nullptr; // no ownership + + /** @internal */ + const PluralRules* rules = nullptr; // no ownership + + /** @internal */ + const CurrencySymbols* currencySymbols = nullptr; // no ownership /** @internal */ int32_t threshold = DEFAULT_THRESHOLD; + + /** @internal */ Locale locale; + // NOTE: Uses default copy and move constructors. + /** * Check all members for errors. * @internal */ bool copyErrorTo(UErrorCode &status) const { - return notation.copyErrorTo(status) || rounder.copyErrorTo(status) || + return notation.copyErrorTo(status) || precision.copyErrorTo(status) || padder.copyErrorTo(status) || integerWidth.copyErrorTo(status) || - symbols.copyErrorTo(status); + symbols.copyErrorTo(status) || scale.copyErrorTo(status); } }; @@ -1505,7 +1439,7 @@ class U_I18N_API NumberFormatterSettings { * *

    * All notation styles will be properly localized with locale data, and all notation styles are compatible with - * units, rounding strategies, and other number formatter settings. + * units, rounding precisions, and other number formatter settings. * *

    * Pass this method the return value of a {@link Notation} factory method. For example: @@ -1522,7 +1456,18 @@ class U_I18N_API NumberFormatterSettings { * @see Notation * @draft ICU 60 */ - Derived notation(const Notation ¬ation) const; + Derived notation(const Notation ¬ation) const &; + + /** + * Overload of notation() for use on an rvalue reference. + * + * @param notation + * The notation strategy to use. + * @return The fluent chain. + * @see #notation + * @draft ICU 62 + */ + Derived notation(const Notation ¬ation) &&; /** * Specifies the unit (unit of measure, currency, or percent) to associate with rendered numbers. @@ -1534,7 +1479,7 @@ class U_I18N_API NumberFormatterSettings { *

* * All units will be properly localized with locale data, and all units are compatible with notation styles, - * rounding strategies, and other number formatter settings. + * rounding precisions, and other number formatter settings. * * Pass this method any instance of {@link MeasureUnit}. For units of measure (which often involve the * factory methods that return a pointer): @@ -1568,7 +1513,18 @@ class U_I18N_API NumberFormatterSettings { * @see #perUnit * @draft ICU 60 */ - Derived unit(const icu::MeasureUnit &unit) const; + Derived unit(const icu::MeasureUnit &unit) const &; + + /** + * Overload of unit() for use on an rvalue reference. + * + * @param unit + * The unit to render. + * @return The fluent chain. + * @see #unit + * @draft ICU 62 + */ + Derived unit(const icu::MeasureUnit &unit) &&; /** * Like unit(), but takes ownership of a pointer. Convenient for use with the MeasureFormat factory @@ -1585,7 +1541,18 @@ class U_I18N_API NumberFormatterSettings { * @see MeasureUnit * @draft ICU 60 */ - Derived adoptUnit(icu::MeasureUnit *unit) const; + Derived adoptUnit(icu::MeasureUnit *unit) const &; + + /** + * Overload of adoptUnit() for use on an rvalue reference. + * + * @param unit + * The unit to render. + * @return The fluent chain. + * @see #adoptUnit + * @draft ICU 62 + */ + Derived adoptUnit(icu::MeasureUnit *unit) &&; /** * Sets a unit to be used in the denominator. For example, to format "3 m/s", pass METER to the unit and SECOND to @@ -1604,7 +1571,18 @@ class U_I18N_API NumberFormatterSettings { * @see #unit * @draft ICU 61 */ - Derived perUnit(const icu::MeasureUnit &perUnit) const; + Derived perUnit(const icu::MeasureUnit &perUnit) const &; + + /** + * Overload of perUnit() for use on an rvalue reference. + * + * @param perUnit + * The unit to render in the denominator. + * @return The fluent chain. + * @see #perUnit + * @draft ICU 62 + */ + Derived perUnit(const icu::MeasureUnit &perUnit) &&; /** * Like perUnit(), but takes ownership of a pointer. Convenient for use with the MeasureFormat factory @@ -1623,10 +1601,21 @@ class U_I18N_API NumberFormatterSettings { * @see MeasureUnit * @draft ICU 61 */ - Derived adoptPerUnit(icu::MeasureUnit *perUnit) const; + Derived adoptPerUnit(icu::MeasureUnit *perUnit) const &; /** - * Specifies the rounding strategy to use when formatting numbers. + * Overload of adoptPerUnit() for use on an rvalue reference. + * + * @param perUnit + * The unit to render in the denominator. + * @return The fluent chain. + * @see #adoptPerUnit + * @draft ICU 62 + */ + Derived adoptPerUnit(icu::MeasureUnit *perUnit) &&; + + /** + * Specifies the rounding precision to use when formatting numbers. * *
    *
  • Round to 3 decimal places: "3.142" @@ -1636,27 +1625,77 @@ class U_I18N_API NumberFormatterSettings { *
* *

- * Pass this method the return value of one of the factory methods on {@link Rounder}. For example: + * Pass this method the return value of one of the factory methods on {@link Precision}. For example: * *

-     * NumberFormatter::with().rounding(Rounder::fixedFraction(2))
+     * NumberFormatter::with().precision(Precision::fixedFraction(2))
      * 
* *

* In most cases, the default rounding strategy is to round to 6 fraction places; i.e., - * Rounder.maxFraction(6). The exceptions are if compact notation is being used, then the compact + * Precision.maxFraction(6). The exceptions are if compact notation is being used, then the compact * notation rounding strategy is used (see {@link Notation#compactShort} for details), or if the unit is a currency, - * then standard currency rounding is used, which varies from currency to currency (see {@link Rounder#currency} for + * then standard currency rounding is used, which varies from currency to currency (see {@link Precision#currency} for * details). * - * @param rounder - * The rounding strategy to use. + * @param precision + * The rounding precision to use. * @return The fluent chain. - * @see Rounder - * @provisional This API might change or be removed in a future release. - * @draft ICU 60 + * @see Precision + * @draft ICU 62 + */ + Derived precision(const Precision& precision) const &; + + /** + * Overload of precision() for use on an rvalue reference. + * + * @param precision + * The rounding precision to use. + * @return The fluent chain. + * @see #precision + * @draft ICU 62 + */ + Derived precision(const Precision& precision) &&; + +#ifndef U_HIDE_DEPRECATED_API + // Compatibility method that will be removed in ICU 64. + // Use precision() instead. + // See http://bugs.icu-project.org/trac/ticket/13746 + /** @deprecated ICU 62 */ + Derived rounding(const Rounder& rounder) const & { + return precision(rounder); + } +#endif /* U_HIDE_DEPRECATED_API */ + + /** + * Specifies how to determine the direction to round a number when it has more digits than fit in the + * desired precision. When formatting 1.235: + * + *

    + *
  • Ceiling rounding mode with integer precision: "2" + *
  • Half-down rounding mode with 2 fixed fraction digits: "1.23" + *
  • Half-up rounding mode with 2 fixed fraction digits: "1.24" + *
+ * + * The default is HALF_EVEN. For more information on rounding mode, see the ICU userguide here: + * + * http://userguide.icu-project.org/formatparse/numbers/rounding-modes + * + * @param roundingMode The rounding mode to use. + * @return The fluent chain. + * @draft ICU 62 + */ + Derived roundingMode(UNumberFormatRoundingMode roundingMode) const &; + + /** + * Overload of roundingMode() for use on an rvalue reference. + * + * @param roundingMode The rounding mode to use. + * @return The fluent chain. + * @see #roundingMode + * @draft ICU 62 */ - Derived rounding(const Rounder &rounder) const; + Derived roundingMode(UNumberFormatRoundingMode roundingMode) &&; /** * Specifies the grouping strategy to use when formatting numbers. @@ -1685,7 +1724,19 @@ class U_I18N_API NumberFormatterSettings { * @return The fluent chain. * @draft ICU 61 */ - Derived grouping(const UGroupingStrategy &strategy) const; + Derived grouping(UGroupingStrategy strategy) const &; + + /** + * Overload of grouping() for use on an rvalue reference. + * + * @param strategy + * The grouping strategy to use. + * @return The fluent chain. + * @see #grouping + * @provisional This API might change or be removed in a future release. + * @draft ICU 62 + */ + Derived grouping(UGroupingStrategy strategy) &&; /** * Specifies the minimum and maximum number of digits to render before the decimal mark. @@ -1711,7 +1762,18 @@ class U_I18N_API NumberFormatterSettings { * @see IntegerWidth * @draft ICU 60 */ - Derived integerWidth(const IntegerWidth &style) const; + Derived integerWidth(const IntegerWidth &style) const &; + + /** + * Overload of integerWidth() for use on an rvalue reference. + * + * @param style + * The integer width to use. + * @return The fluent chain. + * @see #integerWidth + * @draft ICU 62 + */ + Derived integerWidth(const IntegerWidth &style) &&; /** * Specifies the symbols (decimal separator, grouping separator, percent sign, numerals, etc.) to use when rendering @@ -1741,8 +1803,8 @@ class U_I18N_API NumberFormatterSettings { * after passing it into the fluent chain will not be seen. * *

- * Note: Calling this method will override the NumberingSystem previously specified in - * {@link #symbols(NumberingSystem)}. + * Note: Calling this method will override any previously specified DecimalFormatSymbols + * or NumberingSystem. * *

* The default is to choose the symbols based on the locale specified in the fluent chain. @@ -1753,7 +1815,18 @@ class U_I18N_API NumberFormatterSettings { * @see DecimalFormatSymbols * @draft ICU 60 */ - Derived symbols(const DecimalFormatSymbols &symbols) const; + Derived symbols(const DecimalFormatSymbols &symbols) const &; + + /** + * Overload of symbols() for use on an rvalue reference. + * + * @param symbols + * The DecimalFormatSymbols to use. + * @return The fluent chain. + * @see #symbols + * @draft ICU 62 + */ + Derived symbols(const DecimalFormatSymbols &symbols) &&; /** * Specifies that the given numbering system should be used when fetching symbols. @@ -1773,8 +1846,8 @@ class U_I18N_API NumberFormatterSettings { * * *

- * Note: Calling this method will override the DecimalFormatSymbols previously specified in - * {@link #symbols(DecimalFormatSymbols)}. + * Note: Calling this method will override any previously specified DecimalFormatSymbols + * or NumberingSystem. * *

* The default is to choose the best numbering system for the locale. @@ -1788,7 +1861,18 @@ class U_I18N_API NumberFormatterSettings { * @see NumberingSystem * @draft ICU 60 */ - Derived adoptSymbols(NumberingSystem *symbols) const; + Derived adoptSymbols(NumberingSystem *symbols) const &; + + /** + * Overload of adoptSymbols() for use on an rvalue reference. + * + * @param symbols + * The NumberingSystem to use. + * @return The fluent chain. + * @see #adoptSymbols + * @draft ICU 62 + */ + Derived adoptSymbols(NumberingSystem *symbols) &&; /** * Sets the width of the unit (measure unit or currency). Most common values: @@ -1815,7 +1899,18 @@ class U_I18N_API NumberFormatterSettings { * @see UNumberUnitWidth * @draft ICU 60 */ - Derived unitWidth(const UNumberUnitWidth &width) const; + Derived unitWidth(UNumberUnitWidth width) const &; + + /** + * Overload of unitWidth() for use on an rvalue reference. + * + * @param width + * The width to use when rendering numbers. + * @return The fluent chain. + * @see #unitWidth + * @draft ICU 62 + */ + Derived unitWidth(UNumberUnitWidth width) &&; /** * Sets the plus/minus sign display strategy. Most common values: @@ -1836,14 +1931,24 @@ class U_I18N_API NumberFormatterSettings { *

* The default is AUTO sign display. * - * @param width + * @param style * The sign display strategy to use when rendering numbers. * @return The fluent chain * @see UNumberSignDisplay - * @provisional This API might change or be removed in a future release. * @draft ICU 60 */ - Derived sign(const UNumberSignDisplay &width) const; + Derived sign(UNumberSignDisplay style) const &; + + /** + * Overload of sign() for use on an rvalue reference. + * + * @param style + * The sign display strategy to use when rendering numbers. + * @return The fluent chain. + * @see #sign + * @draft ICU 62 + */ + Derived sign(UNumberSignDisplay style) &&; /** * Sets the decimal separator display strategy. This affects integer numbers with no fraction part. Most common @@ -1864,23 +1969,73 @@ class U_I18N_API NumberFormatterSettings { *

* The default is AUTO decimal separator display. * - * @param width + * @param style * The decimal separator display strategy to use when rendering numbers. * @return The fluent chain * @see UNumberDecimalSeparatorDisplay - * @provisional This API might change or be removed in a future release. * @draft ICU 60 */ - Derived decimal(const UNumberDecimalSeparatorDisplay &width) const; + Derived decimal(UNumberDecimalSeparatorDisplay style) const &; + + /** + * Overload of decimal() for use on an rvalue reference. + * + * @param style + * The decimal separator display strategy to use when rendering numbers. + * @return The fluent chain. + * @see #decimal + * @draft ICU 62 + */ + Derived decimal(UNumberDecimalSeparatorDisplay style) &&; + + /** + * Sets a scale (multiplier) to be used to scale the number by an arbitrary amount before formatting. + * Most common values: + * + *

    + *
  • Multiply by 100: useful for percentages. + *
  • Multiply by an arbitrary value: useful for unit conversions. + *
+ * + *

+ * Pass an element from a {@link Scale} factory method to this setter. For example: + * + *

+     * NumberFormatter::with().scale(Scale::powerOfTen(2))
+     * 
+ * + *

+ * The default is to not apply any multiplier. + * + * @param scale + * The scale to apply when rendering numbers. + * @return The fluent chain + * @draft ICU 62 + */ + Derived scale(const Scale &scale) const &; + + /** + * Overload of scale() for use on an rvalue reference. + * + * @param scale + * The scale to apply when rendering numbers. + * @return The fluent chain. + * @see #scale + * @draft ICU 62 + */ + Derived scale(const Scale &scale) &&; #ifndef U_HIDE_INTERNAL_API /** - * Set the padding strategy. May be added to ICU 61; see #13338. + * Set the padding strategy. May be added in the future; see #13338. * * @internal ICU 60: This API is ICU internal only. */ - Derived padding(const impl::Padder &padder) const; + Derived padding(const impl::Padder &padder) const &; + + /** @internal */ + Derived padding(const impl::Padder &padder) &&; /** * Internal fluent setter to support a custom regulation threshold. A threshold of 1 causes the data structures to @@ -1888,10 +2043,45 @@ class U_I18N_API NumberFormatterSettings { * * @internal ICU 60: This API is ICU internal only. */ - Derived threshold(int32_t threshold) const; + Derived threshold(int32_t threshold) const &; + + /** @internal */ + Derived threshold(int32_t threshold) &&; + + /** + * Internal fluent setter to overwrite the entire macros object. + * + * @internal ICU 60: This API is ICU internal only. + */ + Derived macros(const impl::MacroProps& macros) const &; + + /** @internal */ + Derived macros(const impl::MacroProps& macros) &&; + + /** @internal */ + Derived macros(impl::MacroProps&& macros) const &; + + /** @internal */ + Derived macros(impl::MacroProps&& macros) &&; #endif /* U_HIDE_INTERNAL_API */ + /** + * Creates a skeleton string representation of this number formatter. A skeleton string is a + * locale-agnostic serialized form of a number formatter. + * + * Not all options are capable of being represented in the skeleton string; for example, a + * DecimalFormatSymbols object. If any such option is encountered, the error code is set to + * U_UNSUPPORTED_ERROR. + * + * The returned skeleton is in normalized form, such that two number formatters with equivalent + * behavior should produce the same skeleton. + * + * @return A number skeleton string with behavior corresponding to this number formatter. + * @draft ICU 62 + */ + UnicodeString toSkeleton(UErrorCode& status) const; + /** * Sets the UErrorCode if an error occurred in the fluent chain. * Preserves older error codes in the outErrorCode. @@ -1905,7 +2095,9 @@ class U_I18N_API NumberFormatterSettings { } fMacros.copyErrorTo(outErrorCode); return U_FAILURE(outErrorCode); - } + }; + + // NOTE: Uses default copy and move constructors. protected: impl::MacroProps fMacros; @@ -1944,21 +2136,58 @@ class U_I18N_API UnlocalizedNumberFormatter * @return The fluent chain. * @draft ICU 60 */ - LocalizedNumberFormatter locale(const icu::Locale &locale) const; + LocalizedNumberFormatter locale(const icu::Locale &locale) const &; + + /** + * Overload of locale() for use on an rvalue reference. + * + * @param locale + * The locale to use when loading data for number formatting. + * @return The fluent chain. + * @see #locale + * @draft ICU 62 + */ + LocalizedNumberFormatter locale(const icu::Locale &locale) &&; + + /** + * Default constructor: puts the formatter into a valid but undefined state. + * + * @draft ICU 62 + */ + UnlocalizedNumberFormatter() = default; // Make default copy constructor call the NumberFormatterSettings copy constructor. /** * Returns a copy of this UnlocalizedNumberFormatter. * @draft ICU 60 */ - UnlocalizedNumberFormatter(const UnlocalizedNumberFormatter &other) : UnlocalizedNumberFormatter( - static_cast &>(other)) {} + UnlocalizedNumberFormatter(const UnlocalizedNumberFormatter &other); + + /** + * Move constructor: + * The source UnlocalizedNumberFormatter will be left in a valid but undefined state. + * @draft ICU 62 + */ + UnlocalizedNumberFormatter(UnlocalizedNumberFormatter&& src) U_NOEXCEPT; + + /** + * Copy assignment operator. + * @draft ICU 62 + */ + UnlocalizedNumberFormatter& operator=(const UnlocalizedNumberFormatter& other); + + /** + * Move assignment operator: + * The source UnlocalizedNumberFormatter will be left in a valid but undefined state. + * @draft ICU 62 + */ + UnlocalizedNumberFormatter& operator=(UnlocalizedNumberFormatter&& src) U_NOEXCEPT; private: - UnlocalizedNumberFormatter() = default; + explicit UnlocalizedNumberFormatter(const NumberFormatterSettings& other); explicit UnlocalizedNumberFormatter( - const NumberFormatterSettings &other); + NumberFormatterSettings&& src) U_NOEXCEPT; // To give the fluent setters access to this class's constructor: friend class NumberFormatterSettings; @@ -2016,15 +2245,99 @@ class U_I18N_API LocalizedNumberFormatter * @return A FormattedNumber object; call .toString() to get the string. * @draft ICU 60 */ - FormattedNumber formatDecimal(StringPiece value, UErrorCode &status) const; + FormattedNumber formatDecimal(StringPiece value, UErrorCode& status) const; + +#ifndef U_HIDE_INTERNAL_API + + /** Internal method. + * @internal + */ + FormattedNumber formatDecimalQuantity(const impl::DecimalQuantity& dq, UErrorCode& status) const; + + /** Internal method for DecimalFormat compatibility. + * @internal + */ + void getAffixImpl(bool isPrefix, bool isNegative, UnicodeString& result, UErrorCode& status) const; + + /** + * Internal method for testing. + * @internal + */ + const impl::NumberFormatterImpl* getCompiled() const; + + /** + * Internal method for testing. + * @internal + */ + int32_t getCallCount() const; + +#endif + + /** + * Creates a representation of this LocalizedNumberFormat as an icu::Format, enabling the use + * of this number formatter with APIs that need an object of that type, such as MessageFormat. + * + * This API is not intended to be used other than for enabling API compatibility. The formatDouble, + * formatInt, and formatDecimal methods should normally be used when formatting numbers, not the Format + * object returned by this method. + * + * The caller owns the returned object and must delete it when finished. + * + * @return A Format wrapping this LocalizedNumberFormatter. + * @draft ICU 62 + */ + Format* toFormat(UErrorCode& status) const; + + /** + * Default constructor: puts the formatter into a valid but undefined state. + * + * @draft ICU 62 + */ + LocalizedNumberFormatter() = default; // Make default copy constructor call the NumberFormatterSettings copy constructor. /** * Returns a copy of this LocalizedNumberFormatter. * @draft ICU 60 */ - LocalizedNumberFormatter(const LocalizedNumberFormatter &other) : LocalizedNumberFormatter( - static_cast &>(other)) {} + LocalizedNumberFormatter(const LocalizedNumberFormatter &other); + + /** + * Move constructor: + * The source LocalizedNumberFormatter will be left in a valid but undefined state. + * @draft ICU 62 + */ + LocalizedNumberFormatter(LocalizedNumberFormatter&& src) U_NOEXCEPT; + + /** + * Copy assignment operator. + * @draft ICU 62 + */ + LocalizedNumberFormatter& operator=(const LocalizedNumberFormatter& other); + + /** + * Move assignment operator: + * The source LocalizedNumberFormatter will be left in a valid but undefined state. + * @draft ICU 62 + */ + LocalizedNumberFormatter& operator=(LocalizedNumberFormatter&& src) U_NOEXCEPT; + +#ifndef U_HIDE_INTERNAL_API + + /** + * This is the core entrypoint to the number formatting pipeline. It performs self-regulation: a static code path + * for the first few calls, and compiling a more efficient data structure if called repeatedly. + * + *

+ * This function is very hot, being called in every call to the number formatting pipeline. + * + * @param results + * The results object. This method will mutate it to save the results. + * @internal + */ + void formatImpl(impl::UFormattedNumberData *results, UErrorCode &status) const; + +#endif /** * Destruct this LocalizedNumberFormatter, cleaning up any memory it might own. @@ -2033,27 +2346,25 @@ class U_I18N_API LocalizedNumberFormatter ~LocalizedNumberFormatter(); private: + // Note: fCompiled can't be a LocalPointer because impl::NumberFormatterImpl is defined in an internal + // header, and LocalPointer needs the full class definition in order to delete the instance. const impl::NumberFormatterImpl* fCompiled {nullptr}; char fUnsafeCallCount[8] {}; // internally cast to u_atomic_int32_t - LocalizedNumberFormatter() = default; + explicit LocalizedNumberFormatter(const NumberFormatterSettings& other); - explicit LocalizedNumberFormatter(const NumberFormatterSettings &other); + explicit LocalizedNumberFormatter(NumberFormatterSettings&& src) U_NOEXCEPT; LocalizedNumberFormatter(const impl::MacroProps ¯os, const Locale &locale); + LocalizedNumberFormatter(impl::MacroProps &¯os, const Locale &locale); + + void lnfMoveHelper(LocalizedNumberFormatter&& src); + /** - * This is the core entrypoint to the number formatting pipeline. It performs self-regulation: a static code path - * for the first few calls, and compiling a more efficient data structure if called repeatedly. - * - *

- * This function is very hot, being called in every call to the number formatting pipeline. - * - * @param results - * The results object. This method takes ownership. - * @return The formatted number result. + * @return true if the compiled formatter is available. */ - FormattedNumber formatImpl(impl::NumberFormatterResults *results, UErrorCode &status) const; + bool computeCompiled(UErrorCode& status) const; // To give the fluent setters access to this class's constructor: friend class NumberFormatterSettings; @@ -2071,25 +2382,57 @@ class U_I18N_API LocalizedNumberFormatter */ class U_I18N_API FormattedNumber : public UMemory { public: +#ifndef U_HIDE_DEPRECATED_API /** * Returns a UnicodeString representation of the formatted number. * * @return a UnicodeString containing the localized number. - * @draft ICU 60 + * @deprecated ICU 62 Use the version of this method with an error code instead. + * This method was never @stable and will be removed in a future release. + * See http://bugs.icu-project.org/trac/ticket/13746 */ UnicodeString toString() const; +#endif /* U_HIDE_DEPRECATED_API */ + + /** + * Returns a UnicodeString representation of the formatted number. + * + * @param status + * Set if an error occurs while formatting the number to the UnicodeString. + * @return a UnicodeString containing the localized number. + * @draft ICU 62 + */ + UnicodeString toString(UErrorCode& status) const; +#ifndef U_HIDE_DEPRECATED_API /** * Appends the formatted number to an Appendable. * * @param appendable * The Appendable to which to append the formatted number string. * @return The same Appendable, for chaining. - * @draft ICU 60 + * @deprecated ICU 62 Use the version of this method with an error code instead. + * This method was never @stable and will be removed in a future release. + * See http://bugs.icu-project.org/trac/ticket/13746 * @see Appendable */ Appendable &appendTo(Appendable &appendable); +#endif /* U_HIDE_DEPRECATED_API */ + /** + * Appends the formatted number to an Appendable. + * + * @param appendable + * The Appendable to which to append the formatted number string. + * @param status + * Set if an error occurs while formatting the number to the Appendable. + * @return The same Appendable, for chaining. + * @draft ICU 62 + * @see Appendable + */ + Appendable &appendTo(Appendable &appendable, UErrorCode& status); + +#ifndef U_HIDE_DEPRECATED_API /** * Determine the start and end indices of the first occurrence of the given field in the output string. * This allows you to determine the locations of the integer part, fraction part, and sign. @@ -2106,11 +2449,47 @@ class U_I18N_API FormattedNumber : public UMemory { * The FieldPosition to populate with the start and end indices of the desired field. * @param status * Set if an error occurs while populating the FieldPosition. - * @draft ICU 60 + * @deprecated ICU 62 Use {@link #nextFieldPosition} instead. This method will be removed in a future + * release. See http://bugs.icu-project.org/trac/ticket/13746 * @see UNumberFormatFields */ void populateFieldPosition(FieldPosition &fieldPosition, UErrorCode &status); +#endif /* U_HIDE_DEPRECATED_API */ + + /** + * Determines the start and end indices of the next occurrence of the given field in the + * output string. This allows you to determine the locations of, for example, the integer part, + * fraction part, or symbols. + * + * If a field occurs just once, calling this method will find that occurrence and return it. If a + * field occurs multiple times, this method may be called repeatedly with the following pattern: + * + *

+     * FieldPosition fpos(UNUM_GROUPING_SEPARATOR_FIELD);
+     * while (formattedNumber.nextFieldPosition(fpos, status)) {
+     *   // do something with fpos.
+     * }
+     * 
+ * + * This method is useful if you know which field to query. If you want all available field position + * information, use #getAllFieldPositions(). + * + * @param fieldPosition + * Input+output variable. On input, the "field" property determines which field to look + * up, and the "beginIndex" and "endIndex" properties determine where to begin the search. + * On output, the "beginIndex" is set to the beginning of the first occurrence of the + * field with either begin or end indices after the input indices, "endIndex" is set to + * the end of that occurrence of the field (exclusive index). If a field position is not + * found, the method returns FALSE and the FieldPosition may or may not be changed. + * @param status + * Set if an error occurs while populating the FieldPosition. + * @return TRUE if a new occurrence of the field was found; FALSE otherwise. + * @draft ICU 62 + * @see UNumberFormatFields + */ + UBool nextFieldPosition(FieldPosition& fieldPosition, UErrorCode& status) const; +#ifndef U_HIDE_DEPRECATED_API /** * Export the formatted number to a FieldPositionIterator. This allows you to determine which characters in * the output string correspond to which fields, such as the integer part, fraction part, and sign. @@ -2122,10 +2501,67 @@ class U_I18N_API FormattedNumber : public UMemory { * The FieldPositionIterator to populate with all of the fields present in the formatted number. * @param status * Set if an error occurs while populating the FieldPositionIterator. - * @draft ICU 60 + * @deprecated ICU 62 Use {@link #getAllFieldPositions} instead. This method will be removed in a + * future release. See http://bugs.icu-project.org/trac/ticket/13746 * @see UNumberFormatFields */ void populateFieldPositionIterator(FieldPositionIterator &iterator, UErrorCode &status); +#endif /* U_HIDE_DEPRECATED_API */ + + /** + * Export the formatted number to a FieldPositionIterator. This allows you to determine which characters in + * the output string correspond to which fields, such as the integer part, fraction part, and sign. + * + * If information on only one field is needed, use #nextFieldPosition() instead. + * + * @param iterator + * The FieldPositionIterator to populate with all of the fields present in the formatted number. + * @param status + * Set if an error occurs while populating the FieldPositionIterator. + * @draft ICU 62 + * @see UNumberFormatFields + */ + void getAllFieldPositions(FieldPositionIterator &iterator, UErrorCode &status) const; + +#ifndef U_HIDE_INTERNAL_API + + /** + * Gets the raw DecimalQuantity for plural rule selection. + * @internal + */ + void getDecimalQuantity(impl::DecimalQuantity& output, UErrorCode& status) const; + + /** + * Populates the mutable builder type FieldPositionIteratorHandler. + * @internal + */ + void getAllFieldPositionsImpl(FieldPositionIteratorHandler& fpih, UErrorCode& status) const; + +#endif + + /** + * Copying not supported; use move constructor instead. + */ + FormattedNumber(const FormattedNumber&) = delete; + + /** + * Copying not supported; use move assignment instead. + */ + FormattedNumber& operator=(const FormattedNumber&) = delete; + + /** + * Move constructor: + * Leaves the source FormattedNumber in an undefined state. + * @draft ICU 62 + */ + FormattedNumber(FormattedNumber&& src) U_NOEXCEPT; + + /** + * Move assignment: + * Leaves the source FormattedNumber in an undefined state. + * @draft ICU 62 + */ + FormattedNumber& operator=(FormattedNumber&& src) U_NOEXCEPT; /** * Destruct an instance of FormattedNumber, cleaning up any memory it might own. @@ -2134,13 +2570,17 @@ class U_I18N_API FormattedNumber : public UMemory { ~FormattedNumber(); private: - // Can't use LocalPointer because NumberFormatterResults is forward-declared - const impl::NumberFormatterResults *fResults; + // Can't use LocalPointer because UFormattedNumberData is forward-declared + const impl::UFormattedNumberData *fResults; // Error code for the terminal methods UErrorCode fErrorCode; - explicit FormattedNumber(impl::NumberFormatterResults *results) + /** + * Internal constructor from data type. Adopts the data pointer. + * @internal + */ + explicit FormattedNumber(impl::UFormattedNumberData *results) : fResults(results), fErrorCode(U_ZERO_ERROR) {}; explicit FormattedNumber(UErrorCode errorCode) @@ -2177,9 +2617,21 @@ class U_I18N_API NumberFormatter final { */ static LocalizedNumberFormatter withLocale(const Locale &locale); + /** + * Call this method at the beginning of a NumberFormatter fluent chain to create an instance based + * on a given number skeleton string. + * + * @param skeleton + * The skeleton string off of which to base this NumberFormatter. + * @param status + * Set to U_NUMBER_SKELETON_SYNTAX_ERROR if the skeleton was invalid. + * @return An UnlocalizedNumberFormatter, to be used for chaining. + * @draft ICU 62 + */ + static UnlocalizedNumberFormatter forSkeleton(const UnicodeString& skeleton, UErrorCode& status); + /** * Use factory methods instead of the constructor to create a NumberFormatter. - * @draft ICU 60 */ NumberFormatter() = delete; }; diff --git a/deps/icu-small/source/i18n/unicode/numfmt.h b/deps/icu-small/source/i18n/unicode/numfmt.h index 1332f5256628e3..572e6afc71b807 100644 --- a/deps/icu-small/source/i18n/unicode/numfmt.h +++ b/deps/icu-small/source/i18n/unicode/numfmt.h @@ -555,16 +555,18 @@ class U_I18N_API NumberFormat : public Format { UnicodeString& appendTo, FieldPositionIterator* posIter, UErrorCode& status) const; -public: + +// Can't use #ifndef U_HIDE_INTERNAL_API because these are virtual methods + /** * Format a decimal number. - * The number is a DigitList wrapper onto a floating point decimal number. + * The number is a DecimalQuantity wrapper onto a floating point decimal number. * The default implementation in NumberFormat converts the decimal number * to a double and formats that. Subclasses of NumberFormat that want * to specifically handle big decimal numbers must override this method. * class DecimalFormat does so. * - * @param number The number, a DigitList format Decimal Floating Point. + * @param number The number, a DecimalQuantity format Decimal Floating Point. * @param appendTo Output parameter to receive result. * Result is appended to existing contents. * @param posIter On return, can be used to iterate over positions @@ -573,20 +575,20 @@ class U_I18N_API NumberFormat : public Format { * @return Reference to 'appendTo' parameter. * @internal */ - virtual UnicodeString& format(const DigitList &number, + virtual UnicodeString& format(const number::impl::DecimalQuantity &number, UnicodeString& appendTo, FieldPositionIterator* posIter, UErrorCode& status) const; /** * Format a decimal number. - * The number is a DigitList wrapper onto a floating point decimal number. + * The number is a DecimalQuantity wrapper onto a floating point decimal number. * The default implementation in NumberFormat converts the decimal number * to a double and formats that. Subclasses of NumberFormat that want * to specifically handle big decimal numbers must override this method. * class DecimalFormat does so. * - * @param number The number, a DigitList format Decimal Floating Point. + * @param number The number, a DecimalQuantity format Decimal Floating Point. * @param appendTo Output parameter to receive result. * Result is appended to existing contents. * @param pos On input: an alignment field, if desired. @@ -595,13 +597,11 @@ class U_I18N_API NumberFormat : public Format { * @return Reference to 'appendTo' parameter. * @internal */ - virtual UnicodeString& format(const DigitList &number, + virtual UnicodeString& format(const number::impl::DecimalQuantity &number, UnicodeString& appendTo, FieldPosition& pos, UErrorCode& status) const; -public: - /** * Return a long if possible (e.g. within range LONG_MAX, * LONG_MAX], and with no decimals), otherwise a double. If diff --git a/deps/icu-small/source/i18n/unicode/plurrule.h b/deps/icu-small/source/i18n/unicode/plurrule.h index d372d79c845179..03dea3f1b92988 100644 --- a/deps/icu-small/source/i18n/unicode/plurrule.h +++ b/deps/icu-small/source/i18n/unicode/plurrule.h @@ -44,7 +44,6 @@ U_NAMESPACE_BEGIN class Hashtable; class IFixedDecimal; -class VisibleDigitsWithExponent; class RuleChain; class PluralRuleParser; class PluralKeywordEnumeration; @@ -348,30 +347,10 @@ class U_I18N_API PluralRules : public UObject { UnicodeString select(double number) const; #ifndef U_HIDE_INTERNAL_API - /** - * Given a number and a format, returns the keyword of the first applicable - * rule for this PluralRules object. - * Note: This internal preview interface may be removed in the future if - * an architecturally cleaner solution reaches stable status. - * @param obj The numeric object for which the rule should be determined. - * @param fmt The NumberFormat specifying how the number will be formatted - * (this can affect the plural form, e.g. "1 dollar" vs "1.0 dollars"). - * @param status Input/output parameter. If at entry this indicates a - * failure status, the method returns immediately; otherwise - * this is set to indicate the outcome of the call. - * @return The keyword of the selected rule. Undefined in the case of an error. - * @internal ICU 59 technology preview, may be removed in the future - */ - UnicodeString select(const Formattable& obj, const NumberFormat& fmt, UErrorCode& status) const; - /** * @internal */ UnicodeString select(const IFixedDecimal &number) const; - /** - * @internal - */ - UnicodeString select(const VisibleDigitsWithExponent &number) const; #endif /* U_HIDE_INTERNAL_API */ /** diff --git a/deps/icu-small/source/i18n/unicode/rbnf.h b/deps/icu-small/source/i18n/unicode/rbnf.h index d8d33420c2a015..2d284909f8a1d4 100644 --- a/deps/icu-small/source/i18n/unicode/rbnf.h +++ b/deps/icu-small/source/i18n/unicode/rbnf.h @@ -884,7 +884,7 @@ class U_I18N_API RuleBasedNumberFormat : public NumberFormat { * @return Reference to 'appendTo' parameter. * @internal */ - virtual UnicodeString& format(const DigitList &number, + virtual UnicodeString& format(const number::impl::DecimalQuantity &number, UnicodeString& appendTo, FieldPositionIterator* posIter, UErrorCode& status) const; @@ -906,7 +906,7 @@ class U_I18N_API RuleBasedNumberFormat : public NumberFormat { * @return Reference to 'appendTo' parameter. * @internal */ - virtual UnicodeString& format(const DigitList &number, + virtual UnicodeString& format(const number::impl::DecimalQuantity &number, UnicodeString& appendTo, FieldPosition& pos, UErrorCode& status) const; diff --git a/deps/icu-small/source/i18n/unicode/scientificnumberformatter.h b/deps/icu-small/source/i18n/unicode/scientificnumberformatter.h index 30edee7ecce289..6c99a246625e2e 100644 --- a/deps/icu-small/source/i18n/unicode/scientificnumberformatter.h +++ b/deps/icu-small/source/i18n/unicode/scientificnumberformatter.h @@ -24,7 +24,6 @@ U_NAMESPACE_BEGIN class FieldPositionIterator; -class DecimalFormatStaticSets; class DecimalFormatSymbols; class DecimalFormat; class Formattable; @@ -150,7 +149,6 @@ class U_I18N_API ScientificNumberFormatter : public UObject { const UnicodeString &original, FieldPositionIterator &fpi, const UnicodeString &preExponent, - const DecimalFormatStaticSets &decimalFormatSets, UnicodeString &appendTo, UErrorCode &status) const = 0; private: @@ -165,7 +163,6 @@ class U_I18N_API ScientificNumberFormatter : public UObject { const UnicodeString &original, FieldPositionIterator &fpi, const UnicodeString &preExponent, - const DecimalFormatStaticSets &decimalFormatSets, UnicodeString &appendTo, UErrorCode &status) const; }; @@ -184,7 +181,6 @@ class U_I18N_API ScientificNumberFormatter : public UObject { const UnicodeString &original, FieldPositionIterator &fpi, const UnicodeString &preExponent, - const DecimalFormatStaticSets &decimalFormatSets, UnicodeString &appendTo, UErrorCode &status) const; private: @@ -211,7 +207,6 @@ class U_I18N_API ScientificNumberFormatter : public UObject { UnicodeString fPreExponent; DecimalFormat *fDecimalFormat; Style *fStyle; - const DecimalFormatStaticSets *fStaticSets; }; diff --git a/deps/icu-small/source/i18n/unicode/smpdtfmt.h b/deps/icu-small/source/i18n/unicode/smpdtfmt.h index 9801b29bdb749b..305412b8d1535f 100644 --- a/deps/icu-small/source/i18n/unicode/smpdtfmt.h +++ b/deps/icu-small/source/i18n/unicode/smpdtfmt.h @@ -50,6 +50,10 @@ class TimeZoneFormat; class SharedNumberFormat; class SimpleDateFormatMutableNFs; +namespace number { +class LocalizedNumberFormatter; +} + /** * * SimpleDateFormat is a concrete class for formatting and parsing dates in a @@ -1268,7 +1272,6 @@ class U_I18N_API SimpleDateFormat: public DateFormat { int32_t fieldNum, FieldPositionHandler& handler, Calendar& cal, - SimpleDateFormatMutableNFs &mutableNFs, UErrorCode& status) const; // in case of illegal argument /** @@ -1284,7 +1287,7 @@ class U_I18N_API SimpleDateFormat: public DateFormat { * @param minDigits Minimum number of digits the result should have * @param maxDigits Maximum number of digits the result should have */ - void zeroPaddingNumber(NumberFormat *currentNumberFormat, + void zeroPaddingNumber(const NumberFormat *currentNumberFormat, UnicodeString &appendTo, int32_t value, int32_t minDigits, @@ -1414,21 +1417,21 @@ class U_I18N_API SimpleDateFormat: public DateFormat { */ int32_t subParse(const UnicodeString& text, int32_t& start, char16_t ch, int32_t count, UBool obeyCount, UBool allowNegative, UBool ambiguousYear[], int32_t& saveHebrewMonth, Calendar& cal, - int32_t patLoc, MessageFormat * numericLeapMonthFormatter, UTimeZoneFormatTimeType *tzTimeType, SimpleDateFormatMutableNFs &mutableNFs, + int32_t patLoc, MessageFormat * numericLeapMonthFormatter, UTimeZoneFormatTimeType *tzTimeType, int32_t *dayPeriod=NULL) const; void parseInt(const UnicodeString& text, Formattable& number, ParsePosition& pos, UBool allowNegative, - NumberFormat *fmt) const; + const NumberFormat *fmt) const; void parseInt(const UnicodeString& text, Formattable& number, int32_t maxDigits, ParsePosition& pos, UBool allowNegative, - NumberFormat *fmt) const; + const NumberFormat *fmt) const; int32_t checkIntSuffix(const UnicodeString& text, int32_t start, int32_t patLoc, UBool isNegative) const; @@ -1495,6 +1498,16 @@ class U_I18N_API SimpleDateFormat: public DateFormat { */ int32_t skipUWhiteSpace(const UnicodeString& text, int32_t pos) const; + /** + * Initialize LocalizedNumberFormatter instances used for speedup. + */ + void initFastNumberFormatters(UErrorCode& status); + + /** + * Delete the LocalizedNumberFormatter instances used for speedup. + */ + void freeFastNumberFormatters(); + /** * Initialize NumberFormat instances used for numbering system overrides. */ @@ -1518,7 +1531,7 @@ class U_I18N_API SimpleDateFormat: public DateFormat { /** * Lazy TimeZoneFormat instantiation, semantically const */ - TimeZoneFormat *tzFormat() const; + TimeZoneFormat *tzFormat(UErrorCode &status) const; const NumberFormat* getNumberFormatByIndex(UDateFormatField index) const; @@ -1611,6 +1624,20 @@ class U_I18N_API SimpleDateFormat: public DateFormat { */ const SharedNumberFormat **fSharedNumberFormatters; + enum NumberFormatterKey { + SMPDTFMT_NF_1x10, + SMPDTFMT_NF_2x10, + SMPDTFMT_NF_3x10, + SMPDTFMT_NF_4x10, + SMPDTFMT_NF_2x2, + SMPDTFMT_NF_COUNT + }; + + /** + * Number formatters pre-allocated for fast performance on the most common integer lengths. + */ + const number::LocalizedNumberFormatter* fFastNumberFormatters[SMPDTFMT_NF_COUNT] = {}; + UBool fHaveDefaultCentury; BreakIterator* fCapitalizationBrkIter; diff --git a/deps/icu-small/source/i18n/unicode/timezone.h b/deps/icu-small/source/i18n/unicode/timezone.h index d4cd7cb36d326a..bbbb6b958e4514 100644 --- a/deps/icu-small/source/i18n/unicode/timezone.h +++ b/deps/icu-small/source/i18n/unicode/timezone.h @@ -284,6 +284,8 @@ class U_I18N_API TimeZone : public UObject { * and may return a different TimeZone from the one returned by * TimeZone::createDefault(). * + *

This function is not thread safe.

+ * * @return A new instance of TimeZone detected from the current host system * configuration. * @stable ICU 55 diff --git a/deps/icu-small/source/i18n/unicode/unum.h b/deps/icu-small/source/i18n/unicode/unum.h index 0e7b9fffbab2b2..8b76014b1683da 100644 --- a/deps/icu-small/source/i18n/unicode/unum.h +++ b/deps/icu-small/source/i18n/unicode/unum.h @@ -29,12 +29,13 @@ /** * \file - * \brief C API: NumberFormat + * \brief C API: Compatibility APIs for number formatting. * *

Number Format C API

* - *

IMPORTANT: New users with C++ capabilities are - * strongly encouraged to see if numberformatter.h fits their use case. + *

IMPORTANT: 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. + * + *

NOTE:: 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. @@ -1013,6 +1018,8 @@ typedef enum UNumberFormatAttribute { *

Example: setting the scale to 3, 123 formats as "123,000" *

Example: setting the scale to -4, 123 formats as "0.0123" * + * This setting is analogous to getMultiplierScale() and setMultiplierScale() in decimfmt.h. + * * @stable ICU 51 */ UNUM_SCALE = 21, #ifndef U_HIDE_INTERNAL_API @@ -1052,7 +1059,7 @@ typedef enum UNumberFormatAttribute { * Default: 0 (unset) * @stable ICU 50 */ - UNUM_PARSE_NO_EXPONENT, + UNUM_PARSE_NO_EXPONENT = 0x1001, /** * if this attribute is set to 1, specifies that, if the pattern contains a @@ -1067,7 +1074,21 @@ typedef enum UNumberFormatAttribute { /* The following cannot be #ifndef U_HIDE_INTERNAL_API, needed in .h file variable declararions */ /** Limit of boolean attributes. * @internal */ - UNUM_LIMIT_BOOLEAN_ATTRIBUTE = 0x1003 + UNUM_LIMIT_BOOLEAN_ATTRIBUTE = 0x1003, + + /** + * Whether parsing is sensitive to case (lowercase/uppercase). + * TODO: Add to the test suite. + * @internal This API is a technical preview. It may change in an upcoming release. + */ + UNUM_PARSE_CASE_SENSITIVE = 0x1004, + + /** + * Formatting: whether to show the plus sign on non-negative numbers. + * TODO: Add to the test suite. + * @internal This API is a technical preview. It may change in an upcoming release. + */ + UNUM_SIGN_ALWAYS_SHOWN = 0x1005, } UNumberFormatAttribute; /** diff --git a/deps/icu-small/source/i18n/unicode/unumberformatter.h b/deps/icu-small/source/i18n/unicode/unumberformatter.h new file mode 100644 index 00000000000000..b37f80c503a76d --- /dev/null +++ b/deps/icu-small/source/i18n/unicode/unumberformatter.h @@ -0,0 +1,666 @@ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING +#ifndef __UNUMBERFORMATTER_H__ +#define __UNUMBERFORMATTER_H__ + +#include "unicode/ufieldpositer.h" +#include "unicode/umisc.h" + + +/** + * \file + * \brief C-compatible API for localized number formatting; not recommended for C++. + * + * This is the C-compatible version of the NumberFormatter API introduced in ICU 60. C++ users should + * include unicode/numberformatter.h and use the proper C++ APIs. + * + * The C API accepts a number skeleton string for specifying the settings for formatting, which covers a + * very large subset of all possible number formatting features. For more information on number skeleton + * strings, see unicode/numberformatter.h. + * + * When using UNumberFormatter, which is treated as immutable, the results are exported to a mutable + * UFormattedNumber object, which you subsequently use for populating your string buffer or iterating over + * the fields. + * + * Example code: + *

+ * // Setup:
+ * UErrorCode ec = U_ZERO_ERROR;
+ * UNumberFormatter* uformatter = unumf_openForSkeletonAndLocale(u"precision-integer", -1, "en", &ec);
+ * UFormattedNumber* uresult = unumf_openResult(&ec);
+ * if (U_FAILURE(ec)) { return; }
+ *
+ * // Format a double:
+ * unumf_formatDouble(uformatter, 5142.3, uresult, &ec);
+ * if (U_FAILURE(ec)) { return; }
+ *
+ * // Export the string to a malloc'd buffer:
+ * int32_t len = unumf_resultToString(uresult, NULL, 0, &ec);
+ * // at this point, ec == U_BUFFER_OVERFLOW_ERROR
+ * ec = U_ZERO_ERROR;
+ * UChar* buffer = (UChar*) malloc((len+1)*sizeof(UChar));
+ * unumf_resultToString(uresult, buffer, len+1, &ec);
+ * if (U_FAILURE(ec)) { return; }
+ * // buffer should equal "5,142"
+ *
+ * // Cleanup:
+ * unumf_close(uformatter);
+ * unumf_closeResult(uresult);
+ * free(buffer);
+ * 
+ * + * If you are a C++ user linking against the C libraries, you can use the LocalPointer versions of these + * APIs. The following example uses LocalPointer with the decimal number and field position APIs: + * + *
+ * // Setup:
+ * LocalUNumberFormatterPointer uformatter(unumf_openForSkeletonAndLocale(u"percent", -1, "en", &ec));
+ * LocalUFormattedNumberPointer uresult(unumf_openResult(&ec));
+ * if (U_FAILURE(ec)) { return; }
+ *
+ * // Format a decimal number:
+ * unumf_formatDecimal(uformatter.getAlias(), "9.87E-3", -1, uresult.getAlias(), &ec);
+ * if (U_FAILURE(ec)) { return; }
+ *
+ * // Get the location of the percent sign:
+ * UFieldPosition ufpos = {UNUM_PERCENT_FIELD, 0, 0};
+ * unumf_resultNextFieldPosition(uresult.getAlias(), &ufpos, &ec);
+ * // ufpos should contain beginIndex=7 and endIndex=8 since the string is "0.00987%"
+ *
+ * // No need to do any cleanup since we are using LocalPointer.
+ * 
+ */ + + +#ifndef U_HIDE_DRAFT_API +/** + * An enum declaring how to render units, including currencies. Example outputs when formatting 123 USD and 123 + * meters in en-CA: + * + *

+ *

    + *
  • NARROW*: "$123.00" and "123 m" + *
  • SHORT: "US$ 123.00" and "123 m" + *
  • FULL_NAME: "123.00 US dollars" and "123 meters" + *
  • ISO_CODE: "USD 123.00" and undefined behavior + *
  • HIDDEN: "123.00" and "123" + *
+ * + *

+ * This enum is similar to {@link com.ibm.icu.text.MeasureFormat.FormatWidth}. + * + * @draft ICU 60 + */ +typedef enum UNumberUnitWidth { + /** + * Print an abbreviated version of the unit name. Similar to SHORT, but always use the shortest available + * abbreviation or symbol. This option can be used when the context hints at the identity of the unit. For more + * information on the difference between NARROW and SHORT, see SHORT. + * + *

+ * In CLDR, this option corresponds to the "Narrow" format for measure units and the "¤¤¤¤¤" placeholder for + * currencies. + * + * @draft ICU 60 + */ + UNUM_UNIT_WIDTH_NARROW, + + /** + * Print an abbreviated version of the unit name. Similar to NARROW, but use a slightly wider abbreviation or + * symbol when there may be ambiguity. This is the default behavior. + * + *

+ * For example, in es-US, the SHORT form for Fahrenheit is "{0} °F", but the NARROW form is "{0}°", + * since Fahrenheit is the customary unit for temperature in that locale. + * + *

+ * In CLDR, this option corresponds to the "Short" format for measure units and the "¤" placeholder for + * currencies. + * + * @draft ICU 60 + */ + UNUM_UNIT_WIDTH_SHORT, + + /** + * Print the full name of the unit, without any abbreviations. + * + *

+ * In CLDR, this option corresponds to the default format for measure units and the "¤¤¤" placeholder for + * currencies. + * + * @draft ICU 60 + */ + UNUM_UNIT_WIDTH_FULL_NAME, + + /** + * Use the three-digit ISO XXX code in place of the symbol for displaying currencies. The behavior of this + * option is currently undefined for use with measure units. + * + *

+ * In CLDR, this option corresponds to the "¤¤" placeholder for currencies. + * + * @draft ICU 60 + */ + UNUM_UNIT_WIDTH_ISO_CODE, + + /** + * Format the number according to the specified unit, but do not display the unit. For currencies, apply + * monetary symbols and formats as with SHORT, but omit the currency symbol. For measure units, the behavior is + * equivalent to not specifying the unit at all. + * + * @draft ICU 60 + */ + UNUM_UNIT_WIDTH_HIDDEN, + + /** + * One more than the highest UNumberUnitWidth value. + * + * @internal ICU 60: The numeric value may change over time; see ICU ticket #12420. + */ + UNUM_UNIT_WIDTH_COUNT +} UNumberUnitWidth; +#endif /* U_HIDE_DRAFT_API */ + +#ifndef U_HIDE_DRAFT_API +/** + * An enum declaring the strategy for when and how to display grouping separators (i.e., the + * separator, often a comma or period, after every 2-3 powers of ten). The choices are several + * pre-built strategies for different use cases that employ locale data whenever possible. Example + * outputs for 1234 and 1234567 in en-IN: + * + *

    + *
  • OFF: 1234 and 12345 + *
  • MIN2: 1234 and 12,34,567 + *
  • AUTO: 1,234 and 12,34,567 + *
  • ON_ALIGNED: 1,234 and 12,34,567 + *
  • THOUSANDS: 1,234 and 1,234,567 + *
+ * + *

+ * The default is AUTO, which displays grouping separators unless the locale data says that grouping + * is not customary. To force grouping for all numbers greater than 1000 consistently across locales, + * use ON_ALIGNED. On the other hand, to display grouping less frequently than the default, use MIN2 + * or OFF. See the docs of each option for details. + * + *

+ * Note: This enum specifies the strategy for grouping sizes. To set which character to use as the + * grouping separator, use the "symbols" setter. + * + * @draft ICU 61 -- TODO: This should be renamed to UNumberGroupingStrategy before promoting to stable, + * for consistency with the other enums. + */ +typedef enum UGroupingStrategy { + /** + * Do not display grouping separators in any locale. + * + * @draft ICU 61 + */ + UNUM_GROUPING_OFF, + + /** + * Display grouping using locale defaults, except do not show grouping on values smaller than + * 10000 (such that there is a minimum of two digits before the first separator). + * + *

+ * Note that locales may restrict grouping separators to be displayed only on 1 million or + * greater (for example, ee and hu) or disable grouping altogether (for example, bg currency). + * + *

+ * Locale data is used to determine whether to separate larger numbers into groups of 2 + * (customary in South Asia) or groups of 3 (customary in Europe and the Americas). + * + * @draft ICU 61 + */ + UNUM_GROUPING_MIN2, + + /** + * Display grouping using the default strategy for all locales. This is the default behavior. + * + *

+ * Note that locales may restrict grouping separators to be displayed only on 1 million or + * greater (for example, ee and hu) or disable grouping altogether (for example, bg currency). + * + *

+ * Locale data is used to determine whether to separate larger numbers into groups of 2 + * (customary in South Asia) or groups of 3 (customary in Europe and the Americas). + * + * @draft ICU 61 + */ + UNUM_GROUPING_AUTO, + + /** + * Always display the grouping separator on values of at least 1000. + * + *

+ * This option ignores the locale data that restricts or disables grouping, described in MIN2 and + * AUTO. This option may be useful to normalize the alignment of numbers, such as in a + * spreadsheet. + * + *

+ * Locale data is used to determine whether to separate larger numbers into groups of 2 + * (customary in South Asia) or groups of 3 (customary in Europe and the Americas). + * + * @draft ICU 61 + */ + UNUM_GROUPING_ON_ALIGNED, + + /** + * Use the Western defaults: groups of 3 and enabled for all numbers 1000 or greater. Do not use + * locale data for determining the grouping strategy. + * + * @draft ICU 61 + */ + UNUM_GROUPING_THOUSANDS, + + /** + * One more than the highest UGroupingStrategy value. + * + * @internal ICU 62: The numeric value may change over time; see ICU ticket #12420. + */ + UNUM_GROUPING_COUNT + +} UGroupingStrategy; +#endif /* U_HIDE_DRAFT_API */ + +#ifndef U_HIDE_DRAFT_API +/** + * An enum declaring how to denote positive and negative numbers. Example outputs when formatting + * 123, 0, and -123 in en-US: + * + *

    + *
  • AUTO: "123", "0", and "-123" + *
  • ALWAYS: "+123", "+0", and "-123" + *
  • NEVER: "123", "0", and "123" + *
  • ACCOUNTING: "$123", "$0", and "($123)" + *
  • ACCOUNTING_ALWAYS: "+$123", "+$0", and "($123)" + *
  • EXCEPT_ZERO: "+123", "0", and "-123" + *
  • ACCOUNTING_EXCEPT_ZERO: "+$123", "$0", and "($123)" + *
+ * + *

+ * The exact format, including the position and the code point of the sign, differ by locale. + * + * @draft ICU 60 + */ +typedef enum UNumberSignDisplay { + /** + * Show the minus sign on negative numbers, and do not show the sign on positive numbers. This is the default + * behavior. + * + * @draft ICU 60 + */ + UNUM_SIGN_AUTO, + + /** + * Show the minus sign on negative numbers and the plus sign on positive numbers, including zero. + * To hide the sign on zero, see {@link UNUM_SIGN_EXCEPT_ZERO}. + * + * @draft ICU 60 + */ + UNUM_SIGN_ALWAYS, + + /** + * Do not show the sign on positive or negative numbers. + * + * @draft ICU 60 + */ + UNUM_SIGN_NEVER, + + /** + * Use the locale-dependent accounting format on negative numbers, and do not show the sign on positive numbers. + * + *

+ * The accounting format is defined in CLDR and varies by locale; in many Western locales, the format is a pair + * of parentheses around the number. + * + *

+ * Note: Since CLDR defines the accounting format in the monetary context only, this option falls back to the + * AUTO sign display strategy when formatting without a currency unit. This limitation may be lifted in the + * future. + * + * @draft ICU 60 + */ + UNUM_SIGN_ACCOUNTING, + + /** + * Use the locale-dependent accounting format on negative numbers, and show the plus sign on + * positive numbers, including zero. For more information on the accounting format, see the + * ACCOUNTING sign display strategy. To hide the sign on zero, see + * {@link UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO}. + * + * @draft ICU 60 + */ + UNUM_SIGN_ACCOUNTING_ALWAYS, + + /** + * Show the minus sign on negative numbers and the plus sign on positive numbers. Do not show a + * sign on zero. + * + * @draft ICU 61 + */ + UNUM_SIGN_EXCEPT_ZERO, + + /** + * Use the locale-dependent accounting format on negative numbers, and show the plus sign on + * positive numbers. Do not show a sign on zero. For more information on the accounting format, + * see the ACCOUNTING sign display strategy. + * + * @draft ICU 61 + */ + UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO, + + /** + * One more than the highest UNumberSignDisplay value. + * + * @internal ICU 60: The numeric value may change over time; see ICU ticket #12420. + */ + UNUM_SIGN_COUNT +} UNumberSignDisplay; +#endif /* U_HIDE_DRAFT_API */ + +#ifndef U_HIDE_DRAFT_API +/** + * An enum declaring how to render the decimal separator. + * + *

+ *

    + *
  • UNUM_DECIMAL_SEPARATOR_AUTO: "1", "1.1" + *
  • UNUM_DECIMAL_SEPARATOR_ALWAYS: "1.", "1.1" + *
+ */ +typedef enum UNumberDecimalSeparatorDisplay { + /** + * Show the decimal separator when there are one or more digits to display after the separator, and do not show + * it otherwise. This is the default behavior. + * + * @draft ICU 60 + */ + UNUM_DECIMAL_SEPARATOR_AUTO, + + /** + * Always show the decimal separator, even if there are no digits to display after the separator. + * + * @draft ICU 60 + */ + UNUM_DECIMAL_SEPARATOR_ALWAYS, + + /** + * One more than the highest UNumberDecimalSeparatorDisplay value. + * + * @internal ICU 60: The numeric value may change over time; see ICU ticket #12420. + */ + UNUM_DECIMAL_SEPARATOR_COUNT +} UNumberDecimalSeparatorDisplay; +#endif /* U_HIDE_DRAFT_API */ + +#ifndef U_HIDE_DRAFT_API +/** + * C-compatible version of icu::number::LocalizedNumberFormatter. + * + * NOTE: This is a C-compatible API; C++ users should build against numberformatter.h instead. + * + * @draft ICU 62 + */ +struct UNumberFormatter; +typedef struct UNumberFormatter UNumberFormatter; + + +/** + * C-compatible version of icu::number::FormattedNumber. + * + * NOTE: This is a C-compatible API; C++ users should build against numberformatter.h instead. + * + * @draft ICU 62 + */ +struct UFormattedNumber; +typedef struct UFormattedNumber UFormattedNumber; + + +/** + * Creates a new UNumberFormatter for the given skeleton string and locale. This is currently the only + * method for creating a new UNumberFormatter. + * + * Objects of type UNumberFormatter returned by this method are threadsafe. + * + * For more details on skeleton strings, see the documentation in numberformatter.h. For more details on + * the usage of this API, see the documentation at the top of unumberformatter.h. + * + * NOTE: This is a C-compatible API; C++ users should build against numberformatter.h instead. + * + * @param skeleton The skeleton string, like u"percent precision-integer" + * @param skeletonLen The number of UChars in the skeleton string, or -1 it it is NUL-terminated. + * @param locale The NUL-terminated locale ID. + * @param ec Set if an error occurs. + * @draft ICU 62 + */ +U_DRAFT UNumberFormatter* U_EXPORT2 +unumf_openForSkeletonAndLocale(const UChar* skeleton, int32_t skeletonLen, const char* locale, + UErrorCode* ec); + + +/** + * Creates a new UFormattedNumber for holding the result of a number formatting operation. + * + * Objects of type UFormattedNumber are not guaranteed to be threadsafe. + * + * NOTE: This is a C-compatible API; C++ users should build against numberformatter.h instead. + * + * @param ec Set if an error occurs. + * @draft ICU 62 + */ +U_DRAFT UFormattedNumber* U_EXPORT2 +unumf_openResult(UErrorCode* ec); + + +/** + * Uses a UNumberFormatter to format an integer to a UFormattedNumber. A string, field position, and other + * information can be retrieved from the UFormattedNumber. + * + * The UNumberFormatter can be shared between threads. Each thread should have its own local + * UFormattedNumber, however, for storing the result of the formatting operation. + * + * NOTE: This is a C-compatible API; C++ users should build against numberformatter.h instead. + * + * @param uformatter A formatter object created by unumf_openForSkeletonAndLocale or similar. + * @param value The number to be formatted. + * @param uresult The object that will be mutated to store the result; see unumf_openResult. + * @param ec Set if an error occurs. + * @draft ICU 62 + */ +U_DRAFT void U_EXPORT2 +unumf_formatInt(const UNumberFormatter* uformatter, int64_t value, UFormattedNumber* uresult, + UErrorCode* ec); + + +/** + * Uses a UNumberFormatter to format a double to a UFormattedNumber. A string, field position, and other + * information can be retrieved from the UFormattedNumber. + * + * The UNumberFormatter can be shared between threads. Each thread should have its own local + * UFormattedNumber, however, for storing the result of the formatting operation. + * + * NOTE: This is a C-compatible API; C++ users should build against numberformatter.h instead. + * + * @param uformatter A formatter object created by unumf_openForSkeletonAndLocale or similar. + * @param value The number to be formatted. + * @param uresult The object that will be mutated to store the result; see unumf_openResult. + * @param ec Set if an error occurs. + * @draft ICU 62 + */ +U_DRAFT void U_EXPORT2 +unumf_formatDouble(const UNumberFormatter* uformatter, double value, UFormattedNumber* uresult, + UErrorCode* ec); + + +/** + * Uses a UNumberFormatter to format a decimal number to a UFormattedNumber. A string, field position, and + * other information can be retrieved from the UFormattedNumber. + * + * The UNumberFormatter can be shared between threads. Each thread should have its own local + * UFormattedNumber, however, for storing the result of the formatting operation. + * + * The syntax of the unformatted number is a "numeric string" as defined in the Decimal Arithmetic + * Specification, available at http://speleotrove.com/decimal + * + * NOTE: This is a C-compatible API; C++ users should build against numberformatter.h instead. + * + * @param uformatter A formatter object created by unumf_openForSkeletonAndLocale or similar. + * @param value The numeric string to be formatted. + * @param valueLen The length of the numeric string, or -1 if it is NUL-terminated. + * @param uresult The object that will be mutated to store the result; see unumf_openResult. + * @param ec Set if an error occurs. + * @draft ICU 62 + */ +U_DRAFT void U_EXPORT2 +unumf_formatDecimal(const UNumberFormatter* uformatter, const char* value, int32_t valueLen, + UFormattedNumber* uresult, UErrorCode* ec); + + +/** + * Extracts the result number string out of a UFormattedNumber to a UChar buffer if possible. + * If bufferCapacity is greater than the required length, a terminating NUL is written. + * If bufferCapacity is less than the required length, an error code is set. + * + * NOTE: This is a C-compatible API; C++ users should build against numberformatter.h instead. + * + * @param uresult The object containing the formatted number. + * @param buffer Where to save the string output. + * @param bufferCapacity The number of UChars available in the buffer. + * @param ec Set if an error occurs. + * @return The required length. + * @draft ICU 62 + */ +U_DRAFT int32_t U_EXPORT2 +unumf_resultToString(const UFormattedNumber* uresult, UChar* buffer, int32_t bufferCapacity, + UErrorCode* ec); + + +/** + * Determines the start and end indices of the next occurrence of the given field in the + * output string. This allows you to determine the locations of, for example, the integer part, + * fraction part, or symbols. + * + * If a field occurs just once, calling this method will find that occurrence and return it. If a + * field occurs multiple times, this method may be called repeatedly with the following pattern: + * + *
+ * UFieldPosition ufpos = {UNUM_GROUPING_SEPARATOR_FIELD, 0, 0};
+ * while (unumf_resultNextFieldPosition(uresult, ufpos, &ec)) {
+ *   // do something with ufpos.
+ * }
+ * 
+ * + * This method is useful if you know which field to query. If you want all available field position + * information, use unumf_resultGetAllFieldPositions(). + * + * NOTE: All fields of the UFieldPosition must be initialized before calling this method. + * + * @param fieldPosition + * Input+output variable. On input, the "field" property determines which field to look up, + * and the "endIndex" property determines where to begin the search. On output, the + * "beginIndex" field is set to the beginning of the first occurrence of the field after the + * input "endIndex", and "endIndex" is set to the end of that occurrence of the field + * (exclusive index). If a field position is not found, the FieldPosition is not changed and + * the method returns FALSE. + * @param ec Set if an error occurs. + * @draft ICU 62 + */ +U_DRAFT UBool U_EXPORT2 +unumf_resultNextFieldPosition(const UFormattedNumber* uresult, UFieldPosition* ufpos, UErrorCode* ec); + + +/** + * Populates the given iterator with all fields in the formatted output string. This allows you to + * determine the locations of the integer part, fraction part, and sign. + * + * If you need information on only one field, use unumf_resultNextFieldPosition(). + * + * @param uresult The object containing the formatted number. + * @param fpositer + * A pointer to a UFieldPositionIterator created by {@link #ufieldpositer_open}. Iteration + * information already present in the UFieldPositionIterator is deleted, and the iterator is reset + * to apply to the fields in the formatted string created by this function call. The field values + * and indexes returned by {@link #ufieldpositer_next} represent fields denoted by + * the UNumberFormatFields enum. Fields are not returned in a guaranteed order. Fields cannot + * overlap, but they may nest. For example, 1234 could format as "1,234" which might consist of a + * grouping separator field for ',' and an integer field encompassing the entire string. + * @param ec Set if an error occurs. + * @draft ICU 62 + */ +U_DRAFT void U_EXPORT2 +unumf_resultGetAllFieldPositions(const UFormattedNumber* uresult, UFieldPositionIterator* ufpositer, + UErrorCode* ec); + + +/** + * Releases the UNumberFormatter created by unumf_openForSkeletonAndLocale(). + * + * NOTE: This is a C-compatible API; C++ users should build against numberformatter.h instead. + * + * @param uformatter An object created by unumf_openForSkeletonAndLocale(). + * @draft ICU 62 + */ +U_DRAFT void U_EXPORT2 +unumf_close(UNumberFormatter* uformatter); + + +/** + * Releases the UFormattedNumber created by unumf_openResult(). + * + * NOTE: This is a C-compatible API; C++ users should build against numberformatter.h instead. + * + * @param uresult An object created by unumf_openResult(). + * @draft ICU 62 + */ +U_DRAFT void U_EXPORT2 +unumf_closeResult(UFormattedNumber* uresult); + + +#if U_SHOW_CPLUSPLUS_API +U_NAMESPACE_BEGIN + +/** + * \class LocalUNumberFormatterPointer + * "Smart pointer" class; closes a UNumberFormatter via unumf_close(). + * For most methods see the LocalPointerBase base class. + * + * Usage: + *
+ * LocalUNumberFormatterPointer uformatter(unumf_openForSkeletonAndLocale(...));
+ * // no need to explicitly call unumf_close()
+ * 
+ * + * @see LocalPointerBase + * @see LocalPointer + * @draft ICU 62 + */ +U_DEFINE_LOCAL_OPEN_POINTER(LocalUNumberFormatterPointer, UNumberFormatter, unumf_close); + +/** + * \class LocalUNumberFormatterPointer + * "Smart pointer" class; closes a UFormattedNumber via unumf_closeResult(). + * For most methods see the LocalPointerBase base class. + * + * Usage: + *
+ * LocalUFormattedNumberPointer uformatter(unumf_openResult(...));
+ * // no need to explicitly call unumf_closeResult()
+ * 
+ * + * @see LocalPointerBase + * @see LocalPointer + * @draft ICU 62 + */ +U_DEFINE_LOCAL_OPEN_POINTER(LocalUFormattedNumberPointer, UFormattedNumber, unumf_closeResult); + +U_NAMESPACE_END +#endif // U_SHOW_CPLUSPLUS_API + +#endif /* U_HIDE_DRAFT_API */ + +#endif //__UNUMBERFORMATTER_H__ +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/unicode/uspoof.h b/deps/icu-small/source/i18n/unicode/uspoof.h index 9fcfcd3ede836c..781a88247fe4c5 100644 --- a/deps/icu-small/source/i18n/unicode/uspoof.h +++ b/deps/icu-small/source/i18n/unicode/uspoof.h @@ -477,7 +477,7 @@ typedef enum USpoofChecks { */ USPOOF_CHAR_LIMIT = 64, - /** + /** * Check that an identifier does not mix numbers from different numbering systems. * For more information, see UTS 39 section 5.3. * @@ -485,6 +485,29 @@ typedef enum USpoofChecks { */ USPOOF_MIXED_NUMBERS = 128, +#ifndef U_HIDE_DRAFT_API + /** + * Check that an identifier does not have a combining character following a character in which that + * combining character would be hidden; for example 'i' followed by a U+0307 combining dot. + * + * More specifically, the following characters are forbidden from preceding a U+0307: + *
    + *
  • Those with the Soft_Dotted Unicode property (which includes 'i' and 'j')
  • + *
  • Latin lowercase letter 'l'
  • + *
  • Dotless 'i' and 'j' ('ı' and 'ȷ', U+0131 and U+0237)
  • + *
  • Any character whose confusable prototype ends with such a character + * (Soft_Dotted, 'l', 'ı', or 'ȷ')
  • + *
+ * In addition, combining characters are allowed between the above characters and U+0307 except those + * with combining class 0 or combining class "Above" (230, same class as U+0307). + * + * This list and the number of combing characters considered by this check may grow over time. + * + * @draft ICU 62 + */ + USPOOF_HIDDEN_OVERLAY = 256, +#endif /* U_HIDE_DRAFT_API */ + /** * Enable all spoof checks. * diff --git a/deps/icu-small/source/i18n/upluralrules.cpp b/deps/icu-small/source/i18n/upluralrules.cpp index 24e74e3ee223f5..bba6dfe3101ec3 100644 --- a/deps/icu-small/source/i18n/upluralrules.cpp +++ b/deps/icu-small/source/i18n/upluralrules.cpp @@ -17,9 +17,44 @@ #include "unicode/unistr.h" #include "unicode/unum.h" #include "unicode/numfmt.h" +#include "number_decimalquantity.h" U_NAMESPACE_USE +namespace { + +/** + * Given a number and a format, returns the keyword of the first applicable + * rule for the PluralRules object. + * @param rules The plural rules. + * @param obj The numeric object for which the rule should be determined. + * @param fmt The NumberFormat specifying how the number will be formatted + * (this can affect the plural form, e.g. "1 dollar" vs "1.0 dollars"). + * @param status Input/output parameter. If at entry this indicates a + * failure status, the method returns immediately; otherwise + * this is set to indicate the outcome of the call. + * @return The keyword of the selected rule. Undefined in the case of an error. + */ +UnicodeString select(const PluralRules &rules, const Formattable& obj, const NumberFormat& fmt, UErrorCode& status) { + if (U_SUCCESS(status)) { + const DecimalFormat *decFmt = dynamic_cast(&fmt); + if (decFmt != NULL) { + number::impl::DecimalQuantity dq; + decFmt->formatToDecimalQuantity(obj, dq, status); + if (U_SUCCESS(status)) { + return rules.select(dq); + } + } else { + double number = obj.getDouble(status); + if (U_SUCCESS(status)) { + return rules.select(number); + } + } + } + return UnicodeString(); +} + +} // namespace U_CAPI UPluralRules* U_EXPORT2 uplrules_open(const char *locale, UErrorCode *status) @@ -73,7 +108,7 @@ uplrules_selectWithFormat(const UPluralRules *uplrules, return 0; } Formattable obj(number); - UnicodeString result = plrules->select(obj, *nf, *status); + UnicodeString result = select(*plrules, obj, *nf, *status); return result.extract(keyword, capacity, *status); } diff --git a/deps/icu-small/source/i18n/uspoof.cpp b/deps/icu-small/source/i18n/uspoof.cpp index 019819b11cdde1..710adcd08daeaf 100644 --- a/deps/icu-small/source/i18n/uspoof.cpp +++ b/deps/icu-small/source/i18n/uspoof.cpp @@ -55,75 +55,96 @@ uspoof_cleanup(void) { } static void U_CALLCONV initializeStatics(UErrorCode &status) { - static const char *inclusionPat = - "['\\-.\\:\\u00B7\\u0375\\u058A\\u05F3\\u05F4\\u06FD\\u06FE\\u0F0B\\u200C\\u200D\\u2010\\u" - "2019\\u2027\\u30A0\\u30FB]"; - gInclusionSet = new UnicodeSet(UnicodeString(inclusionPat, -1, US_INV), status); + static const char16_t *inclusionPat = + u"['\\-.\\:\\u00B7\\u0375\\u058A\\u05F3\\u05F4\\u06FD\\u06FE\\u0F0B\\u200C" + u"\\u200D\\u2010\\u2019\\u2027\\u30A0\\u30FB]"; + gInclusionSet = new UnicodeSet(UnicodeString(inclusionPat), status); + if (gInclusionSet == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } gInclusionSet->freeze(); - // Note: data from http://unicode.org/Public/security/9.0.0/IdentifierStatus.txt + // Note: data from IdentifierStatus.txt & IdentifierType.txt // There is tooling to generate this constant in the unicodetools project: // org.unicode.text.tools.RecommendedSetGenerator // It will print the Java and C++ code to the console for easy copy-paste into this file. // Note: concatenated string constants do not work with UNICODE_STRING_SIMPLE on all platforms. - static const char *recommendedPat = - "[0-9A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u0131\\u0134-\\u013E\\u0141-\\u014" - "8\\u014A-\\u017E\\u018F\\u01A0\\u01A1\\u01AF\\u01B0\\u01CD-\\u01DC\\u01DE-\\u01E3\\u01E" - "6-\\u01F0\\u01F4\\u01F5\\u01F8-\\u021B\\u021E\\u021F\\u0226-\\u0233\\u0259\\u02BB\\u02B" - "C\\u02EC\\u0300-\\u0304\\u0306-\\u030C\\u030F-\\u0311\\u0313\\u0314\\u031B\\u0323-\\u03" - "28\\u032D\\u032E\\u0330\\u0331\\u0335\\u0338\\u0339\\u0342\\u0345\\u037B-\\u037D\\u0386" - "\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03CE\\u03FC-\\u045F\\u048A-\\u0529\\u05" - "2E\\u052F\\u0531-\\u0556\\u0559\\u0561-\\u0586\\u05B4\\u05D0-\\u05EA\\u05F0-\\u05F2\\u0" - "620-\\u063F\\u0641-\\u0655\\u0660-\\u0669\\u0670-\\u0672\\u0674\\u0679-\\u068D\\u068F-" - "\\u06D3\\u06D5\\u06E5\\u06E6\\u06EE-\\u06FC\\u06FF\\u0750-\\u07B1\\u08A0-\\u08AC\\u08B2" - "\\u08B6-\\u08BD\\u0901-\\u094D\\u094F\\u0950\\u0956\\u0957\\u0960-\\u0963\\u0966-\\u096" - "F\\u0971-\\u0977\\u0979-\\u097F\\u0981-\\u0983\\u0985-\\u098C\\u098F\\u0990\\u0993-\\u0" - "9A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BC-\\u09C4\\u09C7\\u09C8\\u09CB-\\u09CE\\u" - "09D7\\u09E0-\\u09E3\\u09E6-\\u09F1\\u0A01-\\u0A03\\u0A05-\\u0A0A\\u0A0F\\u0A10\\u0A13-" - "\\u0A28\\u0A2A-\\u0A30\\u0A32\\u0A35\\u0A38\\u0A39\\u0A3C\\u0A3E-\\u0A42\\u0A47\\u0A48\\" - "u0A4B-\\u0A4D\\u0A5C\\u0A66-\\u0A74\\u0A81-\\u0A83\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A9" - "3-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2\\u0AB3\\u0AB5-\\u0AB9\\u0ABC-\\u0AC5\\u0AC7-\\u0AC9\\u0" - "ACB-\\u0ACD\\u0AD0\\u0AE0-\\u0AE3\\u0AE6-\\u0AEF\\u0B01-\\u0B03\\u0B05-\\u0B0C\\u0B0F\\" - "u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32\\u0B33\\u0B35-\\u0B39\\u0B3C-\\u0B43\\u0B47" - "\\u0B48\\u0B4B-\\u0B4D\\u0B56\\u0B57\\u0B5F-\\u0B61\\u0B66-\\u0B6F\\u0B71\\u0B82\\u0B83" - "\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99\\u0B9A\\u0B9C\\u0B9E\\u0B9F\\u0BA3" - "\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BBE-\\u0BC2\\u0BC6-\\u0BC8\\u0BCA-\\u0BCD\\u0B" - "D0\\u0BD7\\u0BE6-\\u0BEF\\u0C01-\\u0C03\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u" - "0C2A-\\u0C33\\u0C35-\\u0C39\\u0C3D-\\u0C44\\u0C46-\\u0C48\\u0C4A-\\u0C4D\\u0C55\\u0C56" - "\\u0C60\\u0C61\\u0C66-\\u0C6F\\u0C80\\u0C82\\u0C83\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92" - "-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBC-\\u0CC4\\u0CC6-\\u0CC8\\u0CCA-\\u0CCD\\u0" - "CD5\\u0CD6\\u0CE0-\\u0CE3\\u0CE6-\\u0CEF\\u0CF1\\u0CF2\\u0D02\\u0D03\\u0D05-\\u0D0C\\u0" - "D0E-\\u0D10\\u0D12-\\u0D3A\\u0D3D-\\u0D43\\u0D46-\\u0D48\\u0D4A-\\u0D4E\\u0D54-\\u0D57" - "\\u0D60\\u0D61\\u0D66-\\u0D6F\\u0D7A-\\u0D7F\\u0D82\\u0D83\\u0D85-\\u0D8E\\u0D91-\\u0D9" - "6\\u0D9A-\\u0DA5\\u0DA7-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0DCA\\u0DCF-\\u0" - "DD4\\u0DD6\\u0DD8-\\u0DDE\\u0DF2\\u0E01-\\u0E32\\u0E34-\\u0E3A\\u0E40-\\u0E4E\\u0E50-\\" - "u0E59\\u0E81\\u0E82\\u0E84\\u0E87\\u0E88\\u0E8A\\u0E8D\\u0E94-\\u0E97\\u0E99-\\u0E9F\\u" - "0EA1-\\u0EA3\\u0EA5\\u0EA7\\u0EAA\\u0EAB\\u0EAD-\\u0EB2\\u0EB4-\\u0EB9\\u0EBB-\\u0EBD\\" - "u0EC0-\\u0EC4\\u0EC6\\u0EC8-\\u0ECD\\u0ED0-\\u0ED9\\u0EDE\\u0EDF\\u0F00\\u0F20-\\u0F29" - "\\u0F35\\u0F37\\u0F3E-\\u0F42\\u0F44-\\u0F47\\u0F49-\\u0F4C\\u0F4E-\\u0F51\\u0F53-\\u0F" - "56\\u0F58-\\u0F5B\\u0F5D-\\u0F68\\u0F6A-\\u0F6C\\u0F71\\u0F72\\u0F74\\u0F7A-\\u0F80\\u0" - "F82-\\u0F84\\u0F86-\\u0F92\\u0F94-\\u0F97\\u0F99-\\u0F9C\\u0F9E-\\u0FA1\\u0FA3-\\u0FA6" - "\\u0FA8-\\u0FAB\\u0FAD-\\u0FB8\\u0FBA-\\u0FBC\\u0FC6\\u1000-\\u1049\\u1050-\\u109D\\u10" - "C7\\u10CD\\u10D0-\\u10F0\\u10F7-\\u10FA\\u10FD-\\u10FF\\u1200-\\u1248\\u124A-\\u124D\\u" - "1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2" - "-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1" - "315\\u1318-\\u135A\\u135D-\\u135F\\u1380-\\u138F\\u1780-\\u17A2\\u17A5-\\u17A7\\u17A9-" - "\\u17B3\\u17B6-\\u17CA\\u17D2\\u17D7\\u17DC\\u17E0-\\u17E9\\u1C80-\\u1C88\\u1E00-\\u1E9" - "9\\u1E9E\\u1EA0-\\u1EF9\\u1F00-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1" - "F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F70\\u1F72\\u1F74\\u1F76\\u1F78\\u1F7A\\u1F" - "7C\\u1F80-\\u1FB4\\u1FB6-\\u1FBA\\u1FBC\\u1FC2-\\u1FC4\\u1FC6-\\u1FC8\\u1FCA\\u1FCC\\u1" - "FD0-\\u1FD2\\u1FD6-\\u1FDA\\u1FE0-\\u1FE2\\u1FE4-\\u1FEA\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-" - "\\u1FF8\\u1FFA\\u1FFC\\u2D27\\u2D2D\\u2D80-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0" - "-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u3" - "005-\\u3007\\u3041-\\u3096\\u3099\\u309A\\u309D\\u309E\\u30A1-\\u30FA\\u30FC-\\u30FE\\u" - "3105-\\u312D\\u31A0-\\u31BA\\u3400-\\u4DB5\\u4E00-\\u9FD5\\uA660\\uA661\\uA674-\\uA67B" - "\\uA67F\\uA69F\\uA717-\\uA71F\\uA788\\uA78D\\uA78E\\uA790-\\uA793\\uA7A0-\\uA7AA\\uA7AE" - "\\uA7FA\\uA9E7-\\uA9FE\\uAA60-\\uAA76\\uAA7A-\\uAA7F\\uAB01-\\uAB06\\uAB09-\\uAB0E\\uAB" - "11-\\uAB16\\uAB20-\\uAB26\\uAB28-\\uAB2E\\uAC00-\\uD7A3\\uFA0E\\uFA0F\\uFA11\\uFA13\\uF" - "A14\\uFA1F\\uFA21\\uFA23\\uFA24\\uFA27-\\uFA29\\U00020000-\\U0002A6D6\\U0002A700-\\U0" - "002B734\\U0002B740-\\U0002B81D\\U0002B820-\\U0002CEA1]"; - - gRecommendedSet = new UnicodeSet(UnicodeString(recommendedPat, -1, US_INV), status); + static const char16_t *recommendedPat = + u"[0-9A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u0131\\u0134-\\u013E" + u"\\u0141-\\u0148\\u014A-\\u017E\\u018F\\u01A0\\u01A1\\u01AF\\u01B0\\u01CD-" + u"\\u01DC\\u01DE-\\u01E3\\u01E6-\\u01F0\\u01F4\\u01F5\\u01F8-\\u021B\\u021E" + u"\\u021F\\u0226-\\u0233\\u0259\\u02BB\\u02BC\\u02EC\\u0300-\\u0304\\u0306-" + u"\\u030C\\u030F-\\u0311\\u0313\\u0314\\u031B\\u0323-\\u0328\\u032D\\u032E" + u"\\u0330\\u0331\\u0335\\u0338\\u0339\\u0342\\u0345\\u037B-\\u037D\\u0386" + u"\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03CE\\u03FC-\\u045F\\u048A-" + u"\\u0529\\u052E\\u052F\\u0531-\\u0556\\u0559\\u0560-\\u0586\\u0588\\u05B4" + u"\\u05D0-\\u05EA\\u05EF-\\u05F2\\u0620-\\u063F\\u0641-\\u0655\\u0660-\\u0669" + u"\\u0670-\\u0672\\u0674\\u0679-\\u068D\\u068F-\\u06D3\\u06D5\\u06E5\\u06E6" + u"\\u06EE-\\u06FC\\u06FF\\u0750-\\u07B1\\u08A0-\\u08AC\\u08B2\\u08B6-\\u08BD" + u"\\u0901-\\u094D\\u094F\\u0950\\u0956\\u0957\\u0960-\\u0963\\u0966-\\u096F" + u"\\u0971-\\u0977\\u0979-\\u097F\\u0981-\\u0983\\u0985-\\u098C\\u098F\\u0990" + u"\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BC-\\u09C4\\u09C7" + u"\\u09C8\\u09CB-\\u09CE\\u09D7\\u09E0-\\u09E3\\u09E6-\\u09F1\\u09FC\\u09FE" + u"\\u0A01-\\u0A03\\u0A05-\\u0A0A\\u0A0F\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30" + u"\\u0A32\\u0A35\\u0A38\\u0A39\\u0A3C\\u0A3E-\\u0A42\\u0A47\\u0A48\\u0A4B-" + u"\\u0A4D\\u0A5C\\u0A66-\\u0A74\\u0A81-\\u0A83\\u0A85-\\u0A8D\\u0A8F-\\u0A91" + u"\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2\\u0AB3\\u0AB5-\\u0AB9\\u0ABC-\\u0AC5" + u"\\u0AC7-\\u0AC9\\u0ACB-\\u0ACD\\u0AD0\\u0AE0-\\u0AE3\\u0AE6-\\u0AEF\\u0AFA-" + u"\\u0AFF\\u0B01-\\u0B03\\u0B05-\\u0B0C\\u0B0F\\u0B10\\u0B13-\\u0B28\\u0B2A-" + u"\\u0B30\\u0B32\\u0B33\\u0B35-\\u0B39\\u0B3C-\\u0B43\\u0B47\\u0B48\\u0B4B-" + u"\\u0B4D\\u0B56\\u0B57\\u0B5F-\\u0B61\\u0B66-\\u0B6F\\u0B71\\u0B82\\u0B83" + u"\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99\\u0B9A\\u0B9C\\u0B9E" + u"\\u0B9F\\u0BA3\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BBE-\\u0BC2\\u0BC6-" + u"\\u0BC8\\u0BCA-\\u0BCD\\u0BD0\\u0BD7\\u0BE6-\\u0BEF\\u0C01-\\u0C0C\\u0C0E-" + u"\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C33\\u0C35-\\u0C39\\u0C3D-\\u0C44\\u0C46-" + u"\\u0C48\\u0C4A-\\u0C4D\\u0C55\\u0C56\\u0C60\\u0C61\\u0C66-\\u0C6F\\u0C80" + u"\\u0C82\\u0C83\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3" + u"\\u0CB5-\\u0CB9\\u0CBC-\\u0CC4\\u0CC6-\\u0CC8\\u0CCA-\\u0CCD\\u0CD5\\u0CD6" + u"\\u0CE0-\\u0CE3\\u0CE6-\\u0CEF\\u0CF1\\u0CF2\\u0D00\\u0D02\\u0D03\\u0D05-" + u"\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D43\\u0D46-\\u0D48\\u0D4A-\\u0D4E\\u0D54-" + u"\\u0D57\\u0D60\\u0D61\\u0D66-\\u0D6F\\u0D7A-\\u0D7F\\u0D82\\u0D83\\u0D85-" + u"\\u0D8E\\u0D91-\\u0D96\\u0D9A-\\u0DA5\\u0DA7-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD" + u"\\u0DC0-\\u0DC6\\u0DCA\\u0DCF-\\u0DD4\\u0DD6\\u0DD8-\\u0DDE\\u0DF2\\u0E01-" + u"\\u0E32\\u0E34-\\u0E3A\\u0E40-\\u0E4E\\u0E50-\\u0E59\\u0E81\\u0E82\\u0E84" + u"\\u0E87\\u0E88\\u0E8A\\u0E8D\\u0E94-\\u0E97\\u0E99-\\u0E9F\\u0EA1-\\u0EA3" + u"\\u0EA5\\u0EA7\\u0EAA\\u0EAB\\u0EAD-\\u0EB2\\u0EB4-\\u0EB9\\u0EBB-\\u0EBD" + u"\\u0EC0-\\u0EC4\\u0EC6\\u0EC8-\\u0ECD\\u0ED0-\\u0ED9\\u0EDE\\u0EDF\\u0F00" + u"\\u0F20-\\u0F29\\u0F35\\u0F37\\u0F3E-\\u0F42\\u0F44-\\u0F47\\u0F49-\\u0F4C" + u"\\u0F4E-\\u0F51\\u0F53-\\u0F56\\u0F58-\\u0F5B\\u0F5D-\\u0F68\\u0F6A-\\u0F6C" + u"\\u0F71\\u0F72\\u0F74\\u0F7A-\\u0F80\\u0F82-\\u0F84\\u0F86-\\u0F92\\u0F94-" + u"\\u0F97\\u0F99-\\u0F9C\\u0F9E-\\u0FA1\\u0FA3-\\u0FA6\\u0FA8-\\u0FAB\\u0FAD-" + u"\\u0FB8\\u0FBA-\\u0FBC\\u0FC6\\u1000-\\u1049\\u1050-\\u109D\\u10C7\\u10CD" + u"\\u10D0-\\u10F0\\u10F7-\\u10FA\\u10FD-\\u10FF\\u1200-\\u1248\\u124A-\\u124D" + u"\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-" + u"\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6" + u"\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u135D-\\u135F\\u1380-\\u138F" + u"\\u1780-\\u17A2\\u17A5-\\u17A7\\u17A9-\\u17B3\\u17B6-\\u17CA\\u17D2\\u17D7" + u"\\u17DC\\u17E0-\\u17E9\\u1C80-\\u1C88\\u1C90-\\u1CBA\\u1CBD-\\u1CBF\\u1E00-" + u"\\u1E99\\u1E9E\\u1EA0-\\u1EF9\\u1F00-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45" + u"\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F70\\u1F72" + u"\\u1F74\\u1F76\\u1F78\\u1F7A\\u1F7C\\u1F80-\\u1FB4\\u1FB6-\\u1FBA\\u1FBC" + u"\\u1FC2-\\u1FC4\\u1FC6-\\u1FC8\\u1FCA\\u1FCC\\u1FD0-\\u1FD2\\u1FD6-\\u1FDA" + u"\\u1FE0-\\u1FE2\\u1FE4-\\u1FEA\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FF8\\u1FFA" + u"\\u1FFC\\u2D27\\u2D2D\\u2D80-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-" + u"\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-" + u"\\u2DDE\\u3005-\\u3007\\u3041-\\u3096\\u3099\\u309A\\u309D\\u309E\\u30A1-" + u"\\u30FA\\u30FC-\\u30FE\\u3105-\\u312F\\u31A0-\\u31BA\\u3400-\\u4DB5\\u4E00-" + u"\\u9FEF\\uA660\\uA661\\uA674-\\uA67B\\uA67F\\uA69F\\uA717-\\uA71F\\uA788" + u"\\uA78D\\uA78E\\uA790-\\uA793\\uA7A0-\\uA7AA\\uA7AE\\uA7AF\\uA7B8\\uA7B9" + u"\\uA7FA\\uA9E7-\\uA9FE\\uAA60-\\uAA76\\uAA7A-\\uAA7F\\uAB01-\\uAB06\\uAB09-" + u"\\uAB0E\\uAB11-\\uAB16\\uAB20-\\uAB26\\uAB28-\\uAB2E\\uAC00-\\uD7A3\\uFA0E" + u"\\uFA0F\\uFA11\\uFA13\\uFA14\\uFA1F\\uFA21\\uFA23\\uFA24\\uFA27-\\uFA29" + u"\\U0001133B\\U0001B002-\\U0001B11E\\U00020000-\\U0002A6D6\\U0002A700-" + u"\\U0002B734\\U0002B740-\\U0002B81D\\U0002B820-\\U0002CEA1\\U0002CEB0-" + u"\\U0002EBE0]"; + + gRecommendedSet = new UnicodeSet(UnicodeString(recommendedPat), status); + if (gRecommendedSet == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + delete gInclusionSet; + return; + } gRecommendedSet->freeze(); gNfdNormalizer = Normalizer2::getNFDInstance(status); ucln_i18n_registerCleanup(UCLN_I18N_SPOOF, uspoof_cleanup); @@ -140,12 +161,13 @@ uspoof_open(UErrorCode *status) { return NULL; } SpoofImpl *si = new SpoofImpl(*status); - if (U_SUCCESS(*status) && si == NULL) { + if (si == NULL) { *status = U_MEMORY_ALLOCATION_ERROR; + return NULL; } if (U_FAILURE(*status)) { delete si; - si = NULL; + return NULL; } return si->asUSpoofChecker(); } @@ -157,18 +179,38 @@ uspoof_openFromSerialized(const void *data, int32_t length, int32_t *pActualLeng if (U_FAILURE(*status)) { return NULL; } + + if (data == NULL) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return NULL; + } + umtx_initOnce(gSpoofInitStaticsOnce, &initializeStatics, *status); + if (U_FAILURE(*status)) + { + return NULL; + } + SpoofData *sd = new SpoofData(data, length, *status); - SpoofImpl *si = new SpoofImpl(sd, *status); + if (sd == NULL) { + *status = U_MEMORY_ALLOCATION_ERROR; + return NULL; + } + if (U_FAILURE(*status)) { delete sd; - delete si; return NULL; } - if (sd == NULL || si == NULL) { + + SpoofImpl *si = new SpoofImpl(sd, *status); + if (si == NULL) { *status = U_MEMORY_ALLOCATION_ERROR; - delete sd; - delete si; + delete sd; // explicit delete as the destructor for si won't be called. + return NULL; + } + + if (U_FAILURE(*status)) { + delete si; // no delete for sd, as the si destructor will delete it. return NULL; } @@ -186,6 +228,10 @@ uspoof_clone(const USpoofChecker *sc, UErrorCode *status) { return NULL; } SpoofImpl *result = new SpoofImpl(*src, *status); // copy constructor + if (result == NULL) { + *status = U_MEMORY_ALLOCATION_ERROR; + return NULL; + } if (U_FAILURE(*status)) { delete result; result = NULL; @@ -524,6 +570,13 @@ int32_t checkImpl(const SpoofImpl* This, const UnicodeString& id, CheckResult* c checkResult->fNumerics = numerics; // UnicodeSet::operator= } + if (0 != (This->fChecks & USPOOF_HIDDEN_OVERLAY)) { + int32_t index = This->findHiddenOverlay(id, *status); + if (index != -1) { + result |= USPOOF_HIDDEN_OVERLAY; + } + } + if (0 != (This->fChecks & USPOOF_CHAR_LIMIT)) { int32_t i; diff --git a/deps/icu-small/source/i18n/uspoof_build.cpp b/deps/icu-small/source/i18n/uspoof_build.cpp index 7d2440e5af65e8..7087c1ce596ced 100644 --- a/deps/icu-small/source/i18n/uspoof_build.cpp +++ b/deps/icu-small/source/i18n/uspoof_build.cpp @@ -71,8 +71,29 @@ uspoof_openFromSource(const char *confusables, int32_t confusablesLen, // Set up a shell of a spoof detector, with empty data. SpoofData *newSpoofData = new SpoofData(*status); + + if (newSpoofData == NULL) { + *status = U_MEMORY_ALLOCATION_ERROR; + return NULL; + } + + if (U_FAILURE(*status)) { + delete newSpoofData; + return NULL; + } SpoofImpl *This = new SpoofImpl(newSpoofData, *status); + if (This == NULL) { + *status = U_MEMORY_ALLOCATION_ERROR; + delete newSpoofData; // explicit delete as the destructor for SpoofImpl won't be called. + return NULL; + } + + if (U_FAILURE(*status)) { + delete This; // no delete for newSpoofData, as the SpoofImpl destructor will delete it. + return NULL; + } + // Compile the binary data from the source (text) format. ConfusabledataBuilder::buildConfusableData(This, confusables, confusablesLen, errorType, pe, *status); diff --git a/deps/icu-small/source/i18n/uspoof_conf.cpp b/deps/icu-small/source/i18n/uspoof_conf.cpp index 3a061d9dfcffe1..672b3e0a6c8023 100644 --- a/deps/icu-small/source/i18n/uspoof_conf.cpp +++ b/deps/icu-small/source/i18n/uspoof_conf.cpp @@ -76,6 +76,10 @@ SPUString::~SPUString() { SPUStringPool::SPUStringPool(UErrorCode &status) : fVec(NULL), fHash(NULL) { fVec = new UVector(status); + if (fVec == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } fHash = uhash_open(uhash_hashUnicodeString, // key hash function uhash_compareUnicodeString, // Key Comparator NULL, // Value Comparator @@ -136,6 +140,10 @@ SPUString *SPUStringPool::addString(UnicodeString *src, UErrorCode &status) { delete src; } else { hashedString = new SPUString(src); + if (hashedString == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + return NULL; + } uhash_put(fHash, src, hashedString, &status); fVec->addElement(hashedString, status); } @@ -160,11 +168,32 @@ ConfusabledataBuilder::ConfusabledataBuilder(SpoofImpl *spImpl, UErrorCode &stat if (U_FAILURE(status)) { return; } - fTable = uhash_open(uhash_hashLong, uhash_compareLong, NULL, &status); - fKeySet = new UnicodeSet(); - fKeyVec = new UVector(status); - fValueVec = new UVector(status); + + fTable = uhash_open(uhash_hashLong, uhash_compareLong, NULL, &status); + + fKeySet = new UnicodeSet(); + if (fKeySet == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + + fKeyVec = new UVector(status); + if (fKeyVec == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + + fValueVec = new UVector(status); + if (fValueVec == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + stringPool = new SPUStringPool(status); + if (stringPool == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } } diff --git a/deps/icu-small/source/i18n/uspoof_impl.cpp b/deps/icu-small/source/i18n/uspoof_impl.cpp index 0ca85c00a98a8d..c1034c2e53f6e9 100644 --- a/deps/icu-small/source/i18n/uspoof_impl.cpp +++ b/deps/icu-small/source/i18n/uspoof_impl.cpp @@ -377,6 +377,43 @@ URestrictionLevel SpoofImpl::getRestrictionLevel(const UnicodeString& input, UEr return USPOOF_MINIMALLY_RESTRICTIVE; } +int32_t SpoofImpl::findHiddenOverlay(const UnicodeString& input, UErrorCode&) const { + bool sawLeadCharacter = false; + for (int32_t i=0; iconfusableLookup(cp, skelStr); + UChar32 finalCp = skelStr.char32At(skelStr.moveIndex32(skelStr.length(), -1)); + if (finalCp != cp && isIllegalCombiningDotLeadCharacterNoLookup(finalCp)) { + return true; + } + return false; +} + // Convert a text format hex number. Utility function used by builder code. Static. @@ -532,24 +569,25 @@ uspoof_cleanupDefaultData(void) { if (gDefaultSpoofData) { // Will delete, assuming all user-level spoof checkers were closed. gDefaultSpoofData->removeReference(); - gDefaultSpoofData = NULL; + gDefaultSpoofData = nullptr; gSpoofInitDefaultOnce.reset(); } return TRUE; } static void U_CALLCONV uspoof_loadDefaultData(UErrorCode& status) { - UDataMemory *udm = udata_openChoice(NULL, "cfu", "confusables", + UDataMemory *udm = udata_openChoice(nullptr, "cfu", "confusables", spoofDataIsAcceptable, - NULL, // context, would receive dataVersion if supplied. + nullptr, // context, would receive dataVersion if supplied. &status); if (U_FAILURE(status)) { return; } gDefaultSpoofData = new SpoofData(udm, status); if (U_FAILURE(status)) { delete gDefaultSpoofData; + gDefaultSpoofData = nullptr; return; } - if (gDefaultSpoofData == NULL) { + if (gDefaultSpoofData == nullptr) { status = U_MEMORY_ALLOCATION_ERROR; return; } @@ -590,6 +628,10 @@ SpoofData::SpoofData(const void *data, int32_t length, UErrorCode &status) status = U_INVALID_FORMAT_ERROR; return; } + if (data == NULL) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } void *ncData = const_cast(data); fRawData = static_cast(ncData); if (length < fRawData->fLength) { diff --git a/deps/icu-small/source/i18n/uspoof_impl.h b/deps/icu-small/source/i18n/uspoof_impl.h index 1184b8d9060662..470a31f2c979bc 100644 --- a/deps/icu-small/source/i18n/uspoof_impl.h +++ b/deps/icu-small/source/i18n/uspoof_impl.h @@ -83,6 +83,9 @@ class SpoofImpl : public UObject { void getNumerics(const UnicodeString& input, UnicodeSet& result, UErrorCode& status) const; URestrictionLevel getRestrictionLevel(const UnicodeString& input, UErrorCode& status) const; + int32_t findHiddenOverlay(const UnicodeString& input, UErrorCode& status) const; + bool isIllegalCombiningDotLeadCharacter(UChar32 cp) const; + /** parse a hex number. Untility used by the builders. */ static UChar32 ScanHex(const UChar *s, int32_t start, int32_t limit, UErrorCode &status); diff --git a/deps/icu-small/source/i18n/valueformatter.cpp b/deps/icu-small/source/i18n/valueformatter.cpp deleted file mode 100644 index e769f369d48609..00000000000000 --- a/deps/icu-small/source/i18n/valueformatter.cpp +++ /dev/null @@ -1,223 +0,0 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2015, International Business Machines Corporation and -* others. All Rights Reserved. -******************************************************************************* -*/ - -#include "unicode/plurrule.h" -#include "unicode/unistr.h" -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "digitformatter.h" -#include "digitgrouping.h" -#include "digitinterval.h" -#include "digitlst.h" -#include "precision.h" -#include "plurrule_impl.h" -#include "smallintformatter.h" -#include "uassert.h" -#include "valueformatter.h" -#include "visibledigits.h" - -U_NAMESPACE_BEGIN - -ValueFormatter::~ValueFormatter() {} - -VisibleDigitsWithExponent & -ValueFormatter::toVisibleDigitsWithExponent( - int64_t value, - VisibleDigitsWithExponent &digits, - UErrorCode &status) const { - switch (fType) { - case kFixedDecimal: - return fFixedPrecision->initVisibleDigitsWithExponent( - value, digits, status); - break; - case kScientificNotation: - return fScientificPrecision->initVisibleDigitsWithExponent( - value, digits, status); - break; - default: - U_ASSERT(FALSE); - break; - } - return digits; -} - -VisibleDigitsWithExponent & -ValueFormatter::toVisibleDigitsWithExponent( - DigitList &value, - VisibleDigitsWithExponent &digits, - UErrorCode &status) const { - switch (fType) { - case kFixedDecimal: - return fFixedPrecision->initVisibleDigitsWithExponent( - value, digits, status); - break; - case kScientificNotation: - return fScientificPrecision->initVisibleDigitsWithExponent( - value, digits, status); - break; - default: - U_ASSERT(FALSE); - break; - } - return digits; -} - -static UBool isNoGrouping( - const DigitGrouping &grouping, - int32_t value, - const FixedPrecision &precision) { - IntDigitCountRange range( - precision.fMin.getIntDigitCount(), - precision.fMax.getIntDigitCount()); - return grouping.isNoGrouping(value, range); -} - -UBool -ValueFormatter::isFastFormattable(int32_t value) const { - switch (fType) { - case kFixedDecimal: - { - if (value == INT32_MIN) { - return FALSE; - } - if (value < 0) { - value = -value; - } - return fFixedPrecision->isFastFormattable() && fFixedOptions->isFastFormattable() && isNoGrouping(*fGrouping, value, *fFixedPrecision); - } - case kScientificNotation: - return FALSE; - default: - U_ASSERT(FALSE); - break; - } - return FALSE; -} - -DigitList & -ValueFormatter::round(DigitList &value, UErrorCode &status) const { - if (value.isNaN() || value.isInfinite()) { - return value; - } - switch (fType) { - case kFixedDecimal: - return fFixedPrecision->round(value, 0, status); - case kScientificNotation: - return fScientificPrecision->round(value, status); - default: - U_ASSERT(FALSE); - break; - } - return value; -} - -UnicodeString & -ValueFormatter::formatInt32( - int32_t value, - FieldPositionHandler &handler, - UnicodeString &appendTo) const { - switch (fType) { - case kFixedDecimal: - { - IntDigitCountRange range( - fFixedPrecision->fMin.getIntDigitCount(), - fFixedPrecision->fMax.getIntDigitCount()); - return fDigitFormatter->formatPositiveInt32( - value, - range, - handler, - appendTo); - } - break; - case kScientificNotation: - default: - U_ASSERT(FALSE); - break; - } - return appendTo; -} - -UnicodeString & -ValueFormatter::format( - const VisibleDigitsWithExponent &value, - FieldPositionHandler &handler, - UnicodeString &appendTo) const { - switch (fType) { - case kFixedDecimal: - return fDigitFormatter->format( - value.getMantissa(), - *fGrouping, - *fFixedOptions, - handler, - appendTo); - break; - case kScientificNotation: - return fDigitFormatter->format( - value, - *fScientificOptions, - handler, - appendTo); - break; - default: - U_ASSERT(FALSE); - break; - } - return appendTo; -} - -int32_t -ValueFormatter::countChar32(const VisibleDigitsWithExponent &value) const { - switch (fType) { - case kFixedDecimal: - return fDigitFormatter->countChar32( - value.getMantissa(), - *fGrouping, - *fFixedOptions); - break; - case kScientificNotation: - return fDigitFormatter->countChar32( - value, - *fScientificOptions); - break; - default: - U_ASSERT(FALSE); - break; - } - return 0; -} - -void -ValueFormatter::prepareFixedDecimalFormatting( - const DigitFormatter &formatter, - const DigitGrouping &grouping, - const FixedPrecision &precision, - const DigitFormatterOptions &options) { - fType = kFixedDecimal; - fDigitFormatter = &formatter; - fGrouping = &grouping; - fFixedPrecision = &precision; - fFixedOptions = &options; -} - -void -ValueFormatter::prepareScientificFormatting( - const DigitFormatter &formatter, - const ScientificPrecision &precision, - const SciFormatterOptions &options) { - fType = kScientificNotation; - fDigitFormatter = &formatter; - fScientificPrecision = &precision; - fScientificOptions = &options; -} - -U_NAMESPACE_END - -#endif /* !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/valueformatter.h b/deps/icu-small/source/i18n/valueformatter.h deleted file mode 100644 index 836a05b17c5209..00000000000000 --- a/deps/icu-small/source/i18n/valueformatter.h +++ /dev/null @@ -1,161 +0,0 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2015, International Business Machines Corporation and * -* others. All Rights Reserved. * -******************************************************************************* -*/ - -#ifndef VALUEFORMATTER_H -#define VALUEFORMATTER_H - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/uobject.h" -#include "unicode/utypes.h" - - - -U_NAMESPACE_BEGIN - -class UnicodeString; -class DigitList; -class FieldPositionHandler; -class DigitGrouping; -class PluralRules; -class FixedPrecision; -class DigitFormatter; -class DigitFormatterOptions; -class ScientificPrecision; -class SciFormatterOptions; -class FixedDecimal; -class VisibleDigitsWithExponent; - - -/** - * A closure around rounding and formatting a value. As these instances are - * designed to be short lived (they only exist while formatting a value), they - * do not own their own attributes. Rather the caller maintains ownership of - * all attributes. A caller first calls a prepareXXX method on an instance - * to share its data before using that instance. Using an - * instance without first calling a prepareXXX method results in an - * assertion error and a program crash. - */ -class U_I18N_API ValueFormatter : public UObject { -public: - ValueFormatter() : fType(kFormatTypeCount) { - } - - virtual ~ValueFormatter(); - - /** - * This function is here only to support the protected round() method - * in DecimalFormat. It serves no ther purpose than that. - * - * @param value this value is rounded in place. - * @param status any error returned here. - */ - DigitList &round(DigitList &value, UErrorCode &status) const; - - /** - * Returns TRUE if the absolute value of value can be fast formatted - * using ValueFormatter::formatInt32. - */ - UBool isFastFormattable(int32_t value) const; - - /** - * Converts value to a VisibleDigitsWithExponent. - * Result may be fixed point or scientific. - */ - VisibleDigitsWithExponent &toVisibleDigitsWithExponent( - int64_t value, - VisibleDigitsWithExponent &digits, - UErrorCode &status) const; - - /** - * Converts value to a VisibleDigitsWithExponent. - * Result may be fixed point or scientific. - */ - VisibleDigitsWithExponent &toVisibleDigitsWithExponent( - DigitList &value, - VisibleDigitsWithExponent &digits, - UErrorCode &status) const; - - /** - * formats positiveValue and appends to appendTo. Returns appendTo. - * @param positiveValue If negative, no negative sign is formatted. - * @param handler stores the field positions - * @param appendTo formatted value appended here. - */ - UnicodeString &format( - const VisibleDigitsWithExponent &positiveValue, - FieldPositionHandler &handler, - UnicodeString &appendTo) const; - - - /** - * formats positiveValue and appends to appendTo. Returns appendTo. - * value must be positive. Calling formatInt32 to format a value when - * isFastFormattable indicates that the value cannot be fast formatted - * results in undefined behavior. - */ - UnicodeString &formatInt32( - int32_t positiveValue, - FieldPositionHandler &handler, - UnicodeString &appendTo) const; - - /** - * Returns the number of code points needed to format. - * @param positiveValue if negative, the negative sign is not included - * in count. - */ - int32_t countChar32( - const VisibleDigitsWithExponent &positiveValue) const; - - /** - * Prepares this instance for fixed decimal formatting. - */ - void prepareFixedDecimalFormatting( - const DigitFormatter &formatter, - const DigitGrouping &grouping, - const FixedPrecision &precision, - const DigitFormatterOptions &options); - - /** - * Prepares this instance for scientific formatting. - */ - void prepareScientificFormatting( - const DigitFormatter &formatter, - const ScientificPrecision &precision, - const SciFormatterOptions &options); - -private: - ValueFormatter(const ValueFormatter &); - ValueFormatter &operator=(const ValueFormatter &); - enum FormatType { - kFixedDecimal, - kScientificNotation, - kFormatTypeCount - }; - - FormatType fType; - - // for fixed decimal and scientific formatting - const DigitFormatter *fDigitFormatter; - - // for fixed decimal formatting - const FixedPrecision *fFixedPrecision; - const DigitFormatterOptions *fFixedOptions; - const DigitGrouping *fGrouping; - - // for scientific formatting - const ScientificPrecision *fScientificPrecision; - const SciFormatterOptions *fScientificOptions; -}; - -U_NAMESPACE_END - -#endif /* !UCONFIG_NO_FORMATTING */ - -#endif /* VALUEFORMATTER_H */ diff --git a/deps/icu-small/source/i18n/visibledigits.cpp b/deps/icu-small/source/i18n/visibledigits.cpp deleted file mode 100644 index 03cfc68d255380..00000000000000 --- a/deps/icu-small/source/i18n/visibledigits.cpp +++ /dev/null @@ -1,186 +0,0 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* - * Copyright (C) 2016, International Business Machines - * Corporation and others. All Rights Reserved. - * - * file name: visibledigits.cpp - */ - -#include - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "cstring.h" -#include "decNumber.h" -#include "digitlst.h" -#include "uassert.h" -#include "visibledigits.h" - -static const int32_t kNegative = 1; -static const int32_t kInfinite = 2; -static const int32_t kNaN = 4; - -U_NAMESPACE_BEGIN - -void VisibleDigits::setNegative() { - fFlags |= kNegative; -} - -void VisibleDigits::setNaN() { - fFlags |= kNaN; -} - -void VisibleDigits::setInfinite() { - fFlags |= kInfinite; -} - -void VisibleDigits::clear() { - fInterval.clear(); - fDigits.clear(); - fExponent = 0; - fFlags = 0; - fAbsIntValue = 0LL; - fAbsIntValueSet = FALSE; - fAbsDoubleValue = 0.0; - fAbsDoubleValueSet = FALSE; -} - -UBool VisibleDigits::isNegative() const { - return (fFlags & kNegative); -} - -UBool VisibleDigits::isNaN() const { - return (fFlags & kNaN); -} - -UBool VisibleDigits::isInfinite() const { - return (fFlags & kInfinite); -} - -int32_t VisibleDigits::getDigitByExponent(int32_t digitPos) const { - if (digitPos < fExponent || digitPos >= fExponent + fDigits.length()) { - return 0; - } - const char *ptr = fDigits.data(); - return ptr[digitPos - fExponent]; -} - -UBool VisibleDigits::isOverMaxDigits() const { - return (fExponent + fDigits.length() > fInterval.getMostSignificantExclusive()); -} - -UBool VisibleDigits::isNaNOrInfinity() const { - return (fFlags & (kInfinite | kNaN)) != 0; -} - -double VisibleDigits::computeAbsDoubleValue() const { - // Take care of NaN and infinity - if (isNaN()) { - return uprv_getNaN(); - } - if (isInfinite()) { - return uprv_getInfinity(); - } - - // stack allocate a decNumber to hold MAX_DBL_DIGITS+3 significant digits - struct { - decNumber decNum; - char digits[MAX_DBL_DIGITS+3]; - } decNumberWithStorage; - decNumber *numberPtr = &decNumberWithStorage.decNum; - - int32_t mostSig = fInterval.getMostSignificantExclusive(); - int32_t mostSigNonZero = fExponent + fDigits.length(); - int32_t end = mostSig > mostSigNonZero ? mostSigNonZero : mostSig; - int32_t leastSig = fInterval.getLeastSignificantInclusive(); - int32_t start = leastSig > fExponent ? leastSig : fExponent; - if (end <= start) { - return 0.0; - } - if (start < end - (MAX_DBL_DIGITS+3)) { - start = end - (MAX_DBL_DIGITS+3); - } - uint8_t *pos = numberPtr->lsu; - const char *src = &(fDigits.data()[start - fExponent]); - for (int32_t i = start; i < end; ++i) { - *pos++ = (uint8_t) (*src++); - } - numberPtr->exponent = start; - numberPtr->bits = 0; - numberPtr->digits = end - start; - char str[MAX_DBL_DIGITS+18]; - uprv_decNumberToString(numberPtr, str); - U_ASSERT(uprv_strlen(str) < MAX_DBL_DIGITS+18); - char *unused = NULL; - return DigitList::decimalStrToDouble(str, &unused); -} - -void VisibleDigits::getFixedDecimal( - double &source, int64_t &intValue, int64_t &f, int64_t &t, int32_t &v, UBool &hasIntValue) const { - source = 0.0; - intValue = 0; - f = 0; - t = 0; - v = 0; - hasIntValue = FALSE; - if (isNaNOrInfinity()) { - return; - } - - // source - if (fAbsDoubleValueSet) { - source = fAbsDoubleValue; - } else { - source = computeAbsDoubleValue(); - } - - // visible decimal digits - v = fInterval.getFracDigitCount(); - - // intValue - - // If we initialized from an int64 just use that instead of - // calculating - if (fAbsIntValueSet) { - intValue = fAbsIntValue; - } else { - int32_t startPos = fInterval.getMostSignificantExclusive(); - if (startPos > 18) { - startPos = 18; - } - // process the integer digits - for (int32_t i = startPos - 1; i >= 0; --i) { - intValue = intValue * 10LL + getDigitByExponent(i); - } - if (intValue == 0LL && startPos > 0) { - intValue = 100000000000000000LL; - } - } - - // f (decimal digits) - // skip over any leading 0's in fraction digits. - int32_t idx = -1; - for (; idx >= -v && getDigitByExponent(idx) == 0; --idx) - ; - - // Only process up to first 18 non zero fraction digits for decimalDigits - // since that is all we can fit into an int64. - for (int32_t i = idx; i >= -v && i > idx - 18; --i) { - f = f * 10LL + getDigitByExponent(i); - } - - // If we have no decimal digits, we don't have an integer value - hasIntValue = (f == 0LL); - - // t (decimal digits without trailing zeros) - t = f; - while (t > 0 && t % 10LL == 0) { - t /= 10; - } -} - -U_NAMESPACE_END -#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/deps/icu-small/source/i18n/visibledigits.h b/deps/icu-small/source/i18n/visibledigits.h deleted file mode 100644 index 03c8013e393374..00000000000000 --- a/deps/icu-small/source/i18n/visibledigits.h +++ /dev/null @@ -1,162 +0,0 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* * Copyright (C) 2015, International Business Machines -* Corporation and others. All Rights Reserved. -******************************************************************************* -* visibledigits.h -* -* created on: 2015jun20 -* created by: Travis Keep -*/ - -#ifndef __VISIBLEDIGITS_H__ -#define __VISIBLEDIGITS_H__ - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/uobject.h" - -#include "charstr.h" -#include "digitinterval.h" - -U_NAMESPACE_BEGIN - -class DigitList; - -/** - * VisibleDigits represents the digits visible for formatting. - * Once initialized using a FixedPrecision instance, VisibleDigits instances - * remain unchanged until they are initialized again. A VisibleDigits with - * a numeric value equal to 3.0 could be "3", "3.0", "3.00" or even "003.0" - * depending on settings of the FixedPrecision instance used to initialize it. - */ -class U_I18N_API VisibleDigits : public UMemory { -public: - VisibleDigits() : fExponent(0), fFlags(0), fAbsIntValue(0), fAbsIntValueSet(FALSE), fAbsDoubleValue(0.0), fAbsDoubleValueSet(FALSE) { } - - UBool isNegative() const; - UBool isNaN() const; - UBool isInfinite() const; - UBool isNaNOrInfinity() const; - - /** - * Gets the digit at particular exponent, if number is 987.6, then - * getDigit(2) == 9 and gitDigit(0) == 7 and gitDigit(-1) == 6. - * If isNaN() or isInfinity() return TRUE, then the result of this - * function is undefined. - */ - int32_t getDigitByExponent(int32_t digitPos) const; - - /** - * Returns the digit interval which indicates the leftmost and rightmost - * position of this instance. - * If isNaN() or isInfinity() return TRUE, then the result of this - * function is undefined. - */ - const DigitInterval &getInterval() const { return fInterval; } - - /** - * Gets the parameters needed to create a FixedDecimal. - */ - void getFixedDecimal(double &source, int64_t &intValue, int64_t &f, int64_t &t, int32_t &v, UBool &hasIntValue) const; - - -private: - /** - * The digits, least significant first. Both the least and most - * significant digit in this list are non-zero; however, digits in the - * middle may be zero. This field contains values between (char) 0, and - * (char) 9 inclusive. - */ - CharString fDigits; - - /** - * The range of displayable digits. This field is needed to account for - * any leading and trailing zeros which are not stored in fDigits. - */ - DigitInterval fInterval; - - /** - * The exponent value of the least significant digit in fDigits. For - * example, fExponent = 2 and fDigits = {7, 8, 5} represents 58700. - */ - int32_t fExponent; - - /** - * Contains flags such as NaN, Inf, and negative. - */ - int32_t fFlags; - - /** - * Contains the absolute value of the digits left of the decimal place - * if fAbsIntValueSet is TRUE - */ - int64_t fAbsIntValue; - - /** - * Indicates whether or not fAbsIntValue is set. - */ - UBool fAbsIntValueSet; - - /** - * Contains the absolute value of the value this instance represents - * if fAbsDoubleValueSet is TRUE - */ - double fAbsDoubleValue; - - /** - * Indicates whether or not fAbsDoubleValue is set. - */ - UBool fAbsDoubleValueSet; - - void setNegative(); - void setNaN(); - void setInfinite(); - void clear(); - double computeAbsDoubleValue() const; - UBool isOverMaxDigits() const; - - VisibleDigits(const VisibleDigits &); - VisibleDigits &operator=(const VisibleDigits &); - - friend class FixedPrecision; - friend class VisibleDigitsWithExponent; -}; - -/** - * A VisibleDigits with a possible exponent. - */ -class U_I18N_API VisibleDigitsWithExponent : public UMemory { -public: - VisibleDigitsWithExponent() : fHasExponent(FALSE) { } - const VisibleDigits &getMantissa() const { return fMantissa; } - const VisibleDigits *getExponent() const { - return fHasExponent ? &fExponent : NULL; - } - void clear() { - fMantissa.clear(); - fExponent.clear(); - fHasExponent = FALSE; - } - UBool isNegative() const { return fMantissa.isNegative(); } - UBool isNaN() const { return fMantissa.isNaN(); } - UBool isInfinite() const { return fMantissa.isInfinite(); } -private: - VisibleDigitsWithExponent(const VisibleDigitsWithExponent &); - VisibleDigitsWithExponent &operator=( - const VisibleDigitsWithExponent &); - VisibleDigits fMantissa; - VisibleDigits fExponent; - UBool fHasExponent; - - friend class ScientificPrecision; - friend class FixedPrecision; -}; - - -U_NAMESPACE_END -#endif /* #if !UCONFIG_NO_FORMATTING */ -#endif // __VISIBLEDIGITS_H__ diff --git a/deps/icu-small/source/i18n/windtfmt.cpp b/deps/icu-small/source/i18n/windtfmt.cpp index e8e32abd3ff477..983fd46c122e6f 100644 --- a/deps/icu-small/source/i18n/windtfmt.cpp +++ b/deps/icu-small/source/i18n/windtfmt.cpp @@ -385,7 +385,8 @@ UnicodeString Win32DateFormat::setTimeZoneInfo(TIME_ZONE_INFORMATION *tzi, const for (int z = 0; z < ec; z += 1) { UnicodeString equiv = TimeZone::getEquivalentID(icuid, z); - if (found = uprv_getWindowsTimeZoneInfo(tzi, equiv.getBuffer(), equiv.length())) { + found = uprv_getWindowsTimeZoneInfo(tzi, equiv.getBuffer(), equiv.length()); + if (found) { break; } } diff --git a/deps/icu-small/source/i18n/zonemeta.cpp b/deps/icu-small/source/i18n/zonemeta.cpp index c386b0cae5e2ca..02562048a525da 100644 --- a/deps/icu-small/source/i18n/zonemeta.cpp +++ b/deps/icu-small/source/i18n/zonemeta.cpp @@ -690,6 +690,7 @@ ZoneMeta::createMetazoneMappings(const UnicodeString &tzid) { mzMappings = new UVector(deleteOlsonToMetaMappingEntry, NULL, status); if (U_FAILURE(status)) { delete mzMappings; + mzMappings = NULL; uprv_free(entry); break; } diff --git a/deps/icu-small/source/tools/toolutil/swapimpl.cpp b/deps/icu-small/source/tools/toolutil/swapimpl.cpp index a64a6a1703f2c7..f3f333a005eb43 100644 --- a/deps/icu-small/source/tools/toolutil/swapimpl.cpp +++ b/deps/icu-small/source/tools/toolutil/swapimpl.cpp @@ -336,7 +336,7 @@ ucase_swap(const UDataSwapper *ds, ((pInfo->formatVersion[0]==1 && pInfo->formatVersion[2]==UTRIE_SHIFT && pInfo->formatVersion[3]==UTRIE_INDEX_SHIFT) || - pInfo->formatVersion[0]==2 || pInfo->formatVersion[0]==3) + 2<=pInfo->formatVersion[0] || pInfo->formatVersion[0]<=4) )) { udata_printError(ds, "ucase_swap(): data format %02x.%02x.%02x.%02x (format version %02x) is not recognized as case mapping data\n", pInfo->dataFormat[0], pInfo->dataFormat[1], diff --git a/deps/icu-small/source/tools/toolutil/udbgutil.cpp b/deps/icu-small/source/tools/toolutil/udbgutil.cpp index 446e11aaf9084f..dcc80ebe069519 100644 --- a/deps/icu-small/source/tools/toolutil/udbgutil.cpp +++ b/deps/icu-small/source/tools/toolutil/udbgutil.cpp @@ -554,7 +554,6 @@ static const USystemParams systemParams[] = { #endif { "uconfig.internal_digitlist", paramInteger, "b", 1}, /* always 1 */ { "uconfig.have_parseallinput", paramInteger, "b", UCONFIG_HAVE_PARSEALLINPUT}, - { "uconfig.format_fastpaths_49",paramInteger, "b", UCONFIG_FORMAT_FASTPATHS_49}, }; diff --git a/deps/uv/AUTHORS b/deps/uv/AUTHORS index eb8501aab12d45..ef3d5e181a800f 100644 --- a/deps/uv/AUTHORS +++ b/deps/uv/AUTHORS @@ -343,3 +343,5 @@ Björn Linse zyxwvu Shi Peter Johnson Paolo Greppi +Shelley Vohr +Ujjwal Sharma diff --git a/deps/uv/CMakeLists.txt b/deps/uv/CMakeLists.txt index f13e5295636b49..a133edb8500fa2 100644 --- a/deps/uv/CMakeLists.txt +++ b/deps/uv/CMakeLists.txt @@ -15,6 +15,7 @@ set(uv_sources src/fs-poll.c src/inet.c src/threadpool.c + src/timer.c src/uv-common.c src/uv-data-getter-setters.c src/version.c) @@ -197,7 +198,6 @@ if(WIN32) src/win/stream.c src/win/tcp.c src/win/tty.c - src/win/timer.c src/win/udp.c src/win/util.c src/win/winapi.c @@ -223,7 +223,6 @@ else() src/unix/stream.c src/unix/tcp.c src/unix/thread.c - src/unix/timer.c src/unix/tty.c src/unix/udp.c) list(APPEND uv_test_sources test/runner-unix.c) diff --git a/deps/uv/ChangeLog b/deps/uv/ChangeLog index 3569d5d5751564..acf42e0123a6c1 100644 --- a/deps/uv/ChangeLog +++ b/deps/uv/ChangeLog @@ -1,3 +1,36 @@ +2018.07.11, Version 1.22.0 (Stable), 8568f78a777d79d35eb7d6994617267b9fb33967 + +Changes since version 1.21.0: + +* unix: remove checksparse.sh (Ben Noordhuis) + +* win: fix mingw build error (Ben Noordhuis) + +* win: fix -Wunused-function warnings in thread.c (Ben Noordhuis) + +* unix,win: merge timers implementation (Ben Noordhuis) + +* win: fix pointer type in pipe.c (Ben Noordhuis) + +* win: fixing build for older MSVC compilers (Michael Fero) + +* zos: clear poll events on every iteration (jBarz) + +* zos: write-protect message queue (jBarz) + +* zos: use correct pointer type in strnlen (jBarz) + +* unix,win: merge handle flags (Ben Noordhuis) + +* doc: update Imran Iqbal's GitHub handle (cjihrig) + +* src: add new error apis to prevent memory leaks (Shelley Vohr) + +* test: make test-condvar call uv_cond_wait (Jamie Davis) + +* fs: change position of uv_fs_lchown (Ujjwal Sharma) + + 2018.06.23, Version 1.21.0 (Stable), e4983a9b0c152932f7553ff4a9ff189d2314cdcb Changes since version 1.20.3: diff --git a/deps/uv/MAINTAINERS.md b/deps/uv/MAINTAINERS.md index a895780fc11809..889ee4988c482c 100644 --- a/deps/uv/MAINTAINERS.md +++ b/deps/uv/MAINTAINERS.md @@ -12,7 +12,7 @@ libuv is currently managed by the following individuals: - GPG key: 5735 3E0D BDAA A7E8 39B6 6A1A FF47 D5E4 AD8B 4FDC (pubkey-cjihrig-kb) * **Fedor Indutny** ([@indutny](https://github.com/indutny)) - GPG key: AF2E EA41 EC34 47BF DD86 FED9 D706 3CCE 19B7 E890 (pubkey-indutny) -* **Imran Iqbal** ([@iWuzHere](https://github.com/iWuzHere)) +* **Imran Iqbal** ([@imran-iq](https://github.com/imran-iq)) - GPG key: 9DFE AA5F 481B BF77 2D90 03CE D592 4925 2F8E C41A (pubkey-iwuzhere) * **John Barboza** ([@jbarz](https://github.com/jbarz)) * **Santiago Gimeno** ([@santigimeno](https://github.com/santigimeno)) diff --git a/deps/uv/Makefile.am b/deps/uv/Makefile.am index 5f07dba23a3b7d..04aecab5555886 100644 --- a/deps/uv/Makefile.am +++ b/deps/uv/Makefile.am @@ -32,6 +32,7 @@ libuv_la_SOURCES = src/fs-poll.c \ src/inet.c \ src/queue.h \ src/threadpool.c \ + src/timer.c \ src/uv-data-getter-setters.c \ src/uv-common.c \ src/uv-common.h \ @@ -74,7 +75,6 @@ libuv_la_SOURCES += src/win/async.c \ src/win/stream-inl.h \ src/win/tcp.c \ src/win/thread.c \ - src/win/timer.c \ src/win/tty.c \ src/win/udp.c \ src/win/util.c \ @@ -105,7 +105,6 @@ libuv_la_SOURCES += src/unix/async.c \ src/unix/stream.c \ src/unix/tcp.c \ src/unix/thread.c \ - src/unix/timer.c \ src/unix/tty.c \ src/unix/udp.c diff --git a/deps/uv/checksparse.sh b/deps/uv/checksparse.sh deleted file mode 100755 index 91f130d4f5893c..00000000000000 --- a/deps/uv/checksparse.sh +++ /dev/null @@ -1,254 +0,0 @@ -#!/bin/sh - -# Copyright (c) 2013, Ben Noordhuis -# -# Permission to use, copy, modify, and/or distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -SPARSE=${SPARSE:-sparse} - -SPARSE_FLAGS=${SPARSE_FLAGS:-" --D__POSIX__ --Wsparse-all --Wno-do-while --Wno-transparent-union --Iinclude --Isrc -"} - -SOURCES=" -include/uv/tree.h -include/uv/unix.h -include/uv.h -src/fs-poll.c -src/inet.c -src/queue.h -src/unix/async.c -src/unix/core.c -src/unix/dl.c -src/unix/fs.c -src/unix/getaddrinfo.c -src/unix/internal.h -src/unix/loop-watcher.c -src/unix/loop.c -src/unix/pipe.c -src/unix/poll.c -src/unix/process.c -src/unix/signal.c -src/unix/stream.c -src/unix/tcp.c -src/unix/thread.c -src/unix/threadpool.c -src/unix/timer.c -src/unix/tty.c -src/unix/udp.c -src/uv-common.c -src/uv-common.h -src/uv-data-getter-setters.c -" - -TESTS=" -test/benchmark-async-pummel.c -test/benchmark-async.c -test/benchmark-fs-stat.c -test/benchmark-getaddrinfo.c -test/benchmark-loop-count.c -test/benchmark-million-async.c -test/benchmark-million-timers.c -test/benchmark-multi-accept.c -test/benchmark-ping-pongs.c -test/benchmark-pound.c -test/benchmark-pump.c -test/benchmark-sizes.c -test/benchmark-spawn.c -test/benchmark-tcp-write-batch.c -test/benchmark-thread.c -test/benchmark-udp-pummel.c -test/blackhole-server.c -test/dns-server.c -test/echo-server.c -test/run-benchmarks.c -test/run-tests.c -test/runner-unix.c -test/runner-unix.h -test/runner.c -test/runner.h -test/task.h -test/test-active.c -test/test-async.c -test/test-barrier.c -test/test-callback-order.c -test/test-callback-stack.c -test/test-condvar.c -test/test-connection-fail.c -test/test-cwd-and-chdir.c -test/test-delayed-accept.c -test/test-dlerror.c -test/test-embed.c -test/test-env-vars.c -test/test-error.c -test/test-fail-always.c -test/test-fs-copyfile.c -test/test-fs-event.c -test/test-fs-poll.c -test/test-fs.c -test/test-getters-setters.c -test/test-get-currentexe.c -test/test-get-loadavg.c -test/test-get-memory.c -test/test-get-passwd.c -test/test-getaddrinfo.c -test/test-gethostname.c -test/test-getsockname.c -test/test-homedir.c -test/test-hrtime.c -test/test-idle.c -test/test-ip6-addr.c -test/test-ipc-heavy-traffic-deadlock-bug.c -test/test-ipc-send-recv.c -test/test-ipc.c -test/test-loop-handles.c -test/test-multiple-listen.c -test/test-mutexes.c -test/test-pass-always.c -test/test-ping-pong.c -test/test-pipe-bind-error.c -test/test-pipe-connect-error.c -test/test-pipe-sendmsg.c -test/test-pipe-server-close.c -test/test-platform-output.c -test/test-poll-close.c -test/test-poll.c -test/test-process-title.c -test/test-process-title-threadsafe.c -test/test-ref.c -test/test-run-nowait.c -test/test-run-once.c -test/test-semaphore.c -test/test-shutdown-close.c -test/test-shutdown-eof.c -test/test-signal-multiple-loops.c -test/test-signal.c -test/test-spawn.c -test/test-stdio-over-pipes.c -test/test-tcp-bind-error.c -test/test-tcp-bind6-error.c -test/test-tcp-close-while-connecting.c -test/test-tcp-close-accept.c -test/test-tcp-close.c -test/test-tcp-connect-error-after-write.c -test/test-tcp-connect-error.c -test/test-tcp-connect-timeout.c -test/test-tcp-connect6-error.c -test/test-tcp-flags.c -test/test-tcp-open.c -test/test-tcp-read-stop.c -test/test-tcp-shutdown-after-write.c -test/test-tcp-unexpected-read.c -test/test-tcp-oob.c -test/test-tcp-write-error.c -test/test-tcp-write-to-half-open-connection.c -test/test-tcp-writealot.c -test/test-thread.c -test/test-threadpool-cancel.c -test/test-threadpool.c -test/test-timer-again.c -test/test-timer.c -test/test-tmpdir.c -test/test-tty.c -test/test-udp-dgram-too-big.c -test/test-udp-ipv6.c -test/test-udp-multicast-join.c -test/test-udp-multicast-ttl.c -test/test-udp-open.c -test/test-udp-options.c -test/test-udp-send-and-recv.c -test/test-udp-send-hang-loop.c -test/test-walk-handles.c -test/test-watcher-cross-stop.c -" - -case `uname -s` in -AIX) - SPARSE_FLAGS="$SPARSE_FLAGS -D_AIX=1" - SOURCES="$SOURCES - src/unix/aix-common.c - src/unix/aix.c" - ;; -OS400) - SPARSE_FLAGS="$SPARSE_FLAGS -D__PASE__=1" - SOURCES="$SOURCES - src/unix/aix-common.c - src/unix/ibmi.c - src/unix/posix-poll.c - src/unix/no-fsevents.c - src/unix/no-proctitle.c" - ;; -Darwin) - SPARSE_FLAGS="$SPARSE_FLAGS -D__APPLE__=1" - SOURCES="$SOURCES - include/uv/bsd.h - src/unix/darwin.c - src/unix/kqueue.c - src/unix/fsevents.c" - ;; -DragonFly) - SPARSE_FLAGS="$SPARSE_FLAGS -D__DragonFly__=1" - SOURCES="$SOURCES - include/uv/bsd.h - src/unix/kqueue.c - src/unix/freebsd.c" - ;; -FreeBSD) - SPARSE_FLAGS="$SPARSE_FLAGS -D__FreeBSD__=1" - SOURCES="$SOURCES - include/uv/bsd.h - src/unix/kqueue.c - src/unix/freebsd.c" - ;; -Linux) - SPARSE_FLAGS="$SPARSE_FLAGS -D__linux__=1" - SOURCES="$SOURCES - include/uv/linux.h - src/unix/linux-inotify.c - src/unix/linux-core.c - src/unix/linux-syscalls.c - src/unix/linux-syscalls.h" - ;; -NetBSD) - SPARSE_FLAGS="$SPARSE_FLAGS -D__NetBSD__=1" - SOURCES="$SOURCES - include/uv/bsd.h - src/unix/kqueue.c - src/unix/netbsd.c" - ;; -OpenBSD) - SPARSE_FLAGS="$SPARSE_FLAGS -D__OpenBSD__=1" - SOURCES="$SOURCES - include/uv/bsd.h - src/unix/kqueue.c - src/unix/openbsd.c" - ;; -SunOS) - SPARSE_FLAGS="$SPARSE_FLAGS -D__sun=1" - SOURCES="$SOURCES - include/uv/sunos.h - src/unix/sunos.c" - ;; -esac - -for ARCH in __i386__ __x86_64__ __arm__ __mips__; do - $SPARSE $SPARSE_FLAGS -D$ARCH=1 $SOURCES -done - -# Tests are architecture independent. -$SPARSE $SPARSE_FLAGS -Itest $TESTS diff --git a/deps/uv/configure.ac b/deps/uv/configure.ac index c3a6a7796904c8..aa6e6d16a8c804 100644 --- a/deps/uv/configure.ac +++ b/deps/uv/configure.ac @@ -13,7 +13,7 @@ # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. AC_PREREQ(2.57) -AC_INIT([libuv], [1.21.0], [https://github.com/libuv/libuv/issues]) +AC_INIT([libuv], [1.22.0], [https://github.com/libuv/libuv/issues]) AC_CONFIG_MACRO_DIR([m4]) m4_include([m4/libuv-extra-automake-flags.m4]) m4_include([m4/as_case.m4]) diff --git a/deps/uv/docs/src/errors.rst b/deps/uv/docs/src/errors.rst index 7c3f95d760416f..b8f971f5763511 100644 --- a/deps/uv/docs/src/errors.rst +++ b/deps/uv/docs/src/errors.rst @@ -335,11 +335,25 @@ API Returns the error message for the given error code. Leaks a few bytes of memory when you call it with an unknown error code. +.. c:function:: char* uv_strerror_r(int err, char* buf, size_t buflen) + + Returns the error message for the given error code. The zero-terminated + message is stored in the user-supplied buffer `buf` of at most `buflen` bytes. + + .. versionadded:: 1.22.0 + .. c:function:: const char* uv_err_name(int err) Returns the error name for the given error code. Leaks a few bytes of memory when you call it with an unknown error code. +.. c:function:: char* uv_err_name_r(int err, char* buf, size_t buflen) + + Returns the error name for the given error code. The zero-terminated + name is stored in the user-supplied buffer `buf` of at most `buflen` bytes. + + .. versionadded:: 1.22.0 + .. c:function:: int uv_translate_sys_error(int sys_errno) Returns the libuv error code equivalent to the given platform dependent error diff --git a/deps/uv/docs/src/fs.rst b/deps/uv/docs/src/fs.rst index a390f1409d2f26..f383e5b10cd9d5 100644 --- a/deps/uv/docs/src/fs.rst +++ b/deps/uv/docs/src/fs.rst @@ -92,9 +92,9 @@ Data types UV_FS_READLINK, UV_FS_CHOWN, UV_FS_FCHOWN, - UV_FS_LCHOWN, UV_FS_REALPATH, - UV_FS_COPYFILE + UV_FS_COPYFILE, + UV_FS_LCHOWN } uv_fs_type; .. c:type:: uv_dirent_t diff --git a/deps/uv/include/uv.h b/deps/uv/include/uv.h index 91451ada7885c2..a8b305793d4b14 100644 --- a/deps/uv/include/uv.h +++ b/deps/uv/include/uv.h @@ -370,7 +370,10 @@ typedef enum { UV_EXTERN int uv_translate_sys_error(int sys_errno); UV_EXTERN const char* uv_strerror(int err); +UV_EXTERN char* uv_strerror_r(int err, char* buf, size_t buflen); + UV_EXTERN const char* uv_err_name(int err); +UV_EXTERN char* uv_err_name_r(int err, char* buf, size_t buflen); #define UV_REQ_FIELDS \ @@ -1141,9 +1144,9 @@ typedef enum { UV_FS_READLINK, UV_FS_CHOWN, UV_FS_FCHOWN, - UV_FS_LCHOWN, UV_FS_REALPATH, - UV_FS_COPYFILE + UV_FS_COPYFILE, + UV_FS_LCHOWN } uv_fs_type; /* uv_fs_t is a subclass of uv_req_t. */ diff --git a/deps/uv/include/uv/version.h b/deps/uv/include/uv/version.h index 0b829717b81ab0..a990137f85209c 100644 --- a/deps/uv/include/uv/version.h +++ b/deps/uv/include/uv/version.h @@ -31,7 +31,7 @@ */ #define UV_VERSION_MAJOR 1 -#define UV_VERSION_MINOR 21 +#define UV_VERSION_MINOR 22 #define UV_VERSION_PATCH 0 #define UV_VERSION_IS_RELEASE 1 #define UV_VERSION_SUFFIX "" diff --git a/deps/uv/include/uv/win.h b/deps/uv/include/uv/win.h index f01335296a05e9..d6b8b3a7f7b9a2 100644 --- a/deps/uv/include/uv/win.h +++ b/deps/uv/include/uv/win.h @@ -308,8 +308,6 @@ typedef struct { char* errmsg; } uv_lib_t; -RB_HEAD(uv_timer_tree_s, uv_timer_s); - #define UV_LOOP_PRIVATE_FIELDS \ /* The loop's I/O completion port */ \ HANDLE iocp; \ @@ -321,8 +319,8 @@ RB_HEAD(uv_timer_tree_s, uv_timer_s); uv_req_t* pending_reqs_tail; \ /* Head of a single-linked list of closed handles */ \ uv_handle_t* endgame_handles; \ - /* The head of the timers tree */ \ - struct uv_timer_tree_s timers; \ + /* TODO(bnoordhuis) Stop heap-allocating |timer_heap| in libuv v2.x. */ \ + void* timer_heap; \ /* Lists of active loop (prepare / check / idle) watchers */ \ uv_prepare_t* prepare_handles; \ uv_check_t* check_handles; \ @@ -529,8 +527,9 @@ RB_HEAD(uv_timer_tree_s, uv_timer_s); unsigned char events; #define UV_TIMER_PRIVATE_FIELDS \ - RB_ENTRY(uv_timer_s) tree_entry; \ - uint64_t due; \ + void* heap_node[3]; \ + int unused; \ + uint64_t timeout; \ uint64_t repeat; \ uint64_t start_id; \ uv_timer_cb timer_cb; diff --git a/deps/uv/src/fs-poll.c b/deps/uv/src/fs-poll.c index ee73d5a2e6e949..6c82dfc1d76302 100644 --- a/deps/uv/src/fs-poll.c +++ b/deps/uv/src/fs-poll.c @@ -83,7 +83,7 @@ int uv_fs_poll_start(uv_fs_poll_t* handle, if (err < 0) goto error; - ctx->timer_handle.flags |= UV__HANDLE_INTERNAL; + ctx->timer_handle.flags |= UV_HANDLE_INTERNAL; uv__handle_unref(&ctx->timer_handle); err = uv_fs_stat(loop, &ctx->fs_req, ctx->path, poll_cb); @@ -248,7 +248,7 @@ static int statbuf_eq(const uv_stat_t* a, const uv_stat_t* b) { #include "win/handle-inl.h" void uv__fs_poll_endgame(uv_loop_t* loop, uv_fs_poll_t* handle) { - assert(handle->flags & UV__HANDLE_CLOSING); + assert(handle->flags & UV_HANDLE_CLOSING); assert(!(handle->flags & UV_HANDLE_CLOSED)); uv__handle_close(handle); } diff --git a/deps/uv/src/unix/timer.c b/deps/uv/src/timer.c similarity index 92% rename from deps/uv/src/unix/timer.c rename to deps/uv/src/timer.c index 54dabfe7df9e27..2bf449a736c2bb 100644 --- a/deps/uv/src/unix/timer.c +++ b/deps/uv/src/timer.c @@ -19,13 +19,22 @@ */ #include "uv.h" -#include "internal.h" +#include "uv-common.h" #include "heap-inl.h" #include #include +static struct heap *timer_heap(const uv_loop_t* loop) { +#ifdef _WIN32 + return (struct heap*) loop->timer_heap; +#else + return (struct heap*) &loop->timer_heap; +#endif +} + + static int timer_less_than(const struct heap_node* ha, const struct heap_node* hb) { const uv_timer_t* a; @@ -81,7 +90,7 @@ int uv_timer_start(uv_timer_t* handle, /* start_id is the second index to be compared in uv__timer_cmp() */ handle->start_id = handle->loop->timer_counter++; - heap_insert((struct heap*) &handle->loop->timer_heap, + heap_insert(timer_heap(handle->loop), (struct heap_node*) &handle->heap_node, timer_less_than); uv__handle_start(handle); @@ -94,7 +103,7 @@ int uv_timer_stop(uv_timer_t* handle) { if (!uv__is_active(handle)) return 0; - heap_remove((struct heap*) &handle->loop->timer_heap, + heap_remove(timer_heap(handle->loop), (struct heap_node*) &handle->heap_node, timer_less_than); uv__handle_stop(handle); @@ -131,7 +140,7 @@ int uv__next_timeout(const uv_loop_t* loop) { const uv_timer_t* handle; uint64_t diff; - heap_node = heap_min((const struct heap*) &loop->timer_heap); + heap_node = heap_min(timer_heap(loop)); if (heap_node == NULL) return -1; /* block indefinitely */ @@ -152,7 +161,7 @@ void uv__run_timers(uv_loop_t* loop) { uv_timer_t* handle; for (;;) { - heap_node = heap_min((struct heap*) &loop->timer_heap); + heap_node = heap_min(timer_heap(loop)); if (heap_node == NULL) break; diff --git a/deps/uv/src/unix/core.c b/deps/uv/src/unix/core.c index c2e7bd730d5d45..066c9bee32e2b2 100644 --- a/deps/uv/src/unix/core.c +++ b/deps/uv/src/unix/core.c @@ -116,7 +116,7 @@ uint64_t uv_hrtime(void) { void uv_close(uv_handle_t* handle, uv_close_cb close_cb) { assert(!uv__is_closing(handle)); - handle->flags |= UV_CLOSING; + handle->flags |= UV_HANDLE_CLOSING; handle->close_cb = close_cb; switch (handle->type) { @@ -214,8 +214,8 @@ int uv__socket_sockopt(uv_handle_t* handle, int optname, int* value) { } void uv__make_close_pending(uv_handle_t* handle) { - assert(handle->flags & UV_CLOSING); - assert(!(handle->flags & UV_CLOSED)); + assert(handle->flags & UV_HANDLE_CLOSING); + assert(!(handle->flags & UV_HANDLE_CLOSED)); handle->next_closing = handle->loop->closing_handles; handle->loop->closing_handles = handle; } @@ -241,15 +241,17 @@ int uv__getiovmax(void) { static void uv__finish_close(uv_handle_t* handle) { - /* Note: while the handle is in the UV_CLOSING state now, it's still possible - * for it to be active in the sense that uv__is_active() returns true. + /* Note: while the handle is in the UV_HANDLE_CLOSING state now, it's still + * possible for it to be active in the sense that uv__is_active() returns + * true. + * * A good example is when the user calls uv_shutdown(), immediately followed * by uv_close(). The handle is considered active at this point because the * completion of the shutdown req is still pending. */ - assert(handle->flags & UV_CLOSING); - assert(!(handle->flags & UV_CLOSED)); - handle->flags |= UV_CLOSED; + assert(handle->flags & UV_HANDLE_CLOSING); + assert(!(handle->flags & UV_HANDLE_CLOSED)); + handle->flags |= UV_HANDLE_CLOSED; switch (handle->type) { case UV_PREPARE: diff --git a/deps/uv/src/unix/fsevents.c b/deps/uv/src/unix/fsevents.c index 47d8024b6d51ba..ee45299b791294 100644 --- a/deps/uv/src/unix/fsevents.c +++ b/deps/uv/src/unix/fsevents.c @@ -836,7 +836,7 @@ int uv__fsevents_init(uv_fs_event_t* handle) { handle->cf_cb->data = handle; uv_async_init(handle->loop, handle->cf_cb, uv__fsevents_cb); - handle->cf_cb->flags |= UV__HANDLE_INTERNAL; + handle->cf_cb->flags |= UV_HANDLE_INTERNAL; uv_unref((uv_handle_t*) handle->cf_cb); err = uv_mutex_init(&handle->cf_mutex); diff --git a/deps/uv/src/unix/internal.h b/deps/uv/src/unix/internal.h index b0c5dcadf155f6..cd79037102013e 100644 --- a/deps/uv/src/unix/internal.h +++ b/deps/uv/src/unix/internal.h @@ -127,26 +127,6 @@ int uv__pthread_sigmask(int how, const sigset_t* set, sigset_t* oset); typedef struct uv__stream_queued_fds_s uv__stream_queued_fds_t; -/* handle flags */ -enum { - UV_CLOSING = 0x01, /* uv_close() called but not finished. */ - UV_CLOSED = 0x02, /* close(2) finished. */ - UV_STREAM_READING = 0x04, /* uv_read_start() called. */ - UV_STREAM_SHUTTING = 0x08, /* uv_shutdown() called but not complete. */ - UV_STREAM_SHUT = 0x10, /* Write side closed. */ - UV_STREAM_READABLE = 0x20, /* The stream is readable */ - UV_STREAM_WRITABLE = 0x40, /* The stream is writable */ - UV_STREAM_BLOCKING = 0x80, /* Synchronous writes. */ - UV_STREAM_READ_PARTIAL = 0x100, /* read(2) read less than requested. */ - UV_STREAM_READ_EOF = 0x200, /* read(2) read EOF. */ - UV_TCP_NODELAY = 0x400, /* Disable Nagle. */ - UV_TCP_KEEPALIVE = 0x800, /* Turn on keep-alive. */ - UV_TCP_SINGLE_ACCEPT = 0x1000, /* Only accept() when idle. */ - UV_HANDLE_IPV6 = 0x10000, /* Handle is bound to a IPv6 socket. */ - UV_UDP_PROCESSING = 0x20000, /* Handle is running the send callback queue. */ - UV_HANDLE_BOUND = 0x40000 /* Handle is bound to an address and port */ -}; - /* loop flags */ enum { UV_LOOP_BLOCK_SIGPROF = 1 @@ -252,10 +232,6 @@ int uv__tcp_keepalive(int fd, int on, unsigned int delay); /* pipe */ int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb); -/* timer */ -void uv__run_timers(uv_loop_t* loop); -int uv__next_timeout(const uv_loop_t* loop); - /* signal */ void uv__signal_close(uv_signal_t* handle); void uv__signal_global_once_init(void); @@ -280,7 +256,6 @@ void uv__prepare_close(uv_prepare_t* handle); void uv__process_close(uv_process_t* handle); void uv__stream_close(uv_stream_t* handle); void uv__tcp_close(uv_tcp_t* handle); -void uv__timer_close(uv_timer_t* handle); void uv__udp_close(uv_udp_t* handle); void uv__udp_finish_close(uv_udp_t* handle); uv_handle_type uv__handle_type(int fd); diff --git a/deps/uv/src/unix/loop.c b/deps/uv/src/unix/loop.c index f990403d40c059..c2a03d770f3764 100644 --- a/deps/uv/src/unix/loop.c +++ b/deps/uv/src/unix/loop.c @@ -74,7 +74,7 @@ int uv_loop_init(uv_loop_t* loop) { goto fail_signal_init; uv__handle_unref(&loop->child_watcher); - loop->child_watcher.flags |= UV__HANDLE_INTERNAL; + loop->child_watcher.flags |= UV_HANDLE_INTERNAL; QUEUE_INIT(&loop->process_handles); err = uv_rwlock_init(&loop->cloexec_lock); @@ -90,7 +90,7 @@ int uv_loop_init(uv_loop_t* loop) { goto fail_async_init; uv__handle_unref(&loop->wq_async); - loop->wq_async.flags |= UV__HANDLE_INTERNAL; + loop->wq_async.flags |= UV_HANDLE_INTERNAL; return 0; diff --git a/deps/uv/src/unix/os390-syscalls.c b/deps/uv/src/unix/os390-syscalls.c index a5dd34426de969..1040d66979da04 100644 --- a/deps/uv/src/unix/os390-syscalls.c +++ b/deps/uv/src/unix/os390-syscalls.c @@ -141,7 +141,7 @@ static void init_message_queue(uv__os390_epoll* lst) { } msg; /* initialize message queue */ - lst->msg_queue = msgget(IPC_PRIVATE, 0622 | IPC_CREAT); + lst->msg_queue = msgget(IPC_PRIVATE, 0600 | IPC_CREAT); if (lst->msg_queue == -1) abort(); @@ -255,12 +255,13 @@ int epoll_ctl(uv__os390_epoll* lst, lst->items[fd].events = event->events; lst->items[fd].revents = 0; } else if (op == EPOLL_CTL_MOD) { - if (fd >= lst->size || lst->items[fd].fd == -1) { + if (fd >= lst->size - 1 || lst->items[fd].fd == -1) { uv_mutex_unlock(&global_epoll_lock); errno = ENOENT; return -1; } lst->items[fd].events = event->events; + lst->items[fd].revents = 0; } else abort(); @@ -275,8 +276,9 @@ int epoll_wait(uv__os390_epoll* lst, struct epoll_event* events, struct pollfd* pfds; int pollret; int reventcount; + int nevents; - size = _SET_FDS_MSGS(size, 1, lst->size - 1); + _SET_FDS_MSGS(size, 1, lst->size - 1); pfds = lst->items; pollret = poll(pfds, size, timeout); if (pollret <= 0) @@ -285,19 +287,28 @@ int epoll_wait(uv__os390_epoll* lst, struct epoll_event* events, pollret = _NFDS(pollret) + _NMSGS(pollret); reventcount = 0; + nevents = 0; for (int i = 0; i < lst->size && i < maxevents && reventcount < pollret; ++i) { struct epoll_event ev; + struct pollfd* pfd; - if (pfds[i].fd == -1 || pfds[i].revents == 0) + pfd = &pfds[i]; + if (pfd->fd == -1 || pfd->revents == 0) continue; - ev.fd = pfds[i].fd; - ev.events = pfds[i].revents; - events[reventcount++] = ev; + ev.fd = pfd->fd; + ev.events = pfd->revents; + if (pfd->revents & POLLIN && pfd->revents & POLLOUT) + reventcount += 2; + else if (pfd->revents & (POLLIN | POLLOUT)) + ++reventcount; + + pfd->revents = 0; + events[nevents++] = ev; } - return reventcount; + return nevents; } @@ -493,7 +504,7 @@ ssize_t os390_readlink(const char* path, char* buf, size_t len) { size_t strnlen(const char* str, size_t maxlen) { - void* p = memchr(str, 0, maxlen); + char* p = memchr(str, 0, maxlen); if (p == NULL) return maxlen; else diff --git a/deps/uv/src/unix/pipe.c b/deps/uv/src/unix/pipe.c index 91114db6162f8c..0718bc81b86f63 100644 --- a/deps/uv/src/unix/pipe.c +++ b/deps/uv/src/unix/pipe.c @@ -149,7 +149,7 @@ int uv_pipe_open(uv_pipe_t* handle, uv_file fd) { return uv__stream_open((uv_stream_t*)handle, fd, - UV_STREAM_READABLE | UV_STREAM_WRITABLE); + UV_HANDLE_READABLE | UV_HANDLE_WRITABLE); } @@ -199,7 +199,7 @@ void uv_pipe_connect(uv_connect_t* req, if (new_sock) { err = uv__stream_open((uv_stream_t*)handle, uv__stream_fd(handle), - UV_STREAM_READABLE | UV_STREAM_WRITABLE); + UV_HANDLE_READABLE | UV_HANDLE_WRITABLE); } if (err == 0) diff --git a/deps/uv/src/unix/process.c b/deps/uv/src/unix/process.c index 3a3cfd6f092fa4..101c9c53dfafe3 100644 --- a/deps/uv/src/unix/process.c +++ b/deps/uv/src/unix/process.c @@ -239,9 +239,9 @@ static int uv__process_open_stream(uv_stdio_container_t* container, flags = 0; if (container->flags & UV_WRITABLE_PIPE) - flags |= UV_STREAM_READABLE; + flags |= UV_HANDLE_READABLE; if (container->flags & UV_READABLE_PIPE) - flags |= UV_STREAM_WRITABLE; + flags |= UV_HANDLE_WRITABLE; return uv__stream_open(container->data.stream, pipefds[0], flags); } diff --git a/deps/uv/src/unix/signal.c b/deps/uv/src/unix/signal.c index 8da08b86802425..01aa55f3fe7c66 100644 --- a/deps/uv/src/unix/signal.c +++ b/deps/uv/src/unix/signal.c @@ -396,7 +396,7 @@ static int uv__signal_start(uv_signal_t* handle, */ first_handle = uv__signal_first_handle(signum); if (first_handle == NULL || - (!oneshot && (first_handle->flags & UV__SIGNAL_ONE_SHOT))) { + (!oneshot && (first_handle->flags & UV_SIGNAL_ONE_SHOT))) { err = uv__signal_register_handler(signum, oneshot); if (err) { /* Registering the signal handler failed. Must be an invalid signal. */ @@ -407,7 +407,7 @@ static int uv__signal_start(uv_signal_t* handle, handle->signum = signum; if (oneshot) - handle->flags |= UV__SIGNAL_ONE_SHOT; + handle->flags |= UV_SIGNAL_ONE_SHOT; RB_INSERT(uv__signal_tree_s, &uv__signal_tree, handle); @@ -464,20 +464,20 @@ static void uv__signal_event(uv_loop_t* loop, handle = msg->handle; if (msg->signum == handle->signum) { - assert(!(handle->flags & UV_CLOSING)); + assert(!(handle->flags & UV_HANDLE_CLOSING)); handle->signal_cb(handle, handle->signum); } handle->dispatched_signals++; - if (handle->flags & UV__SIGNAL_ONE_SHOT) + if (handle->flags & UV_SIGNAL_ONE_SHOT) uv__signal_stop(handle); /* If uv_close was called while there were caught signals that were not * yet dispatched, the uv__finish_close was deferred. Make close pending * now if this has happened. */ - if ((handle->flags & UV_CLOSING) && + if ((handle->flags & UV_HANDLE_CLOSING) && (handle->caught_signals == handle->dispatched_signals)) { uv__make_close_pending((uv_handle_t*) handle); } @@ -505,11 +505,11 @@ static int uv__signal_compare(uv_signal_t* w1, uv_signal_t* w2) { if (w1->signum < w2->signum) return -1; if (w1->signum > w2->signum) return 1; - /* Handlers without UV__SIGNAL_ONE_SHOT set will come first, so if the first + /* Handlers without UV_SIGNAL_ONE_SHOT set will come first, so if the first * handler returned is a one-shot handler, the rest will be too. */ - f1 = w1->flags & UV__SIGNAL_ONE_SHOT; - f2 = w2->flags & UV__SIGNAL_ONE_SHOT; + f1 = w1->flags & UV_SIGNAL_ONE_SHOT; + f2 = w2->flags & UV_SIGNAL_ONE_SHOT; if (f1 < f2) return -1; if (f1 > f2) return 1; @@ -558,8 +558,8 @@ static void uv__signal_stop(uv_signal_t* handle) { if (first_handle == NULL) { uv__signal_unregister_handler(handle->signum); } else { - rem_oneshot = handle->flags & UV__SIGNAL_ONE_SHOT; - first_oneshot = first_handle->flags & UV__SIGNAL_ONE_SHOT; + rem_oneshot = handle->flags & UV_SIGNAL_ONE_SHOT; + first_oneshot = first_handle->flags & UV_SIGNAL_ONE_SHOT; if (first_oneshot && !rem_oneshot) { ret = uv__signal_register_handler(handle->signum, 1); assert(ret == 0); diff --git a/deps/uv/src/unix/stream.c b/deps/uv/src/unix/stream.c index fb56a06b43e3be..4d62a23f1b9b7a 100644 --- a/deps/uv/src/unix/stream.c +++ b/deps/uv/src/unix/stream.c @@ -220,7 +220,7 @@ static void uv__stream_osx_select(void* arg) { uv_sem_wait(&s->async_sem); /* Should be processed at this stage */ - assert((s->events == 0) || (stream->flags & UV_CLOSING)); + assert((s->events == 0) || (stream->flags & UV_HANDLE_CLOSING)); } } } @@ -248,7 +248,7 @@ static void uv__stream_osx_select_cb(uv_async_t* handle) { if ((events & POLLOUT) && uv__io_active(&stream->io_watcher, POLLOUT)) uv__stream_io(stream->loop, &stream->io_watcher, POLLOUT); - if (stream->flags & UV_CLOSING) + if (stream->flags & UV_HANDLE_CLOSING) return; /* NOTE: It is important to do it here, otherwise `select()` might be called @@ -342,7 +342,7 @@ int uv__stream_try_select(uv_stream_t* stream, int* fd) { if (err) goto failed_async_init; - s->async.flags |= UV__HANDLE_INTERNAL; + s->async.flags |= UV_HANDLE_INTERNAL; uv__handle_unref(&s->async); err = uv_sem_init(&s->close_sem, 0); @@ -407,12 +407,14 @@ int uv__stream_open(uv_stream_t* stream, int fd, int flags) { stream->flags |= flags; if (stream->type == UV_TCP) { - if ((stream->flags & UV_TCP_NODELAY) && uv__tcp_nodelay(fd, 1)) + if ((stream->flags & UV_HANDLE_TCP_NODELAY) && uv__tcp_nodelay(fd, 1)) return UV__ERR(errno); /* TODO Use delay the user passed in. */ - if ((stream->flags & UV_TCP_KEEPALIVE) && uv__tcp_keepalive(fd, 1, 60)) + if ((stream->flags & UV_HANDLE_TCP_KEEPALIVE) && + uv__tcp_keepalive(fd, 1, 60)) { return UV__ERR(errno); + } } #if defined(__APPLE__) @@ -447,7 +449,7 @@ void uv__stream_flush_write_queue(uv_stream_t* stream, int error) { void uv__stream_destroy(uv_stream_t* stream) { assert(!uv__io_active(&stream->io_watcher, POLLIN | POLLOUT)); - assert(stream->flags & UV_CLOSED); + assert(stream->flags & UV_HANDLE_CLOSED); if (stream->connect_req) { uv__req_unregister(stream->loop, stream->connect_req); @@ -522,7 +524,7 @@ void uv__server_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) { stream = container_of(w, uv_stream_t, io_watcher); assert(events & POLLIN); assert(stream->accepted_fd == -1); - assert(!(stream->flags & UV_CLOSING)); + assert(!(stream->flags & UV_HANDLE_CLOSING)); uv__io_start(stream->loop, &stream->io_watcher, POLLIN); @@ -565,7 +567,8 @@ void uv__server_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) { return; } - if (stream->type == UV_TCP && (stream->flags & UV_TCP_SINGLE_ACCEPT)) { + if (stream->type == UV_TCP && + (stream->flags & UV_HANDLE_TCP_SINGLE_ACCEPT)) { /* Give other processes a chance to accept connections. */ struct timespec timeout = { 0, 1 }; nanosleep(&timeout, NULL); @@ -590,7 +593,7 @@ int uv_accept(uv_stream_t* server, uv_stream_t* client) { case UV_TCP: err = uv__stream_open(client, server->accepted_fd, - UV_STREAM_READABLE | UV_STREAM_WRITABLE); + UV_HANDLE_READABLE | UV_HANDLE_WRITABLE); if (err) { /* TODO handle error */ uv__close(server->accepted_fd); @@ -674,14 +677,14 @@ static void uv__drain(uv_stream_t* stream) { uv__stream_osx_interrupt_select(stream); /* Shutdown? */ - if ((stream->flags & UV_STREAM_SHUTTING) && - !(stream->flags & UV_CLOSING) && - !(stream->flags & UV_STREAM_SHUT)) { + if ((stream->flags & UV_HANDLE_SHUTTING) && + !(stream->flags & UV_HANDLE_CLOSING) && + !(stream->flags & UV_HANDLE_SHUT)) { assert(stream->shutdown_req); req = stream->shutdown_req; stream->shutdown_req = NULL; - stream->flags &= ~UV_STREAM_SHUTTING; + stream->flags &= ~UV_HANDLE_SHUTTING; uv__req_unregister(stream->loop, req); err = 0; @@ -689,7 +692,7 @@ static void uv__drain(uv_stream_t* stream) { err = UV__ERR(errno); if (err == 0) - stream->flags |= UV_STREAM_SHUT; + stream->flags |= UV_HANDLE_SHUT; if (req->cb != NULL) req->cb(req, err); @@ -868,7 +871,7 @@ static void uv__write(uv_stream_t* stream) { if (!WRITE_RETRY_ON_ERROR(req->send_handle)) { err = UV__ERR(errno); goto error; - } else if (stream->flags & UV_STREAM_BLOCKING) { + } else if (stream->flags & UV_HANDLE_BLOCKING_WRITES) { /* If this is a blocking stream, try again. */ goto start; } @@ -888,7 +891,7 @@ static void uv__write(uv_stream_t* stream) { n = 0; /* There is more to write. */ - if (stream->flags & UV_STREAM_BLOCKING) { + if (stream->flags & UV_HANDLE_BLOCKING_WRITES) { /* * If we're blocking then we should not be enabling the write * watcher - instead we need to try again. @@ -924,7 +927,7 @@ static void uv__write(uv_stream_t* stream) { assert(n == 0 || n == -1); /* Only non-blocking streams should use the write_watcher. */ - assert(!(stream->flags & UV_STREAM_BLOCKING)); + assert(!(stream->flags & UV_HANDLE_BLOCKING_WRITES)); /* We're not done. */ uv__io_start(stream->loop, &stream->io_watcher, POLLOUT); @@ -1015,13 +1018,13 @@ uv_handle_type uv__handle_type(int fd) { static void uv__stream_eof(uv_stream_t* stream, const uv_buf_t* buf) { - stream->flags |= UV_STREAM_READ_EOF; + stream->flags |= UV_HANDLE_READ_EOF; uv__io_stop(stream->loop, &stream->io_watcher, POLLIN); if (!uv__io_active(&stream->io_watcher, POLLOUT)) uv__handle_stop(stream); uv__stream_osx_interrupt_select(stream); stream->read_cb(stream, UV_EOF, buf); - stream->flags &= ~UV_STREAM_READING; + stream->flags &= ~UV_HANDLE_READING; } @@ -1133,7 +1136,7 @@ static void uv__read(uv_stream_t* stream) { int err; int is_ipc; - stream->flags &= ~UV_STREAM_READ_PARTIAL; + stream->flags &= ~UV_HANDLE_READ_PARTIAL; /* Prevent loop starvation when the data comes in as fast as (or faster than) * we can read it. XXX Need to rearm fd if we switch to edge-triggered I/O. @@ -1142,11 +1145,11 @@ static void uv__read(uv_stream_t* stream) { is_ipc = stream->type == UV_NAMED_PIPE && ((uv_pipe_t*) stream)->ipc; - /* XXX: Maybe instead of having UV_STREAM_READING we just test if + /* XXX: Maybe instead of having UV_HANDLE_READING we just test if * tcp->read_cb is NULL or not? */ while (stream->read_cb - && (stream->flags & UV_STREAM_READING) + && (stream->flags & UV_HANDLE_READING) && (count-- > 0)) { assert(stream->alloc_cb != NULL); @@ -1187,7 +1190,7 @@ static void uv__read(uv_stream_t* stream) { /* Error */ if (errno == EAGAIN || errno == EWOULDBLOCK) { /* Wait for the next one. */ - if (stream->flags & UV_STREAM_READING) { + if (stream->flags & UV_HANDLE_READING) { uv__io_start(stream->loop, &stream->io_watcher, POLLIN); uv__stream_osx_interrupt_select(stream); } @@ -1200,8 +1203,8 @@ static void uv__read(uv_stream_t* stream) { } else { /* Error. User should call uv_close(). */ stream->read_cb(stream, UV__ERR(errno), &buf); - if (stream->flags & UV_STREAM_READING) { - stream->flags &= ~UV_STREAM_READING; + if (stream->flags & UV_HANDLE_READING) { + stream->flags &= ~UV_HANDLE_READING; uv__io_stop(stream->loop, &stream->io_watcher, POLLIN); if (!uv__io_active(&stream->io_watcher, POLLOUT)) uv__handle_stop(stream); @@ -1251,7 +1254,7 @@ static void uv__read(uv_stream_t* stream) { /* Return if we didn't fill the buffer, there is no more data to read. */ if (nread < buflen) { - stream->flags |= UV_STREAM_READ_PARTIAL; + stream->flags |= UV_HANDLE_READ_PARTIAL; return; } } @@ -1272,9 +1275,9 @@ int uv_shutdown(uv_shutdown_t* req, uv_stream_t* stream, uv_shutdown_cb cb) { stream->type == UV_TTY || stream->type == UV_NAMED_PIPE); - if (!(stream->flags & UV_STREAM_WRITABLE) || - stream->flags & UV_STREAM_SHUT || - stream->flags & UV_STREAM_SHUTTING || + if (!(stream->flags & UV_HANDLE_WRITABLE) || + stream->flags & UV_HANDLE_SHUT || + stream->flags & UV_HANDLE_SHUTTING || uv__is_closing(stream)) { return UV_ENOTCONN; } @@ -1286,7 +1289,7 @@ int uv_shutdown(uv_shutdown_t* req, uv_stream_t* stream, uv_shutdown_cb cb) { req->handle = stream; req->cb = cb; stream->shutdown_req = req; - stream->flags |= UV_STREAM_SHUTTING; + stream->flags |= UV_HANDLE_SHUTTING; uv__io_start(stream->loop, &stream->io_watcher, POLLOUT); uv__stream_osx_interrupt_select(stream); @@ -1303,7 +1306,7 @@ static void uv__stream_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) { assert(stream->type == UV_TCP || stream->type == UV_NAMED_PIPE || stream->type == UV_TTY); - assert(!(stream->flags & UV_CLOSING)); + assert(!(stream->flags & UV_HANDLE_CLOSING)); if (stream->connect_req) { uv__stream_connect(stream); @@ -1326,9 +1329,9 @@ static void uv__stream_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) { * report the EOF yet because there is still data to read. */ if ((events & POLLHUP) && - (stream->flags & UV_STREAM_READING) && - (stream->flags & UV_STREAM_READ_PARTIAL) && - !(stream->flags & UV_STREAM_READ_EOF)) { + (stream->flags & UV_HANDLE_READING) && + (stream->flags & UV_HANDLE_READ_PARTIAL) && + !(stream->flags & UV_HANDLE_READ_EOF)) { uv_buf_t buf = { NULL, 0 }; uv__stream_eof(stream, &buf); } @@ -1418,7 +1421,7 @@ int uv_write2(uv_write_t* req, if (uv__stream_fd(stream) < 0) return UV_EBADF; - if (!(stream->flags & UV_STREAM_WRITABLE)) + if (!(stream->flags & UV_HANDLE_WRITABLE)) return -EPIPE; if (send_handle) { @@ -1488,7 +1491,7 @@ int uv_write2(uv_write_t* req, * if this assert fires then somehow the blocking stream isn't being * sufficiently flushed in uv__write. */ - assert(!(stream->flags & UV_STREAM_BLOCKING)); + assert(!(stream->flags & UV_HANDLE_BLOCKING_WRITES)); uv__io_start(stream->loop, &stream->io_watcher, POLLOUT); uv__stream_osx_interrupt_select(stream); } @@ -1569,16 +1572,16 @@ int uv_read_start(uv_stream_t* stream, assert(stream->type == UV_TCP || stream->type == UV_NAMED_PIPE || stream->type == UV_TTY); - if (stream->flags & UV_CLOSING) + if (stream->flags & UV_HANDLE_CLOSING) return UV_EINVAL; - if (!(stream->flags & UV_STREAM_READABLE)) + if (!(stream->flags & UV_HANDLE_READABLE)) return -ENOTCONN; - /* The UV_STREAM_READING flag is irrelevant of the state of the tcp - it just + /* The UV_HANDLE_READING flag is irrelevant of the state of the tcp - it just * expresses the desired state of the user. */ - stream->flags |= UV_STREAM_READING; + stream->flags |= UV_HANDLE_READING; /* TODO: try to do the read inline? */ /* TODO: keep track of tcp state. If we've gotten a EOF then we should @@ -1599,10 +1602,10 @@ int uv_read_start(uv_stream_t* stream, int uv_read_stop(uv_stream_t* stream) { - if (!(stream->flags & UV_STREAM_READING)) + if (!(stream->flags & UV_HANDLE_READING)) return 0; - stream->flags &= ~UV_STREAM_READING; + stream->flags &= ~UV_HANDLE_READING; uv__io_stop(stream->loop, &stream->io_watcher, POLLIN); if (!uv__io_active(&stream->io_watcher, POLLOUT)) uv__handle_stop(stream); @@ -1615,12 +1618,12 @@ int uv_read_stop(uv_stream_t* stream) { int uv_is_readable(const uv_stream_t* stream) { - return !!(stream->flags & UV_STREAM_READABLE); + return !!(stream->flags & UV_HANDLE_READABLE); } int uv_is_writable(const uv_stream_t* stream) { - return !!(stream->flags & UV_STREAM_WRITABLE); + return !!(stream->flags & UV_HANDLE_WRITABLE); } diff --git a/deps/uv/src/unix/tcp.c b/deps/uv/src/unix/tcp.c index 27a2a6130cba1d..2982851dc6eaa1 100644 --- a/deps/uv/src/unix/tcp.c +++ b/deps/uv/src/unix/tcp.c @@ -216,7 +216,7 @@ int uv__tcp_connect(uv_connect_t* req, err = maybe_new_socket(handle, addr->sa_family, - UV_STREAM_READABLE | UV_STREAM_WRITABLE); + UV_HANDLE_READABLE | UV_HANDLE_WRITABLE); if (err) return err; @@ -272,7 +272,7 @@ int uv_tcp_open(uv_tcp_t* handle, uv_os_sock_t sock) { return uv__stream_open((uv_stream_t*)handle, sock, - UV_STREAM_READABLE | UV_STREAM_WRITABLE); + UV_HANDLE_READABLE | UV_HANDLE_WRITABLE); } @@ -334,7 +334,7 @@ int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb) { } if (single_accept) - tcp->flags |= UV_TCP_SINGLE_ACCEPT; + tcp->flags |= UV_HANDLE_TCP_SINGLE_ACCEPT; flags = 0; #if defined(__MVS__) @@ -401,9 +401,9 @@ int uv_tcp_nodelay(uv_tcp_t* handle, int on) { } if (on) - handle->flags |= UV_TCP_NODELAY; + handle->flags |= UV_HANDLE_TCP_NODELAY; else - handle->flags &= ~UV_TCP_NODELAY; + handle->flags &= ~UV_HANDLE_TCP_NODELAY; return 0; } @@ -419,9 +419,9 @@ int uv_tcp_keepalive(uv_tcp_t* handle, int on, unsigned int delay) { } if (on) - handle->flags |= UV_TCP_KEEPALIVE; + handle->flags |= UV_HANDLE_TCP_KEEPALIVE; else - handle->flags &= ~UV_TCP_KEEPALIVE; + handle->flags &= ~UV_HANDLE_TCP_KEEPALIVE; /* TODO Store delay if uv__stream_fd(handle) == -1 but don't want to enlarge * uv_tcp_t with an int that's almost never used... @@ -433,9 +433,9 @@ int uv_tcp_keepalive(uv_tcp_t* handle, int on, unsigned int delay) { int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable) { if (enable) - handle->flags &= ~UV_TCP_SINGLE_ACCEPT; + handle->flags &= ~UV_HANDLE_TCP_SINGLE_ACCEPT; else - handle->flags |= UV_TCP_SINGLE_ACCEPT; + handle->flags |= UV_HANDLE_TCP_SINGLE_ACCEPT; return 0; } diff --git a/deps/uv/src/unix/tty.c b/deps/uv/src/unix/tty.c index f22b3b80de061f..1b92b5c914ce9e 100644 --- a/deps/uv/src/unix/tty.c +++ b/deps/uv/src/unix/tty.c @@ -135,7 +135,7 @@ int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, int fd, int readable) { if (r < 0) { /* fallback to using blocking writes */ if (!readable) - flags |= UV_STREAM_BLOCKING; + flags |= UV_HANDLE_BLOCKING_WRITES; goto skip; } @@ -177,7 +177,7 @@ int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, int fd, int readable) { * the handle queue, since it was added by uv__handle_init in uv_stream_init. */ - if (!(flags & UV_STREAM_BLOCKING)) + if (!(flags & UV_HANDLE_BLOCKING_WRITES)) uv__nonblock(fd, 1); #if defined(__APPLE__) @@ -195,9 +195,9 @@ int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, int fd, int readable) { #endif if (readable) - flags |= UV_STREAM_READABLE; + flags |= UV_HANDLE_READABLE; else - flags |= UV_STREAM_WRITABLE; + flags |= UV_HANDLE_WRITABLE; uv__stream_open((uv_stream_t*) tty, fd, flags); tty->mode = UV_TTY_MODE_NORMAL; diff --git a/deps/uv/src/unix/udp.c b/deps/uv/src/unix/udp.c index 15da047a5c5fbc..e6668a012c5c27 100644 --- a/deps/uv/src/unix/udp.c +++ b/deps/uv/src/unix/udp.c @@ -92,8 +92,8 @@ static void uv__udp_run_completed(uv_udp_t* handle) { uv_udp_send_t* req; QUEUE* q; - assert(!(handle->flags & UV_UDP_PROCESSING)); - handle->flags |= UV_UDP_PROCESSING; + assert(!(handle->flags & UV_HANDLE_UDP_PROCESSING)); + handle->flags |= UV_HANDLE_UDP_PROCESSING; while (!QUEUE_EMPTY(&handle->write_completed_queue)) { q = QUEUE_HEAD(&handle->write_completed_queue); @@ -128,7 +128,7 @@ static void uv__udp_run_completed(uv_udp_t* handle) { uv__handle_stop(handle); } - handle->flags &= ~UV_UDP_PROCESSING; + handle->flags &= ~UV_HANDLE_UDP_PROCESSING; } @@ -427,7 +427,7 @@ int uv__udp_send(uv_udp_send_t* req, QUEUE_INSERT_TAIL(&handle->write_queue, &req->queue); uv__handle_start(handle); - if (empty_queue && !(handle->flags & UV_UDP_PROCESSING)) { + if (empty_queue && !(handle->flags & UV_HANDLE_UDP_PROCESSING)) { uv__udp_sendmsg(handle); /* `uv__udp_sendmsg` may not be able to do non-blocking write straight diff --git a/deps/uv/src/uv-common.c b/deps/uv/src/uv-common.c index 71345895697732..f0aec452606cb8 100644 --- a/deps/uv/src/uv-common.c +++ b/deps/uv/src/uv-common.c @@ -155,6 +155,18 @@ static const char* uv__unknown_err_code(int err) { return copy != NULL ? copy : "Unknown system error"; } +#define UV_ERR_NAME_GEN_R(name, _) \ +case UV_## name: \ + snprintf(buf, buflen, "%s", #name); break; +char* uv_err_name_r(int err, char* buf, size_t buflen) { + switch (err) { + UV_ERRNO_MAP(UV_ERR_NAME_GEN_R) + default: snprintf(buf, buflen, "Unknown system error %d", err); + } + return buf; +} +#undef UV_ERR_NAME_GEN_R + #define UV_ERR_NAME_GEN(name, _) case UV_ ## name: return #name; const char* uv_err_name(int err) { @@ -166,6 +178,19 @@ const char* uv_err_name(int err) { #undef UV_ERR_NAME_GEN +#define UV_STRERROR_GEN_R(name, msg) \ +case UV_ ## name: \ + snprintf(buf, buflen, "%s", msg); break; +char* uv_strerror_r(int err, char* buf, size_t buflen) { + switch (err) { + UV_ERRNO_MAP(UV_STRERROR_GEN_R) + default: snprintf(buf, buflen, "Unknown system error %d", err); + } + return buf; +} +#undef UV_STRERROR_GEN_R + + #define UV_STRERROR_GEN(name, msg) case UV_ ## name: return msg; const char* uv_strerror(int err) { switch (err) { @@ -357,7 +382,7 @@ void uv_walk(uv_loop_t* loop, uv_walk_cb walk_cb, void* arg) { QUEUE_REMOVE(q); QUEUE_INSERT_TAIL(&loop->handle_queue, q); - if (h->flags & UV__HANDLE_INTERNAL) continue; + if (h->flags & UV_HANDLE_INTERNAL) continue; walk_cb(h, arg); } } @@ -386,9 +411,9 @@ static void uv__print_handles(uv_loop_t* loop, int only_active, FILE* stream) { fprintf(stream, "[%c%c%c] %-8s %p\n", - "R-"[!(h->flags & UV__HANDLE_REF)], - "A-"[!(h->flags & UV__HANDLE_ACTIVE)], - "I-"[!(h->flags & UV__HANDLE_INTERNAL)], + "R-"[!(h->flags & UV_HANDLE_REF)], + "A-"[!(h->flags & UV_HANDLE_ACTIVE)], + "I-"[!(h->flags & UV_HANDLE_INTERNAL)], type, (void*)h); } @@ -632,7 +657,7 @@ int uv_loop_close(uv_loop_t* loop) { QUEUE_FOREACH(q, &loop->handle_queue) { h = QUEUE_DATA(q, uv_handle_t, handle_queue); - if (!(h->flags & UV__HANDLE_INTERNAL)) + if (!(h->flags & UV_HANDLE_INTERNAL)) return UV_EBUSY; } diff --git a/deps/uv/src/uv-common.h b/deps/uv/src/uv-common.h index 85bcbe6c8adece..3289950d009ccd 100644 --- a/deps/uv/src/uv-common.h +++ b/deps/uv/src/uv-common.h @@ -59,22 +59,67 @@ extern int snprintf(char*, size_t, const char*, ...); #define STATIC_ASSERT(expr) \ void uv__static_assert(int static_assert_failed[1 - 2 * !(expr)]) -#ifndef _WIN32 +/* Handle flags. Some flags are specific to Windows or UNIX. */ enum { - UV__SIGNAL_ONE_SHOT = 0x80000, /* On signal reception remove sighandler */ - UV__HANDLE_INTERNAL = 0x8000, - UV__HANDLE_ACTIVE = 0x4000, - UV__HANDLE_REF = 0x2000, - UV__HANDLE_CLOSING = 0 /* no-op on unix */ + /* Used by all handles. */ + UV_HANDLE_CLOSING = 0x00000001, + UV_HANDLE_CLOSED = 0x00000002, + UV_HANDLE_ACTIVE = 0x00000004, + UV_HANDLE_REF = 0x00000008, + UV_HANDLE_INTERNAL = 0x00000010, + UV_HANDLE_ENDGAME_QUEUED = 0x00000020, + + /* Used by streams. */ + UV_HANDLE_LISTENING = 0x00000040, + UV_HANDLE_CONNECTION = 0x00000080, + UV_HANDLE_SHUTTING = 0x00000100, + UV_HANDLE_SHUT = 0x00000200, + UV_HANDLE_READ_PARTIAL = 0x00000400, + UV_HANDLE_READ_EOF = 0x00000800, + + /* Used by streams and UDP handles. */ + UV_HANDLE_READING = 0x00001000, + UV_HANDLE_BOUND = 0x00002000, + UV_HANDLE_READABLE = 0x00004000, + UV_HANDLE_WRITABLE = 0x00008000, + UV_HANDLE_READ_PENDING = 0x00010000, + UV_HANDLE_SYNC_BYPASS_IOCP = 0x00020000, + UV_HANDLE_ZERO_READ = 0x00040000, + UV_HANDLE_EMULATE_IOCP = 0x00080000, + UV_HANDLE_BLOCKING_WRITES = 0x00100000, + UV_HANDLE_CANCELLATION_PENDING = 0x00200000, + + /* Used by uv_tcp_t and uv_udp_t handles */ + UV_HANDLE_IPV6 = 0x00400000, + + /* Only used by uv_tcp_t handles. */ + UV_HANDLE_TCP_NODELAY = 0x01000000, + UV_HANDLE_TCP_KEEPALIVE = 0x02000000, + UV_HANDLE_TCP_SINGLE_ACCEPT = 0x04000000, + UV_HANDLE_TCP_ACCEPT_STATE_CHANGING = 0x08000000, + UV_HANDLE_TCP_SOCKET_CLOSED = 0x10000000, + UV_HANDLE_SHARED_TCP_SOCKET = 0x20000000, + + /* Only used by uv_udp_t handles. */ + UV_HANDLE_UDP_PROCESSING = 0x01000000, + + /* Only used by uv_pipe_t handles. */ + UV_HANDLE_NON_OVERLAPPED_PIPE = 0x01000000, + UV_HANDLE_PIPESERVER = 0x02000000, + + /* Only used by uv_tty_t handles. */ + UV_HANDLE_TTY_READABLE = 0x01000000, + UV_HANDLE_TTY_RAW = 0x02000000, + UV_HANDLE_TTY_SAVED_POSITION = 0x04000000, + UV_HANDLE_TTY_SAVED_ATTRIBUTES = 0x08000000, + + /* Only used by uv_signal_t handles. */ + UV_SIGNAL_ONE_SHOT_DISPATCHED = 0x01000000, + UV_SIGNAL_ONE_SHOT = 0x02000000, + + /* Only used by uv_poll_t handles. */ + UV_HANDLE_POLL_SLOW = 0x01000000 }; -#else -# define UV__SIGNAL_ONE_SHOT_DISPATCHED 0x200 -# define UV__SIGNAL_ONE_SHOT 0x100 -# define UV__HANDLE_INTERNAL 0x80 -# define UV__HANDLE_ACTIVE 0x40 -# define UV__HANDLE_REF 0x20 -# define UV__HANDLE_CLOSING 0x01 -#endif int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap); @@ -132,6 +177,10 @@ int uv__socket_sockopt(uv_handle_t* handle, int optname, int* value); void uv__fs_scandir_cleanup(uv_fs_t* req); +int uv__next_timeout(const uv_loop_t* loop); +void uv__run_timers(uv_loop_t* loop); +void uv__timer_close(uv_timer_t* handle); + #define uv__has_active_reqs(loop) \ ((loop)->active_reqs.count > 0) @@ -164,49 +213,47 @@ void uv__fs_scandir_cleanup(uv_fs_t* req); while (0) #define uv__is_active(h) \ - (((h)->flags & UV__HANDLE_ACTIVE) != 0) + (((h)->flags & UV_HANDLE_ACTIVE) != 0) #define uv__is_closing(h) \ - (((h)->flags & (UV_CLOSING | UV_CLOSED)) != 0) + (((h)->flags & (UV_HANDLE_CLOSING | UV_HANDLE_CLOSED)) != 0) #define uv__handle_start(h) \ do { \ - assert(((h)->flags & UV__HANDLE_CLOSING) == 0); \ - if (((h)->flags & UV__HANDLE_ACTIVE) != 0) break; \ - (h)->flags |= UV__HANDLE_ACTIVE; \ - if (((h)->flags & UV__HANDLE_REF) != 0) uv__active_handle_add(h); \ + if (((h)->flags & UV_HANDLE_ACTIVE) != 0) break; \ + (h)->flags |= UV_HANDLE_ACTIVE; \ + if (((h)->flags & UV_HANDLE_REF) != 0) uv__active_handle_add(h); \ } \ while (0) #define uv__handle_stop(h) \ do { \ - assert(((h)->flags & UV__HANDLE_CLOSING) == 0); \ - if (((h)->flags & UV__HANDLE_ACTIVE) == 0) break; \ - (h)->flags &= ~UV__HANDLE_ACTIVE; \ - if (((h)->flags & UV__HANDLE_REF) != 0) uv__active_handle_rm(h); \ + if (((h)->flags & UV_HANDLE_ACTIVE) == 0) break; \ + (h)->flags &= ~UV_HANDLE_ACTIVE; \ + if (((h)->flags & UV_HANDLE_REF) != 0) uv__active_handle_rm(h); \ } \ while (0) #define uv__handle_ref(h) \ do { \ - if (((h)->flags & UV__HANDLE_REF) != 0) break; \ - (h)->flags |= UV__HANDLE_REF; \ - if (((h)->flags & UV__HANDLE_CLOSING) != 0) break; \ - if (((h)->flags & UV__HANDLE_ACTIVE) != 0) uv__active_handle_add(h); \ + if (((h)->flags & UV_HANDLE_REF) != 0) break; \ + (h)->flags |= UV_HANDLE_REF; \ + if (((h)->flags & UV_HANDLE_CLOSING) != 0) break; \ + if (((h)->flags & UV_HANDLE_ACTIVE) != 0) uv__active_handle_add(h); \ } \ while (0) #define uv__handle_unref(h) \ do { \ - if (((h)->flags & UV__HANDLE_REF) == 0) break; \ - (h)->flags &= ~UV__HANDLE_REF; \ - if (((h)->flags & UV__HANDLE_CLOSING) != 0) break; \ - if (((h)->flags & UV__HANDLE_ACTIVE) != 0) uv__active_handle_rm(h); \ + if (((h)->flags & UV_HANDLE_REF) == 0) break; \ + (h)->flags &= ~UV_HANDLE_REF; \ + if (((h)->flags & UV_HANDLE_CLOSING) != 0) break; \ + if (((h)->flags & UV_HANDLE_ACTIVE) != 0) uv__active_handle_rm(h); \ } \ while (0) #define uv__has_ref(h) \ - (((h)->flags & UV__HANDLE_REF) != 0) + (((h)->flags & UV_HANDLE_REF) != 0) #if defined(_WIN32) # define uv__handle_platform_init(h) ((h)->u.fd = -1) @@ -218,7 +265,7 @@ void uv__fs_scandir_cleanup(uv_fs_t* req); do { \ (h)->loop = (loop_); \ (h)->type = (type_); \ - (h)->flags = UV__HANDLE_REF; /* Ref the loop when active. */ \ + (h)->flags = UV_HANDLE_REF; /* Ref the loop when active. */ \ QUEUE_INSERT_TAIL(&(loop_)->handle_queue, &(h)->handle_queue); \ uv__handle_platform_init(h); \ } \ diff --git a/deps/uv/src/win/async.c b/deps/uv/src/win/async.c index 13d3c7b33e48af..d787f6604eaedd 100644 --- a/deps/uv/src/win/async.c +++ b/deps/uv/src/win/async.c @@ -29,7 +29,7 @@ void uv_async_endgame(uv_loop_t* loop, uv_async_t* handle) { - if (handle->flags & UV__HANDLE_CLOSING && + if (handle->flags & UV_HANDLE_CLOSING && !handle->async_sent) { assert(!(handle->flags & UV_HANDLE_CLOSED)); uv__handle_close(handle); @@ -73,7 +73,7 @@ int uv_async_send(uv_async_t* handle) { /* The user should make sure never to call uv_async_send to a closing or * closed handle. */ - assert(!(handle->flags & UV__HANDLE_CLOSING)); + assert(!(handle->flags & UV_HANDLE_CLOSING)); if (!uv__atomic_exchange_set(&handle->async_sent)) { POST_COMPLETION_FOR_REQ(loop, &handle->async_req); @@ -90,7 +90,7 @@ void uv_process_async_wakeup_req(uv_loop_t* loop, uv_async_t* handle, handle->async_sent = 0; - if (handle->flags & UV__HANDLE_CLOSING) { + if (handle->flags & UV_HANDLE_CLOSING) { uv_want_endgame(loop, (uv_handle_t*)handle); } else if (handle->async_cb != NULL) { handle->async_cb(handle); diff --git a/deps/uv/src/win/core.c b/deps/uv/src/win/core.c index d6af282a2999ee..5a76c900337c3d 100644 --- a/deps/uv/src/win/core.c +++ b/deps/uv/src/win/core.c @@ -33,6 +33,7 @@ #include "internal.h" #include "queue.h" #include "handle-inl.h" +#include "heap-inl.h" #include "req-inl.h" /* uv_once initialization guards */ @@ -221,6 +222,7 @@ static void uv_init(void) { int uv_loop_init(uv_loop_t* loop) { + struct heap* timer_heap; int err; /* Initialize libuv itself first */ @@ -246,7 +248,11 @@ int uv_loop_init(uv_loop_t* loop) { loop->endgame_handles = NULL; - RB_INIT(&loop->timers); + loop->timer_heap = timer_heap = uv__malloc(sizeof(*timer_heap)); + if (timer_heap == NULL) + goto fail_timers_alloc; + + heap_init(timer_heap); loop->check_handles = NULL; loop->prepare_handles = NULL; @@ -273,7 +279,7 @@ int uv_loop_init(uv_loop_t* loop) { goto fail_async_init; uv__handle_unref(&loop->wq_async); - loop->wq_async.flags |= UV__HANDLE_INTERNAL; + loop->wq_async.flags |= UV_HANDLE_INTERNAL; err = uv__loops_add(loop); if (err) @@ -285,6 +291,10 @@ int uv_loop_init(uv_loop_t* loop) { uv_mutex_destroy(&loop->wq_mutex); fail_mutex_init: + uv__free(timer_heap); + loop->timer_heap = NULL; + +fail_timers_alloc: CloseHandle(loop->iocp); loop->iocp = INVALID_HANDLE_VALUE; @@ -292,6 +302,13 @@ int uv_loop_init(uv_loop_t* loop) { } +void uv_update_time(uv_loop_t* loop) { + uint64_t new_time = uv__hrtime(1000); + assert(new_time >= loop->time); + loop->time = new_time; +} + + void uv__once_init(void) { uv_once(&uv_init_guard_, uv_init); } @@ -320,6 +337,9 @@ void uv__loop_close(uv_loop_t* loop) { uv_mutex_unlock(&loop->wq_mutex); uv_mutex_destroy(&loop->wq_mutex); + uv__free(loop->timer_heap); + loop->timer_heap = NULL; + CloseHandle(loop->iocp); } @@ -441,7 +461,7 @@ int uv_run(uv_loop_t *loop, uv_run_mode mode) { while (r != 0 && loop->stop_flag == 0) { uv_update_time(loop); - uv_process_timers(loop); + uv__run_timers(loop); ran_pending = uv_process_reqs(loop); uv_idle_invoke(loop); @@ -465,7 +485,7 @@ int uv_run(uv_loop_t *loop, uv_run_mode mode) { * UV_RUN_NOWAIT makes no guarantees about progress so it's omitted from * the check. */ - uv_process_timers(loop); + uv__run_timers(loop); } r = uv__loop_alive(loop); diff --git a/deps/uv/src/win/fs-event.c b/deps/uv/src/win/fs-event.c index 14c9af9ad754d6..226e6e7aee6c09 100644 --- a/deps/uv/src/win/fs-event.c +++ b/deps/uv/src/win/fs-event.c @@ -419,7 +419,7 @@ void uv_process_fs_event_req(uv_loop_t* loop, uv_req_t* req, * - We are not active, just ignore the callback */ if (!uv__is_active(handle)) { - if (handle->flags & UV__HANDLE_CLOSING) { + if (handle->flags & UV_HANDLE_CLOSING) { uv_want_endgame(loop, (uv_handle_t*) handle); } return; @@ -543,7 +543,7 @@ void uv_process_fs_event_req(uv_loop_t* loop, uv_req_t* req, } offset = file_info->NextEntryOffset; - } while (offset && !(handle->flags & UV__HANDLE_CLOSING)); + } while (offset && !(handle->flags & UV_HANDLE_CLOSING)); } else { handle->cb(handle, NULL, UV_CHANGE, 0); } @@ -552,7 +552,7 @@ void uv_process_fs_event_req(uv_loop_t* loop, uv_req_t* req, handle->cb(handle, NULL, 0, uv_translate_sys_error(err)); } - if (!(handle->flags & UV__HANDLE_CLOSING)) { + if (!(handle->flags & UV_HANDLE_CLOSING)) { uv_fs_event_queue_readdirchanges(loop, handle); } else { uv_want_endgame(loop, (uv_handle_t*)handle); @@ -573,7 +573,7 @@ void uv_fs_event_close(uv_loop_t* loop, uv_fs_event_t* handle) { void uv_fs_event_endgame(uv_loop_t* loop, uv_fs_event_t* handle) { - if ((handle->flags & UV__HANDLE_CLOSING) && !handle->req_pending) { + if ((handle->flags & UV_HANDLE_CLOSING) && !handle->req_pending) { assert(!(handle->flags & UV_HANDLE_CLOSED)); if (handle->buffer) { diff --git a/deps/uv/src/win/handle-inl.h b/deps/uv/src/win/handle-inl.h index ed843072dfc026..82c657d579fb04 100644 --- a/deps/uv/src/win/handle-inl.h +++ b/deps/uv/src/win/handle-inl.h @@ -32,7 +32,7 @@ #define DECREASE_ACTIVE_COUNT(loop, handle) \ do { \ if (--(handle)->activecnt == 0 && \ - !((handle)->flags & UV__HANDLE_CLOSING)) { \ + !((handle)->flags & UV_HANDLE_CLOSING)) { \ uv__handle_stop((handle)); \ } \ assert((handle)->activecnt >= 0); \ @@ -53,7 +53,7 @@ assert(handle->reqs_pending > 0); \ handle->reqs_pending--; \ \ - if (handle->flags & UV__HANDLE_CLOSING && \ + if (handle->flags & UV_HANDLE_CLOSING && \ handle->reqs_pending == 0) { \ uv_want_endgame(loop, (uv_handle_t*)handle); \ } \ @@ -62,14 +62,14 @@ #define uv__handle_closing(handle) \ do { \ - assert(!((handle)->flags & UV__HANDLE_CLOSING)); \ + assert(!((handle)->flags & UV_HANDLE_CLOSING)); \ \ - if (!(((handle)->flags & UV__HANDLE_ACTIVE) && \ - ((handle)->flags & UV__HANDLE_REF))) \ + if (!(((handle)->flags & UV_HANDLE_ACTIVE) && \ + ((handle)->flags & UV_HANDLE_REF))) \ uv__active_handle_add((uv_handle_t*) (handle)); \ \ - (handle)->flags |= UV__HANDLE_CLOSING; \ - (handle)->flags &= ~UV__HANDLE_ACTIVE; \ + (handle)->flags |= UV_HANDLE_CLOSING; \ + (handle)->flags &= ~UV_HANDLE_ACTIVE; \ } while (0) @@ -126,7 +126,8 @@ INLINE static void uv_process_endgames(uv_loop_t* loop) { break; case UV_TIMER: - uv_timer_endgame(loop, (uv_timer_t*) handle); + uv__timer_close((uv_timer_t*) handle); + uv__handle_close(handle); break; case UV_PREPARE: diff --git a/deps/uv/src/win/handle.c b/deps/uv/src/win/handle.c index 39150702ddcd07..738d7ff3248ab4 100644 --- a/deps/uv/src/win/handle.c +++ b/deps/uv/src/win/handle.c @@ -59,15 +59,15 @@ uv_handle_type uv_guess_handle(uv_file file) { int uv_is_active(const uv_handle_t* handle) { - return (handle->flags & UV__HANDLE_ACTIVE) && - !(handle->flags & UV__HANDLE_CLOSING); + return (handle->flags & UV_HANDLE_ACTIVE) && + !(handle->flags & UV_HANDLE_CLOSING); } void uv_close(uv_handle_t* handle, uv_close_cb cb) { uv_loop_t* loop = handle->loop; - if (handle->flags & UV__HANDLE_CLOSING) { + if (handle->flags & UV_HANDLE_CLOSING) { assert(0); return; } @@ -150,7 +150,7 @@ void uv_close(uv_handle_t* handle, uv_close_cb cb) { int uv_is_closing(const uv_handle_t* handle) { - return !!(handle->flags & (UV__HANDLE_CLOSING | UV_HANDLE_CLOSED)); + return !!(handle->flags & (UV_HANDLE_CLOSING | UV_HANDLE_CLOSED)); } diff --git a/deps/uv/src/win/internal.h b/deps/uv/src/win/internal.h index fa926d9a44986e..b37b4c0c9be59f 100644 --- a/deps/uv/src/win/internal.h +++ b/deps/uv/src/win/internal.h @@ -57,69 +57,6 @@ extern UV_THREAD_LOCAL int uv__crt_assert_enabled; #define UV_END_DISABLE_CRT_ASSERT() #endif -/* - * Handles - * (also see handle-inl.h) - */ - -/* Used by all handles. */ -#define UV_HANDLE_CLOSED 0x00000002 -#define UV_HANDLE_ENDGAME_QUEUED 0x00000008 - -/* uv-common.h: #define UV__HANDLE_CLOSING 0x00000001 */ -/* uv-common.h: #define UV__HANDLE_ACTIVE 0x00000040 */ -/* uv-common.h: #define UV__HANDLE_REF 0x00000020 */ -/* uv-common.h: #define UV_HANDLE_INTERNAL 0x00000080 */ - -/* Used by streams and UDP handles. */ -#define UV_HANDLE_READING 0x00000100 -#define UV_HANDLE_BOUND 0x00000200 -#define UV_HANDLE_LISTENING 0x00000800 -#define UV_HANDLE_CONNECTION 0x00001000 -#define UV_HANDLE_READABLE 0x00008000 -#define UV_HANDLE_WRITABLE 0x00010000 -#define UV_HANDLE_READ_PENDING 0x00020000 -#define UV_HANDLE_SYNC_BYPASS_IOCP 0x00040000 -#define UV_HANDLE_ZERO_READ 0x00080000 -#define UV_HANDLE_EMULATE_IOCP 0x00100000 -#define UV_HANDLE_BLOCKING_WRITES 0x00200000 -#define UV_HANDLE_CANCELLATION_PENDING 0x00400000 - -/* Used by uv_tcp_t and uv_udp_t handles */ -#define UV_HANDLE_IPV6 0x01000000 - -/* Only used by uv_tcp_t handles. */ -#define UV_HANDLE_TCP_NODELAY 0x02000000 -#define UV_HANDLE_TCP_KEEPALIVE 0x04000000 -#define UV_HANDLE_TCP_SINGLE_ACCEPT 0x08000000 -#define UV_HANDLE_TCP_ACCEPT_STATE_CHANGING 0x10000000 -#define UV_HANDLE_TCP_SOCKET_CLOSED 0x20000000 -#define UV_HANDLE_SHARED_TCP_SOCKET 0x40000000 - -/* Only used by uv_pipe_t handles. */ -#define UV_HANDLE_NON_OVERLAPPED_PIPE 0x01000000 -#define UV_HANDLE_PIPESERVER 0x02000000 - -/* Only used by uv_tty_t handles. */ -#define UV_HANDLE_TTY_READABLE 0x01000000 -#define UV_HANDLE_TTY_RAW 0x02000000 -#define UV_HANDLE_TTY_SAVED_POSITION 0x04000000 -#define UV_HANDLE_TTY_SAVED_ATTRIBUTES 0x08000000 - -/* Only used by uv_poll_t handles. */ -#define UV_HANDLE_POLL_SLOW 0x02000000 - - -/* - * Requests: see req-inl.h - */ - - -/* - * Streams: see stream-inl.h - */ - - /* * TCP */ @@ -246,15 +183,6 @@ int uv_poll_close(uv_loop_t* loop, uv_poll_t* handle); void uv_poll_endgame(uv_loop_t* loop, uv_poll_t* handle); -/* - * Timers - */ -void uv_timer_endgame(uv_loop_t* loop, uv_timer_t* handle); - -DWORD uv__next_timeout(const uv_loop_t* loop); -void uv_process_timers(uv_loop_t* loop); - - /* * Loop watchers */ diff --git a/deps/uv/src/win/loop-watcher.c b/deps/uv/src/win/loop-watcher.c index 20e4509f838c05..ad7fbea169717f 100644 --- a/deps/uv/src/win/loop-watcher.c +++ b/deps/uv/src/win/loop-watcher.c @@ -27,7 +27,7 @@ void uv_loop_watcher_endgame(uv_loop_t* loop, uv_handle_t* handle) { - if (handle->flags & UV__HANDLE_CLOSING) { + if (handle->flags & UV_HANDLE_CLOSING) { assert(!(handle->flags & UV_HANDLE_CLOSED)); handle->flags |= UV_HANDLE_CLOSED; uv__handle_close(handle); diff --git a/deps/uv/src/win/pipe.c b/deps/uv/src/win/pipe.c index ae569326dd0004..42380f6599a45b 100644 --- a/deps/uv/src/win/pipe.c +++ b/deps/uv/src/win/pipe.c @@ -21,7 +21,6 @@ #include #include -#include #include #include #include @@ -347,7 +346,7 @@ void uv_pipe_endgame(uv_loop_t* loop, uv_pipe_t* handle) { /* Clear the shutdown_req field so we don't go here again. */ handle->stream.conn.shutdown_req = NULL; - if (handle->flags & UV__HANDLE_CLOSING) { + if (handle->flags & UV_HANDLE_CLOSING) { UNREGISTER_HANDLE_REQ(loop, handle, req); /* Already closing. Cancel the shutdown. */ @@ -408,7 +407,7 @@ void uv_pipe_endgame(uv_loop_t* loop, uv_pipe_t* handle) { } } - if (handle->flags & UV__HANDLE_CLOSING && + if (handle->flags & UV_HANDLE_CLOSING && handle->reqs_pending == 0) { assert(!(handle->flags & UV_HANDLE_CLOSED)); @@ -907,7 +906,7 @@ int uv_pipe_accept(uv_pipe_t* server, uv_stream_t* client) { req->next_pending = NULL; req->pipeHandle = INVALID_HANDLE_VALUE; - if (!(server->flags & UV__HANDLE_CLOSING)) { + if (!(server->flags & UV_HANDLE_CLOSING)) { uv_pipe_queue_accept(loop, server, req, FALSE); } } @@ -1294,7 +1293,7 @@ static int uv__pipe_write_data(uv_loop_t* loop, size_t nbufs, uv_stream_t* send_handle, uv_write_cb cb, - bool copy_always) { + int copy_always) { int err; int result; uv_buf_t write_buf; @@ -1536,7 +1535,7 @@ int uv__pipe_write_ipc(uv_loop_t* loop, /* Write buffers. We set the `always_copy` flag, so it is not a problem that * some of the written data lives on the stack. */ err = uv__pipe_write_data( - loop, req, handle, bufs, buf_count, send_handle, cb, true); + loop, req, handle, bufs, buf_count, send_handle, cb, 1); /* If we had to heap-allocate the bufs array, free it now. */ if (bufs != stack_bufs) { @@ -1561,7 +1560,7 @@ int uv__pipe_write(uv_loop_t* loop, /* Non-IPC pipe write: put data on the wire directly. */ assert(send_handle == NULL); return uv__pipe_write_data( - loop, req, handle, bufs, nbufs, NULL, cb, false); + loop, req, handle, bufs, nbufs, NULL, cb, 0); } } @@ -1675,7 +1674,7 @@ static DWORD uv__pipe_read_data(uv_loop_t* loop, static DWORD uv__pipe_read_ipc(uv_loop_t* loop, uv_pipe_t* handle) { - DWORD* data_remaining = &handle->pipe.conn.ipc_data_frame.payload_remaining; + uint32_t* data_remaining = &handle->pipe.conn.ipc_data_frame.payload_remaining; int err; if (*data_remaining > 0) { @@ -1856,7 +1855,7 @@ void uv_process_pipe_accept_req(uv_loop_t* loop, uv_pipe_t* handle, assert(handle->type == UV_NAMED_PIPE); - if (handle->flags & UV__HANDLE_CLOSING) { + if (handle->flags & UV_HANDLE_CLOSING) { /* The req->pipeHandle should be freed already in uv_pipe_cleanup(). */ assert(req->pipeHandle == INVALID_HANDLE_VALUE); DECREASE_PENDING_REQ_COUNT(handle); @@ -1876,7 +1875,7 @@ void uv_process_pipe_accept_req(uv_loop_t* loop, uv_pipe_t* handle, CloseHandle(req->pipeHandle); req->pipeHandle = INVALID_HANDLE_VALUE; } - if (!(handle->flags & UV__HANDLE_CLOSING)) { + if (!(handle->flags & UV_HANDLE_CLOSING)) { uv_pipe_queue_accept(loop, handle, req, FALSE); } } diff --git a/deps/uv/src/win/poll.c b/deps/uv/src/win/poll.c index b1369df3c442d3..77eb071c85a338 100644 --- a/deps/uv/src/win/poll.c +++ b/deps/uv/src/win/poll.c @@ -218,7 +218,7 @@ static void uv__fast_poll_process_poll_req(uv_loop_t* loop, uv_poll_t* handle, if ((handle->events & ~(handle->submitted_events_1 | handle->submitted_events_2)) != 0) { uv__fast_poll_submit_poll_req(loop, handle); - } else if ((handle->flags & UV__HANDLE_CLOSING) && + } else if ((handle->flags & UV_HANDLE_CLOSING) && handle->submitted_events_1 == 0 && handle->submitted_events_2 == 0) { uv_want_endgame(loop, (uv_handle_t*) handle); @@ -228,7 +228,7 @@ static void uv__fast_poll_process_poll_req(uv_loop_t* loop, uv_poll_t* handle, static int uv__fast_poll_set(uv_loop_t* loop, uv_poll_t* handle, int events) { assert(handle->type == UV_POLL); - assert(!(handle->flags & UV__HANDLE_CLOSING)); + assert(!(handle->flags & UV_HANDLE_CLOSING)); assert((events & ~(UV_READABLE | UV_WRITABLE | UV_DISCONNECT)) == 0); handle->events = events; @@ -461,7 +461,7 @@ static void uv__slow_poll_process_poll_req(uv_loop_t* loop, uv_poll_t* handle, if ((handle->events & ~(handle->submitted_events_1 | handle->submitted_events_2)) != 0) { uv__slow_poll_submit_poll_req(loop, handle); - } else if ((handle->flags & UV__HANDLE_CLOSING) && + } else if ((handle->flags & UV_HANDLE_CLOSING) && handle->submitted_events_1 == 0 && handle->submitted_events_2 == 0) { uv_want_endgame(loop, (uv_handle_t*) handle); @@ -471,7 +471,7 @@ static void uv__slow_poll_process_poll_req(uv_loop_t* loop, uv_poll_t* handle, static int uv__slow_poll_set(uv_loop_t* loop, uv_poll_t* handle, int events) { assert(handle->type == UV_POLL); - assert(!(handle->flags & UV__HANDLE_CLOSING)); + assert(!(handle->flags & UV_HANDLE_CLOSING)); assert((events & ~(UV_READABLE | UV_WRITABLE)) == 0); handle->events = events; @@ -633,7 +633,7 @@ int uv_poll_close(uv_loop_t* loop, uv_poll_t* handle) { void uv_poll_endgame(uv_loop_t* loop, uv_poll_t* handle) { - assert(handle->flags & UV__HANDLE_CLOSING); + assert(handle->flags & UV_HANDLE_CLOSING); assert(!(handle->flags & UV_HANDLE_CLOSED)); assert(handle->submitted_events_1 == 0); diff --git a/deps/uv/src/win/process-stdio.c b/deps/uv/src/win/process-stdio.c index 0ae9f0624af88a..355d6188088b4a 100644 --- a/deps/uv/src/win/process-stdio.c +++ b/deps/uv/src/win/process-stdio.c @@ -103,6 +103,7 @@ static int uv__create_stdio_pipe_pair(uv_loop_t* loop, DWORD client_access = 0; HANDLE child_pipe = INVALID_HANDLE_VALUE; int err; + int overlap; if (flags & UV_READABLE_PIPE) { /* The server needs inbound access too, otherwise CreateNamedPipe() won't @@ -130,7 +131,7 @@ static int uv__create_stdio_pipe_pair(uv_loop_t* loop, sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = TRUE; - BOOL overlap = server_pipe->ipc || (flags & UV_OVERLAPPED_PIPE); + overlap = server_pipe->ipc || (flags & UV_OVERLAPPED_PIPE); child_pipe = CreateFileA(pipe_name, client_access, 0, diff --git a/deps/uv/src/win/process.c b/deps/uv/src/win/process.c index 08910088e47ee1..b47f203e9d9c7f 100644 --- a/deps/uv/src/win/process.c +++ b/deps/uv/src/win/process.c @@ -872,7 +872,7 @@ void uv_process_proc_exit(uv_loop_t* loop, uv_process_t* handle) { /* If we're closing, don't call the exit callback. Just schedule a close * callback now. */ - if (handle->flags & UV__HANDLE_CLOSING) { + if (handle->flags & UV_HANDLE_CLOSING) { uv_want_endgame(loop, (uv_handle_t*) handle); return; } @@ -924,7 +924,7 @@ void uv_process_close(uv_loop_t* loop, uv_process_t* handle) { void uv_process_endgame(uv_loop_t* loop, uv_process_t* handle) { assert(!handle->exit_cb_pending); - assert(handle->flags & UV__HANDLE_CLOSING); + assert(handle->flags & UV_HANDLE_CLOSING); assert(!(handle->flags & UV_HANDLE_CLOSED)); /* Clean-up the process handle. */ diff --git a/deps/uv/src/win/signal.c b/deps/uv/src/win/signal.c index 750c1b36ef88bc..3d0b8a35b93ace 100644 --- a/deps/uv/src/win/signal.c +++ b/deps/uv/src/win/signal.c @@ -90,7 +90,7 @@ int uv__signal_dispatch(int signum) { unsigned long previous = InterlockedExchange( (volatile LONG*) &handle->pending_signum, signum); - if (handle->flags & UV__SIGNAL_ONE_SHOT_DISPATCHED) + if (handle->flags & UV_SIGNAL_ONE_SHOT_DISPATCHED) continue; if (!previous) { @@ -98,8 +98,8 @@ int uv__signal_dispatch(int signum) { } dispatched = 1; - if (handle->flags & UV__SIGNAL_ONE_SHOT) - handle->flags |= UV__SIGNAL_ONE_SHOT_DISPATCHED; + if (handle->flags & UV_SIGNAL_ONE_SHOT) + handle->flags |= UV_SIGNAL_ONE_SHOT_DISPATCHED; } LeaveCriticalSection(&uv__signal_lock); @@ -213,7 +213,7 @@ int uv__signal_start(uv_signal_t* handle, handle->signum = signum; if (oneshot) - handle->flags |= UV__SIGNAL_ONE_SHOT; + handle->flags |= UV_SIGNAL_ONE_SHOT; RB_INSERT(uv_signal_tree_s, &uv__signal_tree, handle); @@ -243,10 +243,10 @@ void uv_process_signal_req(uv_loop_t* loop, uv_signal_t* handle, if (dispatched_signum == handle->signum) handle->signal_cb(handle, dispatched_signum); - if (handle->flags & UV__SIGNAL_ONE_SHOT) + if (handle->flags & UV_SIGNAL_ONE_SHOT) uv_signal_stop(handle); - if (handle->flags & UV__HANDLE_CLOSING) { + if (handle->flags & UV_HANDLE_CLOSING) { /* When it is closing, it must be stopped at this point. */ assert(handle->signum == 0); uv_want_endgame(loop, (uv_handle_t*) handle); @@ -265,7 +265,7 @@ void uv_signal_close(uv_loop_t* loop, uv_signal_t* handle) { void uv_signal_endgame(uv_loop_t* loop, uv_signal_t* handle) { - assert(handle->flags & UV__HANDLE_CLOSING); + assert(handle->flags & UV_HANDLE_CLOSING); assert(!(handle->flags & UV_HANDLE_CLOSED)); assert(handle->signum == 0); diff --git a/deps/uv/src/win/stream.c b/deps/uv/src/win/stream.c index 3273a03c1cee4f..7656627e902da0 100644 --- a/deps/uv/src/win/stream.c +++ b/deps/uv/src/win/stream.c @@ -176,7 +176,7 @@ int uv_write2(uv_write_t* req, int uv_try_write(uv_stream_t* stream, const uv_buf_t bufs[], unsigned int nbufs) { - if (stream->flags & UV__HANDLE_CLOSING) + if (stream->flags & UV_HANDLE_CLOSING) return UV_EBADF; if (!(stream->flags & UV_HANDLE_WRITABLE)) return UV_EPIPE; diff --git a/deps/uv/src/win/tcp.c b/deps/uv/src/win/tcp.c index 38cd73e0d12dc3..a97ab2a5188714 100644 --- a/deps/uv/src/win/tcp.c +++ b/deps/uv/src/win/tcp.c @@ -217,7 +217,7 @@ void uv_tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle) { UNREGISTER_HANDLE_REQ(loop, handle, handle->stream.conn.shutdown_req); err = 0; - if (handle->flags & UV__HANDLE_CLOSING) { + if (handle->flags & UV_HANDLE_CLOSING) { err = ERROR_OPERATION_ABORTED; } else if (shutdown(handle->socket, SD_SEND) == SOCKET_ERROR) { err = WSAGetLastError(); @@ -233,7 +233,7 @@ void uv_tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle) { return; } - if (handle->flags & UV__HANDLE_CLOSING && + if (handle->flags & UV_HANDLE_CLOSING && handle->reqs_pending == 0) { assert(!(handle->flags & UV_HANDLE_CLOSED)); @@ -680,7 +680,7 @@ int uv_tcp_accept(uv_tcp_t* server, uv_tcp_t* client) { req->next_pending = NULL; req->accept_socket = INVALID_SOCKET; - if (!(server->flags & UV__HANDLE_CLOSING)) { + if (!(server->flags & UV_HANDLE_CLOSING)) { /* Check if we're in a middle of changing the number of pending accepts. */ if (!(server->flags & UV_HANDLE_TCP_ACCEPT_STATE_CHANGING)) { uv_tcp_queue_accept(server, req); @@ -1166,7 +1166,7 @@ void uv_process_tcp_connect_req(uv_loop_t* loop, uv_tcp_t* handle, err = 0; if (REQ_SUCCESS(req)) { - if (handle->flags & UV__HANDLE_CLOSING) { + if (handle->flags & UV_HANDLE_CLOSING) { /* use UV_ECANCELED for consistency with Unix */ err = ERROR_OPERATION_ABORTED; } else if (setsockopt(handle->socket, diff --git a/deps/uv/src/win/thread.c b/deps/uv/src/win/thread.c index 3342fd759435ef..6e41dcd8a087da 100644 --- a/deps/uv/src/win/thread.c +++ b/deps/uv/src/win/thread.c @@ -26,15 +26,6 @@ #include "uv.h" #include "internal.h" -static int uv_cond_condvar_init(uv_cond_t* cond); -static void uv_cond_condvar_destroy(uv_cond_t* cond); -static void uv_cond_condvar_signal(uv_cond_t* cond); -static void uv_cond_condvar_broadcast(uv_cond_t* cond); -static void uv_cond_condvar_wait(uv_cond_t* cond, uv_mutex_t* mutex); -static int uv_cond_condvar_timedwait(uv_cond_t* cond, - uv_mutex_t* mutex, uint64_t timeout); - - static void uv__once_inner(uv_once_t* guard, void (*callback)(void)) { DWORD result; HANDLE existing_event, created_event; @@ -374,7 +365,7 @@ int uv_cond_init(uv_cond_t* cond) { void uv_cond_destroy(uv_cond_t* cond) { /* nothing to do */ - UV__UNUSED(cond); + (void) &cond; } diff --git a/deps/uv/src/win/timer.c b/deps/uv/src/win/timer.c deleted file mode 100644 index eda5c24f6e8392..00000000000000 --- a/deps/uv/src/win/timer.c +++ /dev/null @@ -1,195 +0,0 @@ -/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#include -#include - -#include "uv.h" -#include "internal.h" -#include "uv/tree.h" -#include "handle-inl.h" - - -/* The number of milliseconds in one second. */ -#define UV__MILLISEC 1000 - - -void uv_update_time(uv_loop_t* loop) { - uint64_t new_time = uv__hrtime(UV__MILLISEC); - assert(new_time >= loop->time); - loop->time = new_time; -} - - -static int uv_timer_compare(uv_timer_t* a, uv_timer_t* b) { - if (a->due < b->due) - return -1; - if (a->due > b->due) - return 1; - /* - * compare start_id when both has the same due. start_id is - * allocated with loop->timer_counter in uv_timer_start(). - */ - if (a->start_id < b->start_id) - return -1; - if (a->start_id > b->start_id) - return 1; - return 0; -} - - -RB_GENERATE_STATIC(uv_timer_tree_s, uv_timer_s, tree_entry, uv_timer_compare) - - -int uv_timer_init(uv_loop_t* loop, uv_timer_t* handle) { - uv__handle_init(loop, (uv_handle_t*) handle, UV_TIMER); - handle->timer_cb = NULL; - handle->repeat = 0; - - return 0; -} - - -void uv_timer_endgame(uv_loop_t* loop, uv_timer_t* handle) { - if (handle->flags & UV__HANDLE_CLOSING) { - assert(!(handle->flags & UV_HANDLE_CLOSED)); - uv__handle_close(handle); - } -} - - -static uint64_t get_clamped_due_time(uint64_t loop_time, uint64_t timeout) { - uint64_t clamped_timeout; - - clamped_timeout = loop_time + timeout; - if (clamped_timeout < timeout) - clamped_timeout = (uint64_t) -1; - - return clamped_timeout; -} - - -int uv_timer_start(uv_timer_t* handle, uv_timer_cb timer_cb, uint64_t timeout, - uint64_t repeat) { - uv_loop_t* loop = handle->loop; - uv_timer_t* old; - - if (timer_cb == NULL) - return UV_EINVAL; - - if (uv__is_active(handle)) - uv_timer_stop(handle); - - handle->timer_cb = timer_cb; - handle->due = get_clamped_due_time(loop->time, timeout); - handle->repeat = repeat; - uv__handle_start(handle); - - /* start_id is the second index to be compared in uv__timer_cmp() */ - handle->start_id = handle->loop->timer_counter++; - - old = RB_INSERT(uv_timer_tree_s, &loop->timers, handle); - assert(old == NULL); - - return 0; -} - - -int uv_timer_stop(uv_timer_t* handle) { - uv_loop_t* loop = handle->loop; - - if (!uv__is_active(handle)) - return 0; - - RB_REMOVE(uv_timer_tree_s, &loop->timers, handle); - uv__handle_stop(handle); - - return 0; -} - - -int uv_timer_again(uv_timer_t* handle) { - /* If timer_cb is NULL that means that the timer was never started. */ - if (!handle->timer_cb) { - return UV_EINVAL; - } - - if (handle->repeat) { - uv_timer_stop(handle); - uv_timer_start(handle, handle->timer_cb, handle->repeat, handle->repeat); - } - - return 0; -} - - -void uv_timer_set_repeat(uv_timer_t* handle, uint64_t repeat) { - assert(handle->type == UV_TIMER); - handle->repeat = repeat; -} - - -uint64_t uv_timer_get_repeat(const uv_timer_t* handle) { - assert(handle->type == UV_TIMER); - return handle->repeat; -} - - -DWORD uv__next_timeout(const uv_loop_t* loop) { - uv_timer_t* timer; - int64_t delta; - - /* Check if there are any running timers - * Need to cast away const first, since RB_MIN doesn't know what we are - * going to do with this return value, it can't be marked const - */ - timer = RB_MIN(uv_timer_tree_s, &((uv_loop_t*)loop)->timers); - if (timer) { - delta = timer->due - loop->time; - if (delta >= UINT_MAX - 1) { - /* A timeout value of UINT_MAX means infinite, so that's no good. */ - return UINT_MAX - 1; - } else if (delta < 0) { - /* Negative timeout values are not allowed */ - return 0; - } else { - return (DWORD)delta; - } - } else { - /* No timers */ - return INFINITE; - } -} - - -void uv_process_timers(uv_loop_t* loop) { - uv_timer_t* timer; - - /* Call timer callbacks */ - for (timer = RB_MIN(uv_timer_tree_s, &loop->timers); - timer != NULL && timer->due <= loop->time; - timer = RB_MIN(uv_timer_tree_s, &loop->timers)) { - - uv_timer_stop(timer); - uv_timer_again(timer); - timer->timer_cb((uv_timer_t*) timer); - } -} diff --git a/deps/uv/src/win/tty.c b/deps/uv/src/win/tty.c index ab4a648b2ec826..d62aafb7d8c921 100644 --- a/deps/uv/src/win/tty.c +++ b/deps/uv/src/win/tty.c @@ -2208,7 +2208,7 @@ void uv_tty_endgame(uv_loop_t* loop, uv_tty_t* handle) { /* TTY shutdown is really just a no-op */ if (handle->stream.conn.shutdown_req->cb) { - if (handle->flags & UV__HANDLE_CLOSING) { + if (handle->flags & UV_HANDLE_CLOSING) { handle->stream.conn.shutdown_req->cb(handle->stream.conn.shutdown_req, UV_ECANCELED); } else { handle->stream.conn.shutdown_req->cb(handle->stream.conn.shutdown_req, 0); @@ -2221,7 +2221,7 @@ void uv_tty_endgame(uv_loop_t* loop, uv_tty_t* handle) { return; } - if (handle->flags & UV__HANDLE_CLOSING && + if (handle->flags & UV_HANDLE_CLOSING && handle->reqs_pending == 0) { /* The wait handle used for raw reading should be unregistered when the * wait callback runs. */ diff --git a/deps/uv/src/win/udp.c b/deps/uv/src/win/udp.c index e56282ae44c74e..402aeea6666b5a 100644 --- a/deps/uv/src/win/udp.c +++ b/deps/uv/src/win/udp.c @@ -188,7 +188,7 @@ void uv_udp_close(uv_loop_t* loop, uv_udp_t* handle) { void uv_udp_endgame(uv_loop_t* loop, uv_udp_t* handle) { - if (handle->flags & UV__HANDLE_CLOSING && + if (handle->flags & UV_HANDLE_CLOSING && handle->reqs_pending == 0) { assert(!(handle->flags & UV_HANDLE_CLOSED)); uv__handle_close(handle); diff --git a/deps/uv/test/test-condvar.c b/deps/uv/test/test-condvar.c index d956efef3c5a00..ec60f16403ad0b 100644 --- a/deps/uv/test/test-condvar.c +++ b/deps/uv/test/test-condvar.c @@ -25,221 +25,243 @@ #include #include +struct worker_config; + +typedef void (*signal_func)(struct worker_config* c, int* flag); +typedef int (*wait_func)(struct worker_config* c, const int* flag); + typedef struct worker_config { + uv_sem_t sem_waiting; /* post before waiting. */ + uv_sem_t sem_signaled; /* post after signaling. */ uv_mutex_t mutex; uv_cond_t cond; - int signal_delay; - int wait_delay; int use_broadcast; - volatile int posted_1; - volatile int posted_2; - void (*signal_cond)(struct worker_config* c, volatile int* flag); - int (*wait_cond)(struct worker_config* c, const volatile int* flag); + int posted_1; + int posted_2; + signal_func signal_cond; + wait_func wait_cond; } worker_config; +void worker_config_init(worker_config* wc, + int use_broadcast, + signal_func signal_f, + wait_func wait_f) { + /* Wipe. */ + memset(wc, 0, sizeof(*wc)); + + /* Copy vars. */ + wc->signal_cond = signal_f; + wc->wait_cond = wait_f; + wc->use_broadcast = use_broadcast; + + /* Init. */ + ASSERT(0 == uv_sem_init(&wc->sem_waiting, 0)); + ASSERT(0 == uv_sem_init(&wc->sem_signaled, 0)); + ASSERT(0 == uv_cond_init(&wc->cond)); + ASSERT(0 == uv_mutex_init(&wc->mutex)); +} +void worker_config_destroy(worker_config* wc) { + uv_mutex_destroy(&wc->mutex); + uv_cond_destroy(&wc->cond); + uv_sem_destroy(&wc->sem_signaled); + uv_sem_destroy(&wc->sem_waiting); +} + +/* arg is a worker_config. + * Call signal_cond then wait_cond. + * Partner should call wait then signal. */ static void worker(void* arg) { worker_config* c = arg; c->signal_cond(c, &c->posted_1); c->wait_cond(c, &c->posted_2); } -static void noop_worker(void* arg) { - return; -} - -static void condvar_signal(worker_config* c, volatile int* flag) { - if (c->signal_delay) - uv_sleep(c->signal_delay); +/* 1. Signal a waiting waiter. + * 2. Tell waiter we finished. */ +static void condvar_signal(worker_config* c, int* flag) { + /* Wait until waiter holds mutex and is preparing to wait. */ + uv_sem_wait(&c->sem_waiting); + /* Make sure waiter has begun waiting. */ uv_mutex_lock(&c->mutex); + + /* Help waiter differentiate between spurious and legitimate wakeup. */ ASSERT(*flag == 0); *flag = 1; + if (c->use_broadcast) uv_cond_broadcast(&c->cond); else uv_cond_signal(&c->cond); + uv_mutex_unlock(&c->mutex); -} + /* Done signaling. */ + uv_sem_post(&c->sem_signaled); +} -static int condvar_wait(worker_config* c, const volatile int* flag) { +/* 1. Wait on a signal. + * 2. Ensure that the signaler finished. */ +static int condvar_wait(worker_config* c, const int* flag) { uv_mutex_lock(&c->mutex); - if (c->wait_delay) - uv_sleep(c->wait_delay); - while (*flag == 0) { + + /* Tell signal'er that I am waiting. */ + uv_sem_post(&c->sem_waiting); + + /* Wait until I get a non-spurious signal. */ + do { uv_cond_wait(&c->cond, &c->mutex); - } + } while (*flag == 0); ASSERT(*flag == 1); + uv_mutex_unlock(&c->mutex); + /* Wait for my signal'er to finish. */ + uv_sem_wait(&c->sem_signaled); + return 0; } - +/* uv_cond_wait: One thread signals, the other waits. */ TEST_IMPL(condvar_1) { - uv_thread_t thread; worker_config wc; + uv_thread_t thread; - memset(&wc, 0, sizeof(wc)); - wc.wait_delay = 100; - wc.signal_cond = condvar_signal; - wc.wait_cond = condvar_wait; - - ASSERT(0 == uv_cond_init(&wc.cond)); - ASSERT(0 == uv_mutex_init(&wc.mutex)); + /* Helper signal-then-wait. */ + worker_config_init(&wc, 0, condvar_signal, condvar_wait); ASSERT(0 == uv_thread_create(&thread, worker, &wc)); + /* We wait-then-signal. */ ASSERT(0 == wc.wait_cond(&wc, &wc.posted_1)); wc.signal_cond(&wc, &wc.posted_2); ASSERT(0 == uv_thread_join(&thread)); - uv_mutex_destroy(&wc.mutex); - uv_cond_destroy(&wc.cond); + worker_config_destroy(&wc); return 0; } - +/* uv_cond_wait: One thread broadcasts, the other waits. */ TEST_IMPL(condvar_2) { - uv_thread_t thread; worker_config wc; + uv_thread_t thread; - memset(&wc, 0, sizeof(wc)); - wc.signal_delay = 100; - wc.signal_cond = condvar_signal; - wc.wait_cond = condvar_wait; - - ASSERT(0 == uv_cond_init(&wc.cond)); - ASSERT(0 == uv_mutex_init(&wc.mutex)); + /* Helper to signal-then-wait. */ + worker_config_init(&wc, 1, condvar_signal, condvar_wait); ASSERT(0 == uv_thread_create(&thread, worker, &wc)); + /* We wait-then-signal. */ ASSERT(0 == wc.wait_cond(&wc, &wc.posted_1)); wc.signal_cond(&wc, &wc.posted_2); ASSERT(0 == uv_thread_join(&thread)); - uv_mutex_destroy(&wc.mutex); - uv_cond_destroy(&wc.cond); + worker_config_destroy(&wc); return 0; } - -static int condvar_timedwait(worker_config* c, const volatile int* flag) { +/* 1. Wait on a signal (hopefully not timeout, else we'll hang). + * 2. Ensure that the signaler finished. */ +static int condvar_timedwait(worker_config* c, const int* flag) { int r; r = 0; uv_mutex_lock(&c->mutex); - if (c->wait_delay) - uv_sleep(c->wait_delay); - while (*flag == 0) { - r = uv_cond_timedwait(&c->cond, &c->mutex, (uint64_t)(150 * 1e6)); - ASSERT(r == 0 || r == UV_ETIMEDOUT); - if (r == UV_ETIMEDOUT) - break; - } + + /* Tell signal'er that I am waiting. */ + uv_sem_post(&c->sem_waiting); + + /* Wait until I get a non-spurious signal. */ + do { + r = uv_cond_timedwait(&c->cond, &c->mutex, (uint64_t)(1 * 1e9)); /* 1 s */ + ASSERT(r == 0); /* Should not time out. */ + } while (*flag == 0); + ASSERT(*flag == 1); + uv_mutex_unlock(&c->mutex); + /* Wait for my signal'er to finish. */ + uv_sem_wait(&c->sem_signaled); return r; } -/* Test that uv_cond_timedwait will return early when cond is signaled. */ +/* uv_cond_timedwait: One thread signals, the other timedwaits. */ TEST_IMPL(condvar_3) { - uv_thread_t thread; worker_config wc; + uv_thread_t thread; - memset(&wc, 0, sizeof(wc)); - wc.signal_delay = 100; - wc.signal_cond = condvar_signal; - wc.wait_cond = condvar_timedwait; - - ASSERT(0 == uv_cond_init(&wc.cond)); - ASSERT(0 == uv_mutex_init(&wc.mutex)); + /* Helper to signal-then-wait. */ + worker_config_init(&wc, 0, condvar_signal, condvar_timedwait); ASSERT(0 == uv_thread_create(&thread, worker, &wc)); - ASSERT(0 == wc.wait_cond(&wc, &wc.posted_1)); + /* We wait-then-signal. */ + wc.wait_cond(&wc, &wc.posted_1); wc.signal_cond(&wc, &wc.posted_2); ASSERT(0 == uv_thread_join(&thread)); - uv_mutex_destroy(&wc.mutex); - uv_cond_destroy(&wc.cond); + worker_config_destroy(&wc); return 0; } - +/* uv_cond_timedwait: One thread broadcasts, the other waits. */ TEST_IMPL(condvar_4) { - uv_thread_t thread; worker_config wc; + uv_thread_t thread; - memset(&wc, 0, sizeof(wc)); - wc.signal_delay = 100; - wc.signal_cond = condvar_signal; - wc.wait_cond = condvar_timedwait; - - ASSERT(0 == uv_cond_init(&wc.cond)); - ASSERT(0 == uv_mutex_init(&wc.mutex)); + /* Helper to signal-then-wait. */ + worker_config_init(&wc, 1, condvar_signal, condvar_timedwait); ASSERT(0 == uv_thread_create(&thread, worker, &wc)); + /* We wait-then-signal. */ wc.wait_cond(&wc, &wc.posted_1); wc.signal_cond(&wc, &wc.posted_2); ASSERT(0 == uv_thread_join(&thread)); - uv_mutex_destroy(&wc.mutex); - uv_cond_destroy(&wc.cond); + worker_config_destroy(&wc); return 0; } - +/* uv_cond_timedwait: One thread waits, no signal. Timeout should be delivered. */ TEST_IMPL(condvar_5) { - uv_thread_t thread; worker_config wc; + int r; + /* ns */ + uint64_t before; + uint64_t after; + uint64_t elapsed; + uint64_t timeout; - memset(&wc, 0, sizeof(wc)); - wc.use_broadcast = 1; - wc.signal_delay = 100; - wc.signal_cond = condvar_signal; - wc.wait_cond = condvar_wait; - - ASSERT(0 == uv_cond_init(&wc.cond)); - ASSERT(0 == uv_mutex_init(&wc.mutex)); - ASSERT(0 == uv_thread_create(&thread, worker, &wc)); - - wc.wait_cond(&wc, &wc.posted_1); - wc.signal_cond(&wc, &wc.posted_2); + timeout = 100 * 1e6; /* 100 ms in ns */ - ASSERT(0 == uv_thread_join(&thread)); - uv_mutex_destroy(&wc.mutex); - uv_cond_destroy(&wc.cond); + /* Mostly irrelevant. We need cond and mutex initialized. */ + worker_config_init(&wc, 0, NULL, NULL); - return 0; -} + uv_mutex_lock(&wc.mutex); -/* Test that uv_cond_timedwait will time out when cond is not signaled. */ -TEST_IMPL(condvar_6) { - uv_thread_t thread; - worker_config wc; - int r; + /* We wait. + * No signaler, so this will only return if timeout is delivered. */ + before = uv_hrtime(); + r = uv_cond_timedwait(&wc.cond, &wc.mutex, timeout); + after = uv_hrtime(); - memset(&wc, 0, sizeof(wc)); - wc.signal_delay = 100; - wc.signal_cond = condvar_signal; - wc.wait_cond = condvar_timedwait; + uv_mutex_unlock(&wc.mutex); - ASSERT(0 == uv_cond_init(&wc.cond)); - ASSERT(0 == uv_mutex_init(&wc.mutex)); - ASSERT(0 == uv_thread_create(&thread, noop_worker, &wc)); - - /* This can only return having timed out, because otherwise we - * loop forever in condvar_timedwait. */ - r = wc.wait_cond(&wc, &wc.posted_1); + /* It timed out. */ ASSERT(r == UV_ETIMEDOUT); - ASSERT(0 == uv_thread_join(&thread)); - uv_mutex_destroy(&wc.mutex); - uv_cond_destroy(&wc.cond); + /* It must have taken at least timeout, modulo system timer ticks. + * But it should not take too much longer. + * cf. MSDN docs: + * https://msdn.microsoft.com/en-us/library/ms687069(VS.85).aspx */ + elapsed = after - before; + ASSERT(0.75 * timeout <= elapsed); /* 1.0 too large for Windows. */ + ASSERT(elapsed <= 1.5 * timeout); /* 1.1 too small for OSX. */ + + worker_config_destroy(&wc); return 0; } diff --git a/deps/uv/test/test-error.c b/deps/uv/test/test-error.c index a2d559a4ee1564..7f44f4a1bc606d 100644 --- a/deps/uv/test/test-error.c +++ b/deps/uv/test/test-error.c @@ -37,6 +37,8 @@ * See https://github.com/joyent/libuv/issues/210 */ TEST_IMPL(error_message) { + char buf[32]; + /* Cop out. Can't do proper checks on systems with * i18n-ized error messages... */ @@ -49,6 +51,10 @@ TEST_IMPL(error_message) { ASSERT(strcmp(uv_strerror(1337), "Unknown error") == 0); ASSERT(strcmp(uv_strerror(-1337), "Unknown error") == 0); + ASSERT(strstr(uv_strerror_r(UV_EINVAL, buf, sizeof(buf)), "Success") == NULL); + ASSERT(strstr(uv_strerror_r(1337, buf, sizeof(buf)), "1337") != NULL); + ASSERT(strstr(uv_strerror_r(-1337, buf, sizeof(buf)), "-1337") != NULL); + return 0; } diff --git a/deps/uv/test/test-list.h b/deps/uv/test/test-list.h index e59c6b65513787..24ba37461eb4de 100644 --- a/deps/uv/test/test-list.h +++ b/deps/uv/test/test-list.h @@ -42,7 +42,6 @@ TEST_DECLARE (condvar_2) TEST_DECLARE (condvar_3) TEST_DECLARE (condvar_4) TEST_DECLARE (condvar_5) -TEST_DECLARE (condvar_6) TEST_DECLARE (semaphore_1) TEST_DECLARE (semaphore_2) TEST_DECLARE (semaphore_3) @@ -459,7 +458,6 @@ TASK_LIST_START TEST_ENTRY (condvar_3) TEST_ENTRY (condvar_4) TEST_ENTRY (condvar_5) - TEST_ENTRY (condvar_6) TEST_ENTRY (semaphore_1) TEST_ENTRY (semaphore_2) TEST_ENTRY (semaphore_3) diff --git a/deps/uv/test/test-tty.c b/deps/uv/test/test-tty.c index e761822fa349fc..6aaeda8f59619b 100644 --- a/deps/uv/test/test-tty.c +++ b/deps/uv/test/test-tty.c @@ -371,12 +371,12 @@ TEST_IMPL(tty_pty) { ASSERT(0 == uv_tty_init(&loop, &slave_tty, slave_fd, 0)); ASSERT(0 == uv_tty_init(&loop, &master_tty, master_fd, 0)); /* Check if the file descriptor was reopened. If it is, - * UV_STREAM_BLOCKING (value 0x80) isn't set on flags. + * UV_HANDLE_BLOCKING_WRITES (value 0x100000) isn't set on flags. */ - ASSERT(0 == (slave_tty.flags & 0x80)); + ASSERT(0 == (slave_tty.flags & 0x100000)); /* The master_fd of a pty should never be reopened. */ - ASSERT(master_tty.flags & 0x80); + ASSERT(master_tty.flags & 0x100000); ASSERT(0 == close(slave_fd)); uv_close((uv_handle_t*) &slave_tty, NULL); ASSERT(0 == close(master_fd)); diff --git a/deps/uv/uv.gyp b/deps/uv/uv.gyp index 8aaf541b996f51..37dcb3604f4c72 100644 --- a/deps/uv/uv.gyp +++ b/deps/uv/uv.gyp @@ -73,6 +73,7 @@ 'src/inet.c', 'src/queue.h', 'src/threadpool.c', + 'src/timer.c', 'src/uv-data-getter-setters.c', 'src/uv-common.c', 'src/uv-common.h', @@ -123,7 +124,6 @@ 'src/win/stream-inl.h', 'src/win/tcp.c', 'src/win/tty.c', - 'src/win/timer.c', 'src/win/udp.c', 'src/win/util.c', 'src/win/winapi.c', @@ -168,7 +168,6 @@ 'src/unix/stream.c', 'src/unix/tcp.c', 'src/unix/thread.c', - 'src/unix/timer.c', 'src/unix/tty.c', 'src/unix/udp.c', ], diff --git a/deps/v8/DEPS b/deps/v8/DEPS index 4a00478633793d..7c4fe68361593d 100644 --- a/deps/v8/DEPS +++ b/deps/v8/DEPS @@ -15,6 +15,8 @@ deps = { Var('chromium_url') + '/chromium/src/build.git' + '@' + '73e352e758d90603e23bdc84734c12aa5817ab5f', 'v8/tools/gyp': Var('chromium_url') + '/external/gyp.git' + '@' + 'd61a9397e668fa9843c4aa7da9e79460fe590bfb', + 'v8/third_party/depot_tools': + Var('chromium_url') + '/chromium/tools/depot_tools.git' + '@' + '024a3317597b06418efea2d45aa54dd2a7030c8a', 'v8/third_party/icu': Var('chromium_url') + '/chromium/deps/icu.git' + '@' + 'd888fd2a1be890f4d35e43f68d6d79f42519a357', 'v8/third_party/instrumented_libraries': diff --git a/deps/v8/include/v8-profiler.h b/deps/v8/include/v8-profiler.h index 5df9b2a217ea8e..c61027b3b94e45 100644 --- a/deps/v8/include/v8-profiler.h +++ b/deps/v8/include/v8-profiler.h @@ -636,7 +636,7 @@ class V8_EXPORT AllocationProfile { * Usage: * 1) Define derived class of EmbedderGraph::Node for embedder objects. * 2) Set the build embedder graph callback on the heap profiler using - * HeapProfiler::SetBuildEmbedderGraphCallback. + * HeapProfiler::AddBuildEmbedderGraphCallback. * 3) In the callback use graph->AddEdge(node1, node2) to add an edge from * node1 to node2. * 4) To represent references from/to V8 object, construct V8 nodes using @@ -736,7 +736,12 @@ class V8_EXPORT HeapProfiler { * The callback must not trigger garbage collection in V8. */ typedef void (*BuildEmbedderGraphCallback)(v8::Isolate* isolate, - v8::EmbedderGraph* graph); + v8::EmbedderGraph* graph, + void* data); + + /** TODO(addaleax): Remove */ + typedef void (*LegacyBuildEmbedderGraphCallback)(v8::Isolate* isolate, + v8::EmbedderGraph* graph); /** Returns the number of snapshots taken. */ int GetSnapshotCount(); @@ -878,15 +883,22 @@ class V8_EXPORT HeapProfiler { /** Binds a callback to embedder's class ID. */ V8_DEPRECATED( - "Use SetBuildEmbedderGraphCallback to provide info about embedder nodes", + "Use AddBuildEmbedderGraphCallback to provide info about embedder nodes", void SetWrapperClassInfoProvider(uint16_t class_id, WrapperInfoCallback callback)); V8_DEPRECATED( - "Use SetBuildEmbedderGraphCallback to provide info about embedder nodes", + "Use AddBuildEmbedderGraphCallback to provide info about embedder nodes", void SetGetRetainerInfosCallback(GetRetainerInfosCallback callback)); - void SetBuildEmbedderGraphCallback(BuildEmbedderGraphCallback callback); + V8_DEPRECATE_SOON( + "Use AddBuildEmbedderGraphCallback to provide info about embedder nodes", + void SetBuildEmbedderGraphCallback( + LegacyBuildEmbedderGraphCallback callback)); + void AddBuildEmbedderGraphCallback(BuildEmbedderGraphCallback callback, + void* data); + void RemoveBuildEmbedderGraphCallback(BuildEmbedderGraphCallback callback, + void* data); /** * Default value of persistent handle class ID. Must not be used to diff --git a/deps/v8/include/v8-version.h b/deps/v8/include/v8-version.h index 440a6068699ef0..09c47d7e91196d 100644 --- a/deps/v8/include/v8-version.h +++ b/deps/v8/include/v8-version.h @@ -11,7 +11,7 @@ #define V8_MAJOR_VERSION 6 #define V8_MINOR_VERSION 7 #define V8_BUILD_NUMBER 288 -#define V8_PATCH_LEVEL 46 +#define V8_PATCH_LEVEL 49 // Use 1 for candidates and 0 otherwise. // (Boolean macro values are not supported by all preprocessors.) diff --git a/deps/v8/include/v8.h b/deps/v8/include/v8.h index ab77238a77f97f..fe8f5a383952f7 100644 --- a/deps/v8/include/v8.h +++ b/deps/v8/include/v8.h @@ -3048,6 +3048,48 @@ class V8_EXPORT Uint32 : public Integer { class V8_EXPORT BigInt : public Primitive { public: static Local New(Isolate* isolate, int64_t value); + static Local NewFromUnsigned(Isolate* isolate, uint64_t value); + /** + * Creates a new BigInt object using a specified sign bit and a + * specified list of digits/words. + * The resulting number is calculated as: + * + * (-1)^sign_bit * (words[0] * (2^64)^0 + words[1] * (2^64)^1 + ...) + */ + static MaybeLocal NewFromWords(Local context, int sign_bit, + int word_count, const uint64_t* words); + + /** + * Returns the value of this BigInt as an unsigned 64-bit integer. + * If `lossless` is provided, it will reflect whether the return value was + * truncated or wrapped around. In particular, it is set to `false` if this + * BigInt is negative. + */ + uint64_t Uint64Value(bool* lossless = nullptr) const; + + /** + * Returns the value of this BigInt as a signed 64-bit integer. + * If `lossless` is provided, it will reflect whether this BigInt was + * truncated or not. + */ + int64_t Int64Value(bool* lossless = nullptr) const; + + /** + * Returns the number of 64-bit words needed to store the result of + * ToWordsArray(). + */ + int WordCount() const; + + /** + * Writes the contents of this BigInt to a specified memory location. + * `sign_bit` must be provided and will be set to 1 if this BigInt is + * negative. + * `*word_count` has to be initialized to the length of the `words` array. + * Upon return, it will be set to the actual number of words that would + * be needed to store this BigInt (i.e. the return value of `WordCount()`). + */ + void ToWordsArray(int* sign_bit, int* word_count, uint64_t* words) const; + V8_INLINE static BigInt* Cast(v8::Value* obj); private: diff --git a/deps/v8/src/api.cc b/deps/v8/src/api.cc index 8a0dfca02b9dcf..192ad90f83e55c 100644 --- a/deps/v8/src/api.cc +++ b/deps/v8/src/api.cc @@ -8121,6 +8121,49 @@ Local v8::BigInt::New(Isolate* isolate, int64_t value) { return Utils::ToLocal(result); } +Local v8::BigInt::NewFromUnsigned(Isolate* isolate, uint64_t value) { + CHECK(i::FLAG_harmony_bigint); + i::Isolate* internal_isolate = reinterpret_cast(isolate); + ENTER_V8_NO_SCRIPT_NO_EXCEPTION(internal_isolate); + i::Handle result = i::BigInt::FromUint64(internal_isolate, value); + return Utils::ToLocal(result); +} + +MaybeLocal v8::BigInt::NewFromWords(Local context, + int sign_bit, int word_count, + const uint64_t* words) { + CHECK(i::FLAG_harmony_bigint); + i::Isolate* isolate = reinterpret_cast(context->GetIsolate()); + ENTER_V8_NO_SCRIPT(isolate, context, BigInt, NewFromWords, + MaybeLocal(), InternalEscapableScope); + i::MaybeHandle result = + i::BigInt::FromWords64(isolate, sign_bit, word_count, words); + has_pending_exception = result.is_null(); + RETURN_ON_FAILED_EXECUTION(BigInt); + RETURN_ESCAPED(Utils::ToLocal(result.ToHandleChecked())); +} + +uint64_t v8::BigInt::Uint64Value(bool* lossless) const { + i::Handle handle = Utils::OpenHandle(this); + return handle->AsUint64(lossless); +} + +int64_t v8::BigInt::Int64Value(bool* lossless) const { + i::Handle handle = Utils::OpenHandle(this); + return handle->AsInt64(lossless); +} + +int BigInt::WordCount() const { + i::Handle handle = Utils::OpenHandle(this); + return handle->Words64Count(); +} + +void BigInt::ToWordsArray(int* sign_bit, int* word_count, + uint64_t* words) const { + i::Handle handle = Utils::OpenHandle(this); + return handle->ToWordsArray64(sign_bit, word_count, words); +} + void Isolate::ReportExternalAllocationLimitReached() { i::Heap* heap = reinterpret_cast(this)->heap(); if (heap->gc_state() != i::Heap::NOT_IN_GC) return; @@ -10515,9 +10558,25 @@ void HeapProfiler::SetGetRetainerInfosCallback( } void HeapProfiler::SetBuildEmbedderGraphCallback( - BuildEmbedderGraphCallback callback) { - reinterpret_cast(this)->SetBuildEmbedderGraphCallback( - callback); + LegacyBuildEmbedderGraphCallback callback) { + reinterpret_cast(this)->AddBuildEmbedderGraphCallback( + [](v8::Isolate* isolate, v8::EmbedderGraph* graph, void* data) { + reinterpret_cast(data)(isolate, + graph); + }, + reinterpret_cast(callback)); +} + +void HeapProfiler::AddBuildEmbedderGraphCallback( + BuildEmbedderGraphCallback callback, void* data) { + reinterpret_cast(this)->AddBuildEmbedderGraphCallback( + callback, data); +} + +void HeapProfiler::RemoveBuildEmbedderGraphCallback( + BuildEmbedderGraphCallback callback, void* data) { + reinterpret_cast(this)->RemoveBuildEmbedderGraphCallback( + callback, data); } v8::Testing::StressType internal::Testing::stress_type_ = diff --git a/deps/v8/src/api.h b/deps/v8/src/api.h index d2e25ae4f72332..c67de0482df66c 100644 --- a/deps/v8/src/api.h +++ b/deps/v8/src/api.h @@ -114,6 +114,7 @@ class RegisteredExtension { V(Promise, JSPromise) \ V(Primitive, Object) \ V(PrimitiveArray, FixedArray) \ + V(BigInt, BigInt) \ V(ScriptOrModule, Script) class Utils { diff --git a/deps/v8/src/counters.h b/deps/v8/src/counters.h index 3c674b0ae01539..3318055185521e 100644 --- a/deps/v8/src/counters.h +++ b/deps/v8/src/counters.h @@ -692,6 +692,7 @@ class RuntimeCallTimer final { V(ArrayBuffer_New) \ V(Array_CloneElementAt) \ V(Array_New) \ + V(BigInt_NewFromWords) \ V(BigInt64Array_New) \ V(BigUint64Array_New) \ V(BigIntObject_New) \ diff --git a/deps/v8/src/objects/bigint.cc b/deps/v8/src/objects/bigint.cc index 0ccc5930b728a4..66962033ffe8b0 100644 --- a/deps/v8/src/objects/bigint.cc +++ b/deps/v8/src/objects/bigint.cc @@ -2205,6 +2205,70 @@ Handle BigInt::FromUint64(Isolate* isolate, uint64_t n) { return MutableBigInt::MakeImmutable(result); } +MaybeHandle BigInt::FromWords64(Isolate* isolate, int sign_bit, + int words64_count, + const uint64_t* words) { + if (words64_count < 0 || words64_count > kMaxLength / (64 / kDigitBits)) { + THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kBigIntTooBig), + BigInt); + } + if (words64_count == 0) return MutableBigInt::Zero(isolate); + STATIC_ASSERT(kDigitBits == 64 || kDigitBits == 32); + int length = (64 / kDigitBits) * words64_count; + DCHECK_GT(length, 0); + if (kDigitBits == 32 && words[words64_count - 1] <= (1ULL << 32)) length--; + + Handle result; + if (!MutableBigInt::New(isolate, length).ToHandle(&result)) { + return MaybeHandle(); + } + + result->set_sign(sign_bit); + if (kDigitBits == 64) { + for (int i = 0; i < length; ++i) { + result->set_digit(i, static_cast(words[i])); + } + } else { + for (int i = 0; i < length; i += 2) { + digit_t lo = static_cast(words[i / 2]); + digit_t hi = static_cast(words[i / 2] >> 32); + result->set_digit(i, lo); + if (i + 1 < length) result->set_digit(i + 1, hi); + } + } + + return MutableBigInt::MakeImmutable(result); +} + +int BigInt::Words64Count() { + STATIC_ASSERT(kDigitBits == 64 || kDigitBits == 32); + return length() / (64 / kDigitBits) + + (kDigitBits == 32 && length() % 2 == 1 ? 1 : 0); +} + +void BigInt::ToWordsArray64(int* sign_bit, int* words64_count, + uint64_t* words) { + DCHECK_NE(sign_bit, nullptr); + DCHECK_NE(words64_count, nullptr); + *sign_bit = sign(); + int available_words = *words64_count; + *words64_count = Words64Count(); + if (available_words == 0) return; + DCHECK_NE(words, nullptr); + + int len = length(); + if (kDigitBits == 64) { + for (int i = 0; i < len && i < available_words; ++i) words[i] = digit(i); + } else { + for (int i = 0; i < len && available_words > 0; i += 2) { + uint64_t lo = digit(i); + uint64_t hi = (i + 1) < len ? digit(i + 1) : 0; + words[i / 2] = lo | (hi << 32); + available_words--; + } + } +} + uint64_t MutableBigInt::GetRawBits(BigIntBase* x, bool* lossless) { if (lossless != nullptr) *lossless = true; if (x->is_zero()) return 0; diff --git a/deps/v8/src/objects/bigint.h b/deps/v8/src/objects/bigint.h index 3899853955d91d..65ce808394452d 100644 --- a/deps/v8/src/objects/bigint.h +++ b/deps/v8/src/objects/bigint.h @@ -144,8 +144,13 @@ class V8_EXPORT_PRIVATE BigInt : public BigIntBase { static Handle FromInt64(Isolate* isolate, int64_t n); static Handle FromUint64(Isolate* isolate, uint64_t n); + static MaybeHandle FromWords64(Isolate* isolate, int sign_bit, + int words64_count, + const uint64_t* words); int64_t AsInt64(bool* lossless = nullptr); uint64_t AsUint64(bool* lossless = nullptr); + int Words64Count(); + void ToWordsArray64(int* sign_bit, int* words64_count, uint64_t* words); DECL_CAST(BigInt) DECL_VERIFIER(BigInt) diff --git a/deps/v8/src/profiler/heap-profiler.cc b/deps/v8/src/profiler/heap-profiler.cc index 7e0bcec97ae75e..2496e24b91cf18 100644 --- a/deps/v8/src/profiler/heap-profiler.cc +++ b/deps/v8/src/profiler/heap-profiler.cc @@ -69,16 +69,25 @@ v8::HeapProfiler::RetainerInfos HeapProfiler::GetRetainerInfos( return infos; } -void HeapProfiler::SetBuildEmbedderGraphCallback( - v8::HeapProfiler::BuildEmbedderGraphCallback callback) { - build_embedder_graph_callback_ = callback; +void HeapProfiler::AddBuildEmbedderGraphCallback( + v8::HeapProfiler::BuildEmbedderGraphCallback callback, void* data) { + build_embedder_graph_callbacks_.push_back({callback, data}); +} + +void HeapProfiler::RemoveBuildEmbedderGraphCallback( + v8::HeapProfiler::BuildEmbedderGraphCallback callback, void* data) { + auto it = std::find(build_embedder_graph_callbacks_.begin(), + build_embedder_graph_callbacks_.end(), + std::make_pair(callback, data)); + if (it != build_embedder_graph_callbacks_.end()) + build_embedder_graph_callbacks_.erase(it); } void HeapProfiler::BuildEmbedderGraph(Isolate* isolate, v8::EmbedderGraph* graph) { - if (build_embedder_graph_callback_ != nullptr) - build_embedder_graph_callback_(reinterpret_cast(isolate), - graph); + for (const auto& cb : build_embedder_graph_callbacks_) { + cb.first(reinterpret_cast(isolate), graph, cb.second); + } } HeapSnapshot* HeapProfiler::TakeSnapshot( diff --git a/deps/v8/src/profiler/heap-profiler.h b/deps/v8/src/profiler/heap-profiler.h index 507dd579bffb65..fc0b005e1c67ce 100644 --- a/deps/v8/src/profiler/heap-profiler.h +++ b/deps/v8/src/profiler/heap-profiler.h @@ -71,11 +71,13 @@ class HeapProfiler : public HeapObjectAllocationTracker { v8::HeapProfiler::GetRetainerInfosCallback callback); v8::HeapProfiler::RetainerInfos GetRetainerInfos(Isolate* isolate); - void SetBuildEmbedderGraphCallback( - v8::HeapProfiler::BuildEmbedderGraphCallback callback); + void AddBuildEmbedderGraphCallback( + v8::HeapProfiler::BuildEmbedderGraphCallback callback, void* data); + void RemoveBuildEmbedderGraphCallback( + v8::HeapProfiler::BuildEmbedderGraphCallback callback, void* data); void BuildEmbedderGraph(Isolate* isolate, v8::EmbedderGraph* graph); bool HasBuildEmbedderGraphCallback() { - return build_embedder_graph_callback_ != nullptr; + return !build_embedder_graph_callbacks_.empty(); } bool is_tracking_object_moves() const { return is_tracking_object_moves_; } @@ -103,8 +105,8 @@ class HeapProfiler : public HeapObjectAllocationTracker { std::unique_ptr sampling_heap_profiler_; v8::HeapProfiler::GetRetainerInfosCallback get_retainer_infos_callback_ = nullptr; - v8::HeapProfiler::BuildEmbedderGraphCallback build_embedder_graph_callback_ = - nullptr; + std::vector> + build_embedder_graph_callbacks_; DISALLOW_COPY_AND_ASSIGN(HeapProfiler); }; diff --git a/deps/v8/test/cctest/test-api.cc b/deps/v8/test/cctest/test-api.cc index d698c1a9e008a4..637dc7d2062f9a 100644 --- a/deps/v8/test/cctest/test-api.cc +++ b/deps/v8/test/cctest/test-api.cc @@ -27613,3 +27613,117 @@ TEST(WasmStreamingAbortNoReject) { streaming.Abort({}); CHECK_EQ(streaming.GetPromise()->State(), v8::Promise::kPending); } + +TEST(BigIntAPI) { + LocalContext env; + v8::Isolate* isolate = env->GetIsolate(); + v8::HandleScope scope(isolate); + bool lossless; + uint64_t words1[10]; + uint64_t words2[10]; + + { + Local bi = CompileRun("12n"); + CHECK(bi->IsBigInt()); + + CHECK_EQ(bi.As()->Uint64Value(), 12); + CHECK_EQ(bi.As()->Uint64Value(&lossless), 12); + CHECK_EQ(lossless, true); + CHECK_EQ(bi.As()->Int64Value(), 12); + CHECK_EQ(bi.As()->Int64Value(&lossless), 12); + CHECK_EQ(lossless, true); + } + + { + Local bi = CompileRun("-12n"); + CHECK(bi->IsBigInt()); + + CHECK_EQ(bi.As()->Uint64Value(), static_cast(-12)); + CHECK_EQ(bi.As()->Uint64Value(&lossless), + static_cast(-12)); + CHECK_EQ(lossless, false); + CHECK_EQ(bi.As()->Int64Value(), -12); + CHECK_EQ(bi.As()->Int64Value(&lossless), -12); + CHECK_EQ(lossless, true); + } + + { + Local bi = CompileRun("123456789012345678901234567890n"); + CHECK(bi->IsBigInt()); + + CHECK_EQ(bi.As()->Uint64Value(), 14083847773837265618ULL); + CHECK_EQ(bi.As()->Uint64Value(&lossless), + 14083847773837265618ULL); + CHECK_EQ(lossless, false); + CHECK_EQ(bi.As()->Int64Value(), -4362896299872285998LL); + CHECK_EQ(bi.As()->Int64Value(&lossless), + -4362896299872285998LL); + CHECK_EQ(lossless, false); + } + + { + Local bi = CompileRun("-123456789012345678901234567890n"); + CHECK(bi->IsBigInt()); + + CHECK_EQ(bi.As()->Uint64Value(), 4362896299872285998LL); + CHECK_EQ(bi.As()->Uint64Value(&lossless), + 4362896299872285998LL); + CHECK_EQ(lossless, false); + CHECK_EQ(bi.As()->Int64Value(), 4362896299872285998LL); + CHECK_EQ(bi.As()->Int64Value(&lossless), 4362896299872285998LL); + CHECK_EQ(lossless, false); + } + + { + Local bi = + v8::BigInt::NewFromWords(env.local(), 0, 0, words1).ToLocalChecked(); + CHECK_EQ(bi->Uint64Value(), 0); + CHECK_EQ(bi->WordCount(), 0); + } + + { + TryCatch try_catch(isolate); + v8::MaybeLocal bi = v8::BigInt::NewFromWords( + env.local(), 0, std::numeric_limits::max(), words1); + CHECK(bi.IsEmpty()); + CHECK(try_catch.HasCaught()); + } + + { + TryCatch try_catch(isolate); + v8::MaybeLocal bi = + v8::BigInt::NewFromWords(env.local(), 0, -1, words1); + CHECK(bi.IsEmpty()); + CHECK(try_catch.HasCaught()); + } + + { + TryCatch try_catch(isolate); + v8::MaybeLocal bi = + v8::BigInt::NewFromWords(env.local(), 0, 1 << 30, words1); + CHECK(bi.IsEmpty()); + CHECK(try_catch.HasCaught()); + } + + for (int sign_bit = 0; sign_bit <= 1; sign_bit++) { + words1[0] = 0xffffffff00000000ULL; + words1[1] = 0x00000000ffffffffULL; + v8::Local bi = + v8::BigInt::NewFromWords(env.local(), sign_bit, 2, words1) + .ToLocalChecked(); + CHECK_EQ(bi->Uint64Value(&lossless), + sign_bit ? static_cast(-static_cast(words1[0])) + : words1[0]); + CHECK_EQ(lossless, false); + CHECK_EQ(bi->Int64Value(&lossless), sign_bit + ? -static_cast(words1[0]) + : static_cast(words1[0])); + CHECK_EQ(lossless, false); + CHECK_EQ(bi->WordCount(), 2); + int real_sign_bit; + int word_count = arraysize(words2); + bi->ToWordsArray(&real_sign_bit, &word_count, words2); + CHECK_EQ(real_sign_bit, sign_bit); + CHECK_EQ(word_count, 2); + } +} diff --git a/deps/v8/test/cctest/test-heap-profiler.cc b/deps/v8/test/cctest/test-heap-profiler.cc index 4372aa3d2e5205..eec739d109dcf1 100644 --- a/deps/v8/test/cctest/test-heap-profiler.cc +++ b/deps/v8/test/cctest/test-heap-profiler.cc @@ -1541,8 +1541,8 @@ class EmbedderGraphBuilder : public v8::PersistentHandleVisitor { graph->AddNode(std::unique_ptr(new Group("ccc-group"))); } - static void BuildEmbedderGraph(v8::Isolate* isolate, - v8::EmbedderGraph* graph) { + static void BuildEmbedderGraph(v8::Isolate* isolate, v8::EmbedderGraph* graph, + void* data) { EmbedderGraphBuilder builder(isolate, graph); isolate->VisitHandlesWithClassIds(&builder); } @@ -1604,8 +1604,8 @@ TEST(HeapSnapshotRetainedObjectInfo) { v8::HandleScope scope(isolate); v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler(); - heap_profiler->SetBuildEmbedderGraphCallback( - EmbedderGraphBuilder::BuildEmbedderGraph); + heap_profiler->AddBuildEmbedderGraphCallback( + EmbedderGraphBuilder::BuildEmbedderGraph, nullptr); v8::Persistent p_AAA(isolate, v8_str("AAA")); p_AAA.SetWrapperClassId(1); v8::Persistent p_BBB(isolate, v8_str("BBB")); @@ -2932,7 +2932,8 @@ class EmbedderRootNode : public EmbedderNode { // global object. v8::Local* global_object_pointer; -void BuildEmbedderGraph(v8::Isolate* v8_isolate, v8::EmbedderGraph* graph) { +void BuildEmbedderGraph(v8::Isolate* v8_isolate, v8::EmbedderGraph* graph, + void* data) { using Node = v8::EmbedderGraph::Node; Node* global_node = graph->V8Node(*global_object_pointer); Node* embedder_node_A = graph->AddNode( @@ -2979,12 +2980,92 @@ TEST(EmbedderGraph) { (isolate->context()->native_context()->global_object()))); global_object_pointer = &global_object; v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); - heap_profiler->SetBuildEmbedderGraphCallback(BuildEmbedderGraph); + heap_profiler->AddBuildEmbedderGraphCallback(BuildEmbedderGraph, nullptr); const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot(); CHECK(ValidateSnapshot(snapshot)); CheckEmbedderGraphSnapshot(env->GetIsolate(), snapshot); } +struct GraphBuildingContext { + int counter = 0; +}; + +void CheckEmbedderGraphSnapshotWithContext( + v8::Isolate* isolate, const v8::HeapSnapshot* snapshot, + const GraphBuildingContext* context) { + const v8::HeapGraphNode* global = GetGlobalObject(snapshot); + CHECK_GE(context->counter, 1); + CHECK_LE(context->counter, 2); + + const v8::HeapGraphNode* embedder_node_A = + GetChildByName(global, "EmbedderNodeA"); + CHECK_EQ(10, GetSize(embedder_node_A)); + + const v8::HeapGraphNode* embedder_node_B = + GetChildByName(global, "EmbedderNodeB"); + if (context->counter == 2) { + CHECK_NOT_NULL(embedder_node_B); + CHECK_EQ(20, GetSize(embedder_node_B)); + } else { + CHECK_NULL(embedder_node_B); + } +} + +void BuildEmbedderGraphWithContext(v8::Isolate* v8_isolate, + v8::EmbedderGraph* graph, void* data) { + using Node = v8::EmbedderGraph::Node; + GraphBuildingContext* context = static_cast(data); + Node* global_node = graph->V8Node(*global_object_pointer); + + CHECK_GE(context->counter, 0); + CHECK_LE(context->counter, 1); + switch (context->counter++) { + case 0: { + Node* embedder_node_A = graph->AddNode( + std::unique_ptr(new EmbedderNode("EmbedderNodeA", 10))); + graph->AddEdge(global_node, embedder_node_A); + break; + } + case 1: { + Node* embedder_node_B = graph->AddNode( + std::unique_ptr(new EmbedderNode("EmbedderNodeB", 20))); + graph->AddEdge(global_node, embedder_node_B); + break; + } + } +} + +TEST(EmbedderGraphMultipleCallbacks) { + i::FLAG_heap_profiler_use_embedder_graph = true; + LocalContext env; + v8::HandleScope scope(env->GetIsolate()); + i::Isolate* isolate = reinterpret_cast(env->GetIsolate()); + v8::Local global_object = + v8::Utils::ToLocal(i::Handle( + (isolate->context()->native_context()->global_object()))); + global_object_pointer = &global_object; + v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); + GraphBuildingContext context; + + heap_profiler->AddBuildEmbedderGraphCallback(BuildEmbedderGraphWithContext, + &context); + heap_profiler->AddBuildEmbedderGraphCallback(BuildEmbedderGraphWithContext, + &context); + const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot(); + CHECK_EQ(context.counter, 2); + CHECK(ValidateSnapshot(snapshot)); + CheckEmbedderGraphSnapshotWithContext(env->GetIsolate(), snapshot, &context); + + heap_profiler->RemoveBuildEmbedderGraphCallback(BuildEmbedderGraphWithContext, + &context); + context.counter = 0; + + snapshot = heap_profiler->TakeHeapSnapshot(); + CHECK_EQ(context.counter, 1); + CHECK(ValidateSnapshot(snapshot)); + CheckEmbedderGraphSnapshotWithContext(env->GetIsolate(), snapshot, &context); +} + TEST(StrongHandleAnnotation) { LocalContext env; v8::HandleScope scope(env->GetIsolate()); @@ -3010,7 +3091,7 @@ TEST(StrongHandleAnnotation) { } void BuildEmbedderGraphWithWrapperNode(v8::Isolate* v8_isolate, - v8::EmbedderGraph* graph) { + v8::EmbedderGraph* graph, void* data) { using Node = v8::EmbedderGraph::Node; Node* global_node = graph->V8Node(*global_object_pointer); Node* wrapper_node = graph->AddNode( @@ -3041,8 +3122,8 @@ TEST(EmbedderGraphWithWrapperNode) { (isolate->context()->native_context()->global_object()))); global_object_pointer = &global_object; v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); - heap_profiler->SetBuildEmbedderGraphCallback( - BuildEmbedderGraphWithWrapperNode); + heap_profiler->AddBuildEmbedderGraphCallback( + BuildEmbedderGraphWithWrapperNode, nullptr); const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot(); CHECK(ValidateSnapshot(snapshot)); const v8::HeapGraphNode* global = GetGlobalObject(snapshot); @@ -3080,7 +3161,7 @@ class EmbedderNodeWithPrefix : public v8::EmbedderGraph::Node { }; void BuildEmbedderGraphWithPrefix(v8::Isolate* v8_isolate, - v8::EmbedderGraph* graph) { + v8::EmbedderGraph* graph, void* data) { using Node = v8::EmbedderGraph::Node; Node* global_node = graph->V8Node(*global_object_pointer); Node* node = graph->AddNode( @@ -3098,7 +3179,8 @@ TEST(EmbedderGraphWithPrefix) { (isolate->context()->native_context()->global_object()))); global_object_pointer = &global_object; v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); - heap_profiler->SetBuildEmbedderGraphCallback(BuildEmbedderGraphWithPrefix); + heap_profiler->AddBuildEmbedderGraphCallback(BuildEmbedderGraphWithPrefix, + nullptr); const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot(); CHECK(ValidateSnapshot(snapshot)); const v8::HeapGraphNode* global = GetGlobalObject(snapshot); diff --git a/deps/v8/tools/whitespace.txt b/deps/v8/tools/whitespace.txt index ed5e51f96a63e8..8a1a4d64c7aac8 100644 --- a/deps/v8/tools/whitespace.txt +++ b/deps/v8/tools/whitespace.txt @@ -7,6 +7,4 @@ A Smi balks into a war and says: The doubles heard this and started to unbox. The Smi looked at them when a crazy v8-autoroll account showed up... The autoroller bought a round of Himbeerbrause. Suddenly... -The bartender starts to shake the bottles....................... -. -. +The bartender starts to shake the bottles... diff --git a/doc/.eslintrc.yaml b/doc/.eslintrc.yaml index 7b38afec10276a..49c4f4f64736c7 100644 --- a/doc/.eslintrc.yaml +++ b/doc/.eslintrc.yaml @@ -5,7 +5,6 @@ rules: no-restricted-properties: off no-undef: off no-unused-vars: off - strict: off symbol-description: off # add new ECMAScript features gradually diff --git a/doc/api/_toc.md b/doc/api/_toc.md deleted file mode 100644 index e307d52ae8544e..00000000000000 --- a/doc/api/_toc.md +++ /dev/null @@ -1,62 +0,0 @@ -@// NB(chrisdickinson): if you move this file, be sure to update -@// tools/doc/html.js to point at the new location. - - - -* [About these Docs](documentation.html) -* [Usage & Example](synopsis.html) - -
- -* [Assertion Testing](assert.html) -* [Async Hooks](async_hooks.html) -* [Buffer](buffer.html) -* [C++ Addons](addons.html) -* [C/C++ Addons - N-API](n-api.html) -* [Child Processes](child_process.html) -* [Cluster](cluster.html) -* [Command Line Options](cli.html) -* [Console](console.html) -* [Crypto](crypto.html) -* [Debugger](debugger.html) -* [Deprecated APIs](deprecations.html) -* [DNS](dns.html) -* [Domain](domain.html) -* [ECMAScript Modules](esm.html) -* [Errors](errors.html) -* [Events](events.html) -* [File System](fs.html) -* [Globals](globals.html) -* [HTTP](http.html) -* [HTTP/2](http2.html) -* [HTTPS](https.html) -* [Inspector](inspector.html) -* [Internationalization](intl.html) -* [Modules](modules.html) -* [Net](net.html) -* [OS](os.html) -* [Path](path.html) -* [Performance Hooks](perf_hooks.html) -* [Process](process.html) -* [Punycode](punycode.html) -* [Query Strings](querystring.html) -* [Readline](readline.html) -* [REPL](repl.html) -* [Stream](stream.html) -* [String Decoder](string_decoder.html) -* [Timers](timers.html) -* [TLS/SSL](tls.html) -* [Trace Events](tracing.html) -* [TTY](tty.html) -* [UDP/Datagram](dgram.html) -* [URL](url.html) -* [Utilities](util.html) -* [V8](v8.html) -* [VM](vm.html) -* [Worker Threads](worker_threads.html) -* [ZLIB](zlib.html) - -
- -* [GitHub Repo & Issue Tracker](https://github.com/nodejs/node) -* [Mailing List](https://groups.google.com/group/nodejs) diff --git a/doc/api/addons.md b/doc/api/addons.md index 578b1a0f90b4ce..9ea0a6b6446ce4 100644 --- a/doc/api/addons.md +++ b/doc/api/addons.md @@ -98,6 +98,140 @@ the `.node` suffix). In the `hello.cc` example, then, the initialization function is `Initialize` and the addon module name is `addon`. +When building addons with `node-gyp`, using the macro `NODE_GYP_MODULE_NAME` as +the first parameter of `NODE_MODULE()` will ensure that the name of the final +binary will be passed to `NODE_MODULE()`. + +### Context-aware addons + +There are environments in which Node.js addons may need to be loaded multiple +times in multiple contexts. For example, the [Electron][] runtime runs multiple +instances of Node.js in a single process. Each instance will have its own +`require()` cache, and thus each instance will need a native addon to behave +correctly when loaded via `require()`. From the addon's perspective, this means +that it must support multiple initializations. + +A context-aware addon can be constructed by using the macro +`NODE_MODULE_INITIALIZER`, which expands to the name of a function which Node.js +will expect to find when it loads an addon. An addon can thus be initialized as +in the following example: + +```cpp +using namespace v8; + +extern "C" NODE_MODULE_EXPORT void +NODE_MODULE_INITIALIZER(Local exports, + Local module, + Local context) { + /* Perform addon initialization steps here. */ +} +``` + +Another option is to use the macro `NODE_MODULE_INIT()`, which will also +construct a context-aware addon. Unlike `NODE_MODULE()`, which is used to +construct an addon around a given addon initializer function, +`NODE_MODULE_INIT()` serves as the declaration of such an initializer to be +followed by a function body. + +The following three variables may be used inside the function body following an +invocation of `NODE_MODULE_INIT()`: +* `Local exports`, +* `Local module`, and +* `Local context` + +The choice to build a context-aware addon carries with it the responsibility of +carefully managing global static data. Since the addon may be loaded multiple +times, potentially even from different threads, any global static data stored +in the addon must be properly protected, and must not contain any persistent +references to JavaScript objects. The reason for this is that JavaScript +objects are only valid in one context, and will likely cause a crash when +accessed from the wrong context or from a different thread than the one on which +they were created. + +The context-aware addon can be structured to avoid global static data by +performing the following steps: +* defining a class which will hold per-addon-instance data. Such +a class should include a `v8::Persistent` which will hold a weak +reference to the addon's `exports` object. The callback associated with the weak +reference will then destroy the instance of the class. +* constructing an instance of this class in the addon initializer such that the +`v8::Persistent` is set to the `exports` object. +* storing the instance of the class in a `v8::External`, and +* passing the `v8::External` to all methods exposed to JavaScript by passing it +to the `v8::FunctionTemplate` constructor which creates the native-backed +JavaScript functions. The `v8::FunctionTemplate` constructor's third parameter +accepts the `v8::External`. + +This will ensure that the per-addon-instance data reaches each binding that can +be called from JavaScript. The per-addon-instance data must also be passed into +any asynchronous callbacks the addon may create. + +The following example illustrates the implementation of a context-aware addon: + +```cpp +#include + +using namespace v8; + +class AddonData { + public: + AddonData(Isolate* isolate, Local exports): + call_count(0) { + // Link the existence of this object instance to the existence of exports. + exports_.Reset(isolate, exports); + exports_.SetWeak(this, DeleteMe, WeakCallbackType::kParameter); + } + + ~AddonData() { + if (!exports_.IsEmpty()) { + // Reset the reference to avoid leaking data. + exports_.ClearWeak(); + exports_.Reset(); + } + } + + // Per-addon data. + int call_count; + + private: + // Method to call when "exports" is about to be garbage-collected. + static void DeleteMe(const WeakCallbackInfo& info) { + delete info.GetParameter(); + } + + // Weak handle to the "exports" object. An instance of this class will be + // destroyed along with the exports object to which it is weakly bound. + v8::Persistent exports_; +}; + +static void Method(const v8::FunctionCallbackInfo& info) { + // Retrieve the per-addon-instance data. + AddonData* data = + reinterpret_cast(info.Data().As()->Value()); + data->call_count++; + info.GetReturnValue().Set((double)data->call_count); +} + +// Initialize this addon to be context-aware. +NODE_MODULE_INIT(/* exports, module, context */) { + Isolate* isolate = context->GetIsolate(); + + // Create a new instance of AddonData for this instance of the addon. + AddonData* data = new AddonData(isolate, exports); + // Wrap the data in a v8::External so we can pass it to the method we expose. + Local external = External::New(isolate, data); + + // Expose the method "Method" to JavaScript, and make sure it receives the + // per-addon-instance data we created above by passing `external` as the + // third parameter to the FunctionTemplate constructor. + exports->Set(context, + String::NewFromUtf8(isolate, "method", NewStringType::kNormal) + .ToLocalChecked(), + FunctionTemplate::New(isolate, Method, external) + ->GetFunction(context).ToLocalChecked()).FromJust(); +} +``` + ### Building Once the source code has been written, it must be compiled into the binary @@ -1162,6 +1296,7 @@ Test in JavaScript by running: require('./build/Release/addon'); ``` +[Electron]: https://electronjs.org/ [Embedder's Guide]: https://github.com/v8/v8/wiki/Embedder's%20Guide [Linking to Node.js' own dependencies]: #addons_linking_to_node_js_own_dependencies [Native Abstractions for Node.js]: https://github.com/nodejs/nan diff --git a/doc/api/all.md b/doc/api/all.md deleted file mode 100644 index 47216b695d3351..00000000000000 --- a/doc/api/all.md +++ /dev/null @@ -1,50 +0,0 @@ - -@include documentation -@include synopsis -@include assert -@include async_hooks -@include buffer -@include addons -@include n-api -@include child_process -@include cluster -@include cli -@include console -@include crypto -@include debugger -@include deprecations -@include dns -@include domain -@include esm -@include errors -@include events -@include fs -@include globals -@include http -@include http2 -@include https -@include inspector -@include intl -@include modules -@include net -@include os -@include path -@include perf_hooks -@include process -@include punycode -@include querystring -@include readline -@include repl -@include stream -@include string_decoder -@include timers -@include tls -@include tracing -@include tty -@include dgram -@include url -@include util -@include v8 -@include vm -@include worker_threads -@include zlib diff --git a/doc/api/async_hooks.md b/doc/api/async_hooks.md index 6e5971c51d10c9..aa9e133b43e3f7 100644 --- a/doc/api/async_hooks.md +++ b/doc/api/async_hooks.md @@ -726,6 +726,6 @@ never be called. [`destroy` callback]: #async_hooks_destroy_asyncid [`init` callback]: #async_hooks_init_asyncid_type_triggerasyncid_resource [Hook Callbacks]: #async_hooks_hook_callbacks -[PromiseHooks]: https://docs.google.com/document/d/1rda3yKGHimKIhg5YeoAmCOtyURgsbTH_qaYR79FELlk +[PromiseHooks]: https://docs.google.com/document/d/1rda3yKGHimKIhg5YeoAmCOtyURgsbTH_qaYR79FELlk/edit [promise execution tracking]: #async_hooks_promise_execution_tracking [`Worker`]: worker_threads.html#worker_threads_class_worker diff --git a/doc/api/buffer.md b/doc/api/buffer.md index c219f00e4a1f48..8e1d1c325b6ca7 100644 --- a/doc/api/buffer.md +++ b/doc/api/buffer.md @@ -992,6 +992,31 @@ console.log(buffer.buffer === arrayBuffer); // Prints: true ``` +### buf.byteOffset + +* {integer} The `byteOffset` on the underlying `ArrayBuffer` object based on + which this `Buffer` object is created. + +When setting `byteOffset` in `Buffer.from(ArrayBuffer, byteOffset, length)` +or sometimes when allocating a buffer smaller than `Buffer.poolSize` the +buffer doesn't start from a zero offset on the underlying `ArrayBuffer`. + +This can cause problems when accessing the underlying `ArrayBuffer` directly +using `buf.buffer`, as the first bytes in this `ArrayBuffer` may be unrelated +to the `buf` object itself. + +A common issue is when casting a `Buffer` object to a `TypedArray` object, +in this case one needs to specify the `byteOffset` correctly: + +```js +// Create a buffer smaller than `Buffer.poolSize`. +const nodeBuffer = new Buffer.from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); + +// When casting the Node.js Buffer to an Int8 TypedArray remember to use the +// byteOffset. +new Int8Array(nodeBuffer.buffer, nodeBuffer.byteOffset, nodeBuffer.length); +``` + ### buf.compare(target[, targetStart[, targetEnd[, sourceStart[, sourceEnd]]]]) + +Set `process.title` on startup. + ### `--tls-cipher-list=list` +* `label` {string} **Default:** `'default'` +* `...data` {any} + +For a timer that was previously started by calling [`console.time()`][], prints +the elapsed time and other `data` arguments to `stdout`: + +```js +console.time('process'); +const value = expensiveProcess1(); // Returns 42 +console.timeLog('process', value); +// Prints "process: 365.227ms 42". +doExpensiveProcess2(value); +console.timeEnd('process'); +``` + ### console.trace([message][, ...args]) @@ -2421,20 +2421,20 @@ the `crypto`, `tls`, and `https` modules and are generally specific to OpenSSL. + https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_set_options.html + for detail. + https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_set_options.html. + https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_set_options.html. @@ -2568,6 +2568,7 @@ the `crypto`, `tls`, and `https` modules and are generally specific to OpenSSL.
Constant
SSL_OP_ALL Applies multiple bug workarounds within OpenSSL. See - https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_set_options.html for - detail.
SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION Allows legacy insecure renegotiation between OpenSSL and unpatched clients or servers. See - https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_set_options.html.
SSL_OP_CIPHER_SERVER_PREFERENCE Attempts to use the server's preferences instead of the client's when selecting a cipher. Behavior depends on protocol version. See - https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_set_options.html.
SSL_OP_CISCO_ANYCONNECT
### OpenSSL Engine Constants + @@ -2673,18 +2674,18 @@ the `crypto`, `tls`, and `https` modules and are generally specific to OpenSSL. - + - + - + @@ -2761,9 +2762,9 @@ the `crypto`, `tls`, and `https` modules and are generally specific to OpenSSL. [Crypto Constants]: #crypto_crypto_constants_1 [HTML 5.2]: https://www.w3.org/TR/html52/changes.html#features-removed [HTML5's `keygen` element]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/keygen -[NIST SP 800-131A]: http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-131Ar1.pdf -[NIST SP 800-132]: http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-132.pdf -[NIST SP 800-38D]: http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf +[NIST SP 800-131A]: https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-131Ar1.pdf +[NIST SP 800-132]: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-132.pdf +[NIST SP 800-38D]: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf [Nonce-Disrespecting Adversaries]: https://github.com/nonce-disrespect/nonce-disrespect [OpenSSL's SPKAC implementation]: https://www.openssl.org/docs/man1.1.0/apps/openssl-spkac.html [RFC 2412]: https://www.rfc-editor.org/rfc/rfc2412.txt diff --git a/doc/api/deprecations.md b/doc/api/deprecations.md index 5aee444a6d6883..d771a3e0e7a02d 100644 --- a/doc/api/deprecations.md +++ b/doc/api/deprecations.md @@ -1070,5 +1070,5 @@ The option `produceCachedData` has been deprecated. Use [alloc_unsafe_size]: buffer.html#buffer_class_method_buffer_allocunsafe_size [from_arraybuffer]: buffer.html#buffer_class_method_buffer_from_arraybuffer_byteoffset_length [from_string_encoding]: buffer.html#buffer_class_method_buffer_from_string_encoding -[NIST SP 800-38D]: http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf +[NIST SP 800-38D]: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf [`REPLServer.clearBufferedCommand()`]: repl.html#repl_replserver_clearbufferedcommand diff --git a/doc/api/errors.md b/doc/api/errors.md index 41a61c48c3166a..be22aa1527727d 100644 --- a/doc/api/errors.md +++ b/doc/api/errors.md @@ -127,12 +127,11 @@ exactly how errors raised by those methods are propagated. Most asynchronous methods exposed by the Node.js core API follow an idiomatic -pattern referred to as an _error-first callback_ (sometimes referred to as -a _Node.js style callback_). With this pattern, a callback function is passed -to the method as an argument. When the operation either completes or an error -is raised, the callback function is called with -the `Error` object (if any) passed as the first argument. If no error was -raised, the first argument will be passed as `null`. +pattern referred to as an _error-first callback_. With this pattern, a callback +function is passed to the method as an argument. When the operation either +completes or an error is raised, the callback function is called with the +`Error` object (if any) passed as the first argument. If no error was raised, +the first argument will be passed as `null`. ```js const fs = require('fs'); @@ -1015,6 +1014,11 @@ provided. The `Http2Session` closed with a non-zero error code. + +### ERR_HTTP2_SETTINGS_CANCEL + +The `Http2Session` settings canceled. + ### ERR_HTTP2_SOCKET_BOUND @@ -1630,6 +1634,11 @@ recommended to use 2048 bits or larger for stronger security. A TLS/SSL handshake timed out. In this case, the server must also abort the connection. + +### ERR_TLS_RENEGOTIATE + +An attempt to renegotiate the TLS session failed. + ### ERR_TLS_RENEGOTIATION_DISABLED diff --git a/doc/api/fs.md b/doc/api/fs.md index 2adbf6fb58cb7b..dafda61124ec00 100644 --- a/doc/api/fs.md +++ b/doc/api/fs.md @@ -45,9 +45,9 @@ try { } ``` -Note that there is no guaranteed ordering when using asynchronous methods. -So the following is prone to error because the `fs.stat()` operation may -complete before the `fs.rename()` operation. +There is no guaranteed ordering when using asynchronous methods. So the +following is prone to error because the `fs.stat()` operation may complete +before the `fs.rename()` operation: ```js fs.rename('/tmp/hello', '/tmp/world', (err) => { @@ -150,8 +150,8 @@ fs.open(Buffer.from('/open/some/file.txt'), 'r', (err, fd) => { }); ``` -*Note:* On Windows Node.js follows the concept of per-drive working directory. -This behavior can be observed when using a drive path without a backslash. For +On Windows, Node.js follows the concept of per-drive working directory. This +behavior can be observed when using a drive path without a backslash. For example `fs.readdirSync('c:\\')` can potentially return a different result than `fs.readdirSync('c:')`. For more information, see [this MSDN page][MSDN-Rel-Path]. @@ -278,9 +278,9 @@ eventually cause an application to crash. ## Threadpool Usage -Note that all file system APIs except `fs.FSWatcher()` and those that are -explicitly synchronous use libuv's threadpool, which can have surprising and -negative performance implications for some applications, see the +All file system APIs except `fs.FSWatcher()` and those that are explicitly +synchronous use libuv's threadpool, which can have surprising and negative +performance implications for some applications. See the [`UV_THREADPOOL_SIZE`][] documentation for more information. ## Class: fs.FSWatcher @@ -689,15 +689,13 @@ The times in the stat object have the following semantics: * `birthtime` "Birth Time" - Time of file creation. Set once when the file is created. On filesystems where birthtime is not available, this field may instead hold either the `ctime` or - `1970-01-01T00:00Z` (ie, unix epoch timestamp `0`). Note that this - value may be greater than `atime` or `mtime` in this case. On Darwin - and other FreeBSD variants, also set if the `atime` is explicitly - set to an earlier value than the current `birthtime` using the - utimes(2) system call. + `1970-01-01T00:00Z` (ie, unix epoch timestamp `0`). This value may be greater + than `atime` or `mtime` in this case. On Darwin and other FreeBSD variants, + also set if the `atime` is explicitly set to an earlier value than the current + `birthtime` using the utimes(2) system call. -Prior to Node.js v0.12, the `ctime` held the `birthtime` on Windows -systems. Note that as of v0.12, `ctime` is not "creation time", and -on Unix systems, it never was. +Prior to Node.js 0.12, the `ctime` held the `birthtime` on Windows systems. As +of 0.12, `ctime` is not "creation time", and on Unix systems, it never was. ## Class: fs.WriteStream + + + +* [About these Docs](documentation.html) +* [Usage & Example](synopsis.html) + +
+ +* [Assertion Testing](assert.html) +* [Async Hooks](async_hooks.html) +* [Buffer](buffer.html) +* [C++ Addons](addons.html) +* [C/C++ Addons - N-API](n-api.html) +* [Child Processes](child_process.html) +* [Cluster](cluster.html) +* [Command Line Options](cli.html) +* [Console](console.html) +* [Crypto](crypto.html) +* [Debugger](debugger.html) +* [Deprecated APIs](deprecations.html) +* [DNS](dns.html) +* [Domain](domain.html) +* [ECMAScript Modules](esm.html) +* [Errors](errors.html) +* [Events](events.html) +* [File System](fs.html) +* [Globals](globals.html) +* [HTTP](http.html) +* [HTTP/2](http2.html) +* [HTTPS](https.html) +* [Inspector](inspector.html) +* [Internationalization](intl.html) +* [Modules](modules.html) +* [Net](net.html) +* [OS](os.html) +* [Path](path.html) +* [Performance Hooks](perf_hooks.html) +* [Process](process.html) +* [Punycode](punycode.html) +* [Query Strings](querystring.html) +* [Readline](readline.html) +* [REPL](repl.html) +* [Stream](stream.html) +* [String Decoder](string_decoder.html) +* [Timers](timers.html) +* [TLS/SSL](tls.html) +* [Trace Events](tracing.html) +* [TTY](tty.html) +* [UDP/Datagram](dgram.html) +* [URL](url.html) +* [Utilities](util.html) +* [V8](v8.html) +* [VM](vm.html) +* [Worker Threads](worker_threads.html) +* [ZLIB](zlib.html) + +
+ +* [GitHub Repo & Issue Tracker](https://github.com/nodejs/node) +* [Mailing List](https://groups.google.com/forum/#!forum/nodejs) diff --git a/doc/api/inspector.md b/doc/api/inspector.md index 7b1adcc47b32a0..92fcb0e03f89e4 100644 --- a/doc/api/inspector.md +++ b/doc/api/inspector.md @@ -12,6 +12,21 @@ It can be accessed using: const inspector = require('inspector'); ``` +## inspector.close() + +Deactivate the inspector. Blocks until there are no active connections. + +## inspector.console + +* {Object} An object to send messages to the remote inspector console. + +```js +require('inspector').console.log('a message'); +``` + +The inspector console does not have API parity with Node.js +console. + ## inspector.open([port[, host[, wait]]]) * `port` {number} Port to listen on for inspector connections. Optional. @@ -28,11 +43,7 @@ started. If wait is `true`, will block until a client has connected to the inspect port and flow control has been passed to the debugger client. -### inspector.close() - -Deactivate the inspector. Blocks until there are no active connections. - -### inspector.url() +## inspector.url() * Returns: {string|undefined} @@ -101,6 +112,16 @@ Connects a session to the inspector back-end. An exception will be thrown if there is already a connected session established either through the API or by a front-end connected to the Inspector WebSocket port. +### session.disconnect() + + +Immediately close the session. All pending message callbacks will be called +with an error. [`session.connect()`] will need to be called to be able to send +messages again. Reconnected session will lose all inspector state, such as +enabled agents or configured breakpoints. + ### session.post(method[, params][, callback]) - -Immediately close the session. All pending message callbacks will be called -with an error. [`session.connect()`] will need to be called to be able to send -messages again. Reconnected session will lose all inspector state, such as -enabled agents or configured breakpoints. - ## Example usage ### CPU Profiler diff --git a/doc/api/intl.md b/doc/api/intl.md index fc10bf9dda2e82..cce6661521c832 100644 --- a/doc/api/intl.md +++ b/doc/api/intl.md @@ -209,7 +209,7 @@ to be helpful: [BUILDING.md#full-icu]: https://github.com/nodejs/node/blob/master/BUILDING.md#build-with-full-icu-support-all-locales-supported-by-icu [ECMA-262]: https://tc39.github.io/ecma262/ [ECMA-402]: https://tc39.github.io/ecma402/ -[ICU]: http://icu-project.org/ +[ICU]: http://site.icu-project.org/ [REPL]: repl.html#repl_repl [Test262]: https://github.com/tc39/test262/tree/master/test/intl402 [WHATWG URL parser]: url.html#url_the_whatwg_url_api diff --git a/doc/api/modules.md b/doc/api/modules.md index c46b30560da81f..71de4e519e119a 100644 --- a/doc/api/modules.md +++ b/doc/api/modules.md @@ -693,7 +693,7 @@ added: v0.1.16 * {module[]} -The module objects required by this one. +The module objects required for the first time by this one. ### module.exports ```C napi_status @@ -424,6 +461,7 @@ TypeError [ERR_ERROR_1] #### napi_throw ```C NAPI_EXTERN napi_status napi_throw(napi_env env, napi_value error); @@ -438,6 +476,7 @@ This API throws the JavaScript value provided. #### napi_throw_error ```C NAPI_EXTERN napi_status napi_throw_error(napi_env env, @@ -456,6 +495,7 @@ This API throws a JavaScript `Error` with the text provided. #### napi_throw_type_error ```C NAPI_EXTERN napi_status napi_throw_type_error(napi_env env, @@ -474,6 +514,7 @@ This API throws a JavaScript `TypeError` with the text provided. #### napi_throw_range_error ```C NAPI_EXTERN napi_status napi_throw_range_error(napi_env env, @@ -492,6 +533,7 @@ This API throws a JavaScript `RangeError` with the text provided. #### napi_is_error ```C NAPI_EXTERN napi_status napi_is_error(napi_env env, @@ -510,6 +552,7 @@ This API queries a `napi_value` to check if it represents an error object. #### napi_create_error ```C NAPI_EXTERN napi_status napi_create_error(napi_env env, @@ -531,6 +574,7 @@ This API returns a JavaScript `Error` with the text provided. #### napi_create_type_error ```C NAPI_EXTERN napi_status napi_create_type_error(napi_env env, @@ -552,6 +596,7 @@ This API returns a JavaScript `TypeError` with the text provided. #### napi_create_range_error ```C NAPI_EXTERN napi_status napi_create_range_error(napi_env env, @@ -573,6 +618,7 @@ This API returns a JavaScript `RangeError` with the text provided. #### napi_get_and_clear_last_exception ```C napi_status napi_get_and_clear_last_exception(napi_env env, @@ -591,6 +637,7 @@ This API can be called even if there is a pending JavaScript exception. #### napi_is_exception_pending ```C napi_status napi_is_exception_pending(napi_env env, bool* result); @@ -608,7 +655,9 @@ This API can be called even if there is a pending JavaScript exception. #### napi_fatal_exception + ```C napi_status napi_fatal_exception(napi_env env, napi_value err); ``` @@ -627,6 +676,7 @@ thrown to immediately terminate the process. #### napi_fatal_error ```C NAPI_NO_RETURN void napi_fatal_error(const char* location, @@ -740,6 +790,7 @@ can only be called once. #### napi_open_handle_scope ```C NAPI_EXTERN napi_status napi_open_handle_scope(napi_env env, @@ -755,6 +806,7 @@ This API open a new scope. #### napi_close_handle_scope ```C NAPI_EXTERN napi_status napi_close_handle_scope(napi_env env, @@ -773,6 +825,7 @@ This API can be called even if there is a pending JavaScript exception. #### napi_open_escapable_handle_scope ```C NAPI_EXTERN napi_status @@ -790,6 +843,7 @@ to the outer scope. #### napi_close_escapable_handle_scope ```C NAPI_EXTERN napi_status @@ -809,6 +863,7 @@ This API can be called even if there is a pending JavaScript exception. #### napi_escape_handle ```C napi_status napi_escape_handle(napi_env env, @@ -874,6 +929,7 @@ individual count. #### napi_create_reference ```C NAPI_EXTERN napi_status napi_create_reference(napi_env env, @@ -896,6 +952,7 @@ to the `Object` passed in. #### napi_delete_reference ```C NAPI_EXTERN napi_status napi_delete_reference(napi_env env, napi_ref ref); @@ -913,6 +970,7 @@ This API can be called even if there is a pending JavaScript exception. #### napi_reference_ref ```C NAPI_EXTERN napi_status napi_reference_ref(napi_env env, @@ -931,6 +989,7 @@ passed in and returns the resulting reference count. #### napi_reference_unref ```C NAPI_EXTERN napi_status napi_reference_unref(napi_env env, @@ -949,6 +1008,7 @@ passed in and returns the resulting reference count. #### napi_get_reference_value ```C NAPI_EXTERN napi_status napi_get_reference_value(napi_env env, @@ -982,7 +1042,9 @@ should be freed up. #### napi_add_env_cleanup_hook + ```C NODE_EXTERN napi_status napi_add_env_cleanup_hook(napi_env env, void (*fun)(void* arg), @@ -1007,7 +1069,9 @@ is being torn down anyway. #### napi_remove_env_cleanup_hook + ```C NAPI_EXTERN napi_status napi_remove_env_cleanup_hook(napi_env env, void (*fun)(void* arg), @@ -1160,6 +1224,7 @@ typedef enum { napi_object, napi_function, napi_external, + napi_bigint, } napi_valuetype; ``` @@ -1185,6 +1250,8 @@ typedef enum { napi_uint32_array, napi_float32_array, napi_float64_array, + napi_bigint64_array, + napi_biguint64_array, } napi_typedarray_type; ``` @@ -1196,6 +1263,7 @@ Elements of this enum correspond to #### napi_create_array ```C napi_status napi_create_array(napi_env env, napi_value* result) @@ -1213,6 +1281,7 @@ JavaScript arrays are described in #### napi_create_array_with_length ```C napi_status napi_create_array_with_length(napi_env env, @@ -1241,6 +1310,7 @@ JavaScript arrays are described in #### napi_create_arraybuffer ```C napi_status napi_create_arraybuffer(napi_env env, @@ -1272,6 +1342,7 @@ JavaScript `ArrayBuffer` objects are described in #### napi_create_buffer ```C napi_status napi_create_buffer(napi_env env, @@ -1293,6 +1364,7 @@ fully-supported data structure, in most cases using a `TypedArray` will suffice. #### napi_create_buffer_copy ```C napi_status napi_create_buffer_copy(napi_env env, @@ -1318,6 +1390,7 @@ structure, in most cases using a `TypedArray` will suffice. #### napi_create_external ```C napi_status napi_create_external(napi_env env, @@ -1350,6 +1423,7 @@ an external value yields `napi_external`. #### napi_create_external_arraybuffer ```C napi_status @@ -1384,6 +1458,7 @@ JavaScript `ArrayBuffer`s are described in #### napi_create_external_buffer ```C napi_status napi_create_external_buffer(napi_env env, @@ -1412,42 +1487,10 @@ structure, in most cases using a `TypedArray` will suffice. For Node.js >=4 `Buffers` are `Uint8Array`s. -#### napi_create_function - -```C -napi_status napi_create_function(napi_env env, - const char* utf8name, - size_t length, - napi_callback cb, - void* data, - napi_value* result) -``` - -- `[in] env`: The environment that the API is invoked under. -- `[in] utf8name`: A string representing the name of the function encoded as -UTF8. -- `[in] length`: The length of the `utf8name` in bytes, or -`NAPI_AUTO_LENGTH` if it is null-terminated. -- `[in] cb`: A function pointer to the native function to be invoked when the -created function is invoked from JavaScript. -- `[in] data`: Optional arbitrary context data to be passed into the native -function when it is invoked. -- `[out] result`: A `napi_value` representing a JavaScript function. - -Returns `napi_ok` if the API succeeded. - -This API returns an N-API value corresponding to a JavaScript `Function` object. -It's used to wrap native functions so that they can be invoked from JavaScript. - -JavaScript `Function`s are described in -[Section 19.2](https://tc39.github.io/ecma262/#sec-function-objects) -of the ECMAScript Language Specification. - #### napi_create_object ```C napi_status napi_create_object(napi_env env, napi_value* result) @@ -1468,6 +1511,7 @@ ECMAScript Language Specification. #### napi_create_symbol ```C napi_status napi_create_symbol(napi_env env, @@ -1491,6 +1535,7 @@ of the ECMAScript Language Specification. #### napi_create_typedarray ```C napi_status napi_create_typedarray(napi_env env, @@ -1526,6 +1571,7 @@ JavaScript `TypedArray` objects are described in #### napi_create_dataview ```C @@ -1560,6 +1606,7 @@ JavaScript `DataView` objects are described in #### napi_create_int32 ```C napi_status napi_create_int32(napi_env env, int32_t value, napi_value* result) @@ -1580,6 +1627,7 @@ The JavaScript `Number` type is described in #### napi_create_uint32 ```C napi_status napi_create_uint32(napi_env env, uint32_t value, napi_value* result) @@ -1600,6 +1648,7 @@ The JavaScript `Number` type is described in #### napi_create_int64 ```C napi_status napi_create_int64(napi_env env, int64_t value, napi_value* result) @@ -1626,6 +1675,7 @@ outside the range of #### napi_create_double ```C napi_status napi_create_double(napi_env env, double value, napi_value* result) @@ -1643,9 +1693,82 @@ This API is used to convert from the C `double` type to the JavaScript The JavaScript `Number` type is described in [Section 6.1.6][] of the ECMAScript Language Specification. +#### napi_create_bigint_int64 + + +> Stability: 1 - Experimental + +```C +napi_status napi_create_bigint_int64(napi_env env, + int64_t value, + napi_value* result); +``` + +- `[in] env`: The environment that the API is invoked under. +- `[in] value`: Integer value to be represented in JavaScript. +- `[out] result`: A `napi_value` representing a JavaScript `BigInt`. + +Returns `napi_ok` if the API succeeded. + +This API converts the C `int64_t` type to the JavaScript `BigInt` type. + +#### napi_create_bigint_uint64 + + +> Stability: 1 - Experimental + +```C +napi_status napi_create_bigint_uint64(napi_env env, + uint64_t vaue, + napi_value* result); +``` + +- `[in] env`: The environment that the API is invoked under. +- `[in] value`: Unsigned integer value to be represented in JavaScript. +- `[out] result`: A `napi_value` representing a JavaScript `BigInt`. + +Returns `napi_ok` if the API succeeded. + +This API converts the C `uint64_t` type to the JavaScript `BigInt` type. + +#### napi_create_bigint_words + + +> Stability: 1 - Experimental + +```C +napi_status napi_create_bigint_words(napi_env env, + int sign_bit, + size_t word_count, + const uint64_t* words, + napi_value* result); +``` + +- `[in] env`: The environment that the API is invoked under. +- `[in] sign_bit`: Determines if the resulting `BigInt` will be positive or + negative. +- `[in] word_count`: The length of the `words` array. +- `[in] words`: An array of `uint64_t` little-endian 64-bit words. +- `[out] result`: A `napi_value` representing a JavaScript `BigInt`. + +Returns `napi_ok` if the API succeeded. + +This API converts an array of unsigned 64-bit words into a single `BigInt` +value. + +The resulting `BigInt` is calculated as: (–1)`sign_bit` (`words[0]` +× (264)0 + `words[1]` × (264)1 + …) + #### napi_create_string_latin1 ```C napi_status napi_create_string_latin1(napi_env env, @@ -1671,6 +1794,7 @@ The JavaScript `String` type is described in #### napi_create_string_utf16 ```C napi_status napi_create_string_utf16(napi_env env, @@ -1696,6 +1820,7 @@ The JavaScript `String` type is described in #### napi_create_string_utf8 ```C napi_status napi_create_string_utf8(napi_env env, @@ -1722,6 +1847,7 @@ The JavaScript `String` type is described in #### napi_get_array_length ```C napi_status napi_get_array_length(napi_env env, @@ -1745,6 +1871,7 @@ of the ECMAScript Language Specification. #### napi_get_arraybuffer_info ```C napi_status napi_get_arraybuffer_info(napi_env env, @@ -1774,6 +1901,7 @@ trigger a GC. #### napi_get_buffer_info ```C napi_status napi_get_buffer_info(napi_env env, @@ -1798,6 +1926,7 @@ lifetime is not guaranteed if it's managed by the VM. #### napi_get_prototype ```C napi_status napi_get_prototype(napi_env env, @@ -1816,6 +1945,7 @@ Returns `napi_ok` if the API succeeded. #### napi_get_typedarray_info ```C napi_status napi_get_typedarray_info(napi_env env, @@ -1852,6 +1982,7 @@ is managed by the VM. #### napi_get_dataview_info ```C @@ -1879,6 +2010,7 @@ This API returns various properties of a `DataView`. #### napi_get_value_bool ```C napi_status napi_get_value_bool(napi_env env, napi_value value, bool* result) @@ -1898,6 +2030,7 @@ This API returns the C boolean primitive equivalent of the given JavaScript #### napi_get_value_double ```C napi_status napi_get_value_double(napi_env env, @@ -1916,9 +2049,96 @@ in it returns `napi_number_expected`. This API returns the C double primitive equivalent of the given JavaScript `Number`. +#### napi_get_value_bigint_int64 + + +> Stability: 1 - Experimental + +```C +napi_status napi_get_value_bigint_int64(napi_env env, + napi_value value, + int64_t* result, + bool* lossless); +``` + +- `[in] env`: The environment that the API is invoked under +- `[in] value`: `napi_value` representing JavaScript `BigInt`. +- `[out] result`: C `int64_t` primitive equivalent of the given JavaScript + `BigInt`. +- `[out] lossless`: Indicates whether the `BigInt` value was converted + losslessly. + +Returns `napi_ok` if the API succeeded. If a non-`BigInt` is passed in it +returns `napi_bigint_expected`. + +This API returns the C `int64_t` primitive equivalent of the given JavaScript +`BigInt`. If needed it will truncate the value, setting `lossless` to `false`. + + +#### napi_get_value_bigint_uint64 + + +> Stability: 1 - Experimental + +```C +napi_status napi_get_value_bigint_uint64(napi_env env, + napi_value value, + uint64_t* result, + bool* lossless); +``` + +- `[in] env`: The environment that the API is invoked under. +- `[in] value`: `napi_value` representing JavaScript `BigInt`. +- `[out] result`: C `uint64_t` primitive equivalent of the given JavaScript + `BigInt`. +- `[out] lossless`: Indicates whether the `BigInt` value was converted + losslessly. + +Returns `napi_ok` if the API succeeded. If a non-`BigInt` is passed in it +returns `napi_bigint_expected`. + +This API returns the C `uint64_t` primitive equivalent of the given JavaScript +`BigInt`. If needed it will truncate the value, setting `lossless` to `false`. + + +#### napi_get_value_bigint_words + + +> Stability: 1 - Experimental + +```C +napi_status napi_get_value_bigint_words(napi_env env, + napi_value value, + size_t* word_count, + int* sign_bit, + uint64_t* words); +``` + +- `[in] env`: The environment that the API is invoked under. +- `[in] value`: `napi_value` representing JavaScript `BigInt`. +- `[out] sign_bit`: Integer representing if the JavaScript `BigInt` is positive + or negative. +- `[in/out] word_count`: Must be initialized to the length of the `words` + array. Upon return, it will be set to the actual number of words that + would be needed to store this `BigInt`. +- `[out] words`: Pointer to a pre-allocated 64-bit word array. + +Returns `napi_ok` if the API succeeded. + +This API converts a single `BigInt` value into a sign bit, 64-bit little-endian +array, and the number of elements in the array. `sign_bit` and `words` may be +both set to `NULL`, in order to get only `word_count`. + #### napi_get_value_external ```C napi_status napi_get_value_external(napi_env env, @@ -1939,6 +2159,7 @@ This API retrieves the external data pointer that was previously passed to #### napi_get_value_int32 ```C napi_status napi_get_value_int32(napi_env env, @@ -1967,6 +2188,7 @@ result to zero. #### napi_get_value_int64 ```C napi_status napi_get_value_int64(napi_env env, @@ -1997,6 +2219,7 @@ result to zero. #### napi_get_value_string_latin1 ```C napi_status napi_get_value_string_latin1(napi_env env, @@ -2024,6 +2247,7 @@ in. #### napi_get_value_string_utf8 ```C napi_status napi_get_value_string_utf8(napi_env env, @@ -2050,6 +2274,7 @@ This API returns the UTF8-encoded string corresponding the value passed in. #### napi_get_value_string_utf16 ```C napi_status napi_get_value_string_utf16(napi_env env, @@ -2076,6 +2301,7 @@ This API returns the UTF16-encoded string corresponding the value passed in. #### napi_get_value_uint32 ```C napi_status napi_get_value_uint32(napi_env env, @@ -2098,6 +2324,7 @@ This API returns the C primitive equivalent of the given `napi_value` as a #### napi_get_boolean ```C napi_status napi_get_boolean(napi_env env, bool value, napi_value* result) @@ -2116,6 +2343,7 @@ represent the given boolean value. #### napi_get_global ```C napi_status napi_get_global(napi_env env, napi_value* result) @@ -2131,6 +2359,7 @@ This API returns the `global` object. #### napi_get_null ```C napi_status napi_get_null(napi_env env, napi_value* result) @@ -2146,6 +2375,7 @@ This API returns the `null` object. #### napi_get_undefined ```C napi_status napi_get_undefined(napi_env env, napi_value* result) @@ -2174,6 +2404,7 @@ These APIs support doing one of the following: ### napi_coerce_to_bool ```C napi_status napi_coerce_to_bool(napi_env env, @@ -2195,6 +2426,7 @@ This API can be re-entrant if getters are defined on the passed-in `Object`. ### napi_coerce_to_number ```C napi_status napi_coerce_to_number(napi_env env, @@ -2216,6 +2448,7 @@ This API can be re-entrant if getters are defined on the passed-in `Object`. ### napi_coerce_to_object ```C napi_status napi_coerce_to_object(napi_env env, @@ -2237,6 +2470,7 @@ This API can be re-entrant if getters are defined on the passed-in `Object`. ### napi_coerce_to_string ```C napi_status napi_coerce_to_string(napi_env env, @@ -2258,6 +2492,7 @@ This API can be re-entrant if getters are defined on the passed-in `Object`. ### napi_typeof ```C napi_status napi_typeof(napi_env env, napi_value value, napi_valuetype* result) @@ -2279,6 +2514,7 @@ If `value` has a type that is invalid, an error is returned. ### napi_instanceof ```C napi_status napi_instanceof(napi_env env, @@ -2304,6 +2540,7 @@ of the ECMAScript Language Specification. ### napi_is_array ```C napi_status napi_is_array(napi_env env, napi_value value, bool* result) @@ -2322,6 +2559,7 @@ of the ECMAScript Language Specification. ### napi_is_arraybuffer ```C napi_status napi_is_arraybuffer(napi_env env, napi_value value, bool* result) @@ -2338,6 +2576,7 @@ This API checks if the `Object` passed in is an array buffer. ### napi_is_buffer ```C napi_status napi_is_buffer(napi_env env, napi_value value, bool* result) @@ -2355,6 +2594,7 @@ This API checks if the `Object` passed in is a buffer. ### napi_is_error ```C napi_status napi_is_error(napi_env env, napi_value value, bool* result) @@ -2371,6 +2611,7 @@ This API checks if the `Object` passed in is an `Error`. ### napi_is_typedarray ```C napi_status napi_is_typedarray(napi_env env, napi_value value, bool* result) @@ -2387,6 +2628,7 @@ This API checks if the `Object` passed in is a typed array. ### napi_is_dataview ```C @@ -2404,6 +2646,7 @@ This API checks if the `Object` passed in is a `DataView`. ### napi_strict_equals ```C napi_status napi_strict_equals(napi_env env, @@ -2636,6 +2879,7 @@ this function is invoked. #### napi_get_property_names ```C napi_status napi_get_property_names(napi_env env, @@ -2659,6 +2903,7 @@ included. #### napi_set_property ```C napi_status napi_set_property(napi_env env, @@ -2679,6 +2924,7 @@ This API set a property on the `Object` passed in. #### napi_get_property ```C napi_status napi_get_property(napi_env env, @@ -2699,6 +2945,7 @@ This API gets the requested property from the `Object` passed in. #### napi_has_property ```C napi_status napi_has_property(napi_env env, @@ -2719,6 +2966,7 @@ This API checks if the `Object` passed in has the named property. #### napi_delete_property ```C napi_status napi_delete_property(napi_env env, @@ -2740,6 +2988,7 @@ This API attempts to delete the `key` own property from `object`. #### napi_has_own_property ```C napi_status napi_has_own_property(napi_env env, @@ -2762,6 +3011,7 @@ any conversion between data types. #### napi_set_named_property ```C napi_status napi_set_named_property(napi_env env, @@ -2783,6 +3033,7 @@ created from the string passed in as `utf8Name`. #### napi_get_named_property ```C napi_status napi_get_named_property(napi_env env, @@ -2804,6 +3055,7 @@ created from the string passed in as `utf8Name`. #### napi_has_named_property ```C napi_status napi_has_named_property(napi_env env, @@ -2825,6 +3077,7 @@ created from the string passed in as `utf8Name`. #### napi_set_element ```C napi_status napi_set_element(napi_env env, @@ -2845,6 +3098,7 @@ This API sets and element on the `Object` passed in. #### napi_get_element ```C napi_status napi_get_element(napi_env env, @@ -2865,6 +3119,7 @@ This API gets the element at the requested index. #### napi_has_element ```C napi_status napi_has_element(napi_env env, @@ -2886,6 +3141,7 @@ requested index. #### napi_delete_element ```C napi_status napi_delete_element(napi_env env, @@ -2907,6 +3163,7 @@ This API attempts to delete the specified `index` from `object`. #### napi_define_properties ```C napi_status napi_define_properties(napi_env env, @@ -2950,6 +3207,7 @@ function. ### napi_call_function ```C napi_status napi_call_function(napi_env env, @@ -3016,10 +3274,12 @@ if (status != napi_ok) return; ### napi_create_function ```C napi_status napi_create_function(napi_env env, const char* utf8name, + size_t length, napi_callback cb, void* data, napi_value* result); @@ -3028,6 +3288,8 @@ napi_status napi_create_function(napi_env env, - `[in] env`: The environment that the API is invoked under. - `[in] utf8Name`: The name of the function encoded as UTF8. This is visible within JavaScript as the new function object's `name` property. +- `[in] length`: The length of the `utf8name` in bytes, or +`NAPI_AUTO_LENGTH` if it is null-terminated. - `[in] cb`: The native function which should be called when this function object is invoked. - `[in] data`: User-provided data context. This will be passed back into the @@ -3076,13 +3338,17 @@ const myaddon = require('./addon'); myaddon.sayHello(); ``` -The string passed to require is not necessarily the name passed into -`NAPI_MODULE` in the earlier snippet but the name of the target in `binding.gyp` +The string passed to `require()` is the name of the target in `binding.gyp` responsible for creating the `.node` file. +JavaScript `Function`s are described in +[Section 19.2](https://tc39.github.io/ecma262/#sec-function-objects) +of the ECMAScript Language Specification. + ### napi_get_cb_info ```C napi_status napi_get_cb_info(napi_env env, @@ -3113,6 +3379,7 @@ call like the arguments and the `this` pointer from a given callback info. ### napi_get_new_target ```C napi_status napi_get_new_target(napi_env env, @@ -3132,6 +3399,7 @@ callback is not a constructor call, the result is `NULL`. ### napi_new_instance ```C napi_status napi_new_instance(napi_env env, @@ -3227,6 +3495,7 @@ The reference must be freed once it is no longer needed. ### napi_define_class ```C napi_status napi_define_class(napi_env env, @@ -3283,6 +3552,7 @@ reference count is kept >= 1. ### napi_wrap ```C napi_status napi_wrap(napi_env env, @@ -3343,6 +3613,7 @@ first. ### napi_unwrap ```C napi_status napi_unwrap(napi_env env, @@ -3368,6 +3639,7 @@ then by calling `napi_unwrap()` on the wrapper object. ### napi_remove_wrap ```C napi_status napi_remove_wrap(napi_env env, @@ -3444,6 +3716,7 @@ callback invocation, even when it was cancelled. ### napi_create_async_work ```C napi_status napi_delete_async_work(napi_env env, @@ -3509,6 +3783,7 @@ This API can be called even if there is a pending JavaScript exception. ### napi_queue_async_work ```C napi_status napi_queue_async_work(napi_env env, @@ -3526,6 +3801,7 @@ for execution. ### napi_cancel_async_work ```C napi_status napi_cancel_async_work(napi_env env, @@ -3555,6 +3831,7 @@ the runtime. ### napi_async_init ```C napi_status napi_async_init(napi_env env, @@ -3576,6 +3853,7 @@ Returns `napi_ok` if the API succeeded. ### napi_async_destroy ```C napi_status napi_async_destroy(napi_env env, @@ -3592,6 +3870,7 @@ This API can be called even if there is a pending JavaScript exception. ### napi_make_callback ```C NAPI_EXTERN napi_status napi_open_callback_scope(napi_env env, @@ -3663,6 +3943,7 @@ the required scope. ### napi_close_callback_scope ```C NAPI_EXTERN napi_status napi_close_callback_scope(napi_env env, @@ -3678,6 +3959,7 @@ This API can be called even if there is a pending JavaScript exception. ### napi_get_node_version ```C @@ -3706,6 +3988,7 @@ The returned buffer is statically allocated and does not need to be freed. ### napi_get_version ```C napi_status napi_get_version(napi_env env, @@ -3736,6 +4019,7 @@ support it: ### napi_adjust_external_memory ```C NAPI_EXTERN napi_status napi_adjust_external_memory(napi_env env, @@ -3815,6 +4099,7 @@ deferred = NULL; ### napi_create_promise ```C napi_status napi_create_promise(napi_env env, @@ -3835,6 +4120,7 @@ This API creates a deferred object and a JavaScript promise. ### napi_resolve_deferred ```C napi_status napi_resolve_deferred(napi_env env, @@ -3858,6 +4144,7 @@ The deferred object is freed upon successful completion. ### napi_reject_deferred ```C napi_status napi_reject_deferred(napi_env env, @@ -3881,6 +4168,7 @@ The deferred object is freed upon successful completion. ### napi_is_promise ```C napi_status napi_is_promise(napi_env env, @@ -3901,6 +4189,7 @@ underlying JavaScript engine. ### napi_run_script ```C NAPI_EXTERN napi_status napi_run_script(napi_env env, @@ -3919,7 +4208,10 @@ a specific `napi_env`. ### napi_get_uv_event_loop ```C NAPI_EXTERN napi_status napi_get_uv_event_loop(napi_env env, diff --git a/doc/api/os.md b/doc/api/os.md index 8db4904d29a489..b7bd246f97dbfd 100644 --- a/doc/api/os.md +++ b/doc/api/os.md @@ -442,7 +442,7 @@ The following signal constants are exported by `os.constants.signals`: + ((Ctrl+C)). @@ -855,9 +855,9 @@ The following error constants are exported by `os.constants.errno`: - + @@ -1114,7 +1114,8 @@ The following error codes are specific to the Windows operating system: - + @@ -1197,8 +1198,8 @@ information. - + diff --git a/doc/api/path.md b/doc/api/path.md index c6960c7ee47109..887928dd1a46cb 100644 --- a/doc/api/path.md +++ b/doc/api/path.md @@ -564,4 +564,4 @@ of the `path` methods. [`path.sep`]: #path_path_sep [`path.win32`]: #path_path_win32 [MSDN-Rel-Path]: https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#fully-qualified-vs-relative-paths -[namespace-prefixed path]: https://msdn.microsoft.com/library/windows/desktop/aa365247(v=vs.85).aspx#namespaces +[namespace-prefixed path]: https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#namespaces diff --git a/doc/api/process.md b/doc/api/process.md index ebd84afd698872..dd0314cf6c3c1d 100644 --- a/doc/api/process.md +++ b/doc/api/process.md @@ -151,9 +151,13 @@ added: v0.1.18 The `'uncaughtException'` event is emitted when an uncaught JavaScript exception bubbles all the way back to the event loop. By default, Node.js -handles such exceptions by printing the stack trace to `stderr` and exiting. +handles such exceptions by printing the stack trace to `stderr` and exiting +with code 1, overriding any previously set [`process.exitCode`][]. Adding a handler for the `'uncaughtException'` event overrides this default -behavior. +behavior. You may also change the [`process.exitCode`][] in +`'uncaughtException'` handler which will result in process exiting with +provided exit code, otherwise in the presence of such handler the process will +exit with 0. The listener function is called with the `Error` object passed as the only argument. @@ -1159,6 +1163,9 @@ added: v0.7.6 * `time` {integer[]} The result of a previous call to `process.hrtime()` * Returns: {integer[]} +This is the legacy version of [`process.hrtime.bigint()`][] +before `bigint` was introduced in JavaScript. + The `process.hrtime()` method returns the current high-resolution real time in a `[seconds, nanoseconds]` tuple `Array`, where `nanoseconds` is the remaining part of the real time that can't be represented in second precision. @@ -1187,6 +1194,33 @@ setTimeout(() => { }, 1000); ``` +## process.hrtime.bigint() + + +* Returns: {bigint} + +The `bigint` version of the [`process.hrtime()`][] method returning the +current high-resolution real time in a `bigint`. + +Unlike [`process.hrtime()`][], it does not support an additional `time` +argument since the difference can just be computed directly +by subtraction of the two `bigint`s. + +```js +const start = process.hrtime.bigint(); +// 191051479007711n + +setTimeout(() => { + const end = process.hrtime.bigint(); + // 191052633396993n + + console.log(`Benchmark took ${end - start} nanoseconds`); + // Benchmark took 1154389282 nanoseconds +}, 1000); +``` + ## process.initgroups(user, extraGroup) diff --git a/doc/api/worker_threads.md b/doc/api/worker_threads.md index d20fd0fce57c80..8b301f42a87620 100644 --- a/doc/api/worker_threads.md +++ b/doc/api/worker_threads.md @@ -286,7 +286,7 @@ For example: ```js const assert = require('assert'); const { - Worker, MessageChannel, MessagePort, isMainThread + Worker, MessageChannel, MessagePort, isMainThread, parentPort } = require('worker_threads'); if (isMainThread) { const worker = new Worker(__filename); @@ -296,7 +296,7 @@ if (isMainThread) { console.log('received:', value); }); } else { - require('worker_threads').once('message', (value) => { + parentPort.once('message', (value) => { assert(value.hereIsYourPort instanceof MessagePort); value.hereIsYourPort.postMessage('the worker is sending this'); value.hereIsYourPort.close(); diff --git a/doc/api_assets/style.css b/doc/api_assets/style.css index b4da3375d5b521..f59f3770048097 100644 --- a/doc/api_assets/style.css +++ b/doc/api_assets/style.css @@ -504,6 +504,11 @@ th > *:last-child, td > *:last-child { visibility: hidden; } +.github_icon { + vertical-align: middle; + margin: -2px 3px 0 0; +} + @media only screen and (max-width: 1024px) { #content { overflow: visible; diff --git a/doc/changelogs/CHANGELOG_V10.md b/doc/changelogs/CHANGELOG_V10.md index 1973dfd3f3ee97..bf90f2a09d8e40 100644 --- a/doc/changelogs/CHANGELOG_V10.md +++ b/doc/changelogs/CHANGELOG_V10.md @@ -9,6 +9,7 @@
RSA_PSS_SALTLEN_DIGESTSets the salt length for `RSA_PKCS1_PSS_PADDING` to the digest size - when signing or verifying.Sets the salt length for RSA_PKCS1_PSS_PADDING to the + digest size when signing or verifying.
RSA_PSS_SALTLEN_MAX_SIGNSets the salt length for `RSA_PKCS1_PSS_PADDING` to the maximum - permissible value when signing data.Sets the salt length for RSA_PKCS1_PSS_PADDING to the + maximum permissible value when signing data.
RSA_PSS_SALTLEN_AUTOCauses the salt length for `RSA_PKCS1_PSS_PADDING` to be determined - automatically when verifying a signature.Causes the salt length for RSA_PKCS1_PSS_PADDING to be + determined automatically when verifying a signature.
POINT_CONVERSION_COMPRESSED
SIGINT Sent to indicate when a user wishes to interrupt a process - (`(Ctrl+C)`).
SIGQUIT
EOPNOTSUPPIndicates that an operation is not supported on the socket. - Note that while `ENOTSUP` and `EOPNOTSUPP` have the same value on Linux, - according to POSIX.1 these error values should be distinct.)Indicates that an operation is not supported on the socket. Note that + while ENOTSUP and EOPNOTSUPP have the same value + on Linux, according to POSIX.1 these error values should be distinct.)
EOVERFLOW
WSAVERNOTSUPPORTEDIndicates that the `winsock.dll` version is out of range.Indicates that the winsock.dll version is out of + range.
WSANOTINITIALISED
RTLD_LOCALThe converse of `RTLD_GLOBAL`. This is the default behavior if neither - flag is specified.The converse of RTLD_GLOBAL. This is the default behavior + if neither flag is specified.
RTLD_DEEPBIND
+10.7.0
10.6.0
10.5.0
10.4.1
@@ -34,6 +35,150 @@ * [io.js](CHANGELOG_IOJS.md) * [Archive](CHANGELOG_ARCHIVE.md) + +## 2018-07-18, Version 10.7.0 (Current), @targos + +### Notable Changes + +* **console**: + * The `console.timeLog()` method has been implemented. [#21312](https://github.com/nodejs/node/pull/21312) +* **deps**: + * Upgrade to libuv 1.22.0. [#21731](https://github.com/nodejs/node/pull/21731) + * Upgrade to ICU 62.1 (Unicode 11, CLDR 33.1). [#21728](https://github.com/nodejs/node/pull/21728) +* **http**: + * Added support for passing both `timeout` and `agent` options to + `http.request`. [#21204](https://github.com/nodejs/node/pull/21204) +* **inspector**: + * Expose the original console API in `require('inspector').console`. [#21659](https://github.com/nodejs/node/pull/21659) +* **napi**: + * Added experimental support for functions dealing with bigint numbers. [#21226](https://github.com/nodejs/node/pull/21226) +* **process**: + * The `process.hrtime.bigint()` method has been implemented. [#21256](https://github.com/nodejs/node/pull/21256) + * Added the `--title` command line argument to set the process title on + startup. [#21477](https://github.com/nodejs/node/pull/21477) +* **trace_events**: + * Added process\_name metadata. [#21477](https://github.com/nodejs/node/pull/21477) +* **Added new collaborators** + * [codebytere](https://github.com/codebytere) - Shelley Vohr + +### Commits + +* [[`8c97ffb2f5`](https://github.com/nodejs/node/commit/8c97ffb2f5)] - **assert**: improve simple assert (Ruben Bridgewater) [#21626](https://github.com/nodejs/node/pull/21626) +* [[`9776f1cbef`](https://github.com/nodejs/node/commit/9776f1cbef)] - **benchmark**: add n-api function args benchmark (Kenny Yuan) [#21555](https://github.com/nodejs/node/pull/21555) +* [[`576f1ea978`](https://github.com/nodejs/node/commit/576f1ea978)] - **buffer**: remove superfluous assignment (Tobias Nießen) [#21844](https://github.com/nodejs/node/pull/21844) +* [[`6bb2b5a51d`](https://github.com/nodejs/node/commit/6bb2b5a51d)] - **build**: account for pure C sources in `build-addons-napi` (Anna Henningsen) [#21797](https://github.com/nodejs/node/pull/21797) +* [[`c02fb88936`](https://github.com/nodejs/node/commit/c02fb88936)] - **build**: enabling lto at configure (Octavian Soldea) [#21677](https://github.com/nodejs/node/pull/21677) +* [[`2a0862cec9`](https://github.com/nodejs/node/commit/2a0862cec9)] - **console**: fix timeEnd() not coercing the input (Ruben Bridgewater) [#21779](https://github.com/nodejs/node/pull/21779) +* [[`f3c397cd21`](https://github.com/nodejs/node/commit/f3c397cd21)] - **(SEMVER-MINOR)** **console**: implement timeLog method (Michaël Zasso) [#21312](https://github.com/nodejs/node/pull/21312) +* [[`73cafd853c`](https://github.com/nodejs/node/commit/73cafd853c)] - **console,util**: avoid pair array generation in C++ (Anna Henningsen) [#20831](https://github.com/nodejs/node/pull/20831) +* [[`d9825c7a16`](https://github.com/nodejs/node/commit/d9825c7a16)] - **crypto**: prevent Sign::SignFinal from crashing (Tobias Nießen) [#21815](https://github.com/nodejs/node/pull/21815) +* [[`07cce880bf`](https://github.com/nodejs/node/commit/07cce880bf)] - **crypto**: handle OpenSSL error queue in CipherBase (Tobias Nießen) [#21288](https://github.com/nodejs/node/pull/21288) +* [[`355c5e3c95`](https://github.com/nodejs/node/commit/355c5e3c95)] - **deps**: cherry-pick 555c811 from upstream V8 (Anna Henningsen) [#21741](https://github.com/nodejs/node/pull/21741) +* [[`42d75392c5`](https://github.com/nodejs/node/commit/42d75392c5)] - **deps**: patch V8 to 6.7.288.49 (Myles Borins) [#21727](https://github.com/nodejs/node/pull/21727) +* [[`6920091488`](https://github.com/nodejs/node/commit/6920091488)] - **deps**: upgrade to libuv 1.22.0 (cjihrig) [#21731](https://github.com/nodejs/node/pull/21731) +* [[`122ae24f62`](https://github.com/nodejs/node/commit/122ae24f62)] - **deps**: icu 62.1 bump (Unicode 11, CLDR 33.1) (Steven R. Loomis) [#21728](https://github.com/nodejs/node/pull/21728) +* [[`a5233c7e17`](https://github.com/nodejs/node/commit/a5233c7e17)] - **deps**: cherry-pick 477df06 from upstream v8 (Gus Caplan) [#21644](https://github.com/nodejs/node/pull/21644) +* [[`506631a9f9`](https://github.com/nodejs/node/commit/506631a9f9)] - **doc**: fix structure and formatting in inspector.md (Vse Mozhet Byt) [#21709](https://github.com/nodejs/node/pull/21709) +* [[`53b587a5af`](https://github.com/nodejs/node/commit/53b587a5af)] - **doc**: add documentation for buffer.byteOffset (Andreas Madsen) [#21718](https://github.com/nodejs/node/pull/21718) +* [[`51dfebf9ac`](https://github.com/nodejs/node/commit/51dfebf9ac)] - **doc**: fix vm.runInNewContext signature (Michaël Zasso) [#21824](https://github.com/nodejs/node/pull/21824) +* [[`10f9374ea3`](https://github.com/nodejs/node/commit/10f9374ea3)] - **doc**: make markdown input compliant (Sam Ruby) [#21780](https://github.com/nodejs/node/pull/21780) +* [[`02982998db`](https://github.com/nodejs/node/commit/02982998db)] - **doc**: add my pronoun (Ruben Bridgewater) [#21813](https://github.com/nodejs/node/pull/21813) +* [[`ca8c96035a`](https://github.com/nodejs/node/commit/ca8c96035a)] - **doc**: update readme with my pronouns (Lance Ball) [#21818](https://github.com/nodejs/node/pull/21818) +* [[`d33281b36f`](https://github.com/nodejs/node/commit/d33281b36f)] - **doc**: prevent some redirections (Vse Mozhet Byt) [#21811](https://github.com/nodejs/node/pull/21811) +* [[`0de0f89d0c`](https://github.com/nodejs/node/commit/0de0f89d0c)] - **doc**: add "Edit on GitHub" link (Rich Trott) [#21703](https://github.com/nodejs/node/pull/21703) +* [[`7ab6efdb94`](https://github.com/nodejs/node/commit/7ab6efdb94)] - **doc**: add policy for landing new npm releases (Myles Borins) [#21594](https://github.com/nodejs/node/pull/21594) +* [[`3d93273bf7`](https://github.com/nodejs/node/commit/3d93273bf7)] - **doc**: add OS X to instead of only macOS (XadillaX) [#21033](https://github.com/nodejs/node/pull/21033) +* [[`577d24baa4`](https://github.com/nodejs/node/commit/577d24baa4)] - **doc**: fix module.children description (Travis Fischer) [#21672](https://github.com/nodejs/node/pull/21672) +* [[`cd6601b87a`](https://github.com/nodejs/node/commit/cd6601b87a)] - **doc**: fix HTTP res 'finish' description (Sergey Zelenov) [#21670](https://github.com/nodejs/node/pull/21670) +* [[`51db88b0f1`](https://github.com/nodejs/node/commit/51db88b0f1)] - **doc**: fix http2stream.pushStream error doc (Сковорода Никита Андреевич) [#21487](https://github.com/nodejs/node/pull/21487) +* [[`6e1917a596`](https://github.com/nodejs/node/commit/6e1917a596)] - **doc**: update changelog with 9.x EOL (Сковорода Никита Андреевич) [#21612](https://github.com/nodejs/node/pull/21612) +* [[`cd77d8782a`](https://github.com/nodejs/node/commit/cd77d8782a)] - **doc**: improve documentation of fs sync methods (iwko) [#21243](https://github.com/nodejs/node/pull/21243) +* [[`1044bafec4`](https://github.com/nodejs/node/commit/1044bafec4)] - **doc**: remove \_Node.js style callback\_ (Rich Trott) [#21701](https://github.com/nodejs/node/pull/21701) +* [[`971679328e`](https://github.com/nodejs/node/commit/971679328e)] - **doc**: add codebytere as collaborator (Shelley Vohr) [#21700](https://github.com/nodejs/node/pull/21700) +* [[`034fe19862`](https://github.com/nodejs/node/commit/034fe19862)] - **doc**: add links to inline HTML table (Rich Trott) [#21678](https://github.com/nodejs/node/pull/21678) +* [[`04eed2342d`](https://github.com/nodejs/node/commit/04eed2342d)] - **doc**: remove "note that" from fs doc (Rich Trott) [#21646](https://github.com/nodejs/node/pull/21646) +* [[`c8d5bab022`](https://github.com/nodejs/node/commit/c8d5bab022)] - **doc**: fix doc for napi\_create\_function (Gabriel Schulhof) +* [[`f7aa22a0eb`](https://github.com/nodejs/node/commit/f7aa22a0eb)] - **doc**: improve guide text for CI runs (Rich Trott) [#21645](https://github.com/nodejs/node/pull/21645) +* [[`6f8ebc08b9`](https://github.com/nodejs/node/commit/6f8ebc08b9)] - **doc**: unify spelling of backpressure (Thomas Watson) [#21630](https://github.com/nodejs/node/pull/21630) +* [[`3fffc7e95f`](https://github.com/nodejs/node/commit/3fffc7e95f)] - **errors**: fix undefined HTTP2 and tls errors (Shailesh Shekhawat) [#21564](https://github.com/nodejs/node/pull/21564) +* [[`b758006c23`](https://github.com/nodejs/node/commit/b758006c23)] - **fs**: fix fsPromises.lchmod error on non-Mac (Masashi Hirano) [#21435](https://github.com/nodejs/node/pull/21435) +* [[`4fa7150962`](https://github.com/nodejs/node/commit/4fa7150962)] - **fs**: support pseudofiles in promises.readFile (Timothy Gu) [#21497](https://github.com/nodejs/node/pull/21497) +* [[`bba500d0ea`](https://github.com/nodejs/node/commit/bba500d0ea)] - **(SEMVER-MINOR)** **http**: fix request with option timeout and agent (killagu) [#21204](https://github.com/nodejs/node/pull/21204) +* [[`0b3c80ca31`](https://github.com/nodejs/node/commit/0b3c80ca31)] - **http2**: fix issues with aborted `respondWithFile()`s (Anna Henningsen) [#21561](https://github.com/nodejs/node/pull/21561) +* [[`238ef58841`](https://github.com/nodejs/node/commit/238ef58841)] - **http2**: remove `waitTrailers` listener after closing a stream (RidgeA) [#21764](https://github.com/nodejs/node/pull/21764) +* [[`07160cd2fd`](https://github.com/nodejs/node/commit/07160cd2fd)] - **http2**: order declarations in core.js (Rich Trott) [#21689](https://github.com/nodejs/node/pull/21689) +* [[`c88af232c8`](https://github.com/nodejs/node/commit/c88af232c8)] - **http2**: pass incoming set-cookie header as array (Gerhard Stoebich) [#21360](https://github.com/nodejs/node/pull/21360) +* [[`2922028362`](https://github.com/nodejs/node/commit/2922028362)] - **(SEMVER-MINOR)** **inspector**: expose original console (Matteo Collina) [#21659](https://github.com/nodejs/node/pull/21659) +* [[`b2291296ef`](https://github.com/nodejs/node/commit/b2291296ef)] - **inspector**: split main thread interface from transport (Eugene Ostroukhov) [#21182](https://github.com/nodejs/node/pull/21182) +* [[`4ed4bf3bdd`](https://github.com/nodejs/node/commit/4ed4bf3bdd)] - **lib**: update punycode to 2.1.1 (Rich Trott) [#21768](https://github.com/nodejs/node/pull/21768) +* [[`4433ecbf30`](https://github.com/nodejs/node/commit/4433ecbf30)] - **lib**: refactor cli table (Ruben Bridgewater) [#20960](https://github.com/nodejs/node/pull/20960) +* [[`92d79212ec`](https://github.com/nodejs/node/commit/92d79212ec)] - **lib**: consolidate redundant require() calls (cjihrig) [#21699](https://github.com/nodejs/node/pull/21699) +* [[`ed774b7930`](https://github.com/nodejs/node/commit/ed774b7930)] - **messaging**: fix edge cases with transferring ports (Timothy Gu) [#21540](https://github.com/nodejs/node/pull/21540) +* [[`221c8bd58f`](https://github.com/nodejs/node/commit/221c8bd58f)] - **messaging**: use actual DOMException for DataCloneError (Timothy Gu) [#21540](https://github.com/nodejs/node/pull/21540) +* [[`4f3bbfaaca`](https://github.com/nodejs/node/commit/4f3bbfaaca)] - **n-api**: test uint32 truncation (Gabriel Schulhof) [#21722](https://github.com/nodejs/node/pull/21722) +* [[`b8ba003fbf`](https://github.com/nodejs/node/commit/b8ba003fbf)] - **n-api**: remove experimental gate from status codes (Gabriel Schulhof) [#21680](https://github.com/nodejs/node/pull/21680) +* [[`109c59971a`](https://github.com/nodejs/node/commit/109c59971a)] - **n-api**: create functions directly (Gabriel Schulhof) [#21688](https://github.com/nodejs/node/pull/21688) +* [[`cec166e85f`](https://github.com/nodejs/node/commit/cec166e85f)] - **n-api**: restrict exports by version (Kyle Farnung) [#19962](https://github.com/nodejs/node/pull/19962) +* [[`3096ee5a4b`](https://github.com/nodejs/node/commit/3096ee5a4b)] - **(SEMVER-MINOR)** **napi**: add bigint support (Gus Caplan) [#21226](https://github.com/nodejs/node/pull/21226) +* [[`581390c59d`](https://github.com/nodejs/node/commit/581390c59d)] - **process**: split bootstrappers by threads that can run them (Joyee Cheung) [#21378](https://github.com/nodejs/node/pull/21378) +* [[`f1b18ba412`](https://github.com/nodejs/node/commit/f1b18ba412)] - **(SEMVER-MINOR)** **process**: implement process.hrtime.bigint() (Joyee Cheung) [#21256](https://github.com/nodejs/node/pull/21256) +* [[`961f6e8623`](https://github.com/nodejs/node/commit/961f6e8623)] - **process**: fix process.exitCode handling for fatalException (Denys Otrishko) [#21739](https://github.com/nodejs/node/pull/21739) +* [[`4b613d3976`](https://github.com/nodejs/node/commit/4b613d3976)] - **repl**: make own properties shadow prototype properties (Sam Ruby) [#21588](https://github.com/nodejs/node/pull/21588) +* [[`1019c2d317`](https://github.com/nodejs/node/commit/1019c2d317)] - **src**: fix async hooks crashing when there is no node context (Javier Gonzalez) [#19134](https://github.com/nodejs/node/pull/19134) +* [[`a9a718696e`](https://github.com/nodejs/node/commit/a9a718696e)] - **src**: make heap snapshot & embedder graph accessible for tests (Anna Henningsen) [#21741](https://github.com/nodejs/node/pull/21741) +* [[`5121278f5c`](https://github.com/nodejs/node/commit/5121278f5c)] - **src**: use V8 graph heap snapshot API (Anna Henningsen) [#21741](https://github.com/nodejs/node/pull/21741) +* [[`d42dbde1a8`](https://github.com/nodejs/node/commit/d42dbde1a8)] - **src**: add iteration over all base objects to Environment (Anna Henningsen) [#21741](https://github.com/nodejs/node/pull/21741) +* [[`4ed5d1a623`](https://github.com/nodejs/node/commit/4ed5d1a623)] - **src**: add HandleWrap::AddWrapMethods (Jon Moss) [#21769](https://github.com/nodejs/node/pull/21769) +* [[`51d613db2d`](https://github.com/nodejs/node/commit/51d613db2d)] - **src**: start annotating native code side effect (Timothy Gu) [#21458](https://github.com/nodejs/node/pull/21458) +* [[`466601f47f`](https://github.com/nodejs/node/commit/466601f47f)] - **src**: remove .h if -inl.h is already included (Daniel Bevenius) [#21381](https://github.com/nodejs/node/pull/21381) +* [[`a68b7dda5f`](https://github.com/nodejs/node/commit/a68b7dda5f)] - **src**: add node\_process.cc (James M Snell) [#21105](https://github.com/nodejs/node/pull/21105) +* [[`cb698111c4`](https://github.com/nodejs/node/commit/cb698111c4)] - **src**: add comment on CallbackScope exception behaviour (Anna Henningsen) [#21743](https://github.com/nodejs/node/pull/21743) +* [[`712809eb1b`](https://github.com/nodejs/node/commit/712809eb1b)] - **src**: enable more detailed memory tracking (Anna Henningsen) [#21742](https://github.com/nodejs/node/pull/21742) +* [[`277077853f`](https://github.com/nodejs/node/commit/277077853f)] - **src**: make Environment::is\_stopping\_worker inline (Jon Moss) [#21720](https://github.com/nodejs/node/pull/21720) +* [[`d06305635d`](https://github.com/nodejs/node/commit/d06305635d)] - **(SEMVER-MINOR)** **src**: add --title command line argument (James M Snell) [#21477](https://github.com/nodejs/node/pull/21477) +* [[`ceec23e6e4`](https://github.com/nodejs/node/commit/ceec23e6e4)] - **src**: remove using directives from spawn\_sync.h (Daniel Bevenius) [#21634](https://github.com/nodejs/node/pull/21634) +* [[`3a627c830b`](https://github.com/nodejs/node/commit/3a627c830b)] - **src**: add context-aware init macro and doc (Gabriel Schulhof) [#21318](https://github.com/nodejs/node/pull/21318) +* [[`aa5994f2b9`](https://github.com/nodejs/node/commit/aa5994f2b9)] - **src,tools**: use https://nodejs.org URL when possible. (XhmikosR) [#21719](https://github.com/nodejs/node/pull/21719) +* [[`0108ff6b51`](https://github.com/nodejs/node/commit/0108ff6b51)] - **test**: add support for NODE\_TEST\_DIR on a separate mount point (Antoine du HAMEL) [#21552](https://github.com/nodejs/node/pull/21552) +* [[`eef975ebae`](https://github.com/nodejs/node/commit/eef975ebae)] - **test**: move inspector test back to parallel, unmark flaky (Anna Henningsen) [#21806](https://github.com/nodejs/node/pull/21806) +* [[`67908e9933`](https://github.com/nodejs/node/commit/67908e9933)] - **test**: fix build warnings in bigint N-API test (Anna Henningsen) [#21796](https://github.com/nodejs/node/pull/21796) +* [[`6b72583bf8`](https://github.com/nodejs/node/commit/6b72583bf8)] - **test**: refactor test-tls-connect-memleak, move to parallel (Anna Henningsen) [#21794](https://github.com/nodejs/node/pull/21794) +* [[`174a9db51a`](https://github.com/nodejs/node/commit/174a9db51a)] - **test**: refactor test-net-connect-memleak, move to parallel (Anna Henningsen) [#21794](https://github.com/nodejs/node/pull/21794) +* [[`b338ff54bb`](https://github.com/nodejs/node/commit/b338ff54bb)] - **test**: add gc tracking to common API (Anna Henningsen) [#21794](https://github.com/nodejs/node/pull/21794) +* [[`4e60ce8f87`](https://github.com/nodejs/node/commit/4e60ce8f87)] - **test**: fix flaky test-debug-prompt (Rich Trott) [#21826](https://github.com/nodejs/node/pull/21826) +* [[`a2edb59870`](https://github.com/nodejs/node/commit/a2edb59870)] - **test**: fix comment of fs.promises write (Ryuichi Sakagami) [#21708](https://github.com/nodejs/node/pull/21708) +* [[`32ad163038`](https://github.com/nodejs/node/commit/32ad163038)] - **test**: add test of fs.promises write for non-string buffers (Ryuichi Sakagami) [#21708](https://github.com/nodejs/node/pull/21708) +* [[`7352b72fc9`](https://github.com/nodejs/node/commit/7352b72fc9)] - **test**: add heap snapshot tests (Anna Henningsen) [#21741](https://github.com/nodejs/node/pull/21741) +* [[`678313d18b`](https://github.com/nodejs/node/commit/678313d18b)] - **test**: add filehandle sync() and datasync() tests (Masashi Hirano) [#20530](https://github.com/nodejs/node/pull/20530) +* [[`a09bdb5847`](https://github.com/nodejs/node/commit/a09bdb5847)] - **test**: improve console table error output (Ruben Bridgewater) [#20960](https://github.com/nodejs/node/pull/20960) +* [[`600349aaba`](https://github.com/nodejs/node/commit/600349aaba)] - **test**: refactor process/worker exitCode tests (Denys Otrishko) [#21739](https://github.com/nodejs/node/pull/21739) +* [[`15026511b8`](https://github.com/nodejs/node/commit/15026511b8)] - **test**: remove timer in fs.watchFile() test (Rich Trott) [#21694](https://github.com/nodejs/node/pull/21694) +* [[`ae5d5658b9`](https://github.com/nodejs/node/commit/ae5d5658b9)] - **test**: fix flaky watchFile() (Rich Trott) [#21694](https://github.com/nodejs/node/pull/21694) +* [[`ada3f34cd4`](https://github.com/nodejs/node/commit/ada3f34cd4)] - **test**: fix weird string error (Jon Moss) [#21793](https://github.com/nodejs/node/pull/21793) +* [[`f46536be23`](https://github.com/nodejs/node/commit/f46536be23)] - **test**: fix timeouts when running worker tests with `--worker` (Anna Henningsen) [#21791](https://github.com/nodejs/node/pull/21791) +* [[`f386c0a517`](https://github.com/nodejs/node/commit/f386c0a517)] - **test**: add test for dns.promises.resolve . (Keita Akutsu) [#21691](https://github.com/nodejs/node/pull/21691) +* [[`11e9b4ecee`](https://github.com/nodejs/node/commit/11e9b4ecee)] - **test**: fix parallel/test-tls-env-extra-ca.js (Niicck) [#21647](https://github.com/nodejs/node/pull/21647) +* [[`eda7fffba4`](https://github.com/nodejs/node/commit/eda7fffba4)] - **test**: swap arguments in strictEqual() (Sohail Rajdev) [#21660](https://github.com/nodejs/node/pull/21660) +* [[`194d1955a7`](https://github.com/nodejs/node/commit/194d1955a7)] - **test**: fix test-tls-connect-memleak (Rich Trott) [#21681](https://github.com/nodejs/node/pull/21681) +* [[`24f649c8cf`](https://github.com/nodejs/node/commit/24f649c8cf)] - **test**: fix pummel/test-net-connect-memleak (Rich Trott) [#21658](https://github.com/nodejs/node/pull/21658) +* [[`021dd5404c`](https://github.com/nodejs/node/commit/021dd5404c)] - **test**: remove unnecessary string literals (Jacek Pospychała) [#21638](https://github.com/nodejs/node/pull/21638) +* [[`47b10e30c0`](https://github.com/nodejs/node/commit/47b10e30c0)] - **test**: replace third argument with comment in strict equals (Developer Davo) [#21603](https://github.com/nodejs/node/pull/21603) +* [[`25dac95164`](https://github.com/nodejs/node/commit/25dac95164)] - **test**: fix args passed to strictEqual (Haroon Khan) [#21584](https://github.com/nodejs/node/pull/21584) +* [[`fe9888a34a`](https://github.com/nodejs/node/commit/fe9888a34a)] - **test**: check type for Worker filename argument (Masashi Hirano) [#21620](https://github.com/nodejs/node/pull/21620) +* [[`9cd5c0ec79`](https://github.com/nodejs/node/commit/9cd5c0ec79)] - **test**: add test for missing dynamic instantiate hook (Michaël Zasso) [#21506](https://github.com/nodejs/node/pull/21506) +* [[`dc84858787`](https://github.com/nodejs/node/commit/dc84858787)] - **test,util**: add missing tests and conditions (MaleDong) [#21455](https://github.com/nodejs/node/pull/21455) +* [[`c26ba082ae`](https://github.com/nodejs/node/commit/c26ba082ae)] - **tools**: avoid global install of dmn for lint update (Rich Trott) [#21744](https://github.com/nodejs/node/pull/21744) +* [[`e030dd7d65`](https://github.com/nodejs/node/commit/e030dd7d65)] - **tools**: add no-duplicate-requires rule (Gus Caplan) [#21712](https://github.com/nodejs/node/pull/21712) +* [[`b9bbbbe5d1`](https://github.com/nodejs/node/commit/b9bbbbe5d1)] - **tools**: build all.json by combining generated JSON (Sam Ruby) [#21637](https://github.com/nodejs/node/pull/21637) +* [[`214c608208`](https://github.com/nodejs/node/commit/214c608208)] - **tools**: lint doc code examples in strict mode (Vse Mozhet Byt) [#21615](https://github.com/nodejs/node/pull/21615) +* [[`27d17d4600`](https://github.com/nodejs/node/commit/27d17d4600)] - **trace_events**: add traced\_value.cc/traced\_value.h (James M Snell) [#21475](https://github.com/nodejs/node/pull/21475) +* [[`c4d7413a15`](https://github.com/nodejs/node/commit/c4d7413a15)] - **(SEMVER-MINOR)** **trace_events**: add process\_name metadata (James M Snell) [#21477](https://github.com/nodejs/node/pull/21477) +* [[`b0943a655e`](https://github.com/nodejs/node/commit/b0943a655e)] - **worker**: exit after uncaught exception (Denys Otrishko) [#21739](https://github.com/nodejs/node/pull/21739) +* [[`25fef3d8d4`](https://github.com/nodejs/node/commit/25fef3d8d4)] - **workers**: fix invalid exit code in parent upon uncaught exception (Denys Otrishko) [#21713](https://github.com/nodejs/node/pull/21713) +* [[`48b16aad47`](https://github.com/nodejs/node/commit/48b16aad47)] - **zlib**: instance-ify two methods (Jon Moss) [#21702](https://github.com/nodejs/node/pull/21702) +* [[`dae7130929`](https://github.com/nodejs/node/commit/dae7130929)] - **zlib**: track memory allocated by zlib (Anna Henningsen) [#21608](https://github.com/nodejs/node/pull/21608) +* [[`96dae83713`](https://github.com/nodejs/node/commit/96dae83713)] - **zlib**: fix memory leak for unused zlib instances (Anna Henningsen) [#21607](https://github.com/nodejs/node/pull/21607) + ## 2018-07-04, Version 10.6.0 (Current), @targos diff --git a/doc/guides/maintaining-npm.md b/doc/guides/maintaining-npm.md index 97da87feee64d3..6017e78fded70a 100644 --- a/doc/guides/maintaining-npm.md +++ b/doc/guides/maintaining-npm.md @@ -1,5 +1,19 @@ # Maintaining npm in Node.js +New pull requests should be opened when a "next" version of npm has +been released. Once the "next" version has been promoted to "latest" +the PR should be updated as necessary. + +Two weeks after the "latest" release has been promoted it can land on master +assuming no major regressions are found. There are no additional constraints +for Semver-Major releases. + +The specific Node.js release streams the new version will be able to land into +are at the discretion of the release and LTS teams. + +This process only covers full updates to new versions of npm. Cherry-picked +changes can be reviewed and landed via the normal consensus seeking process. + ## Step 1: Clone npm ```console diff --git a/doc/node.1 b/doc/node.1 index ac3a8d48a898db..31d39c22681860 100644 --- a/doc/node.1 +++ b/doc/node.1 @@ -167,6 +167,9 @@ instead of printing to stderr. .It Fl -throw-deprecation Throw errors for deprecations. . +.It Fl -title Ns = Ns Ar title +Specify process.title on startup. +. .It Fl -tls-cipher-list Ns = Ns Ar list Specify an alternative default TLS cipher list. Requires Node.js to be built with crypto support. (Default) diff --git a/doc/template.html b/doc/template.html index bb3e2bf8b4f2f0..fdcf0d5821b56f 100644 --- a/doc/template.html +++ b/doc/template.html @@ -35,6 +35,7 @@

Node.js __VERSION__ Documentation

View as JSON __ALTDOCS__ + __EDIT_ON_GITHUB__
diff --git a/lib/_http_agent.js b/lib/_http_agent.js index 67f3d667b28169..1a2920cf098298 100644 --- a/lib/_http_agent.js +++ b/lib/_http_agent.js @@ -66,7 +66,8 @@ function Agent(options) { if (socket.writable && this.requests[name] && this.requests[name].length) { - this.requests[name].shift().onSocket(socket); + const req = this.requests[name].shift(); + setRequestSocket(this, req, socket); if (this.requests[name].length === 0) { // don't leak delete this.requests[name]; @@ -176,12 +177,12 @@ Agent.prototype.addRequest = function addRequest(req, options, port/* legacy */, delete this.freeSockets[name]; this.reuseSocket(socket, req); - req.onSocket(socket); + setRequestSocket(this, req, socket); this.sockets[name].push(socket); } else if (sockLen < this.maxSockets) { debug('call onSocket', sockLen, freeLen); // If we are under maxSockets create a new one. - this.createSocket(req, options, handleSocketCreation(req, true)); + this.createSocket(req, options, handleSocketCreation(this, req, true)); } else { debug('wait for socket'); // We are over limit so we'll add it to the queue. @@ -305,9 +306,10 @@ Agent.prototype.removeSocket = function removeSocket(s, options) { if (this.requests[name] && this.requests[name].length) { debug('removeSocket, have a request, make a socket'); - var req = this.requests[name][0]; + const req = this.requests[name][0]; // If we have pending requests and a socket gets closed make a new one - this.createSocket(req, options, handleSocketCreation(req, false)); + const socketCreationHandler = handleSocketCreation(this, req, false); + this.createSocket(req, options, socketCreationHandler); } }; @@ -337,19 +339,36 @@ Agent.prototype.destroy = function destroy() { } }; -function handleSocketCreation(request, informRequest) { +function handleSocketCreation(agent, request, informRequest) { return function handleSocketCreation_Inner(err, socket) { if (err) { process.nextTick(emitErrorNT, request, err); return; } if (informRequest) - request.onSocket(socket); + setRequestSocket(agent, request, socket); else socket.emit('free'); }; } +function setRequestSocket(agent, req, socket) { + req.onSocket(socket); + const agentTimeout = agent.options.timeout || 0; + if (req.timeout === undefined || req.timeout === agentTimeout) { + return; + } + socket.setTimeout(req.timeout); + // reset timeout after response end + req.once('response', (res) => { + res.once('end', () => { + if (socket.timeout !== agentTimeout) { + socket.setTimeout(agentTimeout); + } + }); + }); +} + function emitErrorNT(emitter, err) { emitter.emit('error', err); } diff --git a/lib/_http_client.js b/lib/_http_client.js index d3616eb6b5e7f9..0612f5822a303f 100644 --- a/lib/_http_client.js +++ b/lib/_http_client.js @@ -639,18 +639,35 @@ function tickOnSocket(req, socket) { socket.on('end', socketOnEnd); socket.on('close', socketCloseListener); - if (req.timeout) { - const emitRequestTimeout = () => req.emit('timeout'); - socket.once('timeout', emitRequestTimeout); - req.once('response', (res) => { - res.once('end', () => { - socket.removeListener('timeout', emitRequestTimeout); - }); - }); + if (req.timeout !== undefined) { + listenSocketTimeout(req); } req.emit('socket', socket); } +function listenSocketTimeout(req) { + if (req.timeoutCb) { + return; + } + const emitRequestTimeout = () => req.emit('timeout'); + // Set timeoutCb so it will get cleaned up on request end. + req.timeoutCb = emitRequestTimeout; + // Delegate socket timeout event. + if (req.socket) { + req.socket.once('timeout', emitRequestTimeout); + } else { + req.on('socket', (socket) => { + socket.once('timeout', emitRequestTimeout); + }); + } + // Remove socket timeout listener after response end. + req.once('response', (res) => { + res.once('end', () => { + req.socket.removeListener('timeout', emitRequestTimeout); + }); + }); +} + ClientRequest.prototype.onSocket = function onSocket(socket) { process.nextTick(onSocketNT, this, socket); }; @@ -700,42 +717,29 @@ function _deferToConnect(method, arguments_, cb) { } ClientRequest.prototype.setTimeout = function setTimeout(msecs, callback) { + listenSocketTimeout(this); msecs = validateTimerDuration(msecs); if (callback) this.once('timeout', callback); - const emitTimeout = () => this.emit('timeout'); - - if (this.socket && this.socket.writable) { - if (this.timeoutCb) - this.socket.setTimeout(0, this.timeoutCb); - this.timeoutCb = emitTimeout; - this.socket.setTimeout(msecs, emitTimeout); - return this; - } - - // Set timeoutCb so that it'll get cleaned up on request end - this.timeoutCb = emitTimeout; if (this.socket) { - var sock = this.socket; - this.socket.once('connect', function() { - sock.setTimeout(msecs, emitTimeout); - }); - return this; + setSocketTimeout(this.socket, msecs); + } else { + this.once('socket', (sock) => setSocketTimeout(sock, msecs)); } - this.once('socket', function(sock) { - if (sock.connecting) { - sock.once('connect', function() { - sock.setTimeout(msecs, emitTimeout); - }); - } else { - sock.setTimeout(msecs, emitTimeout); - } - }); - return this; }; +function setSocketTimeout(sock, msecs) { + if (sock.connecting) { + sock.once('connect', function() { + sock.setTimeout(msecs); + }); + } else { + sock.setTimeout(msecs); + } +} + ClientRequest.prototype.setNoDelay = function setNoDelay(noDelay) { this._deferToConnect('setNoDelay', [noDelay]); }; diff --git a/lib/_http_outgoing.js b/lib/_http_outgoing.js index f075e0ecad7a2b..913935048680eb 100644 --- a/lib/_http_outgoing.js +++ b/lib/_http_outgoing.js @@ -25,12 +25,11 @@ const assert = require('assert').ok; const Stream = require('stream'); const util = require('util'); const internalUtil = require('internal/util'); -const internalHttp = require('internal/http'); +const { outHeadersKey, utcDate } = require('internal/http'); const { Buffer } = require('buffer'); const common = require('_http_common'); const checkIsHttpToken = common._checkIsHttpToken; const checkInvalidHeaderChar = common._checkInvalidHeaderChar; -const { outHeadersKey } = require('internal/http'); const { defaultTriggerAsyncIdScope, symbols: { async_id_symbol } @@ -48,7 +47,6 @@ const { } = require('internal/errors').codes; const { CRLF, debug } = common; -const { utcDate } = internalHttp; const kIsCorked = Symbol('isCorked'); diff --git a/lib/assert.js b/lib/assert.js index 47c0e3c1402bdd..5a25e73a9dc1bc 100644 --- a/lib/assert.js +++ b/lib/assert.js @@ -34,6 +34,10 @@ const { NativeModule } = require('internal/bootstrap/loaders'); let isDeepEqual; let isDeepStrictEqual; +let parseExpressionAt; +let findNodeAround; +let columnOffset = 0; +let decoder; function lazyLoadComparison() { const comparison = require('internal/util/comparisons'); @@ -113,52 +117,141 @@ function fail(actual, expected, message, operator, stackStartFn) { assert.fail = fail; // The AssertionError is defined in internal/error. -// new assert.AssertionError({ message: message, -// actual: actual, -// expected: expected }); assert.AssertionError = AssertionError; -function getBuffer(fd, assertLine) { +function findColumn(fd, column, code) { + if (code.length > column + 100) { + try { + return parseCode(code, column); + } catch { + // End recursion in case no code could be parsed. The expression should + // have been found after 2500 characters, so stop trying. + if (code.length - column > 2500) { + // eslint-disable-next-line no-throw-literal + throw null; + } + } + } + // Read up to 2500 bytes more than necessary in columns. That way we address + // multi byte characters and read enough data to parse the code. + const bytesToRead = column - code.length + 2500; + const buffer = Buffer.allocUnsafe(bytesToRead); + const bytesRead = readSync(fd, buffer, 0, bytesToRead); + code += decoder.write(buffer.slice(0, bytesRead)); + // EOF: fast path. + if (bytesRead < bytesToRead) { + return parseCode(code, column); + } + // Read potentially missing code. + return findColumn(fd, column, code); +} + +function getCode(fd, line, column) { + let bytesRead = 0; + if (line === 0) { + // Special handle line number one. This is more efficient and simplifies the + // rest of the algorithm. Read more than the regular column number in bytes + // to prevent multiple reads in case multi byte characters are used. + return findColumn(fd, column, ''); + } let lines = 0; // Prevent blocking the event loop by limiting the maximum amount of // data that may be read. let maxReads = 64; // bytesPerRead * maxReads = 512 kb - let bytesRead = 0; - let startBuffer = 0; // Start reading from that char on const bytesPerRead = 8192; - const buffers = []; - do { - const buffer = Buffer.allocUnsafe(bytesPerRead); + // Use a single buffer up front that is reused until the call site is found. + let buffer = Buffer.allocUnsafe(bytesPerRead); + while (maxReads-- !== 0) { + // Only allocate a new buffer in case the needed line is found. All data + // before that can be discarded. + buffer = lines < line ? buffer : Buffer.allocUnsafe(bytesPerRead); bytesRead = readSync(fd, buffer, 0, bytesPerRead); + // Read the buffer until the required code line is found. for (var i = 0; i < bytesRead; i++) { - if (buffer[i] === 10) { - lines++; - if (lines === assertLine) { - startBuffer = i + 1; - // Read up to 15 more lines to make sure all code gets matched - } else if (lines === assertLine + 16) { - buffers.push(buffer.slice(startBuffer, i)); - return buffers; + if (buffer[i] === 10 && ++lines === line) { + // If the end of file is reached, directly parse the code and return. + if (bytesRead < bytesPerRead) { + return parseCode(buffer.toString('utf8', i + 1, bytesRead), column); } + // Check if the read code is sufficient or read more until the whole + // expression is read. Make sure multi byte characters are preserved + // properly by using the decoder. + const code = decoder.write(buffer.slice(i + 1, bytesRead)); + return findColumn(fd, column, code); } } - if (lines >= assertLine) { - buffers.push(buffer.slice(startBuffer, bytesRead)); - // Reset the startBuffer in case we need more than one chunk - startBuffer = 0; + } +} + +function parseCode(code, offset) { + // Lazy load acorn. + if (parseExpressionAt === undefined) { + ({ parseExpressionAt } = require('internal/deps/acorn/dist/acorn')); + ({ findNodeAround } = require('internal/deps/acorn/dist/walk')); + } + let node; + let start = 0; + // Parse the read code until the correct expression is found. + do { + try { + node = parseExpressionAt(code, start); + start = node.end + 1 || start; + // Find the CallExpression in the tree. + node = findNodeAround(node, offset, 'CallExpression'); + } catch (err) { + // Unexpected token error and the like. + start += err.raisedAt || 1; + if (start > offset) { + // No matching expression found. This could happen if the assert + // expression is bigger than the provided buffer. + // eslint-disable-next-line no-throw-literal + throw null; + } } - } while (--maxReads !== 0 && bytesRead !== 0); - return buffers; + } while (node === undefined || node.node.end < offset); + + return [ + node.node.start, + code.slice(node.node.start, node.node.end) + .replace(escapeSequencesRegExp, escapeFn) + ]; } -function getErrMessage(call) { +function getErrMessage(message, fn) { + const tmpLimit = Error.stackTraceLimit; + // Make sure the limit is set to 1. Otherwise it could fail (<= 0) or it + // does to much work. + Error.stackTraceLimit = 1; + // We only need the stack trace. To minimize the overhead use an object + // instead of an error. + const err = {}; + Error.captureStackTrace(err, fn); + Error.stackTraceLimit = tmpLimit; + + const tmpPrepare = Error.prepareStackTrace; + Error.prepareStackTrace = (_, stack) => stack; + const call = err.stack[0]; + Error.prepareStackTrace = tmpPrepare; + const filename = call.getFileName(); + if (!filename) { - return; + return message; } const line = call.getLineNumber() - 1; - const column = call.getColumnNumber() - 1; + let column = call.getColumnNumber() - 1; + + // Line number one reports the wrong column due to being wrapped in a + // function. Remove that offset to get the actual call. + if (line === 0) { + if (columnOffset === 0) { + const { wrapper } = require('internal/modules/cjs/loader'); + columnOffset = wrapper[0].length; + } + column -= columnOffset; + } + const identifier = `${filename}${line}${column}`; if (errorCache.has(identifier)) { @@ -171,57 +264,49 @@ function getErrMessage(call) { return; } - let fd, message; + let fd; try { + // Set the stack trace limit to zero. This makes sure unexpected token + // errors are handled faster. + Error.stackTraceLimit = 0; + + if (decoder === undefined) { + const { StringDecoder } = require('string_decoder'); + decoder = new StringDecoder('utf8'); + } + fd = openSync(filename, 'r', 0o666); - const buffers = getBuffer(fd, line); - const code = Buffer.concat(buffers).toString('utf8'); - // Lazy load acorn. - const { parseExpressionAt } = require('internal/deps/acorn/dist/acorn'); - const nodes = parseExpressionAt(code, column); - // Node type should be "CallExpression" and some times - // "SequenceExpression". - const node = nodes.type === 'CallExpression' ? nodes : nodes.expressions[0]; - const name = node.callee.name; - // Calling `ok` with .apply or .call is uncommon but we use a simple - // safeguard nevertheless. - if (name !== 'apply' && name !== 'call') { - // Only use `assert` and `assert.ok` to reference the "real API" and - // not user defined function names. - const ok = name === 'ok' ? '.ok' : ''; - const args = node.arguments; - message = code - .slice(args[0].start, args[args.length - 1].end) - .replace(escapeSequencesRegExp, escapeFn); + // Reset column and message. + [column, message] = getCode(fd, line, column); + // Flush unfinished multi byte characters. + decoder.end(); + // Always normalize indentation, otherwise the message could look weird. + if (message.indexOf('\n') !== -1) { if (EOL === '\r\n') { message = message.replace(/\r\n/g, '\n'); } - // Always normalize indentation, otherwise the message could look weird. - if (message.indexOf('\n') !== -1) { - const tmp = message.split('\n'); - message = tmp[0]; - for (var i = 1; i < tmp.length; i++) { - let pos = 0; - while (pos < column && - (tmp[i][pos] === ' ' || tmp[i][pos] === '\t')) { - pos++; - } - message += `\n ${tmp[i].slice(pos)}`; + const frames = message.split('\n'); + message = frames.shift(); + for (const frame of frames) { + let pos = 0; + while (pos < column && (frame[pos] === ' ' || frame[pos] === '\t')) { + pos++; } + message += `\n ${frame.slice(pos)}`; } - message = 'The expression evaluated to a falsy value:' + - `\n\n assert${ok}(${message})\n`; } + message = `The expression evaluated to a falsy value:\n\n ${message}\n`; // Make sure to always set the cache! No matter if the message is // undefined or not errorCache.set(identifier, message); return message; - } catch (e) { // Invalidate cache to prevent trying to read this part again. errorCache.set(identifier, undefined); } finally { + // Reset limit. + Error.stackTraceLimit = tmpLimit; if (fd !== undefined) closeSync(fd); } @@ -235,25 +320,8 @@ function innerOk(fn, argLen, value, message) { generatedMessage = true; message = 'No value argument passed to `assert.ok()`'; } else if (message == null) { - // Use the call as error message if possible. - // This does not work with e.g. the repl. - // eslint-disable-next-line no-restricted-syntax - const err = new Error(); - // Make sure the limit is set to 1. Otherwise it could fail (<= 0) or it - // does to much work. - const tmpLimit = Error.stackTraceLimit; - Error.stackTraceLimit = 1; - Error.captureStackTrace(err, fn); - Error.stackTraceLimit = tmpLimit; - - const tmpPrepare = Error.prepareStackTrace; - Error.prepareStackTrace = (_, stack) => stack; - const call = err.stack[0]; - Error.prepareStackTrace = tmpPrepare; - - // Make sure it would be "null" in case that is used. - message = getErrMessage(call) || message; generatedMessage = true; + message = getErrMessage(message, fn); } else if (message instanceof Error) { throw message; } diff --git a/lib/console.js b/lib/console.js index ed4b6fe9145069..98ab0c35712d97 100644 --- a/lib/console.js +++ b/lib/console.js @@ -233,16 +233,34 @@ Console.prototype.time = function time(label = 'default') { Console.prototype.timeEnd = function timeEnd(label = 'default') { // Coerces everything other than Symbol to a string label = `${label}`; - const time = this._times.get(label); + const hasWarned = timeLogImpl(this, 'timeEnd', label); + if (!hasWarned) { + this._times.delete(label); + } +}; + +Console.prototype.timeLog = function timeLog(label, ...data) { + // Coerces everything other than Symbol to a string + label = `${label}`; + timeLogImpl(this, 'timeLog', label, data); +}; + +// Returns true if label was not found +function timeLogImpl(self, name, label = 'default', data) { + const time = self._times.get(label); if (!time) { - process.emitWarning(`No such label '${label}' for console.timeEnd()`); - return; + process.emitWarning(`No such label '${label}' for console.${name}()`); + return true; } const duration = process.hrtime(time); const ms = duration[0] * 1000 + duration[1] / 1e6; - this.log('%s: %sms', label, ms.toFixed(3)); - this._times.delete(label); -}; + if (data === undefined) { + self.log('%s: %sms', label, ms.toFixed(3)); + } else { + self.log('%s: %sms', label, ms.toFixed(3), ...data); + } + return false; +} Console.prototype.trace = function trace(...args) { const err = { @@ -340,17 +358,27 @@ Console.prototype.table = function(tabularData, properties) { const getIndexArray = (length) => ArrayFrom({ length }, (_, i) => inspect(i)); const mapIter = isMapIterator(tabularData); + let isKeyValue = false; + let i = 0; if (mapIter) - tabularData = previewEntries(tabularData); + [ tabularData, isKeyValue ] = previewEntries(tabularData); - if (mapIter || isMap(tabularData)) { + if (isKeyValue || isMap(tabularData)) { const keys = []; const values = []; let length = 0; - for (const [k, v] of tabularData) { - keys.push(inspect(k)); - values.push(inspect(v)); - length++; + if (mapIter) { + for (; i < tabularData.length / 2; ++i) { + keys.push(inspect(tabularData[i * 2])); + values.push(inspect(tabularData[i * 2 + 1])); + length++; + } + } else { + for (const [k, v] of tabularData) { + keys.push(inspect(k)); + values.push(inspect(v)); + length++; + } } return final([ iterKey, keyKey, valuesKey @@ -363,9 +391,9 @@ Console.prototype.table = function(tabularData, properties) { const setIter = isSetIterator(tabularData); if (setIter) - tabularData = previewEntries(tabularData); + [ tabularData ] = previewEntries(tabularData); - const setlike = setIter || isSet(tabularData); + const setlike = setIter || (mapIter && !isKeyValue) || isSet(tabularData); if (setlike) { const values = []; let length = 0; @@ -384,7 +412,7 @@ Console.prototype.table = function(tabularData, properties) { const valuesKeyArray = []; const indexKeyArray = ObjectKeys(tabularData); - for (var i = 0; i < indexKeyArray.length; i++) { + for (; i < indexKeyArray.length; i++) { const item = tabularData[indexKeyArray[i]]; const primitive = item === null || (typeof item !== 'function' && typeof item !== 'object'); diff --git a/lib/inspector.js b/lib/inspector.js index f4ec71fd6c2105..f4e365b774be79 100644 --- a/lib/inspector.js +++ b/lib/inspector.js @@ -11,6 +11,7 @@ const { } = require('internal/errors').codes; const util = require('util'); const { Connection, open, url } = process.binding('inspector'); +const { originalConsole } = require('internal/process/per_thread'); if (!Connection || !require('internal/worker').isMainThread) throw new ERR_INSPECTOR_NOT_AVAILABLE(); @@ -103,5 +104,6 @@ module.exports = { open: (port, host, wait) => open(port, host, !!wait), close: process._debugEnd, url: url, + console: originalConsole, Session }; diff --git a/lib/internal/bootstrap/node.js b/lib/internal/bootstrap/node.js index d120d8ccae14e6..016c0c5e2304bc 100644 --- a/lib/internal/bootstrap/node.js +++ b/lib/internal/bootstrap/node.js @@ -18,7 +18,8 @@ // object. { _setupProcessObject, _setupNextTick, _setupPromises, _chdir, _cpuUsage, - _hrtime, _memoryUsage, _rawDebug, + _hrtime, _hrtimeBigInt, + _memoryUsage, _rawDebug, _umask, _initgroups, _setegid, _seteuid, _setgid, _setuid, _setgroups, _shouldAbortOnUncaughtToggle }, @@ -43,33 +44,57 @@ setupGlobalVariables(); - const _process = NativeModule.require('internal/process'); - _process.setupConfig(NativeModule._source); - _process.setupSignalHandlers(); - _process.setupUncaughtExceptionCapture(exceptionHandlerState, - _shouldAbortOnUncaughtToggle); + // Bootstrappers for all threads, including worker threads and main thread + const perThreadSetup = NativeModule.require('internal/process/per_thread'); + // Bootstrappers for the main thread only + let mainThreadSetup; + // Bootstrappers for the worker threads only + let workerThreadSetup; + if (isMainThread) { + mainThreadSetup = NativeModule.require( + 'internal/process/main_thread_only' + ); + } else { + workerThreadSetup = NativeModule.require( + 'internal/process/worker_thread_only' + ); + } + + perThreadSetup.setupAssert(); + perThreadSetup.setupConfig(NativeModule._source); + + if (isMainThread) { + mainThreadSetup.setupSignalHandlers(); + } + + perThreadSetup.setupUncaughtExceptionCapture(exceptionHandlerState, + _shouldAbortOnUncaughtToggle); + NativeModule.require('internal/process/warning').setup(); NativeModule.require('internal/process/next_tick').setup(_setupNextTick, _setupPromises); - NativeModule.require('internal/process/stdio').setup(); - NativeModule.require('internal/process/methods').setup(_chdir, - _umask, - _initgroups, - _setegid, - _seteuid, - _setgid, - _setuid, - _setgroups); + + if (isMainThread) { + mainThreadSetup.setupStdio(); + mainThreadSetup.setupProcessMethods( + _chdir, _umask, _initgroups, _setegid, _seteuid, + _setgid, _setuid, _setgroups + ); + } else { + workerThreadSetup.setupStdio(); + } const perf = process.binding('performance'); const { NODE_PERFORMANCE_MILESTONE_BOOTSTRAP_COMPLETE, } = perf.constants; - _process.setup_hrtime(_hrtime); - _process.setup_cpuUsage(_cpuUsage); - _process.setupMemoryUsage(_memoryUsage); - _process.setupKillAndExit(); + perThreadSetup.setupRawDebug(_rawDebug); + perThreadSetup.setupHrtime(_hrtime, _hrtimeBigInt); + perThreadSetup.setupCpuUsage(_cpuUsage); + perThreadSetup.setupMemoryUsage(_memoryUsage); + perThreadSetup.setupKillAndExit(); + if (global.__coverage__) NativeModule.require('internal/process/write-coverage').setup(); @@ -89,18 +114,23 @@ NativeModule.require('internal/inspector_async_hook').setup(); } - if (isMainThread) - _process.setupChannel(); - - _process.setupRawDebug(_rawDebug); + if (isMainThread) { + mainThreadSetup.setupChildProcessIpcChannel(); + } const browserGlobals = !process._noBrowserGlobals; if (browserGlobals) { + // we are setting this here to foward it to the inspector later + perThreadSetup.originalConsole = global.console; setupGlobalTimeouts(); setupGlobalConsole(); setupGlobalURL(); } + if (process.binding('config').experimentalWorker) { + setupDOMException(); + } + // On OpenBSD process.execPath will be relative unless we // get the full path before process.execPath is used. if (process.platform === 'openbsd') { @@ -381,6 +411,11 @@ }); } + function setupDOMException() { + // Registers the constructor with C++. + NativeModule.require('internal/domexception'); + } + function setupInspector(originalConsole, wrappedConsole, CJSModule) { if (!process.config.variables.v8_enable_inspector) { return; @@ -440,6 +475,7 @@ try { if (!process._exiting) { process._exiting = true; + process.exitCode = 1; process.emit('exit', 1); } } catch { diff --git a/lib/internal/cli_table.js b/lib/internal/cli_table.js index 4c07d92eebdaa7..f6c711ece8e0ad 100644 --- a/lib/internal/cli_table.js +++ b/lib/internal/cli_table.js @@ -51,11 +51,11 @@ const table = (head, columns) => { for (var i = 0; i < head.length; i++) { const column = columns[i]; for (var j = 0; j < longestColumn; j++) { - if (!rows[j]) + if (rows[j] === undefined) rows[j] = []; - const v = rows[j][i] = HasOwnProperty(column, j) ? column[j] : ''; + const value = rows[j][i] = HasOwnProperty(column, j) ? column[j] : ''; const width = columnWidths[i] || 0; - const counted = countSymbols(v); + const counted = countSymbols(value); columnWidths[i] = Math.max(width, counted); } } @@ -63,19 +63,16 @@ const table = (head, columns) => { const divider = columnWidths.map((i) => tableChars.middleMiddle.repeat(i + 2)); - const tl = tableChars.topLeft; - const tr = tableChars.topRight; - const lm = tableChars.leftMiddle; - let result = `${tl}${divider.join(tableChars.topMiddle)}${tr} -${renderRow(head, columnWidths)} -${lm}${divider.join(tableChars.rowMiddle)}${tableChars.rightMiddle} -`; + let result = `${tableChars.topLeft}${divider.join(tableChars.topMiddle)}` + + `${tableChars.topRight}\n${renderRow(head, columnWidths)}\n` + + `${tableChars.leftMiddle}${divider.join(tableChars.rowMiddle)}` + + `${tableChars.rightMiddle}\n`; for (const row of rows) result += `${renderRow(row, columnWidths)}\n`; - result += `${tableChars.bottomLeft}${ - divider.join(tableChars.bottomMiddle)}${tableChars.bottomRight}`; + result += `${tableChars.bottomLeft}${divider.join(tableChars.bottomMiddle)}` + + tableChars.bottomRight; return result; }; diff --git a/lib/internal/crypto/random.js b/lib/internal/crypto/random.js index a30f8acdf27786..15fbc37c2239d7 100644 --- a/lib/internal/crypto/random.js +++ b/lib/internal/crypto/random.js @@ -1,7 +1,7 @@ 'use strict'; const { AsyncWrap, Providers } = process.binding('async_wrap'); -const { Buffer } = require('buffer'); +const { Buffer, kMaxLength } = require('buffer'); const { randomBytes: _randomBytes } = process.binding('crypto'); const { ERR_INVALID_ARG_TYPE, @@ -10,7 +10,6 @@ const { } = require('internal/errors').codes; const { isArrayBufferView } = require('internal/util/types'); -const { kMaxLength } = require('buffer'); const kMaxUint32 = 2 ** 32 - 1; const kMaxPossibleLength = Math.min(kMaxLength, kMaxUint32); diff --git a/lib/internal/crypto/sig.js b/lib/internal/crypto/sig.js index b6f8ceb50186d1..8aff7354735404 100644 --- a/lib/internal/crypto/sig.js +++ b/lib/internal/crypto/sig.js @@ -57,10 +57,11 @@ function getSaltLength(options) { function getIntOption(name, defaultValue, options) { if (options.hasOwnProperty(name)) { - if (options[name] === options[name] >> 0) { - return options[name]; + const value = options[name]; + if (value === value >> 0) { + return value; } else { - throw new ERR_INVALID_OPT_VALUE(name, options[name]); + throw new ERR_INVALID_OPT_VALUE(name, value); } } return defaultValue; diff --git a/lib/internal/domexception.js b/lib/internal/domexception.js new file mode 100644 index 00000000000000..fe371e099eb17f --- /dev/null +++ b/lib/internal/domexception.js @@ -0,0 +1,83 @@ +'use strict'; + +const { internalBinding } = require('internal/bootstrap/loaders'); +const { registerDOMException } = internalBinding('messaging'); +const { ERR_INVALID_THIS } = require('internal/errors').codes; + +const internalsMap = new WeakMap(); + +const nameToCodeMap = new Map(); + +class DOMException extends Error { + constructor(message = '', name = 'Error') { + super(); + internalsMap.set(this, { + message: `${message}`, + name: `${name}` + }); + } + + get name() { + const internals = internalsMap.get(this); + if (internals === undefined) { + throw new ERR_INVALID_THIS('DOMException'); + } + return internals.name; + } + + get message() { + const internals = internalsMap.get(this); + if (internals === undefined) { + throw new ERR_INVALID_THIS('DOMException'); + } + return internals.message; + } + + get code() { + const internals = internalsMap.get(this); + if (internals === undefined) { + throw new ERR_INVALID_THIS('DOMException'); + } + const code = nameToCodeMap.get(internals.name); + return code === undefined ? 0 : code; + } +} + +for (const [name, codeName, value] of [ + ['IndexSizeError', 'INDEX_SIZE_ERR', 1], + ['DOMStringSizeError', 'DOMSTRING_SIZE_ERR', 2], + ['HierarchyRequestError', 'HIERARCHY_REQUEST_ERR', 3], + ['WrongDocumentError', 'WRONG_DOCUMENT_ERR', 4], + ['InvalidCharacterError', 'INVALID_CHARACTER_ERR', 5], + ['NoDataAllowedError', 'NO_DATA_ALLOWED_ERR', 6], + ['NoModificationAllowedError', 'NO_MODIFICATION_ALLOWED_ERR', 7], + ['NotFoundError', 'NOT_FOUND_ERR', 8], + ['NotSupportedError', 'NOT_SUPPORTED_ERR', 9], + ['InUseAttributeError', 'INUSE_ATTRIBUTE_ERR', 10], + ['InvalidStateError', 'INVALID_STATE_ERR', 11], + ['SyntaxError', 'SYNTAX_ERR', 12], + ['InvalidModificationError', 'INVALID_MODIFICATION_ERR', 13], + ['NamespaceError', 'NAMESPACE_ERR', 14], + ['InvalidAccessError', 'INVALID_ACCESS_ERR', 15], + ['ValidationError', 'VALIDATION_ERR', 16], + ['TypeMismatchError', 'TYPE_MISMATCH_ERR', 17], + ['SecurityError', 'SECURITY_ERR', 18], + ['NetworkError', 'NETWORK_ERR', 19], + ['AbortError', 'ABORT_ERR', 20], + ['URLMismatchError', 'URL_MISMATCH_ERR', 21], + ['QuotaExceededError', 'QUOTA_EXCEEDED_ERR', 22], + ['TimeoutError', 'TIMEOUT_ERR', 23], + ['InvalidNodeTypeError', 'INVALID_NODE_TYPE_ERR', 24], + ['DataCloneError', 'DATA_CLONE_ERR', 25] + // There are some more error names, but since they don't have codes assigned, + // we don't need to care about them. +]) { + const desc = { enumerable: true, value }; + Object.defineProperty(DOMException, codeName, desc); + Object.defineProperty(DOMException.prototype, codeName, desc); + nameToCodeMap.set(name, value); +} + +module.exports = DOMException; + +registerDOMException(DOMException); diff --git a/lib/internal/errors.js b/lib/internal/errors.js index c3f1c374011b05..aef05489b48c74 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -585,6 +585,7 @@ E('ERR_HTTP2_SEND_FILE', 'Directories cannot be sent', Error); E('ERR_HTTP2_SEND_FILE_NOSEEK', 'Offset or length can only be specified for regular files', Error); E('ERR_HTTP2_SESSION_ERROR', 'Session closed with error code %s', Error); +E('ERR_HTTP2_SETTINGS_CANCEL', 'HTTP2 session settings canceled', Error); E('ERR_HTTP2_SOCKET_BOUND', 'The socket is already bound to an Http2Session', Error); E('ERR_HTTP2_STATUS_101', @@ -812,6 +813,7 @@ E('ERR_TLS_CERT_ALTNAME_INVALID', 'Hostname/IP does not match certificate\'s altnames: %s', Error); E('ERR_TLS_DH_PARAM_SIZE', 'DH parameter size %s is less than 2048', Error); E('ERR_TLS_HANDSHAKE_TIMEOUT', 'TLS handshake timeout', Error); +E('ERR_TLS_RENEGOTIATE', 'Attempt to renegotiate TLS session failed', Error); E('ERR_TLS_RENEGOTIATION_DISABLED', 'TLS session renegotiation disabled for this socket', Error); diff --git a/lib/internal/fs/promises.js b/lib/internal/fs/promises.js index befcccb2769589..a8d94a739e2bee 100644 --- a/lib/internal/fs/promises.js +++ b/lib/internal/fs/promises.js @@ -125,6 +125,10 @@ async function writeFileHandle(filehandle, data, options) { } while (remaining > 0); } +// Note: This is different from kReadFileBufferLength used for non-promisified +// fs.readFile. +const kReadFileMaxChunkSize = 16384; + async function readFileHandle(filehandle, options) { const statFields = await binding.fstat(filehandle.fd, false, kUsePromises); @@ -135,22 +139,19 @@ async function readFileHandle(filehandle, options) { size = 0; } - if (size === 0) - return options.encoding ? '' : Buffer.alloc(0); - if (size > kMaxLength) throw new ERR_FS_FILE_TOO_LARGE(size); const chunks = []; - const chunkSize = Math.min(size, 16384); - let totalRead = 0; + const chunkSize = size === 0 ? + kReadFileMaxChunkSize : + Math.min(size, kReadFileMaxChunkSize); let endOfFile = false; do { const buf = Buffer.alloc(chunkSize); const { bytesRead, buffer } = - await read(filehandle, buf, 0, chunkSize, totalRead); - totalRead += bytesRead; - endOfFile = bytesRead !== chunkSize; + await read(filehandle, buf, 0, chunkSize, -1); + endOfFile = bytesRead === 0; if (bytesRead > 0) chunks.push(buffer.slice(0, bytesRead)); } while (!endOfFile); @@ -370,7 +371,7 @@ async function chmod(path, mode) { async function lchmod(path, mode) { if (O_SYMLINK === undefined) - throw new ERR_METHOD_NOT_IMPLEMENTED(); + throw new ERR_METHOD_NOT_IMPLEMENTED('lchmod()'); const fd = await open(path, O_WRONLY | O_SYMLINK); return fchmod(fd, mode).finally(fd.close.bind(fd)); diff --git a/lib/internal/http2/compat.js b/lib/internal/http2/compat.js index 88879484ebaab7..4a006107b2fcea 100644 --- a/lib/internal/http2/compat.js +++ b/lib/internal/http2/compat.js @@ -391,6 +391,8 @@ function onStreamCloseResponse() { state.closed = true; this[kProxySocket] = null; + + this.removeListener('wantTrailers', onStreamTrailersReady); this[kResponse] = undefined; res.emit('finish'); diff --git a/lib/internal/http2/core.js b/lib/internal/http2/core.js index bbbc3c21300a74..db251caf5f73ef 100644 --- a/lib/internal/http2/core.js +++ b/lib/internal/http2/core.js @@ -2,21 +2,35 @@ /* eslint-disable no-use-before-define */ -require('internal/util').assertCrypto(); +const { + assertCrypto, + customInspectSymbol: kInspect, + promisify +} = require('internal/util'); + +assertCrypto(); -const { internalBinding } = require('internal/bootstrap/loaders'); -const { async_id_symbol } = require('internal/async_hooks').symbols; -const { UV_EOF } = process.binding('uv'); -const http = require('http'); -const binding = process.binding('http2'); -const { FileHandle } = process.binding('fs'); -const { StreamPipe } = internalBinding('stream_pipe'); const assert = require('assert'); const EventEmitter = require('events'); +const fs = require('fs'); +const http = require('http'); const net = require('net'); +const { Duplex } = require('stream'); const tls = require('tls'); +const { URL } = require('url'); const util = require('util'); -const fs = require('fs'); + +const { kIncomingMessage } = require('_http_common'); +const { kServerResponse } = require('_http_server'); +const { StreamWrap } = require('_stream_wrap'); + +const { + defaultTriggerAsyncIdScope, + symbols: { + async_id_symbol, + }, +} = require('internal/async_hooks'); +const { internalBinding } = require('internal/bootstrap/loaders'); const { codes: { ERR_HTTP2_ALTSVC_INVALID_ORIGIN, @@ -60,30 +74,11 @@ const { ERR_SOCKET_CLOSED } } = require('internal/errors'); -const { StreamWrap } = require('_stream_wrap'); -const { Duplex } = require('stream'); -const { URL } = require('url'); +const { utcDate } = require('internal/http'); const { onServerStream, Http2ServerRequest, Http2ServerResponse, } = require('internal/http2/compat'); -const { utcDate } = require('internal/http'); -const { - promisify, - customInspectSymbol: kInspect -} = require('internal/util'); -const { isArrayBufferView } = require('internal/util/types'); -const { defaultTriggerAsyncIdScope } = require('internal/async_hooks'); -const { _connectionListener: httpConnectionListener } = http; -const { createPromise, promiseResolve } = process.binding('util'); -const debug = util.debuglog('http2'); - -const kMaxFrameSize = (2 ** 24) - 1; -const kMaxInt = (2 ** 32) - 1; -const kMaxStreams = (2 ** 31) - 1; - -// eslint-disable-next-line no-control-regex -const kQuotedString = /^[\x09\x20-\x5b\x5d-\x7e\x80-\xff]*$/; const { assertIsObject, @@ -103,27 +98,40 @@ const { updateOptionsBuffer, updateSettingsBuffer } = require('internal/http2/util'); - -const { - kTimeout, - setUnrefTimeout, - validateTimerDuration -} = require('internal/timers'); const { createWriteWrap, writeGeneric, writevGeneric } = require('internal/stream_base_commons'); +const { + kTimeout, + setUnrefTimeout, + validateTimerDuration +} = require('internal/timers'); +const { isArrayBufferView } = require('internal/util/types'); +const { FileHandle } = process.binding('fs'); +const binding = process.binding('http2'); const { ShutdownWrap } = process.binding('stream_wrap'); +const { createPromise, promiseResolve } = process.binding('util'); +const { UV_EOF } = process.binding('uv'); + +const { StreamPipe } = internalBinding('stream_pipe'); +const { _connectionListener: httpConnectionListener } = http; +const debug = util.debuglog('http2'); + +const kMaxFrameSize = (2 ** 24) - 1; +const kMaxInt = (2 ** 32) - 1; +const kMaxStreams = (2 ** 31) - 1; + +// eslint-disable-next-line no-control-regex +const kQuotedString = /^[\x09\x20-\x5b\x5d-\x7e\x80-\xff]*$/; + const { constants, nameForErrorCode } = binding; const NETServer = net.Server; const TLSServer = tls.Server; -const { kIncomingMessage } = require('_http_common'); -const { kServerResponse } = require('_http_server'); - const kAlpnProtocol = Symbol('alpnProtocol'); const kAuthority = Symbol('authority'); const kEncrypted = Symbol('encrypted'); @@ -2814,13 +2822,13 @@ function getUnpackedSettings(buf, options = {}) { // Exports module.exports = { + connect, constants, + createServer, + createSecureServer, getDefaultSettings, getPackedSettings, getUnpackedSettings, - createServer, - createSecureServer, - connect, Http2Session, Http2Stream, Http2ServerRequest, diff --git a/lib/internal/http2/util.js b/lib/internal/http2/util.js index bf5d07219391e2..bc93662a70239d 100644 --- a/lib/internal/http2/util.js +++ b/lib/internal/http2/util.js @@ -509,7 +509,7 @@ function toHeaderObject(headers) { value |= 0; var existing = obj[name]; if (existing === undefined) { - obj[name] = value; + obj[name] = name === HTTP2_HEADER_SET_COOKIE ? [value] : value; } else if (!kSingleValueHeaders.has(name)) { switch (name) { case HTTP2_HEADER_COOKIE: @@ -528,10 +528,7 @@ function toHeaderObject(headers) { // fields with the same name. Since it cannot be combined into a // single field-value, recipients ought to handle "Set-Cookie" as a // special case while processing header fields." - if (Array.isArray(existing)) - existing.push(value); - else - obj[name] = [existing, value]; + existing.push(value); break; default: // https://tools.ietf.org/html/rfc7230#section-3.2.2 diff --git a/lib/internal/modules/esm/translators.js b/lib/internal/modules/esm/translators.js index 476a420b8a9620..618f1adac8deb7 100644 --- a/lib/internal/modules/esm/translators.js +++ b/lib/internal/modules/esm/translators.js @@ -14,12 +14,14 @@ const fs = require('fs'); const { _makeLong } = require('path'); const { SafeMap } = require('internal/safe_globals'); const { URL } = require('url'); -const debug = require('util').debuglog('esm'); -const readFileAsync = require('util').promisify(fs.readFile); +const { debuglog, promisify } = require('util'); +const readFileAsync = promisify(fs.readFile); const readFileSync = fs.readFileSync; const StringReplace = Function.call.bind(String.prototype.replace); const JsonParse = JSON.parse; +const debug = debuglog('esm'); + const translators = new SafeMap(); module.exports = translators; diff --git a/lib/internal/process/main_thread_only.js b/lib/internal/process/main_thread_only.js new file mode 100644 index 00000000000000..814cca266d6675 --- /dev/null +++ b/lib/internal/process/main_thread_only.js @@ -0,0 +1,127 @@ +'use strict'; + +// This file contains process bootstrappers that can only be +// run in the main thread + +const { + errnoException +} = require('internal/errors'); + +const { + setupProcessStdio, + getMainThreadStdio +} = require('internal/process/stdio'); + +const assert = require('assert').strict; + +function setupStdio() { + setupProcessStdio(getMainThreadStdio()); +} + +// Non-POSIX platforms like Windows don't have certain methods. +// Workers also lack these methods since they change process-global state. +function setupProcessMethods(_chdir, _umask, _initgroups, _setegid, + _seteuid, _setgid, _setuid, _setgroups) { + if (_setgid !== undefined) { + setupPosixMethods(_initgroups, _setegid, _seteuid, + _setgid, _setuid, _setgroups); + } + + process.chdir = function chdir(...args) { + return _chdir(...args); + }; + + process.umask = function umask(...args) { + return _umask(...args); + }; +} + +function setupPosixMethods(_initgroups, _setegid, _seteuid, + _setgid, _setuid, _setgroups) { + + process.initgroups = function initgroups(...args) { + return _initgroups(...args); + }; + + process.setegid = function setegid(...args) { + return _setegid(...args); + }; + + process.seteuid = function seteuid(...args) { + return _seteuid(...args); + }; + + process.setgid = function setgid(...args) { + return _setgid(...args); + }; + + process.setuid = function setuid(...args) { + return _setuid(...args); + }; + + process.setgroups = function setgroups(...args) { + return _setgroups(...args); + }; +} + +// Worker threads don't receive signals. +function setupSignalHandlers() { + const constants = process.binding('constants').os.signals; + const signalWraps = Object.create(null); + let Signal; + + function isSignal(event) { + return typeof event === 'string' && constants[event] !== undefined; + } + + // Detect presence of a listener for the special signal types + process.on('newListener', function(type) { + if (isSignal(type) && signalWraps[type] === undefined) { + if (Signal === undefined) + Signal = process.binding('signal_wrap').Signal; + const wrap = new Signal(); + + wrap.unref(); + + wrap.onsignal = process.emit.bind(process, type, type); + + const signum = constants[type]; + const err = wrap.start(signum); + if (err) { + wrap.close(); + throw errnoException(err, 'uv_signal_start'); + } + + signalWraps[type] = wrap; + } + }); + + process.on('removeListener', function(type) { + if (signalWraps[type] !== undefined && this.listenerCount(type) === 0) { + signalWraps[type].close(); + delete signalWraps[type]; + } + }); +} + +function setupChildProcessIpcChannel() { + // If we were spawned with env NODE_CHANNEL_FD then load that up and + // start parsing data from that stream. + if (process.env.NODE_CHANNEL_FD) { + const fd = parseInt(process.env.NODE_CHANNEL_FD, 10); + assert(fd >= 0); + + // Make sure it's not accidentally inherited by child processes. + delete process.env.NODE_CHANNEL_FD; + + require('child_process')._forkChild(fd); + assert(process.send); + } +} + +module.exports = { + setupStdio, + setupProcessMethods, + setupSignalHandlers, + setupChildProcessIpcChannel +}; diff --git a/lib/internal/process/methods.js b/lib/internal/process/methods.js deleted file mode 100644 index 38ba5f84b5a30a..00000000000000 --- a/lib/internal/process/methods.js +++ /dev/null @@ -1,56 +0,0 @@ -'use strict'; - -const { - isMainThread -} = require('internal/worker'); - -function setupProcessMethods(_chdir, _umask, _initgroups, _setegid, - _seteuid, _setgid, _setuid, _setgroups) { - // Non-POSIX platforms like Windows don't have certain methods. - // Workers also lack these methods since they change process-global state. - if (!isMainThread) - return; - - if (_setgid !== undefined) { - setupPosixMethods(_initgroups, _setegid, _seteuid, - _setgid, _setuid, _setgroups); - } - - process.chdir = function chdir(...args) { - return _chdir(...args); - }; - - process.umask = function umask(...args) { - return _umask(...args); - }; -} - -function setupPosixMethods(_initgroups, _setegid, _seteuid, - _setgid, _setuid, _setgroups) { - - process.initgroups = function initgroups(...args) { - return _initgroups(...args); - }; - - process.setegid = function setegid(...args) { - return _setegid(...args); - }; - - process.seteuid = function seteuid(...args) { - return _seteuid(...args); - }; - - process.setgid = function setgid(...args) { - return _setgid(...args); - }; - - process.setuid = function setuid(...args) { - return _setuid(...args); - }; - - process.setgroups = function setgroups(...args) { - return _setgroups(...args); - }; -} - -exports.setup = setupProcessMethods; diff --git a/lib/internal/process.js b/lib/internal/process/per_thread.js similarity index 75% rename from lib/internal/process.js rename to lib/internal/process/per_thread.js index 45d9c77b32007f..fbf60bac6e9fff 100644 --- a/lib/internal/process.js +++ b/lib/internal/process/per_thread.js @@ -1,5 +1,9 @@ 'use strict'; +// This files contains process bootstrappers that can be +// run when setting up each thread, including the main +// thread and the worker threads. + const { errnoException, codes: { @@ -14,19 +18,19 @@ const { } = require('internal/errors'); const util = require('util'); const constants = process.binding('constants').os.signals; -const assert = require('assert').strict; const { deprecate } = require('internal/util'); -const { isMainThread } = require('internal/worker'); -process.assert = deprecate( - function(x, msg) { - if (!x) throw new ERR_ASSERTION(msg || 'assertion error'); - }, - 'process.assert() is deprecated. Please use the `assert` module instead.', - 'DEP0100'); +function setupAssert() { + process.assert = deprecate( + function(x, msg) { + if (!x) throw new ERR_ASSERTION(msg || 'assertion error'); + }, + 'process.assert() is deprecated. Please use the `assert` module instead.', + 'DEP0100'); +} // Set up the process.cpuUsage() function. -function setup_cpuUsage(_cpuUsage) { +function setupCpuUsage(_cpuUsage) { // Create the argument array that will be passed to the native function. const cpuValues = new Float64Array(2); @@ -90,7 +94,7 @@ function setup_cpuUsage(_cpuUsage) { // The 3 entries filled in by the original process.hrtime contains // the upper/lower 32 bits of the second part of the value, // and the remaining nanoseconds of the value. -function setup_hrtime(_hrtime) { +function setupHrtime(_hrtime, _hrtimeBigInt) { const hrValues = new Uint32Array(3); process.hrtime = function hrtime(time) { @@ -115,6 +119,14 @@ function setup_hrtime(_hrtime) { hrValues[2] ]; }; + + // Use a BigUint64Array in the closure because V8 does not have an API for + // creating a BigInt out of a uint64_t yet. + const hrBigintValues = new BigUint64Array(1); + process.hrtime.bigint = function() { + _hrtimeBigInt(hrBigintValues); + return hrBigintValues[0]; + }; } function setupMemoryUsage(_memoryUsage) { @@ -185,67 +197,6 @@ function setupKillAndExit() { }; } - -function setupSignalHandlers() { - if (!isMainThread) { - // Worker threads don't receive signals. - return; - } - - const signalWraps = Object.create(null); - let Signal; - - function isSignal(event) { - return typeof event === 'string' && constants[event] !== undefined; - } - - // Detect presence of a listener for the special signal types - process.on('newListener', function(type) { - if (isSignal(type) && signalWraps[type] === undefined) { - if (Signal === undefined) - Signal = process.binding('signal_wrap').Signal; - const wrap = new Signal(); - - wrap.unref(); - - wrap.onsignal = process.emit.bind(process, type, type); - - const signum = constants[type]; - const err = wrap.start(signum); - if (err) { - wrap.close(); - throw errnoException(err, 'uv_signal_start'); - } - - signalWraps[type] = wrap; - } - }); - - process.on('removeListener', function(type) { - if (signalWraps[type] !== undefined && this.listenerCount(type) === 0) { - signalWraps[type].close(); - delete signalWraps[type]; - } - }); -} - - -function setupChannel() { - // If we were spawned with env NODE_CHANNEL_FD then load that up and - // start parsing data from that stream. - if (process.env.NODE_CHANNEL_FD) { - const fd = parseInt(process.env.NODE_CHANNEL_FD, 10); - assert(fd >= 0); - - // Make sure it's not accidentally inherited by child processes. - delete process.env.NODE_CHANNEL_FD; - - require('child_process')._forkChild(fd); - assert(process.send); - } -} - - function setupRawDebug(_rawDebug) { process._rawDebug = function() { _rawDebug(util.format.apply(null, arguments)); @@ -280,13 +231,12 @@ function setupUncaughtExceptionCapture(exceptionHandlerState, } module.exports = { - setup_cpuUsage, - setup_hrtime, + setupAssert, + setupCpuUsage, + setupHrtime, setupMemoryUsage, setupConfig, setupKillAndExit, - setupSignalHandlers, - setupChannel, setupRawDebug, setupUncaughtExceptionCapture }; diff --git a/lib/internal/process/stdio.js b/lib/internal/process/stdio.js index f4a07cd06d09d9..e4207a1aa768bd 100644 --- a/lib/internal/process/stdio.js +++ b/lib/internal/process/stdio.js @@ -6,21 +6,17 @@ const { ERR_UNKNOWN_STDIN_TYPE, ERR_UNKNOWN_STREAM_TYPE } = require('internal/errors').codes; -const { - isMainThread, - workerStdio -} = require('internal/worker'); -exports.setup = setupStdio; +exports.setupProcessStdio = setupProcessStdio; +exports.getMainThreadStdio = getMainThreadStdio; -function setupStdio() { +function getMainThreadStdio() { var stdin; var stdout; var stderr; function getStdout() { if (stdout) return stdout; - if (!isMainThread) return workerStdio.stdout; stdout = createWritableStdioStream(1); stdout.destroySoon = stdout.destroy; stdout._destroy = function(er, cb) { @@ -36,7 +32,6 @@ function setupStdio() { function getStderr() { if (stderr) return stderr; - if (!isMainThread) return workerStdio.stderr; stderr = createWritableStdioStream(2); stderr.destroySoon = stderr.destroy; stderr._destroy = function(er, cb) { @@ -52,8 +47,6 @@ function setupStdio() { function getStdin() { if (stdin) return stdin; - if (!isMainThread) return workerStdio.stdin; - const tty_wrap = process.binding('tty_wrap'); const fd = 0; @@ -136,6 +129,14 @@ function setupStdio() { return stdin; } + return { + getStdout, + getStderr, + getStdin + }; +} + +function setupProcessStdio({ getStdout, getStdin, getStderr }) { Object.defineProperty(process, 'stdout', { configurable: true, enumerable: true, diff --git a/lib/internal/process/worker_thread_only.js b/lib/internal/process/worker_thread_only.js new file mode 100644 index 00000000000000..834ba6078fca44 --- /dev/null +++ b/lib/internal/process/worker_thread_only.js @@ -0,0 +1,24 @@ +'use strict'; + +// This file contains process bootstrappers that can only be +// run in the worker thread. + +const { + setupProcessStdio +} = require('internal/process/stdio'); + +const { + workerStdio +} = require('internal/worker'); + +function setupStdio() { + setupProcessStdio({ + getStdout: () => workerStdio.stdout, + getStderr: () => workerStdio.stderr, + getStdin: () => workerStdio.stdin + }); +} + +module.exports = { + setupStdio +}; diff --git a/lib/internal/test/heap.js b/lib/internal/test/heap.js new file mode 100644 index 00000000000000..a9260f651b9c1a --- /dev/null +++ b/lib/internal/test/heap.js @@ -0,0 +1,87 @@ +'use strict'; + +process.emitWarning( + 'These APIs are exposed only for testing and are not ' + + 'tracked by any versioning system or deprecation process.', + 'internal/test/heap'); + +const { internalBinding } = require('internal/bootstrap/loaders'); +const { createHeapDump, buildEmbedderGraph } = internalBinding('heap_utils'); +const assert = require('assert'); + +// This is not suitable for production code. It creates a full V8 heap dump, +// parses it as JSON, and then creates complex objects from it, leading +// to significantly increased memory usage. +function createJSHeapDump() { + const dump = createHeapDump(); + const meta = dump.snapshot.meta; + + const nodes = + readHeapInfo(dump.nodes, meta.node_fields, meta.node_types, dump.strings); + const edges = + readHeapInfo(dump.edges, meta.edge_fields, meta.edge_types, dump.strings); + + for (const node of nodes) { + node.incomingEdges = []; + node.outgoingEdges = []; + } + + let fromNodeIndex = 0; + let edgeIndex = 0; + for (const { type, name_or_index, to_node } of edges) { + while (edgeIndex === nodes[fromNodeIndex].edge_count) { + edgeIndex = 0; + fromNodeIndex++; + } + const toNode = nodes[to_node / meta.node_fields.length]; + const fromNode = nodes[fromNodeIndex]; + const edge = { + type, + toNode, + fromNode, + name: typeof name_or_index === 'string' ? name_or_index : null + }; + toNode.incomingEdges.push(edge); + fromNode.outgoingEdges.push(edge); + edgeIndex++; + } + + for (const node of nodes) + assert.strictEqual(node.edge_count, node.outgoingEdges.length); + + return nodes; +} + +function readHeapInfo(raw, fields, types, strings) { + const items = []; + + for (var i = 0; i < raw.length; i += fields.length) { + const item = {}; + for (var j = 0; j < fields.length; j++) { + const name = fields[j]; + let type = types[j]; + if (Array.isArray(type)) { + item[name] = type[raw[i + j]]; + } else if (name === 'name_or_index') { // type === 'string_or_number' + if (item.type === 'element' || item.type === 'hidden') + type = 'number'; + else + type = 'string'; + } + + if (type === 'string') { + item[name] = strings[raw[i + j]]; + } else if (type === 'number' || type === 'node') { + item[name] = raw[i + j]; + } + } + items.push(item); + } + + return items; +} + +module.exports = { + createJSHeapDump, + buildEmbedderGraph +}; diff --git a/lib/internal/util.js b/lib/internal/util.js index 07515e2e090daa..bc513e7fedf79e 100644 --- a/lib/internal/util.js +++ b/lib/internal/util.js @@ -115,7 +115,7 @@ function slowCases(enc) { if (enc === 'ucs2' || enc === 'UCS2') return 'utf16le'; enc = `${enc}`.toLowerCase(); if (enc === 'utf8') return 'utf8'; - if (enc === 'ucs2' || enc === 'UCS2') return 'utf16le'; + if (enc === 'ucs2') return 'utf16le'; break; case 3: if (enc === 'hex' || enc === 'HEX' || `${enc}`.toLowerCase() === 'hex') @@ -131,6 +131,7 @@ function slowCases(enc) { if (enc === 'utf-8') return 'utf8'; if (enc === 'ascii') return 'ascii'; if (enc === 'usc-2') return 'utf16le'; + if (enc === 'ucs-2') return 'utf16le'; break; case 6: if (enc === 'base64') return 'base64'; diff --git a/lib/internal/worker.js b/lib/internal/worker.js index bcc864b5b8b330..26c16f86e3e8fc 100644 --- a/lib/internal/worker.js +++ b/lib/internal/worker.js @@ -467,6 +467,8 @@ function setupChild(evalScript) { else port.postMessage({ type: messageTypes.COULD_NOT_SERIALIZE_ERROR }); clearAsyncIdStack(); + + process.exit(); } } } diff --git a/lib/net.js b/lib/net.js index 2393539737910a..889f28b0aa7042 100644 --- a/lib/net.js +++ b/lib/net.js @@ -408,6 +408,7 @@ function writeAfterFIN(chunk, encoding, cb) { } Socket.prototype.setTimeout = function(msecs, callback) { + this.timeout = msecs; // Type checking identical to timers.enroll() msecs = validateTimerDuration(msecs); diff --git a/lib/punycode.js b/lib/punycode.js index 34da3ca5ad13b6..ea61fd0d39a39d 100644 --- a/lib/punycode.js +++ b/lib/punycode.js @@ -15,7 +15,7 @@ const delimiter = '-'; // '\x2D' /** Regular expressions */ const regexPunycode = /^xn--/; -const regexNonASCII = /[^\x20-\x7E]/; // unprintable ASCII chars + non-ASCII chars +const regexNonASCII = /[^\0-\x7E]/; // non-ASCII chars const regexSeparators = /[\x2E\u3002\uFF0E\uFF61]/g; // RFC 3490 separators /** Error messages */ @@ -210,7 +210,7 @@ const decode = function(input) { basic = 0; } - for (var j = 0; j < basic; ++j) { + for (let j = 0; j < basic; ++j) { // if it's not a basic code point if (input.charCodeAt(j) >= 0x80) { error('not-basic'); @@ -221,7 +221,7 @@ const decode = function(input) { // Main decoding loop: start just after the last delimiter if any basic code // points were copied; start at the beginning otherwise. - for (var index = basic > 0 ? basic + 1 : 0; index < inputLength; /* no final expression */) { + for (let index = basic > 0 ? basic + 1 : 0; index < inputLength; /* no final expression */) { // `index` is the index of the next character to be consumed. // Decode a generalized variable-length integer into `delta`, @@ -229,7 +229,7 @@ const decode = function(input) { // if we increase `i` as we go, then subtract off its starting // value at the end to obtain `delta`. let oldi = i; - for (var w = 1, k = base; /* no condition */; k += base) { + for (let w = 1, k = base; /* no condition */; k += base) { if (index >= inputLength) { error('invalid-input'); @@ -345,7 +345,7 @@ const encode = function(input) { if (currentValue == n) { // Represent delta as a generalized variable-length integer. let q = delta; - for (var k = base; /* no condition */; k += base) { + for (let k = base; /* no condition */; k += base) { const t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias); if (q < t) { break; @@ -419,7 +419,7 @@ const punycode = { * @memberOf punycode * @type String */ - 'version': '2.0.0', + 'version': '2.1.0', /** * An object of methods to convert from JavaScript's internal character * representation (UCS-2) to Unicode code points, and back. diff --git a/lib/repl.js b/lib/repl.js index 6345c742f68f33..92c90de7bb1646 100644 --- a/lib/repl.js +++ b/lib/repl.js @@ -1229,20 +1229,20 @@ function complete(line, callback) { // Completion group 0 is the "closest" // (least far up the inheritance chain) // so we put its completions last: to be closest in the REPL. - for (i = completionGroups.length - 1; i >= 0; i--) { + for (i = 0; i < completionGroups.length; i++) { group = completionGroups[i]; group.sort(); - for (var j = 0; j < group.length; j++) { + for (var j = group.length - 1; j >= 0; j--) { c = group[j]; if (!hasOwnProperty(uniq, c)) { - completions.push(c); + completions.unshift(c); uniq[c] = true; } } - completions.push(''); // Separator btwn groups + completions.unshift(''); // Separator btwn groups } - while (completions.length && completions[completions.length - 1] === '') { - completions.pop(); + while (completions.length && completions[0] === '') { + completions.shift(); } } diff --git a/lib/util.js b/lib/util.js index 15611fdae108df..8160d802b0332f 100644 --- a/lib/util.js +++ b/lib/util.js @@ -984,7 +984,7 @@ function formatMap(ctx, value, recurseTimes, keys) { function formatWeakSet(ctx, value, recurseTimes, keys) { const maxArrayLength = Math.max(ctx.maxArrayLength, 0); - const entries = previewEntries(value).slice(0, maxArrayLength + 1); + const [ entries ] = previewEntries(value).slice(0, maxArrayLength + 1); const maxLength = Math.min(maxArrayLength, entries.length); let output = new Array(maxLength); for (var i = 0; i < maxLength; ++i) @@ -1001,14 +1001,16 @@ function formatWeakSet(ctx, value, recurseTimes, keys) { function formatWeakMap(ctx, value, recurseTimes, keys) { const maxArrayLength = Math.max(ctx.maxArrayLength, 0); - const entries = previewEntries(value).slice(0, maxArrayLength + 1); - const remainder = entries.length > maxArrayLength; - const len = entries.length - (remainder ? 1 : 0); + const [ entries ] = previewEntries(value).slice(0, (maxArrayLength + 1) * 2); + // Entries exist as [key1, val1, key2, val2, ...] + const remainder = entries.length / 2 > maxArrayLength; + const len = entries.length / 2 - (remainder ? 1 : 0); const maxLength = Math.min(maxArrayLength, len); let output = new Array(maxLength); - for (var i = 0; i < len; i++) { - output[i] = `${formatValue(ctx, entries[i][0], recurseTimes)} => ` + - formatValue(ctx, entries[i][1], recurseTimes); + for (var i = 0; i < maxLength; i++) { + const pos = i * 2; + output[i] = `${formatValue(ctx, entries[pos], recurseTimes)} => ` + + formatValue(ctx, entries[pos + 1], recurseTimes); } // Sort all entries to have a halfway reliable output (if more entries than // retrieved ones exist, we can not reliably return the same output). @@ -1020,9 +1022,19 @@ function formatWeakMap(ctx, value, recurseTimes, keys) { return output; } +function zip2(list) { + const ret = Array(list.length / 2); + for (var i = 0; i < ret.length; ++i) + ret[i] = [list[2 * i], list[2 * i + 1]]; + return ret; +} + function formatCollectionIterator(ctx, value, recurseTimes, keys) { const output = []; - for (const entry of previewEntries(value)) { + var [ entries, isKeyValue ] = previewEntries(value); + if (isKeyValue) + entries = zip2(entries); + for (const entry of entries) { if (ctx.maxArrayLength === output.length) { output.push('... more items'); break; diff --git a/node.gyp b/node.gyp index 300a20b6e306c4..56315baf7006b3 100644 --- a/node.gyp +++ b/node.gyp @@ -106,6 +106,7 @@ 'lib/internal/constants.js', 'lib/internal/dns/promises.js', 'lib/internal/dns/utils.js', + 'lib/internal/domexception.js', 'lib/internal/encoding.js', 'lib/internal/errors.js', 'lib/internal/error-serdes.js', @@ -132,12 +133,13 @@ 'lib/internal/net.js', 'lib/internal/os.js', 'lib/internal/process/esm_loader.js', - 'lib/internal/process/methods.js', + 'lib/internal/process/main_thread_only.js', 'lib/internal/process/next_tick.js', + 'lib/internal/process/per_thread.js', 'lib/internal/process/promises.js', 'lib/internal/process/stdio.js', 'lib/internal/process/warning.js', - 'lib/internal/process.js', + 'lib/internal/process/worker_thread_only.js', 'lib/internal/querystring.js', 'lib/internal/process/write-coverage.js', 'lib/internal/readline.js', @@ -145,6 +147,7 @@ 'lib/internal/repl/await.js', 'lib/internal/socket_list.js', 'lib/internal/test/binding.js', + 'lib/internal/test/heap.js', 'lib/internal/test/unicode.js', 'lib/internal/timers.js', 'lib/internal/tls.js', @@ -327,6 +330,7 @@ 'src/exceptions.cc', 'src/fs_event_wrap.cc', 'src/handle_wrap.cc', + 'src/heap_utils.cc', 'src/js_stream.cc', 'src/module_wrap.cc', 'src/node.cc', @@ -349,6 +353,7 @@ 'src/node_platform.cc', 'src/node_perf.cc', 'src/node_postmortem_metadata.cc', + 'src/node_process.cc', 'src/node_serdes.cc', 'src/node_trace_events.cc', 'src/node_types.cc', @@ -376,6 +381,7 @@ 'src/tracing/node_trace_buffer.cc', 'src/tracing/node_trace_writer.cc', 'src/tracing/trace_event.cc', + 'src/tracing/traced_value.cc', 'src/tty_wrap.cc', 'src/udp_wrap.cc', 'src/util.cc', @@ -418,6 +424,8 @@ 'src/node_revert.h', 'src/node_i18n.h', 'src/node_worker.h', + 'src/memory_tracker.h', + 'src/memory_tracker-inl.h', 'src/pipe_wrap.h', 'src/tty_wrap.h', 'src/tcp_wrap.h', @@ -436,6 +444,7 @@ 'src/tracing/node_trace_buffer.h', 'src/tracing/node_trace_writer.h', 'src/tracing/trace_event.h', + 'src/tracing/traced_value.h', 'src/util.h', 'src/util-inl.h', 'deps/http_parser/http_parser.h', @@ -485,12 +494,14 @@ 'src/inspector_js_api.cc', 'src/inspector_socket.cc', 'src/inspector_socket_server.cc', - 'src/inspector/tracing_agent.cc', + 'src/inspector/main_thread_interface.cc', 'src/inspector/node_string.cc', + 'src/inspector/tracing_agent.cc', 'src/inspector_agent.h', 'src/inspector_io.h', 'src/inspector_socket.h', 'src/inspector_socket_server.h', + 'src/inspector/main_thread_interface.h', 'src/inspector/node_string.h', 'src/inspector/tracing_agent.h', '<@(node_inspector_generated_sources)' @@ -949,6 +960,7 @@ 'test/cctest/test_node_postmortem_metadata.cc', 'test/cctest/test_environment.cc', 'test/cctest/test_platform.cc', + 'test/cctest/test_traced_value.cc', 'test/cctest/test_util.cc', 'test/cctest/test_url.cc' ], diff --git a/src/async_wrap.cc b/src/async_wrap.cc index 89e82b9ce364b6..7ef3dafdf992c5 100644 --- a/src/async_wrap.cc +++ b/src/async_wrap.cc @@ -32,7 +32,6 @@ using v8::Function; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; using v8::HandleScope; -using v8::HeapProfiler; using v8::Integer; using v8::Isolate; using v8::Local; @@ -43,7 +42,6 @@ using v8::ObjectTemplate; using v8::Promise; using v8::PromiseHookType; using v8::PropertyCallbackInfo; -using v8::RetainedObjectInfo; using v8::String; using v8::Uint32; using v8::Undefined; @@ -61,79 +59,6 @@ static const char* const provider_names[] = { }; -// Report correct information in a heapdump. - -class RetainedAsyncInfo: public RetainedObjectInfo { - public: - explicit RetainedAsyncInfo(uint16_t class_id, AsyncWrap* wrap); - - void Dispose() override; - bool IsEquivalent(RetainedObjectInfo* other) override; - intptr_t GetHash() override; - const char* GetLabel() override; - intptr_t GetSizeInBytes() override; - - private: - const char* label_; - const AsyncWrap* wrap_; - const int length_; -}; - - -RetainedAsyncInfo::RetainedAsyncInfo(uint16_t class_id, AsyncWrap* wrap) - : label_(provider_names[class_id - NODE_ASYNC_ID_OFFSET]), - wrap_(wrap), - length_(wrap->self_size()) { -} - - -void RetainedAsyncInfo::Dispose() { - delete this; -} - - -bool RetainedAsyncInfo::IsEquivalent(RetainedObjectInfo* other) { - return label_ == other->GetLabel() && - wrap_ == static_cast(other)->wrap_; -} - - -intptr_t RetainedAsyncInfo::GetHash() { - return reinterpret_cast(wrap_); -} - - -const char* RetainedAsyncInfo::GetLabel() { - return label_; -} - - -intptr_t RetainedAsyncInfo::GetSizeInBytes() { - return length_; -} - - -RetainedObjectInfo* WrapperInfo(uint16_t class_id, Local wrapper) { - // No class_id should be the provider type of NONE. - CHECK_GT(class_id, NODE_ASYNC_ID_OFFSET); - // And make sure the class_id doesn't extend past the last provider. - CHECK_LE(class_id - NODE_ASYNC_ID_OFFSET, AsyncWrap::PROVIDERS_LENGTH); - CHECK(wrapper->IsObject()); - CHECK(!wrapper.IsEmpty()); - - Local object = wrapper.As(); - CHECK_GT(object->InternalFieldCount(), 0); - - AsyncWrap* wrap; - ASSIGN_OR_RETURN_UNWRAP(&wrap, object, nullptr); - - return new RetainedAsyncInfo(class_id, wrap); -} - - -// end RetainedAsyncInfo - - struct AsyncWrapObject : public AsyncWrap { static inline void New(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); @@ -147,7 +72,9 @@ struct AsyncWrapObject : public AsyncWrap { inline AsyncWrapObject(Environment* env, Local object, ProviderType type) : AsyncWrap(env, object, type) {} - inline size_t self_size() const override { return sizeof(*this); } + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + } }; @@ -252,7 +179,10 @@ class PromiseWrap : public AsyncWrap { : AsyncWrap(env, object, PROVIDER_PROMISE, -1, silent) { MakeWeak(); } - size_t self_size() const override { return sizeof(*this); } + + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + } static constexpr int kPromiseField = 1; static constexpr int kIsChainedPromiseField = 2; @@ -603,16 +533,6 @@ void AsyncWrap::Initialize(Local target, } -void LoadAsyncWrapperInfo(Environment* env) { - HeapProfiler* heap_profiler = env->isolate()->GetHeapProfiler(); -#define V(PROVIDER) \ - heap_profiler->SetWrapperClassInfoProvider( \ - (NODE_ASYNC_ID_OFFSET + AsyncWrap::PROVIDER_ ## PROVIDER), WrapperInfo); - NODE_ASYNC_PROVIDER_TYPES(V) -#undef V -} - - AsyncWrap::AsyncWrap(Environment* env, Local object, ProviderType provider, @@ -801,9 +721,12 @@ void EmitAsyncDestroy(Isolate* isolate, async_context asyncContext) { Environment::GetCurrent(isolate), asyncContext.async_id); } +std::string AsyncWrap::MemoryInfoName() const { + return provider_names[provider_type()]; +} + std::string AsyncWrap::diagnostic_name() const { - return std::string(provider_names[provider_type()]) + - " (" + std::to_string(env()->thread_id()) + ":" + + return MemoryInfoName() + " (" + std::to_string(env()->thread_id()) + ":" + std::to_string(static_cast(async_id_)) + ")"; } diff --git a/src/async_wrap.h b/src/async_wrap.h index 82c57910925be9..e61837af61c242 100644 --- a/src/async_wrap.h +++ b/src/async_wrap.h @@ -174,8 +174,8 @@ class AsyncWrap : public BaseObject { int argc, v8::Local* argv); - virtual size_t self_size() const = 0; virtual std::string diagnostic_name() const; + std::string MemoryInfoName() const override; static void WeakCallback(const v8::WeakCallbackInfo &info); @@ -206,8 +206,6 @@ class AsyncWrap : public BaseObject { double trigger_async_id_; }; -void LoadAsyncWrapperInfo(Environment* env); - } // namespace node #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS diff --git a/src/base_object-inl.h b/src/base_object-inl.h index 06a29223973c5d..d067a807cb8e14 100644 --- a/src/base_object-inl.h +++ b/src/base_object-inl.h @@ -61,11 +61,11 @@ Persistent& BaseObject::persistent() { } -v8::Local BaseObject::object() { +v8::Local BaseObject::object() const { return PersistentToLocal(env_->isolate(), persistent_handle_); } -v8::Local BaseObject::object(v8::Isolate* isolate) { +v8::Local BaseObject::object(v8::Isolate* isolate) const { v8::Local handle = object(); #ifdef DEBUG CHECK_EQ(handle->CreationContext()->GetIsolate(), isolate); @@ -91,12 +91,6 @@ T* BaseObject::FromJSObject(v8::Local object) { } -void BaseObject::DeleteMe(void* data) { - BaseObject* self = static_cast(data); - delete self; -} - - void BaseObject::MakeWeak() { persistent_handle_.SetWeak( this, diff --git a/src/base_object.h b/src/base_object.h index 38291d598feb1c..e0f3f27950e7d0 100644 --- a/src/base_object.h +++ b/src/base_object.h @@ -25,6 +25,7 @@ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS #include "node_persistent.h" +#include "memory_tracker-inl.h" #include "v8.h" #include // std::remove_reference @@ -32,7 +33,7 @@ namespace node { class Environment; -class BaseObject { +class BaseObject : public MemoryRetainer { public: // Associates this object with `object`. It uses the 0th internal field for // that, and in particular aborts if there is no such field. @@ -41,11 +42,11 @@ class BaseObject { // Returns the wrapped object. Returns an empty handle when // persistent.IsEmpty() is true. - inline v8::Local object(); + inline v8::Local object() const; // Same as the above, except it additionally verifies that this object // is associated with the passed Isolate in debug mode. - inline v8::Local object(v8::Isolate* isolate); + inline v8::Local object(v8::Isolate* isolate) const; inline Persistent& persistent(); @@ -75,7 +76,9 @@ class BaseObject { private: BaseObject(); - static inline void DeleteMe(void* data); + v8::Local WrappedObject() const override; + bool IsRootNode() const override; + static void DeleteMe(void* data); // persistent_handle_ needs to be at a fixed offset from the start of the // class because it is used by src/node_postmortem_metadata.cc to calculate @@ -83,6 +86,8 @@ class BaseObject { // position of members in memory are predictable. For more information please // refer to `doc/guides/node-postmortem-support.md` friend int GenDebugSymbols(); + friend class Environment; + Persistent persistent_handle_; Environment* env_; }; diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index f9db02562d9c8a..8bcc0493f5be6d 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -1,5 +1,4 @@ #include "node.h" -#include "env.h" #include "env-inl.h" #include "node_internals.h" #include "v8.h" @@ -109,6 +108,7 @@ void SetupBootstrapObject(Environment* env, BOOTSTRAP_METHOD(_chdir, Chdir); BOOTSTRAP_METHOD(_cpuUsage, CPUUsage); BOOTSTRAP_METHOD(_hrtime, Hrtime); + BOOTSTRAP_METHOD(_hrtimeBigInt, HrtimeBigInt); BOOTSTRAP_METHOD(_memoryUsage, MemoryUsage); BOOTSTRAP_METHOD(_rawDebug, RawDebug); BOOTSTRAP_METHOD(_umask, Umask); diff --git a/src/callback_scope.cc b/src/callback_scope.cc index 23e6d5b0632f2c..feb7e23b6e5f84 100644 --- a/src/callback_scope.cc +++ b/src/callback_scope.cc @@ -1,7 +1,5 @@ #include "node.h" -#include "async_wrap.h" #include "async_wrap-inl.h" -#include "env.h" #include "env-inl.h" #include "v8.h" diff --git a/src/cares_wrap.cc b/src/cares_wrap.cc index 05ef2b7e12090e..69a3d46668193a 100644 --- a/src/cares_wrap.cc +++ b/src/cares_wrap.cc @@ -121,10 +121,12 @@ inline const char* ToErrorCodeString(int status) { class ChannelWrap; -struct node_ares_task { +struct node_ares_task : public MemoryRetainer { ChannelWrap* channel; ares_socket_t sock; uv_poll_t poll_watcher; + + void MemoryInfo(MemoryTracker* tracker) const override; }; struct TaskHash { @@ -167,7 +169,12 @@ class ChannelWrap : public AsyncWrap { inline int active_query_count() { return active_query_count_; } inline node_ares_task_list* task_list() { return &task_list_; } - size_t self_size() const override { return sizeof(*this); } + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + if (timer_handle_ != nullptr) + tracker->TrackFieldWithSize("timer handle", sizeof(*timer_handle_)); + tracker->TrackField("task list", task_list_); + } static void AresTimeout(uv_timer_t* handle); @@ -181,6 +188,11 @@ class ChannelWrap : public AsyncWrap { node_ares_task_list task_list_; }; +void node_ares_task::MemoryInfo(MemoryTracker* tracker) const { + tracker->TrackThis(this); + tracker->TrackField("channel", channel); +} + ChannelWrap::ChannelWrap(Environment* env, Local object) : AsyncWrap(env, object, PROVIDER_DNSCHANNEL), @@ -209,7 +221,10 @@ class GetAddrInfoReqWrap : public ReqWrap { Local req_wrap_obj, bool verbatim); - size_t self_size() const override { return sizeof(*this); } + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + } + bool verbatim() const { return verbatim_; } private: @@ -228,7 +243,9 @@ class GetNameInfoReqWrap : public ReqWrap { public: GetNameInfoReqWrap(Environment* env, Local req_wrap_obj); - size_t self_size() const override { return sizeof(*this); } + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + } }; GetNameInfoReqWrap::GetNameInfoReqWrap(Environment* env, @@ -270,13 +287,13 @@ void ares_poll_cb(uv_poll_t* watcher, int status, int events) { void ares_poll_close_cb(uv_poll_t* watcher) { node_ares_task* task = ContainerOf(&node_ares_task::poll_watcher, watcher); - free(task); + delete task; } /* Allocates and returns a new node_ares_task */ node_ares_task* ares_task_create(ChannelWrap* channel, ares_socket_t sock) { - auto task = node::UncheckedMalloc(1); + auto task = new node_ares_task(); if (task == nullptr) { /* Out of memory. */ @@ -1172,7 +1189,9 @@ class QueryAnyWrap: public QueryWrap { return 0; } - size_t self_size() const override { return sizeof(*this); } + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + } protected: void Parse(unsigned char* buf, int len) override { @@ -1349,7 +1368,9 @@ class QueryAWrap: public QueryWrap { return 0; } - size_t self_size() const override { return sizeof(*this); } + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + } protected: void Parse(unsigned char* buf, int len) override { @@ -1393,7 +1414,9 @@ class QueryAaaaWrap: public QueryWrap { return 0; } - size_t self_size() const override { return sizeof(*this); } + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + } protected: void Parse(unsigned char* buf, int len) override { @@ -1437,7 +1460,9 @@ class QueryCnameWrap: public QueryWrap { return 0; } - size_t self_size() const override { return sizeof(*this); } + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + } protected: void Parse(unsigned char* buf, int len) override { @@ -1468,7 +1493,9 @@ class QueryMxWrap: public QueryWrap { return 0; } - size_t self_size() const override { return sizeof(*this); } + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + } protected: void Parse(unsigned char* buf, int len) override { @@ -1499,7 +1526,9 @@ class QueryNsWrap: public QueryWrap { return 0; } - size_t self_size() const override { return sizeof(*this); } + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + } protected: void Parse(unsigned char* buf, int len) override { @@ -1530,7 +1559,9 @@ class QueryTxtWrap: public QueryWrap { return 0; } - size_t self_size() const override { return sizeof(*this); } + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + } protected: void Parse(unsigned char* buf, int len) override { @@ -1560,7 +1591,9 @@ class QuerySrvWrap: public QueryWrap { return 0; } - size_t self_size() const override { return sizeof(*this); } + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + } protected: void Parse(unsigned char* buf, int len) override { @@ -1589,7 +1622,9 @@ class QueryPtrWrap: public QueryWrap { return 0; } - size_t self_size() const override { return sizeof(*this); } + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + } protected: void Parse(unsigned char* buf, int len) override { @@ -1620,7 +1655,9 @@ class QueryNaptrWrap: public QueryWrap { return 0; } - size_t self_size() const override { return sizeof(*this); } + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + } protected: void Parse(unsigned char* buf, int len) override { @@ -1650,7 +1687,9 @@ class QuerySoaWrap: public QueryWrap { return 0; } - size_t self_size() const override { return sizeof(*this); } + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + } protected: void Parse(unsigned char* buf, int len) override { @@ -1729,7 +1768,9 @@ class GetHostByAddrWrap: public QueryWrap { return 0; } - size_t self_size() const override { return sizeof(*this); } + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + } protected: void Parse(struct hostent* host) override { @@ -2107,8 +2148,8 @@ void Initialize(Local target, env->SetMethod(target, "getaddrinfo", GetAddrInfo); env->SetMethod(target, "getnameinfo", GetNameInfo); - env->SetMethod(target, "isIPv6", IsIPv6); - env->SetMethod(target, "canonicalizeIP", CanonicalizeIP); + env->SetMethodNoSideEffect(target, "isIPv6", IsIPv6); + env->SetMethodNoSideEffect(target, "canonicalizeIP", CanonicalizeIP); env->SetMethod(target, "strerror", StrError); @@ -2165,7 +2206,7 @@ void Initialize(Local target, env->SetProtoMethod(channel_wrap, "querySoa", Query); env->SetProtoMethod(channel_wrap, "getHostByAddr", Query); - env->SetProtoMethod(channel_wrap, "getServers", GetServers); + env->SetProtoMethodNoSideEffect(channel_wrap, "getServers", GetServers); env->SetProtoMethod(channel_wrap, "setServers", SetServers); env->SetProtoMethod(channel_wrap, "cancel", Cancel); diff --git a/src/connect_wrap.h b/src/connect_wrap.h index 80eae7f9bb8290..587e4c6b0593e5 100644 --- a/src/connect_wrap.h +++ b/src/connect_wrap.h @@ -16,7 +16,9 @@ class ConnectWrap : public ReqWrap { v8::Local req_wrap_obj, AsyncWrap::ProviderType provider); - size_t self_size() const override { return sizeof(*this); } + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + } }; } // namespace node diff --git a/src/env-inl.h b/src/env-inl.h index bbb80c6f7ae916..5c359c04d907cb 100644 --- a/src/env-inl.h +++ b/src/env-inl.h @@ -33,6 +33,7 @@ #include "node_perf_common.h" #include "node_context_data.h" #include "tracing/agent.h" +#include "node_worker.h" #include #include @@ -289,6 +290,9 @@ inline void Environment::AssignToContext(v8::Local context, const ContextInfo& info) { context->SetAlignedPointerInEmbedderData( ContextEmbedderIndex::kEnvironment, this); + // Used by EnvPromiseHook to know that we are on a node context. + context->SetAlignedPointerInEmbedderData( + ContextEmbedderIndex::kContextTag, Environment::kNodeContextTagPtr); #if HAVE_INSPECTOR inspector_agent()->ContextCreated(context, info); #endif // HAVE_INSPECTOR @@ -623,6 +627,11 @@ inline void Environment::remove_sub_worker_context(worker::Worker* context) { sub_worker_contexts_.erase(context); } +inline bool Environment::is_stopping_worker() const { + CHECK(!is_main_thread()); + return worker_context_->is_stopped(); +} + inline performance::performance_state* Environment::performance_state() { return performance_state_.get(); } @@ -675,17 +684,41 @@ inline void Environment::ThrowUVException(int errorno, inline v8::Local Environment::NewFunctionTemplate(v8::FunctionCallback callback, v8::Local signature, - v8::ConstructorBehavior behavior) { + v8::ConstructorBehavior behavior, + v8::SideEffectType side_effect_type) { v8::Local external = as_external(); return v8::FunctionTemplate::New(isolate(), callback, external, - signature, 0, behavior); + signature, 0, behavior, side_effect_type); } inline void Environment::SetMethod(v8::Local that, const char* name, v8::FunctionCallback callback) { v8::Local function = - NewFunctionTemplate(callback)->GetFunction(); + NewFunctionTemplate(callback, + v8::Local(), + // TODO(TimothyGu): Investigate if SetMethod is ever + // used for constructors. + v8::ConstructorBehavior::kAllow, + v8::SideEffectType::kHasSideEffect)->GetFunction(); + // kInternalized strings are created in the old space. + const v8::NewStringType type = v8::NewStringType::kInternalized; + v8::Local name_string = + v8::String::NewFromUtf8(isolate(), name, type).ToLocalChecked(); + that->Set(name_string, function); + function->SetName(name_string); // NODE_SET_METHOD() compatibility. +} + +inline void Environment::SetMethodNoSideEffect(v8::Local that, + const char* name, + v8::FunctionCallback callback) { + v8::Local function = + NewFunctionTemplate(callback, + v8::Local(), + // TODO(TimothyGu): Investigate if SetMethod is ever + // used for constructors. + v8::ConstructorBehavior::kAllow, + v8::SideEffectType::kHasNoSideEffect)->GetFunction(); // kInternalized strings are created in the old space. const v8::NewStringType type = v8::NewStringType::kInternalized; v8::Local name_string = @@ -699,7 +732,24 @@ inline void Environment::SetProtoMethod(v8::Local that, v8::FunctionCallback callback) { v8::Local signature = v8::Signature::New(isolate(), that); v8::Local t = - NewFunctionTemplate(callback, signature, v8::ConstructorBehavior::kThrow); + NewFunctionTemplate(callback, signature, v8::ConstructorBehavior::kThrow, + v8::SideEffectType::kHasSideEffect); + // kInternalized strings are created in the old space. + const v8::NewStringType type = v8::NewStringType::kInternalized; + v8::Local name_string = + v8::String::NewFromUtf8(isolate(), name, type).ToLocalChecked(); + that->PrototypeTemplate()->Set(name_string, t); + t->SetClassName(name_string); // NODE_SET_PROTOTYPE_METHOD() compatibility. +} + +inline void Environment::SetProtoMethodNoSideEffect( + v8::Local that, + const char* name, + v8::FunctionCallback callback) { + v8::Local signature = v8::Signature::New(isolate(), that); + v8::Local t = + NewFunctionTemplate(callback, signature, v8::ConstructorBehavior::kThrow, + v8::SideEffectType::kHasNoSideEffect); // kInternalized strings are created in the old space. const v8::NewStringType type = v8::NewStringType::kInternalized; v8::Local name_string = @@ -711,7 +761,26 @@ inline void Environment::SetProtoMethod(v8::Local that, inline void Environment::SetTemplateMethod(v8::Local that, const char* name, v8::FunctionCallback callback) { - v8::Local t = NewFunctionTemplate(callback); + v8::Local t = + NewFunctionTemplate(callback, v8::Local(), + v8::ConstructorBehavior::kAllow, + v8::SideEffectType::kHasSideEffect); + // kInternalized strings are created in the old space. + const v8::NewStringType type = v8::NewStringType::kInternalized; + v8::Local name_string = + v8::String::NewFromUtf8(isolate(), name, type).ToLocalChecked(); + that->Set(name_string, t); + t->SetClassName(name_string); // NODE_SET_METHOD() compatibility. +} + +inline void Environment::SetTemplateMethodNoSideEffect( + v8::Local that, + const char* name, + v8::FunctionCallback callback) { + v8::Local t = + NewFunctionTemplate(callback, v8::Local(), + v8::ConstructorBehavior::kAllow, + v8::SideEffectType::kHasNoSideEffect); // kInternalized strings are created in the old space. const v8::NewStringType type = v8::NewStringType::kInternalized; v8::Local name_string = @@ -743,6 +812,22 @@ bool Environment::CleanupHookCallback::Equal::operator()( return a.fn_ == b.fn_ && a.arg_ == b.arg_; } +BaseObject* Environment::CleanupHookCallback::GetBaseObject() const { + if (fn_ == BaseObject::DeleteMe) + return static_cast(arg_); + else + return nullptr; +} + +template +void Environment::ForEachBaseObject(T&& iterator) { + for (const auto& hook : cleanup_hooks_) { + BaseObject* obj = hook.GetBaseObject(); + if (obj != nullptr) + iterator(obj); + } +} + #define VP(PropertyName, StringValue) V(v8::Private, PropertyName) #define VY(PropertyName, StringValue) V(v8::Symbol, PropertyName) #define VS(PropertyName, StringValue) V(v8::String, PropertyName) diff --git a/src/env.cc b/src/env.cc index 2236893dd77923..fffe5d61d0685a 100644 --- a/src/env.cc +++ b/src/env.cc @@ -4,6 +4,7 @@ #include "node_buffer.h" #include "node_platform.h" #include "node_file.h" +#include "node_context_data.h" #include "node_worker.h" #include "tracing/agent.h" @@ -28,6 +29,10 @@ using v8::Symbol; using v8::Value; using worker::Worker; +int const Environment::kNodeContextTag = 0x6e6f64; +void* Environment::kNodeContextTagPtr = const_cast( + static_cast(&Environment::kNodeContextTag)); + IsolateData::IsolateData(Isolate* isolate, uv_loop_t* event_loop, MultiIsolatePlatform* platform, @@ -142,9 +147,15 @@ Environment::Environment(IsolateData* isolate_data, std::string debug_cats; SafeGetenv("NODE_DEBUG_NATIVE", &debug_cats); set_debug_categories(debug_cats, true); + + isolate()->GetHeapProfiler()->AddBuildEmbedderGraphCallback( + BuildEmbedderGraph, this); } Environment::~Environment() { + isolate()->GetHeapProfiler()->RemoveBuildEmbedderGraphCallback( + BuildEmbedderGraph, this); + // Make sure there are no re-used libuv wrapper objects. // CleanupHandles() should have removed all of them. CHECK(file_handle_read_wrap_freelist_.empty()); @@ -212,7 +223,6 @@ void Environment::Start(int argc, set_process_object(process_object); SetupProcessObject(this, argc, argv, exec_argc, exec_argv); - LoadAsyncWrapperInfo(this); static uv_once_t init_once = UV_ONCE_INIT; uv_once(&init_once, InitThreadLocalOnce); @@ -425,7 +435,20 @@ bool Environment::RemovePromiseHook(promise_hook_func fn, void* arg) { void Environment::EnvPromiseHook(v8::PromiseHookType type, v8::Local promise, v8::Local parent) { - Environment* env = Environment::GetCurrent(promise->CreationContext()); + Local context = promise->CreationContext(); + + // Grow the embedder data if necessary to make sure we are not out of bounds + // when reading the magic number. + context->SetAlignedPointerInEmbedderData( + ContextEmbedderIndex::kContextTagBoundary, nullptr); + int* magicNumberPtr = reinterpret_cast( + context->GetAlignedPointerFromEmbedderData( + ContextEmbedderIndex::kContextTag)); + if (magicNumberPtr != Environment::kNodeContextTagPtr) { + return; + } + + Environment* env = Environment::GetCurrent(context); for (const PromiseHookCallback& hook : env->promise_hooks_) { hook.cb_(type, promise, parent, hook.arg_); } @@ -653,9 +676,28 @@ void Environment::stop_sub_worker_contexts() { } } -bool Environment::is_stopping_worker() const { - CHECK(!is_main_thread()); - return worker_context_->is_stopped(); +void Environment::BuildEmbedderGraph(v8::Isolate* isolate, + v8::EmbedderGraph* graph, + void* data) { + MemoryTracker tracker(isolate, graph); + static_cast(data)->ForEachBaseObject([&](BaseObject* obj) { + tracker.Track(obj); + }); +} + + +// Not really any better place than env.cc at this moment. +void BaseObject::DeleteMe(void* data) { + BaseObject* self = static_cast(data); + delete self; +} + +Local BaseObject::WrappedObject() const { + return object(); +} + +bool BaseObject::IsRootNode() const { + return !persistent_handle_.IsWeak(); } } // namespace node diff --git a/src/env.h b/src/env.h index 6c759c84e7685d..ede221400b4965 100644 --- a/src/env.h +++ b/src/env.h @@ -322,6 +322,7 @@ struct PackageConfig { V(buffer_prototype_object, v8::Object) \ V(context, v8::Context) \ V(domain_callback, v8::Function) \ + V(domexception_function, v8::Function) \ V(fdclose_constructor_template, v8::ObjectTemplate) \ V(fd_constructor_template, v8::ObjectTemplate) \ V(filehandlereadwrap_template, v8::ObjectTemplate) \ @@ -722,9 +723,6 @@ class Environment { inline bool can_call_into_js() const; inline void set_can_call_into_js(bool can_call_into_js); - // TODO(addaleax): This should be inline. - bool is_stopping_worker() const; - inline bool is_main_thread() const; inline uint64_t thread_id() const; inline void set_thread_id(uint64_t id); @@ -733,6 +731,7 @@ class Environment { inline void add_sub_worker_context(worker::Worker* context); inline void remove_sub_worker_context(worker::Worker* context); void stop_sub_worker_contexts(); + inline bool is_stopping_worker() const; inline void ThrowError(const char* errmsg); inline void ThrowTypeError(const char* errmsg); @@ -752,12 +751,15 @@ class Environment { v8::Local signature = v8::Local(), v8::ConstructorBehavior behavior = - v8::ConstructorBehavior::kAllow); + v8::ConstructorBehavior::kAllow, + v8::SideEffectType side_effect = + v8::SideEffectType::kHasSideEffect); // Convenience methods for NewFunctionTemplate(). inline void SetMethod(v8::Local that, const char* name, v8::FunctionCallback callback); + inline void SetProtoMethod(v8::Local that, const char* name, v8::FunctionCallback callback); @@ -765,6 +767,18 @@ class Environment { const char* name, v8::FunctionCallback callback); + // Safe variants denote the function has no side effects. + inline void SetMethodNoSideEffect(v8::Local that, + const char* name, + v8::FunctionCallback callback); + inline void SetProtoMethodNoSideEffect(v8::Local that, + const char* name, + v8::FunctionCallback callback); + inline void SetTemplateMethodNoSideEffect( + v8::Local that, + const char* name, + v8::FunctionCallback callback); + void BeforeExit(void (*cb)(void* arg), void* arg); void RunBeforeExitCallbacks(); void AtExit(void (*cb)(void* arg), void* arg); @@ -845,6 +859,10 @@ class Environment { inline void RemoveCleanupHook(void (*fn)(void*), void* arg); void RunCleanup(); + static void BuildEmbedderGraph(v8::Isolate* isolate, + v8::EmbedderGraph* graph, + void* data); + private: inline void CreateImmediate(native_immediate_callback cb, void* data, @@ -884,6 +902,8 @@ class Environment { uint64_t thread_id_ = 0; std::unordered_set sub_worker_contexts_; + static void* kNodeContextTagPtr; + static int const kNodeContextTag; #if HAVE_INSPECTOR std::unique_ptr inspector_agent_; @@ -962,6 +982,8 @@ class Environment { inline bool operator()(const CleanupHookCallback& a, const CleanupHookCallback& b) const; }; + + inline BaseObject* GetBaseObject() const; }; // Use an unordered_set, so that we have efficient insertion and removal. @@ -974,6 +996,9 @@ class Environment { v8::Local promise, v8::Local parent); + template + void ForEachBaseObject(T&& iterator); + #define V(PropertyName, TypeName) Persistent PropertyName ## _; ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V) #undef V diff --git a/src/exceptions.cc b/src/exceptions.cc index 0007854595a467..4bd5ab13134c86 100644 --- a/src/exceptions.cc +++ b/src/exceptions.cc @@ -1,8 +1,6 @@ #include "node.h" #include "node_internals.h" -#include "env.h" #include "env-inl.h" -#include "util.h" #include "util-inl.h" #include "v8.h" #include "uv.h" diff --git a/src/fs_event_wrap.cc b/src/fs_event_wrap.cc index 164614ae81145f..7587ace8e3ce70 100644 --- a/src/fs_event_wrap.cc +++ b/src/fs_event_wrap.cc @@ -56,7 +56,10 @@ class FSEventWrap: public HandleWrap { static void New(const FunctionCallbackInfo& args); static void Start(const FunctionCallbackInfo& args); static void GetInitialized(const FunctionCallbackInfo& args); - size_t self_size() const override { return sizeof(*this); } + + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + } private: static const encoding kDefaultEncoding = UTF8; diff --git a/src/handle_wrap.cc b/src/handle_wrap.cc index 4c2a33aa84459d..9281300146c4f3 100644 --- a/src/handle_wrap.cc +++ b/src/handle_wrap.cc @@ -29,6 +29,7 @@ namespace node { using v8::Context; using v8::FunctionCallbackInfo; +using v8::FunctionTemplate; using v8::HandleScope; using v8::Local; using v8::Object; @@ -130,4 +131,13 @@ void HandleWrap::OnClose(uv_handle_t* handle) { } +void HandleWrap::AddWrapMethods(Environment* env, + Local t) { + env->SetProtoMethod(t, "close", HandleWrap::Close); + env->SetProtoMethodNoSideEffect(t, "hasRef", HandleWrap::HasRef); + env->SetProtoMethod(t, "ref", HandleWrap::Ref); + env->SetProtoMethod(t, "unref", HandleWrap::Unref); +} + + } // namespace node diff --git a/src/handle_wrap.h b/src/handle_wrap.h index bd7ef4000bad6f..443d28bf523933 100644 --- a/src/handle_wrap.h +++ b/src/handle_wrap.h @@ -73,6 +73,9 @@ class HandleWrap : public AsyncWrap { virtual void Close( v8::Local close_callback = v8::Local()); + static void AddWrapMethods(Environment* env, + v8::Local constructor); + protected: HandleWrap(Environment* env, v8::Local object, diff --git a/src/heap_utils.cc b/src/heap_utils.cc new file mode 100644 index 00000000000000..2d339c580fa076 --- /dev/null +++ b/src/heap_utils.cc @@ -0,0 +1,232 @@ +#include "node_internals.h" +#include "env.h" + +using v8::Array; +using v8::Boolean; +using v8::Context; +using v8::EmbedderGraph; +using v8::EscapableHandleScope; +using v8::FunctionCallbackInfo; +using v8::HandleScope; +using v8::HeapSnapshot; +using v8::Isolate; +using v8::JSON; +using v8::Local; +using v8::MaybeLocal; +using v8::Number; +using v8::Object; +using v8::String; +using v8::Value; + +namespace node { +namespace heap { + +class JSGraphJSNode : public EmbedderGraph::Node { + public: + const char* Name() override { return ""; } + size_t SizeInBytes() override { return 0; } + bool IsEmbedderNode() override { return false; } + Local JSValue() { return StrongPersistentToLocal(persistent_); } + + int IdentityHash() { + Local v = JSValue(); + if (v->IsObject()) return v.As()->GetIdentityHash(); + if (v->IsName()) return v.As()->GetIdentityHash(); + if (v->IsInt32()) return v.As()->Value(); + return 0; + } + + JSGraphJSNode(Isolate* isolate, Local val) + : persistent_(isolate, val) { + CHECK(!val.IsEmpty()); + } + + struct Hash { + inline size_t operator()(JSGraphJSNode* n) const { + return n->IdentityHash(); + } + }; + + struct Equal { + inline bool operator()(JSGraphJSNode* a, JSGraphJSNode* b) const { + return a->JSValue()->SameValue(b->JSValue()); + } + }; + + private: + Persistent persistent_; +}; + +class JSGraph : public EmbedderGraph { + public: + explicit JSGraph(Isolate* isolate) : isolate_(isolate) {} + + Node* V8Node(const Local& value) override { + std::unique_ptr n { new JSGraphJSNode(isolate_, value) }; + auto it = engine_nodes_.find(n.get()); + if (it != engine_nodes_.end()) + return *it; + engine_nodes_.insert(n.get()); + return AddNode(std::unique_ptr(n.release())); + } + + Node* AddNode(std::unique_ptr node) override { + Node* n = node.get(); + nodes_.emplace(std::move(node)); + return n; + } + + void AddEdge(Node* from, Node* to) override { + edges_[from].insert(to); + } + + MaybeLocal CreateObject() const { + EscapableHandleScope handle_scope(isolate_); + Local context = isolate_->GetCurrentContext(); + + std::unordered_map> info_objects; + Local nodes = Array::New(isolate_, nodes_.size()); + Local edges_string = FIXED_ONE_BYTE_STRING(isolate_, "edges"); + Local is_root_string = FIXED_ONE_BYTE_STRING(isolate_, "isRoot"); + Local name_string = FIXED_ONE_BYTE_STRING(isolate_, "name"); + Local size_string = FIXED_ONE_BYTE_STRING(isolate_, "size"); + Local value_string = FIXED_ONE_BYTE_STRING(isolate_, "value"); + Local wraps_string = FIXED_ONE_BYTE_STRING(isolate_, "wraps"); + + for (const std::unique_ptr& n : nodes_) + info_objects[n.get()] = Object::New(isolate_); + + { + HandleScope handle_scope(isolate_); + size_t i = 0; + for (const std::unique_ptr& n : nodes_) { + Local obj = info_objects[n.get()]; + Local value; + if (!String::NewFromUtf8(isolate_, n->Name(), + v8::NewStringType::kNormal).ToLocal(&value) || + obj->Set(context, name_string, value).IsNothing() || + obj->Set(context, is_root_string, + Boolean::New(isolate_, n->IsRootNode())).IsNothing() || + obj->Set(context, size_string, + Number::New(isolate_, n->SizeInBytes())).IsNothing() || + obj->Set(context, edges_string, + Array::New(isolate_)).IsNothing()) { + return MaybeLocal(); + } + if (nodes->Set(context, i++, obj).IsNothing()) + return MaybeLocal(); + if (!n->IsEmbedderNode()) { + value = static_cast(n.get())->JSValue(); + if (obj->Set(context, value_string, value).IsNothing()) + return MaybeLocal(); + } + } + } + + for (const std::unique_ptr& n : nodes_) { + Node* wraps = n->WrapperNode(); + if (wraps == nullptr) continue; + Local from = info_objects[n.get()]; + Local to = info_objects[wraps]; + if (from->Set(context, wraps_string, to).IsNothing()) + return MaybeLocal(); + } + + for (const auto& edge_info : edges_) { + Node* source = edge_info.first; + Local edges; + if (!info_objects[source]->Get(context, edges_string).ToLocal(&edges) || + !edges->IsArray()) { + return MaybeLocal(); + } + + size_t i = 0; + for (Node* target : edge_info.second) { + if (edges.As()->Set(context, + i++, + info_objects[target]).IsNothing()) { + return MaybeLocal(); + } + } + } + + return handle_scope.Escape(nodes); + } + + private: + Isolate* isolate_; + std::unordered_set> nodes_; + std::unordered_set + engine_nodes_; + std::unordered_map> edges_; +}; + +void BuildEmbedderGraph(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + JSGraph graph(env->isolate()); + Environment::BuildEmbedderGraph(env->isolate(), &graph, env); + Local ret; + if (graph.CreateObject().ToLocal(&ret)) + args.GetReturnValue().Set(ret); +} + + +class BufferOutputStream : public v8::OutputStream { + public: + BufferOutputStream() : buffer_(new JSString()) {} + + void EndOfStream() override {} + int GetChunkSize() override { return 1024 * 1024; } + WriteResult WriteAsciiChunk(char* data, int size) override { + buffer_->Append(data, size); + return kContinue; + } + + Local ToString(Isolate* isolate) { + return String::NewExternalOneByte(isolate, + buffer_.release()).ToLocalChecked(); + } + + private: + class JSString : public String::ExternalOneByteStringResource { + public: + void Append(char* data, size_t count) { + store_.append(data, count); + } + + const char* data() const override { return store_.data(); } + size_t length() const override { return store_.size(); } + + private: + std::string store_; + }; + + std::unique_ptr buffer_; +}; + +void CreateHeapDump(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + const HeapSnapshot* snapshot = isolate->GetHeapProfiler()->TakeHeapSnapshot(); + BufferOutputStream out; + snapshot->Serialize(&out, HeapSnapshot::kJSON); + const_cast(snapshot)->Delete(); + Local ret; + if (JSON::Parse(isolate->GetCurrentContext(), + out.ToString(isolate)).ToLocal(&ret)) { + args.GetReturnValue().Set(ret); + } +} + +void Initialize(Local target, + Local unused, + Local context) { + Environment* env = Environment::GetCurrent(context); + + env->SetMethodNoSideEffect(target, "buildEmbedderGraph", BuildEmbedderGraph); + env->SetMethodNoSideEffect(target, "createHeapDump", CreateHeapDump); +} + +} // namespace heap +} // namespace node + +NODE_MODULE_CONTEXT_AWARE_INTERNAL(heap_utils, node::heap::Initialize) diff --git a/src/inspector/main_thread_interface.cc b/src/inspector/main_thread_interface.cc new file mode 100644 index 00000000000000..da43c95bea5fe3 --- /dev/null +++ b/src/inspector/main_thread_interface.cc @@ -0,0 +1,317 @@ +#include "main_thread_interface.h" + +#include "node_mutex.h" +#include "v8-inspector.h" + +#include + +namespace node { +namespace inspector { +namespace { + +using v8_inspector::StringView; +using v8_inspector::StringBuffer; + +template +class DeleteRequest : public Request { + public: + explicit DeleteRequest(T* object) : object_(object) {} + void Call() override { + delete object_; + } + + private: + T* object_; +}; + +template +class SingleArgumentFunctionCall : public Request { + public: + using Fn = void (Target::*)(Arg); + + SingleArgumentFunctionCall(Target* target, Fn fn, Arg argument) + : target_(target), + fn_(fn), + arg_(std::move(argument)) {} + + void Call() override { + Apply(target_, fn_, std::move(arg_)); + } + + private: + template + void Apply(Element* target, Fn fn, Arg arg) { + (target->*fn)(std::move(arg)); + } + + Target* target_; + Fn fn_; + Arg arg_; +}; + +class PostMessageRequest : public Request { + public: + PostMessageRequest(InspectorSessionDelegate* delegate, + StringView message) + : delegate_(delegate), + message_(StringBuffer::create(message)) {} + + void Call() override { + delegate_->SendMessageToFrontend(message_->string()); + } + + private: + InspectorSessionDelegate* delegate_; + std::unique_ptr message_; +}; + +class DispatchMessagesTask : public v8::Task { + public: + explicit DispatchMessagesTask(MainThreadInterface* thread) + : thread_(thread) {} + + void Run() override { + thread_->DispatchMessages(); + } + + private: + MainThreadInterface* thread_; +}; + +void DisposePairCallback(uv_handle_t* ref) { + using AsyncAndInterface = std::pair; + AsyncAndInterface* pair = node::ContainerOf( + &AsyncAndInterface::first, reinterpret_cast(ref)); + delete pair; +} + +template +class AnotherThreadObjectReference { + public: + // We create it on whatever thread, just make sure it gets disposed on the + // proper thread. + AnotherThreadObjectReference(std::shared_ptr thread, + T* object) + : thread_(thread), object_(object) { + } + AnotherThreadObjectReference(AnotherThreadObjectReference&) = delete; + + ~AnotherThreadObjectReference() { + // Disappearing thread may cause a memory leak + CHECK(thread_->Post( + std::unique_ptr>(new DeleteRequest(object_)))); + object_ = nullptr; + } + + template + void Post(Fn fn, Arg argument) const { + using R = SingleArgumentFunctionCall; + thread_->Post(std::unique_ptr(new R(object_, fn, std::move(argument)))); + } + + T* get() const { + return object_; + } + + private: + std::shared_ptr thread_; + T* object_; +}; + +class MainThreadSessionState { + public: + MainThreadSessionState( + std::shared_ptr thread, + bool prevent_shutdown) : thread_(thread), + prevent_shutdown_(prevent_shutdown) {} + + void Connect(std::unique_ptr delegate) { + Agent* agent = thread_->GetInspectorAgent(); + if (agent != nullptr) + session_ = agent->Connect(std::move(delegate), prevent_shutdown_); + } + + void Dispatch(std::unique_ptr message) { + session_->Dispatch(message->string()); + } + + private: + std::shared_ptr thread_; + bool prevent_shutdown_; + std::unique_ptr session_; +}; + +class CrossThreadInspectorSession : public InspectorSession { + public: + CrossThreadInspectorSession( + int id, + std::shared_ptr thread, + std::unique_ptr delegate, + bool prevent_shutdown) + : state_(thread, new MainThreadSessionState(thread, prevent_shutdown)) { + state_.Post(&MainThreadSessionState::Connect, std::move(delegate)); + } + + void Dispatch(const StringView& message) override { + state_.Post(&MainThreadSessionState::Dispatch, + StringBuffer::create(message)); + } + + private: + AnotherThreadObjectReference state_; +}; + +class ThreadSafeDelegate : public InspectorSessionDelegate { + public: + ThreadSafeDelegate(std::shared_ptr thread, + std::unique_ptr delegate) + : thread_(thread), delegate_(thread, delegate.release()) {} + + void SendMessageToFrontend(const v8_inspector::StringView& message) override { + thread_->Post(std::unique_ptr( + new PostMessageRequest(delegate_.get(), message))); + } + + private: + std::shared_ptr thread_; + AnotherThreadObjectReference delegate_; +}; +} // namespace + + +MainThreadInterface::MainThreadInterface(Agent* agent, uv_loop_t* loop, + v8::Isolate* isolate, + v8::Platform* platform) + : agent_(agent), isolate_(isolate), + platform_(platform) { + main_thread_request_.reset(new AsyncAndInterface(uv_async_t(), this)); + CHECK_EQ(0, uv_async_init(loop, &main_thread_request_->first, + DispatchMessagesAsyncCallback)); + // Inspector uv_async_t should not prevent main loop shutdown. + uv_unref(reinterpret_cast(&main_thread_request_->first)); +} + +MainThreadInterface::~MainThreadInterface() { + if (handle_) + handle_->Reset(); +} + +// static +void MainThreadInterface::DispatchMessagesAsyncCallback(uv_async_t* async) { + AsyncAndInterface* asyncAndInterface = + node::ContainerOf(&AsyncAndInterface::first, async); + asyncAndInterface->second->DispatchMessages(); +} + +// static +void MainThreadInterface::CloseAsync(AsyncAndInterface* pair) { + uv_close(reinterpret_cast(&pair->first), DisposePairCallback); +} + +void MainThreadInterface::Post(std::unique_ptr request) { + Mutex::ScopedLock scoped_lock(requests_lock_); + bool needs_notify = requests_.empty(); + requests_.push_back(std::move(request)); + if (needs_notify) { + CHECK_EQ(0, uv_async_send(&main_thread_request_->first)); + if (isolate_ != nullptr && platform_ != nullptr) { + platform_->CallOnForegroundThread(isolate_, + new DispatchMessagesTask(this)); + isolate_->RequestInterrupt([](v8::Isolate* isolate, void* thread) { + static_cast(thread)->DispatchMessages(); + }, this); + } + } + incoming_message_cond_.Broadcast(scoped_lock); +} + +bool MainThreadInterface::WaitForFrontendEvent() { + // We allow DispatchMessages reentry as we enter the pause. This is important + // to support debugging the code invoked by an inspector call, such + // as Runtime.evaluate + dispatching_messages_ = false; + if (dispatching_message_queue_.empty()) { + Mutex::ScopedLock scoped_lock(requests_lock_); + while (requests_.empty()) incoming_message_cond_.Wait(scoped_lock); + } + return true; +} + +void MainThreadInterface::DispatchMessages() { + if (dispatching_messages_) + return; + dispatching_messages_ = true; + bool had_messages = false; + do { + if (dispatching_message_queue_.empty()) { + Mutex::ScopedLock scoped_lock(requests_lock_); + requests_.swap(dispatching_message_queue_); + } + had_messages = !dispatching_message_queue_.empty(); + while (!dispatching_message_queue_.empty()) { + MessageQueue::value_type task; + std::swap(dispatching_message_queue_.front(), task); + dispatching_message_queue_.pop_front(); + task->Call(); + } + } while (had_messages); + dispatching_messages_ = false; +} + +std::shared_ptr MainThreadInterface::GetHandle() { + if (handle_ == nullptr) + handle_ = std::make_shared(this); + return handle_; +} + +std::unique_ptr Utf8ToStringView(const std::string& message) { + icu::UnicodeString utf16 = icu::UnicodeString::fromUTF8( + icu::StringPiece(message.data(), message.length())); + StringView view(reinterpret_cast(utf16.getBuffer()), + utf16.length()); + return StringBuffer::create(view); +} + +std::unique_ptr MainThreadHandle::Connect( + std::unique_ptr delegate, + bool prevent_shutdown) { + return std::unique_ptr( + new CrossThreadInspectorSession(++next_session_id_, + shared_from_this(), + std::move(delegate), + prevent_shutdown)); +} + +bool MainThreadHandle::Post(std::unique_ptr request) { + Mutex::ScopedLock scoped_lock(block_lock_); + if (!main_thread_) + return false; + main_thread_->Post(std::move(request)); + return true; +} + +void MainThreadHandle::Reset() { + Mutex::ScopedLock scoped_lock(block_lock_); + main_thread_ = nullptr; +} + +Agent* MainThreadHandle::GetInspectorAgent() { + Mutex::ScopedLock scoped_lock(block_lock_); + if (main_thread_ == nullptr) + return nullptr; + return main_thread_->inspector_agent(); +} + +std::unique_ptr +MainThreadHandle::MakeThreadSafeDelegate( + std::unique_ptr delegate) { + return std::unique_ptr( + new ThreadSafeDelegate(shared_from_this(), std::move(delegate))); +} + +bool MainThreadHandle::Expired() { + Mutex::ScopedLock scoped_lock(block_lock_); + return main_thread_ == nullptr; +} +} // namespace inspector +} // namespace node diff --git a/src/inspector/main_thread_interface.h b/src/inspector/main_thread_interface.h new file mode 100644 index 00000000000000..75df5ffe809048 --- /dev/null +++ b/src/inspector/main_thread_interface.h @@ -0,0 +1,99 @@ +#ifndef SRC_INSPECTOR_MAIN_THREAD_INTERFACE_H_ +#define SRC_INSPECTOR_MAIN_THREAD_INTERFACE_H_ + +#if !HAVE_INSPECTOR +#error("This header can only be used when inspector is enabled") +#endif + +#include "env.h" +#include "inspector_agent.h" +#include "node_mutex.h" + +#include +#include +#include +#include + +namespace v8_inspector { +class StringBuffer; +class StringView; +} // namespace v8_inspector + +namespace node { +namespace inspector { +class Request { + public: + virtual void Call() = 0; + virtual ~Request() {} +}; + +std::unique_ptr Utf8ToStringView( + const std::string& message); + +using MessageQueue = std::deque>; +class MainThreadInterface; + +class MainThreadHandle : public std::enable_shared_from_this { + public: + explicit MainThreadHandle(MainThreadInterface* main_thread) + : main_thread_(main_thread) {} + ~MainThreadHandle() { + CHECK_NULL(main_thread_); // main_thread_ should have called Reset + } + std::unique_ptr Connect( + std::unique_ptr delegate, + bool prevent_shutdown); + bool Post(std::unique_ptr request); + Agent* GetInspectorAgent(); + std::unique_ptr MakeThreadSafeDelegate( + std::unique_ptr delegate); + bool Expired(); + + private: + void Reset(); + + MainThreadInterface* main_thread_; + Mutex block_lock_; + int next_session_id_ = 0; + + friend class MainThreadInterface; +}; + +class MainThreadInterface { + public: + MainThreadInterface(Agent* agent, uv_loop_t*, v8::Isolate* isolate, + v8::Platform* platform); + ~MainThreadInterface(); + + void DispatchMessages(); + void Post(std::unique_ptr request); + bool WaitForFrontendEvent(); + std::shared_ptr GetHandle(); + Agent* inspector_agent() { + return agent_; + } + + private: + using AsyncAndInterface = std::pair; + + static void DispatchMessagesAsyncCallback(uv_async_t* async); + static void CloseAsync(AsyncAndInterface*); + + MessageQueue requests_; + Mutex requests_lock_; // requests_ live across threads + // This queue is to maintain the order of the messages for the cases + // when we reenter the DispatchMessages function. + MessageQueue dispatching_message_queue_; + bool dispatching_messages_ = false; + ConditionVariable incoming_message_cond_; + // Used from any thread + Agent* const agent_; + v8::Isolate* const isolate_; + v8::Platform* const platform_; + DeleteFnPtr main_thread_request_; + std::shared_ptr handle_; +}; + +} // namespace inspector +} // namespace node +#endif // SRC_INSPECTOR_MAIN_THREAD_INTERFACE_H_ diff --git a/src/inspector_agent.cc b/src/inspector_agent.cc index 2b03fd05b115a3..ebb8d8a161606d 100644 --- a/src/inspector_agent.cc +++ b/src/inspector_agent.cc @@ -1,6 +1,7 @@ #include "inspector_agent.h" #include "inspector_io.h" +#include "inspector/main_thread_interface.h" #include "inspector/node_string.h" #include "inspector/tracing_agent.h" #include "node/inspector/protocol/Protocol.h" @@ -49,7 +50,7 @@ class StartIoTask : public v8::Task { explicit StartIoTask(Agent* agent) : agent(agent) {} void Run() override { - agent->StartIoThread(false); + agent->StartIoThread(); } private: @@ -64,11 +65,11 @@ std::unique_ptr ToProtocolString(Isolate* isolate, // Called on the main thread. void StartIoThreadAsyncCallback(uv_async_t* handle) { - static_cast(handle->data)->StartIoThread(false); + static_cast(handle->data)->StartIoThread(); } void StartIoInterrupt(Isolate* isolate, void* agent) { - static_cast(agent)->StartIoThread(false); + static_cast(agent)->StartIoThread(); } @@ -195,8 +196,10 @@ class ChannelImpl final : public v8_inspector::V8Inspector::Channel, public: explicit ChannelImpl(Environment* env, const std::unique_ptr& inspector, - std::unique_ptr delegate) - : delegate_(std::move(delegate)) { + std::unique_ptr delegate, + bool prevent_shutdown) + : delegate_(std::move(delegate)), + prevent_shutdown_(prevent_shutdown) { session_ = inspector->connect(1, this, StringView()); node_dispatcher_.reset(new protocol::UberDispatcher(this)); tracing_agent_.reset(new protocol::TracingAgent(env)); @@ -208,7 +211,7 @@ class ChannelImpl final : public v8_inspector::V8Inspector::Channel, tracing_agent_.reset(); // Dispose before the dispatchers } - void dispatchProtocolMessage(const StringView& message) { + std::string dispatchProtocolMessage(const StringView& message) { std::unique_ptr parsed; std::string method; node_dispatcher_->getCommandName( @@ -219,6 +222,7 @@ class ChannelImpl final : public v8_inspector::V8Inspector::Channel, } else { node_dispatcher_->dispatch(std::move(parsed)); } + return method; } void schedulePauseOnNextStatement(const std::string& reason) { @@ -226,6 +230,10 @@ class ChannelImpl final : public v8_inspector::V8Inspector::Channel, session_->schedulePauseOnNextStatement(buffer->string(), buffer->string()); } + bool preventShutdown() { + return prevent_shutdown_; + } + private: void sendResponse( int callId, @@ -263,6 +271,7 @@ class ChannelImpl final : public v8_inspector::V8Inspector::Channel, std::unique_ptr delegate_; std::unique_ptr session_; std::unique_ptr node_dispatcher_; + bool prevent_shutdown_; }; class InspectorTimer { @@ -324,6 +333,44 @@ class InspectorTimerHandle { private: InspectorTimer* timer_; }; + +class SameThreadInspectorSession : public InspectorSession { + public: + SameThreadInspectorSession( + int session_id, std::shared_ptr client) + : session_id_(session_id), client_(client) {} + ~SameThreadInspectorSession() override; + void Dispatch(const v8_inspector::StringView& message) override; + + private: + int session_id_; + std::weak_ptr client_; +}; + +void NotifyClusterWorkersDebugEnabled(Environment* env) { + v8::Isolate* isolate = env->isolate(); + HandleScope handle_scope(isolate); + auto context = env->context(); + + // Send message to enable debug in cluster workers + Local process_object = env->process_object(); + Local emit_fn = + process_object->Get(context, FIXED_ONE_BYTE_STRING(isolate, "emit")) + .ToLocalChecked(); + // In case the thread started early during the startup + if (!emit_fn->IsFunction()) + return; + + Local message = Object::New(isolate); + message->Set(context, FIXED_ONE_BYTE_STRING(isolate, "cmd"), + FIXED_ONE_BYTE_STRING(isolate, "NODE_DEBUG_ENABLED")).FromJust(); + Local argv[] = { + FIXED_ONE_BYTE_STRING(isolate, "internalMessage"), + message + }; + MakeCallback(env->isolate(), process_object, emit_fn.As(), + arraysize(argv), argv, {0, 0}); +} } // namespace class NodeInspectorClient : public V8InspectorClient { @@ -337,31 +384,18 @@ class NodeInspectorClient : public V8InspectorClient { } void runMessageLoopOnPause(int context_group_id) override { - runMessageLoop(false); - } - - void runMessageLoop(bool ignore_terminated) { - if (running_nested_loop_) - return; - terminated_ = false; - running_nested_loop_ = true; - MultiIsolatePlatform* platform = env_->isolate_data()->platform(); - while ((ignore_terminated || !terminated_) && waitForFrontendEvent()) { - while (platform->FlushForegroundTasks(env_->isolate())) {} - } - terminated_ = false; - running_nested_loop_ = false; + waiting_for_resume_ = true; + runMessageLoop(); } - bool waitForFrontendEvent() { - InspectorIo* io = env_->inspector_agent()->io(); - if (io == nullptr) - return false; - return io->WaitForFrontendEvent(); + void waitForIoShutdown() { + waiting_for_io_shutdown_ = true; + runMessageLoop(); } - double currentTimeMS() override { - return uv_hrtime() * 1.0 / NANOS_PER_MSEC; + void waitForFrontend() { + waiting_for_frontend_ = true; + runMessageLoop(); } void maxAsyncCallStackDepthChanged(int depth) override { @@ -398,16 +432,17 @@ class NodeInspectorClient : public V8InspectorClient { } void quitMessageLoopOnPause() override { - terminated_ = true; + waiting_for_resume_ = false; } - int connectFrontend(std::unique_ptr delegate) { + int connectFrontend(std::unique_ptr delegate, + bool prevent_shutdown) { events_dispatched_ = true; int session_id = next_session_id_++; // TODO(addaleax): Revert back to using make_unique once we get issues // with CI resolved (i.e. revert the patch that added this comment). channels_[session_id].reset( - new ChannelImpl(env_, client_, std::move(delegate))); + new ChannelImpl(env_, client_, std::move(delegate), prevent_shutdown)); return session_id; } @@ -418,7 +453,10 @@ class NodeInspectorClient : public V8InspectorClient { void dispatchMessageFromFrontend(int session_id, const StringView& message) { events_dispatched_ = true; - channels_[session_id]->dispatchProtocolMessage(message); + std::string method = + channels_[session_id]->dispatchProtocolMessage(message); + if (waiting_for_frontend_) + waiting_for_frontend_ = method != "Runtime.runIfWaitingForDebugger"; } Local ensureDefaultContextInGroup(int contextGroupId) override { @@ -509,116 +547,150 @@ class NodeInspectorClient : public V8InspectorClient { } bool hasConnectedSessions() { + for (const auto& id_channel : channels_) { + // Other sessions are "invisible" more most purposes + if (id_channel.second->preventShutdown()) + return true; + } + return false; + } + + std::shared_ptr getThreadHandle() { + if (interface_ == nullptr) { + interface_.reset(new MainThreadInterface( + env_->inspector_agent(), env_->event_loop(), env_->isolate(), + env_->isolate_data()->platform())); + } + return interface_->GetHandle(); + } + + bool IsActive() { return !channels_.empty(); } private: + bool shouldRunMessageLoop() { + if (waiting_for_frontend_) + return true; + if (waiting_for_io_shutdown_ || waiting_for_resume_) + return hasConnectedSessions(); + return false; + } + + void runMessageLoop() { + if (running_nested_loop_) + return; + + running_nested_loop_ = true; + + MultiIsolatePlatform* platform = env_->isolate_data()->platform(); + while (shouldRunMessageLoop()) { + if (interface_ && hasConnectedSessions()) + interface_->WaitForFrontendEvent(); + while (platform->FlushForegroundTasks(env_->isolate())) {} + } + running_nested_loop_ = false; + } + + double currentTimeMS() override { + return uv_hrtime() * 1.0 / NANOS_PER_MSEC; + } + node::Environment* env_; - bool terminated_ = false; bool running_nested_loop_ = false; std::unique_ptr client_; std::unordered_map> channels_; std::unordered_map timers_; int next_session_id_ = 1; bool events_dispatched_ = false; + bool waiting_for_resume_ = false; + bool waiting_for_frontend_ = false; + bool waiting_for_io_shutdown_ = false; + // Allows accessing Inspector from non-main threads + std::unique_ptr interface_; }; Agent::Agent(Environment* env) : parent_env_(env) {} -// Destructor needs to be defined here in implementation file as the header -// does not have full definition of some classes. -Agent::~Agent() { -} +Agent::~Agent() = default; -bool Agent::Start(const char* path, const DebugOptions& options) { - path_ = path == nullptr ? "" : path; +bool Agent::Start(const std::string& path, const DebugOptions& options) { + path_ = path; debug_options_ = options; client_ = std::make_shared(parent_env_); - CHECK_EQ(0, uv_async_init(uv_default_loop(), - &start_io_thread_async, - StartIoThreadAsyncCallback)); - start_io_thread_async.data = this; - uv_unref(reinterpret_cast(&start_io_thread_async)); - - // Ignore failure, SIGUSR1 won't work, but that should not block node start. - StartDebugSignalHandler(); - if (options.inspector_enabled()) { - // This will return false if listen failed on the inspector port. - return StartIoThread(options.wait_for_connect()); + if (parent_env_->is_main_thread()) { + CHECK_EQ(0, uv_async_init(parent_env_->event_loop(), + &start_io_thread_async, + StartIoThreadAsyncCallback)); + uv_unref(reinterpret_cast(&start_io_thread_async)); + start_io_thread_async.data = this; + // Ignore failure, SIGUSR1 won't work, but that should not block node start. + StartDebugSignalHandler(); + } + + bool wait_for_connect = options.wait_for_connect(); + if (!options.inspector_enabled() || !StartIoThread()) { + return false; + } + if (wait_for_connect) { + HandleScope scope(parent_env_->isolate()); + parent_env_->process_object()->DefineOwnProperty( + parent_env_->context(), + FIXED_ONE_BYTE_STRING(parent_env_->isolate(), "_breakFirstLine"), + True(parent_env_->isolate()), + static_cast(v8::ReadOnly | v8::DontEnum)) + .FromJust(); + client_->waitForFrontend(); } return true; } -bool Agent::StartIoThread(bool wait_for_connect) { +bool Agent::StartIoThread() { if (io_ != nullptr) return true; CHECK_NOT_NULL(client_); - io_ = std::unique_ptr( - new InspectorIo(parent_env_, path_, debug_options_, wait_for_connect)); - if (!io_->Start()) { - client_.reset(); + io_ = InspectorIo::Start( + client_->getThreadHandle(), path_, debug_options_); + if (io_ == nullptr) { return false; } - - v8::Isolate* isolate = parent_env_->isolate(); - HandleScope handle_scope(isolate); - auto context = parent_env_->context(); - - // Send message to enable debug in workers - Local process_object = parent_env_->process_object(); - Local emit_fn = - process_object->Get(context, FIXED_ONE_BYTE_STRING(isolate, "emit")) - .ToLocalChecked(); - // In case the thread started early during the startup - if (!emit_fn->IsFunction()) - return true; - - Local message = Object::New(isolate); - message->Set(context, FIXED_ONE_BYTE_STRING(isolate, "cmd"), - FIXED_ONE_BYTE_STRING(isolate, "NODE_DEBUG_ENABLED")).FromJust(); - Local argv[] = { - FIXED_ONE_BYTE_STRING(isolate, "internalMessage"), - message - }; - MakeCallback(parent_env_->isolate(), process_object, emit_fn.As(), - arraysize(argv), argv, {0, 0}); - + NotifyClusterWorkersDebugEnabled(parent_env_); return true; } void Agent::Stop() { - if (io_ != nullptr) { - io_->Stop(); - io_.reset(); - } + io_.reset(); } std::unique_ptr Agent::Connect( - std::unique_ptr delegate) { - int session_id = client_->connectFrontend(std::move(delegate)); + std::unique_ptr delegate, + bool prevent_shutdown) { + CHECK_NOT_NULL(client_); + int session_id = client_->connectFrontend(std::move(delegate), + prevent_shutdown); return std::unique_ptr( - new InspectorSession(session_id, client_)); + new SameThreadInspectorSession(session_id, client_)); } void Agent::WaitForDisconnect() { CHECK_NOT_NULL(client_); + if (client_->hasConnectedSessions()) { + fprintf(stderr, "Waiting for the debugger to disconnect...\n"); + fflush(stderr); + } // TODO(addaleax): Maybe this should use an at-exit hook for the Environment // or something similar? client_->contextDestroyed(parent_env_->context()); if (io_ != nullptr) { - io_->WaitForDisconnect(); - // There is a bug in V8 Inspector (https://crbug.com/834056) that - // calls V8InspectorClient::quitMessageLoopOnPause when a session - // disconnects. We are using this flag to ignore those calls so the message - // loop is spinning as long as there's a reason to expect inspector messages - client_->runMessageLoop(true); + io_->StopAcceptingNewConnections(); + client_->waitForIoShutdown(); } } void Agent::FatalException(Local error, Local message) { - if (!IsStarted()) + if (!IsListening()) return; client_->FatalException(error, message); WaitForDisconnect(); @@ -718,26 +790,35 @@ void Agent::ContextCreated(Local context, const ContextInfo& info) { client_->contextCreated(context, info); } -bool Agent::IsWaitingForConnect() { +bool Agent::WillWaitForConnect() { return debug_options_.wait_for_connect(); } -bool Agent::HasConnectedSessions() { +bool Agent::IsActive() { if (client_ == nullptr) return false; - return client_->hasConnectedSessions(); + return io_ != nullptr || client_->IsActive(); } -InspectorSession::InspectorSession(int session_id, - std::shared_ptr client) - : session_id_(session_id), client_(client) {} +void Agent::WaitForConnect() { + CHECK_NOT_NULL(client_); + client_->waitForFrontend(); +} -InspectorSession::~InspectorSession() { - client_->disconnectFrontend(session_id_); +SameThreadInspectorSession::~SameThreadInspectorSession() { + auto client = client_.lock(); + if (client) + client->disconnectFrontend(session_id_); } -void InspectorSession::Dispatch(const StringView& message) { - client_->dispatchMessageFromFrontend(session_id_, message); +void SameThreadInspectorSession::Dispatch( + const v8_inspector::StringView& message) { + auto client = client_.lock(); + if (client) + client->dispatchMessageFromFrontend(session_id_, message); } + + + } // namespace inspector } // namespace node diff --git a/src/inspector_agent.h b/src/inspector_agent.h index 7295d048b64357..dcd6e13aba275f 100644 --- a/src/inspector_agent.h +++ b/src/inspector_agent.h @@ -20,7 +20,6 @@ class StringView; namespace node { // Forward declaration to break recursive dependency chain with src/env.h. class Environment; -class NodePlatform; struct ContextInfo; namespace inspector { @@ -29,12 +28,8 @@ class NodeInspectorClient; class InspectorSession { public: - InspectorSession(int session_id, std::shared_ptr client); - ~InspectorSession(); - void Dispatch(const v8_inspector::StringView& message); - private: - int session_id_; - std::shared_ptr client_; + virtual ~InspectorSession() {} + virtual void Dispatch(const v8_inspector::StringView& message) = 0; }; class InspectorSessionDelegate { @@ -50,15 +45,21 @@ class Agent { ~Agent(); // Create client_, may create io_ if option enabled - bool Start(const char* path, const DebugOptions& options); + bool Start(const std::string& path, const DebugOptions& options); // Stop and destroy io_ void Stop(); - bool IsStarted() { return !!client_; } - - // IO thread started, and client connected - bool IsWaitingForConnect(); - + bool IsListening() { return io_ != nullptr; } + // Returns true if the Node inspector is actually in use. It will be true + // if either the user explicitely opted into inspector (e.g. with the + // --inspect command line flag) or if inspector JS API had been used. + bool IsActive(); + + // Option is set to wait for session connection + bool WillWaitForConnect(); + // Blocks till frontend connects and sends "runIfWaitingForDebugger" + void WaitForConnect(); + // Blocks till all the sessions with "WaitForDisconnectOnShutdown" disconnect void WaitForDisconnect(); void FatalException(v8::Local error, v8::Local message); @@ -77,22 +78,20 @@ class Agent { void EnableAsyncHook(); void DisableAsyncHook(); - // Called by the WS protocol and JS binding to create inspector sessions. + // Called to create inspector sessions that can be used from the main thread. // The inspector responds by using the delegate to send messages back. std::unique_ptr Connect( - std::unique_ptr delegate); + std::unique_ptr delegate, + bool prevent_shutdown); void PauseOnNextJavascriptStatement(const std::string& reason); - // Returns true as long as there is at least one connected session. - bool HasConnectedSessions(); - InspectorIo* io() { return io_.get(); } // Can only be called from the main thread. - bool StartIoThread(bool wait_for_connect); + bool StartIoThread(); // Calls StartIoThread() from off the main thread. void RequestIoThreadStart(); @@ -105,7 +104,9 @@ class Agent { const node::Persistent& fn); node::Environment* parent_env_; + // Encapsulates majority of the Inspector functionality std::shared_ptr client_; + // Interface for transports, e.g. WebSocket server std::unique_ptr io_; std::string path_; DebugOptions debug_options_; diff --git a/src/inspector_io.cc b/src/inspector_io.cc index 78ecce7398bd59..41fea546a83265 100644 --- a/src/inspector_io.cc +++ b/src/inspector_io.cc @@ -1,6 +1,7 @@ #include "inspector_io.h" #include "inspector_socket_server.h" +#include "inspector/main_thread_interface.h" #include "inspector/node_string.h" #include "env-inl.h" #include "debug_utils.h" @@ -11,23 +12,16 @@ #include "util.h" #include "zlib.h" -#include -#include - +#include #include #include - namespace node { namespace inspector { namespace { -using AsyncAndAgent = std::pair; using v8_inspector::StringBuffer; using v8_inspector::StringView; -template -using TransportAndIo = std::pair; - std::string ScriptPath(uv_loop_t* loop, const std::string& script_name) { std::string script_path; @@ -64,45 +58,151 @@ std::string GenerateID() { return uuid; } -void HandleSyncCloseCb(uv_handle_t* handle) { - *static_cast(handle->data) = true; -} +class RequestToServer { + public: + RequestToServer(TransportAction action, + int session_id, + std::unique_ptr message) + : action_(action), + session_id_(session_id), + message_(std::move(message)) {} + + void Dispatch(InspectorSocketServer* server) const { + switch (action_) { + case TransportAction::kKill: + server->TerminateConnections(); + // Fallthrough + case TransportAction::kStop: + server->Stop(); + break; + case TransportAction::kSendMessage: + server->Send( + session_id_, + protocol::StringUtil::StringViewToUtf8(message_->string())); + break; + } + } -void CloseAsyncAndLoop(uv_async_t* async) { - bool is_closed = false; - async->data = &is_closed; - uv_close(reinterpret_cast(async), HandleSyncCloseCb); - while (!is_closed) - uv_run(async->loop, UV_RUN_ONCE); - async->data = nullptr; - CheckedUvLoopClose(async->loop); -} + private: + TransportAction action_; + int session_id_; + std::unique_ptr message_; +}; -// Delete main_thread_req_ on async handle close -void ReleasePairOnAsyncClose(uv_handle_t* async) { - std::unique_ptr pair(node::ContainerOf(&AsyncAndAgent::first, - reinterpret_cast(async))); - // Unique_ptr goes out of scope here and pointer is deleted. -} +class RequestQueueData { + public: + using MessageQueue = std::deque; + + explicit RequestQueueData(uv_loop_t* loop) + : handle_(std::make_shared(this)) { + int err = uv_async_init(loop, &async_, [](uv_async_t* async) { + RequestQueueData* wrapper = + node::ContainerOf(&RequestQueueData::async_, async); + wrapper->DoDispatch(); + }); + CHECK_EQ(0, err); + } + + static void CloseAndFree(RequestQueueData* queue); + + void Post(int session_id, + TransportAction action, + std::unique_ptr message) { + Mutex::ScopedLock scoped_lock(state_lock_); + bool notify = messages_.empty(); + messages_.emplace_back(action, session_id, std::move(message)); + if (notify) { + CHECK_EQ(0, uv_async_send(&async_)); + incoming_message_cond_.Broadcast(scoped_lock); + } + } + + void Wait() { + Mutex::ScopedLock scoped_lock(state_lock_); + if (messages_.empty()) { + incoming_message_cond_.Wait(scoped_lock); + } + } + + void SetServer(InspectorSocketServer* server) { + server_ = server; + } + std::shared_ptr handle() { + return handle_; + } + + private: + ~RequestQueueData() = default; + + MessageQueue GetMessages() { + Mutex::ScopedLock scoped_lock(state_lock_); + MessageQueue messages; + messages_.swap(messages); + return messages; + } + + void DoDispatch() { + if (server_ == nullptr) + return; + for (const auto& request : GetMessages()) { + request.Dispatch(server_); + } + } + + std::shared_ptr handle_; + uv_async_t async_; + InspectorSocketServer* server_ = nullptr; + MessageQueue messages_; + Mutex state_lock_; // Locked before mutating the queue. + ConditionVariable incoming_message_cond_; +}; } // namespace -std::unique_ptr Utf8ToStringView(const std::string& message) { - icu::UnicodeString utf16 = - icu::UnicodeString::fromUTF8(icu::StringPiece(message.data(), - message.length())); - StringView view(reinterpret_cast(utf16.getBuffer()), - utf16.length()); - return StringBuffer::create(view); -} +class RequestQueue { + public: + explicit RequestQueue(RequestQueueData* data) : data_(data) {} + + void Reset() { + Mutex::ScopedLock scoped_lock(lock_); + data_ = nullptr; + } + void Post(int session_id, + TransportAction action, + std::unique_ptr message) { + Mutex::ScopedLock scoped_lock(lock_); + if (data_ != nullptr) + data_->Post(session_id, action, std::move(message)); + } + + void SetServer(InspectorSocketServer* server) { + Mutex::ScopedLock scoped_lock(lock_); + if (data_ != nullptr) + data_->SetServer(server); + } + + bool Expired() { + Mutex::ScopedLock scoped_lock(lock_); + return data_ == nullptr; + } + + private: + RequestQueueData* data_; + Mutex lock_; +}; class IoSessionDelegate : public InspectorSessionDelegate { public: - explicit IoSessionDelegate(InspectorIo* io, int id) : io_(io), id_(id) { } - void SendMessageToFrontend(const v8_inspector::StringView& message) override; + explicit IoSessionDelegate(std::shared_ptr queue, int id) + : request_queue_(queue), id_(id) { } + void SendMessageToFrontend(const v8_inspector::StringView& message) override { + request_queue_->Post(id_, TransportAction::kSendMessage, + StringBuffer::create(message)); + } + private: - InspectorIo* io_; + std::shared_ptr request_queue_; int id_; }; @@ -110,361 +210,133 @@ class IoSessionDelegate : public InspectorSessionDelegate { // mostly session start, message received, and session end. class InspectorIoDelegate: public node::inspector::SocketServerDelegate { public: - InspectorIoDelegate(InspectorIo* io, const std::string& target_id, + InspectorIoDelegate(std::shared_ptr queue, + std::shared_ptr main_threade, + const std::string& target_id, const std::string& script_path, - const std::string& script_name, bool wait); + const std::string& script_name); ~InspectorIoDelegate() { - io_->ServerDone(); } - // Calls PostIncomingMessage() with appropriate InspectorAction: - // kStartSession + void StartSession(int session_id, const std::string& target_id) override; - // kSendMessage void MessageReceived(int session_id, const std::string& message) override; - // kEndSession void EndSession(int session_id) override; std::vector GetTargetIds() override; std::string GetTargetTitle(const std::string& id) override; std::string GetTargetUrl(const std::string& id) override; - void AssignServer(InspectorSocketServer* server) override { - server_ = server; + request_queue_->SetServer(server); } private: - InspectorIo* io_; - int session_id_; + std::shared_ptr request_queue_; + std::shared_ptr main_thread_; + std::unordered_map> sessions_; const std::string script_name_; const std::string script_path_; const std::string target_id_; - bool waiting_; - InspectorSocketServer* server_; }; -void InterruptCallback(v8::Isolate*, void* agent) { - InspectorIo* io = static_cast(agent)->io(); - if (io != nullptr) - io->DispatchMessages(); -} - -class DispatchMessagesTask : public v8::Task { - public: - explicit DispatchMessagesTask(Agent* agent) : agent_(agent) {} - - void Run() override { - InspectorIo* io = agent_->io(); - if (io != nullptr) - io->DispatchMessages(); +// static +std::unique_ptr InspectorIo::Start( + std::shared_ptr main_thread, + const std::string& path, + const DebugOptions& options) { + auto io = std::unique_ptr( + new InspectorIo(main_thread, path, options)); + if (io->request_queue_->Expired()) { // Thread is not running + return nullptr; } - - private: - Agent* agent_; -}; - -InspectorIo::InspectorIo(Environment* env, const std::string& path, - const DebugOptions& options, bool wait_for_connect) - : options_(options), thread_(), state_(State::kNew), - parent_env_(env), thread_req_(), - platform_(parent_env_->isolate_data()->platform()), - dispatching_messages_(false), script_name_(path), - wait_for_connect_(wait_for_connect), port_(-1), - id_(GenerateID()) { - main_thread_req_ = new AsyncAndAgent({uv_async_t(), env->inspector_agent()}); - CHECK_EQ(0, uv_async_init(env->event_loop(), &main_thread_req_->first, - InspectorIo::MainThreadReqAsyncCb)); - uv_unref(reinterpret_cast(&main_thread_req_->first)); - CHECK_EQ(0, uv_sem_init(&thread_start_sem_, 0)); + return io; } -InspectorIo::~InspectorIo() { - uv_sem_destroy(&thread_start_sem_); - uv_close(reinterpret_cast(&main_thread_req_->first), - ReleasePairOnAsyncClose); -} - -bool InspectorIo::Start() { - CHECK_EQ(state_, State::kNew); +InspectorIo::InspectorIo(std::shared_ptr main_thread, + const std::string& path, + const DebugOptions& options) + : main_thread_(main_thread), options_(options), + thread_(), script_name_(path), id_(GenerateID()) { + Mutex::ScopedLock scoped_lock(thread_start_lock_); CHECK_EQ(uv_thread_create(&thread_, InspectorIo::ThreadMain, this), 0); - uv_sem_wait(&thread_start_sem_); - - if (state_ == State::kError) { - return false; - } - state_ = State::kAccepting; - if (wait_for_connect_) { - DispatchMessages(); - } - return true; + thread_start_condition_.Wait(scoped_lock); } -void InspectorIo::Stop() { - CHECK_IMPLIES(sessions_.empty(), state_ == State::kAccepting); - Write(TransportAction::kKill, 0, StringView()); +InspectorIo::~InspectorIo() { + request_queue_->Post(0, TransportAction::kKill, nullptr); int err = uv_thread_join(&thread_); CHECK_EQ(err, 0); - state_ = State::kShutDown; - DispatchMessages(); } -bool InspectorIo::IsStarted() { - return platform_ != nullptr; -} - -void InspectorIo::WaitForDisconnect() { - if (state_ == State::kAccepting) - state_ = State::kDone; - if (!sessions_.empty()) { - state_ = State::kShutDown; - Write(TransportAction::kStop, 0, StringView()); - fprintf(stderr, "Waiting for the debugger to disconnect...\n"); - fflush(stderr); - } +void InspectorIo::StopAcceptingNewConnections() { + request_queue_->Post(0, TransportAction::kStop, nullptr); } // static void InspectorIo::ThreadMain(void* io) { - static_cast(io)->ThreadMain(); -} - -// static -template -void InspectorIo::IoThreadAsyncCb(uv_async_t* async) { - TransportAndIo* transport_and_io = - static_cast*>(async->data); - if (transport_and_io == nullptr) { - return; - } - Transport* transport = transport_and_io->first; - InspectorIo* io = transport_and_io->second; - MessageQueue outgoing_message_queue; - io->SwapBehindLock(&io->outgoing_message_queue_, &outgoing_message_queue); - for (const auto& outgoing : outgoing_message_queue) { - int session_id = std::get<1>(outgoing); - switch (std::get<0>(outgoing)) { - case TransportAction::kKill: - transport->TerminateConnections(); - // Fallthrough - case TransportAction::kStop: - transport->Stop(); - break; - case TransportAction::kSendMessage: - transport->Send(session_id, - protocol::StringUtil::StringViewToUtf8( - std::get<2>(outgoing)->string())); - break; - case TransportAction::kAcceptSession: - transport->AcceptSession(session_id); - break; - case TransportAction::kDeclineSession: - transport->DeclineSession(session_id); - break; - } - } + static_cast(io)->ThreadMain(); } -template void InspectorIo::ThreadMain() { uv_loop_t loop; loop.data = nullptr; int err = uv_loop_init(&loop); CHECK_EQ(err, 0); - thread_req_.data = nullptr; - err = uv_async_init(&loop, &thread_req_, IoThreadAsyncCb); - CHECK_EQ(err, 0); + std::shared_ptr queue(new RequestQueueData(&loop), + RequestQueueData::CloseAndFree); std::string script_path = ScriptPath(&loop, script_name_); - auto delegate = std::unique_ptr( - new InspectorIoDelegate(this, id_, script_path, script_name_, - wait_for_connect_)); - Transport server(std::move(delegate), &loop, options_.host_name(), - options_.port()); - TransportAndIo queue_transport(&server, this); - thread_req_.data = &queue_transport; - if (!server.Start()) { - state_ = State::kError; // Safe, main thread is waiting on semaphore - CloseAsyncAndLoop(&thread_req_); - uv_sem_post(&thread_start_sem_); - return; - } - port_ = server.Port(); // Safe, main thread is waiting on semaphore. - if (!wait_for_connect_) { - uv_sem_post(&thread_start_sem_); + std::unique_ptr delegate( + new InspectorIoDelegate(queue, main_thread_, id_, + script_path, script_name_)); + InspectorSocketServer server(std::move(delegate), &loop, + options_.host_name(), options_.port()); + request_queue_ = queue->handle(); + // Its lifetime is now that of the server delegate + queue.reset(); + { + Mutex::ScopedLock scoped_lock(thread_start_lock_); + if (server.Start()) { + port_ = server.Port(); + } + thread_start_condition_.Broadcast(scoped_lock); } uv_run(&loop, UV_RUN_DEFAULT); - thread_req_.data = nullptr; CheckedUvLoopClose(&loop); } -template -bool InspectorIo::AppendMessage(MessageQueue* queue, - ActionType action, int session_id, - std::unique_ptr buffer) { - Mutex::ScopedLock scoped_lock(state_lock_); - bool trigger_pumping = queue->empty(); - queue->push_back(std::make_tuple(action, session_id, std::move(buffer))); - return trigger_pumping; -} - -template -void InspectorIo::SwapBehindLock(MessageQueue* vector1, - MessageQueue* vector2) { - Mutex::ScopedLock scoped_lock(state_lock_); - vector1->swap(*vector2); -} - -void InspectorIo::PostIncomingMessage(InspectorAction action, int session_id, - const std::string& message) { - Debug(parent_env_, DebugCategory::INSPECTOR_SERVER, - ">>> %s\n", message.c_str()); - if (AppendMessage(&incoming_message_queue_, action, session_id, - Utf8ToStringView(message))) { - Agent* agent = main_thread_req_->second; - v8::Isolate* isolate = parent_env_->isolate(); - platform_->CallOnForegroundThread(isolate, - new DispatchMessagesTask(agent)); - isolate->RequestInterrupt(InterruptCallback, agent); - CHECK_EQ(0, uv_async_send(&main_thread_req_->first)); - } - Mutex::ScopedLock scoped_lock(state_lock_); - incoming_message_cond_.Broadcast(scoped_lock); -} - std::vector InspectorIo::GetTargetIds() const { return { id_ }; } -TransportAction InspectorIo::Attach(int session_id) { - Agent* agent = parent_env_->inspector_agent(); - fprintf(stderr, "Debugger attached.\n"); - sessions_[session_id] = agent->Connect(std::unique_ptr( - new IoSessionDelegate(this, session_id))); - return TransportAction::kAcceptSession; -} - -void InspectorIo::DispatchMessages() { - if (dispatching_messages_) - return; - dispatching_messages_ = true; - bool had_messages = false; - do { - if (dispatching_message_queue_.empty()) - SwapBehindLock(&incoming_message_queue_, &dispatching_message_queue_); - had_messages = !dispatching_message_queue_.empty(); - while (!dispatching_message_queue_.empty()) { - MessageQueue::value_type task; - std::swap(dispatching_message_queue_.front(), task); - dispatching_message_queue_.pop_front(); - int id = std::get<1>(task); - StringView message = std::get<2>(task)->string(); - switch (std::get<0>(task)) { - case InspectorAction::kStartSession: - Write(Attach(id), id, StringView()); - break; - case InspectorAction::kStartSessionUnconditionally: - Attach(id); - break; - case InspectorAction::kEndSession: - sessions_.erase(id); - if (!sessions_.empty()) - continue; - if (state_ == State::kShutDown) { - state_ = State::kDone; - } else { - state_ = State::kAccepting; - } - break; - case InspectorAction::kSendMessage: - auto session = sessions_.find(id); - if (session != sessions_.end() && session->second) { - session->second->Dispatch(message); - } - break; - } - } - } while (had_messages); - dispatching_messages_ = false; -} - -// static -void InspectorIo::MainThreadReqAsyncCb(uv_async_t* req) { - AsyncAndAgent* pair = node::ContainerOf(&AsyncAndAgent::first, req); - // Note that this may be called after io was closed or even after a new - // one was created and ran. - InspectorIo* io = pair->second->io(); - if (io != nullptr) - io->DispatchMessages(); -} - -void InspectorIo::Write(TransportAction action, int session_id, - const StringView& inspector_message) { - std::string message_str = - protocol::StringUtil::StringViewToUtf8(inspector_message); - Debug(parent_env_, DebugCategory::INSPECTOR_SERVER, - "<<< %s\n", message_str.c_str()); - AppendMessage(&outgoing_message_queue_, action, session_id, - StringBuffer::create(inspector_message)); - int err = uv_async_send(&thread_req_); - CHECK_EQ(0, err); -} - -bool InspectorIo::WaitForFrontendEvent() { - // We allow DispatchMessages reentry as we enter the pause. This is important - // to support debugging the code invoked by an inspector call, such - // as Runtime.evaluate - dispatching_messages_ = false; - Mutex::ScopedLock scoped_lock(state_lock_); - if (sessions_.empty()) - return false; - if (dispatching_message_queue_.empty() && incoming_message_queue_.empty()) { - incoming_message_cond_.Wait(scoped_lock); - } - return true; -} - -InspectorIoDelegate::InspectorIoDelegate(InspectorIo* io, - const std::string& target_id, - const std::string& script_path, - const std::string& script_name, - bool wait) - : io_(io), - session_id_(0), - script_name_(script_name), - script_path_(script_path), - target_id_(target_id), - waiting_(wait), - server_(nullptr) { } - +InspectorIoDelegate::InspectorIoDelegate( + std::shared_ptr queue, + std::shared_ptr main_thread, + const std::string& target_id, + const std::string& script_path, + const std::string& script_name) + : request_queue_(queue), main_thread_(main_thread), + script_name_(script_name), script_path_(script_path), + target_id_(target_id) {} void InspectorIoDelegate::StartSession(int session_id, const std::string& target_id) { - session_id_ = session_id; - InspectorAction action = InspectorAction::kStartSession; - if (waiting_) { - action = InspectorAction::kStartSessionUnconditionally; - server_->AcceptSession(session_id); + auto session = main_thread_->Connect( + std::unique_ptr( + new IoSessionDelegate(request_queue_->handle(), session_id)), true); + if (session) { + sessions_[session_id] = std::move(session); + fprintf(stderr, "Debugger attached.\n"); } - io_->PostIncomingMessage(action, session_id, ""); } void InspectorIoDelegate::MessageReceived(int session_id, const std::string& message) { - // TODO(pfeldman): Instead of blocking execution while debugger - // engages, node should wait for the run callback from the remote client - // and initiate its startup. This is a change to node.cc that should be - // upstreamed separately. - if (waiting_) { - if (message.find("\"Runtime.runIfWaitingForDebugger\"") != - std::string::npos) { - waiting_ = false; - io_->ResumeStartup(); - } - } - io_->PostIncomingMessage(InspectorAction::kSendMessage, session_id, - message); + auto session = sessions_.find(session_id); + if (session != sessions_.end()) + session->second->Dispatch(Utf8ToStringView(message)->string()); } void InspectorIoDelegate::EndSession(int session_id) { - io_->PostIncomingMessage(InspectorAction::kEndSession, session_id, ""); + sessions_.erase(session_id); } std::vector InspectorIoDelegate::GetTargetIds() { @@ -479,10 +351,17 @@ std::string InspectorIoDelegate::GetTargetUrl(const std::string& id) { return "file://" + script_path_; } -void IoSessionDelegate::SendMessageToFrontend( - const v8_inspector::StringView& message) { - io_->Write(TransportAction::kSendMessage, id_, message); +// static +void RequestQueueData::CloseAndFree(RequestQueueData* queue) { + queue->handle_->Reset(); + queue->handle_.reset(); + uv_close(reinterpret_cast(&queue->async_), + [](uv_handle_t* handle) { + uv_async_t* async = reinterpret_cast(handle); + RequestQueueData* wrapper = + node::ContainerOf(&RequestQueueData::async_, async); + delete wrapper; + }); } - } // namespace inspector } // namespace node diff --git a/src/inspector_io.h b/src/inspector_io.h index c897a44a528ec2..7c43d212f0422e 100644 --- a/src/inspector_io.h +++ b/src/inspector_io.h @@ -6,8 +6,6 @@ #include "node_mutex.h" #include "uv.h" -#include -#include #include #include @@ -16,17 +14,14 @@ #endif -// Forward declaration to break recursive dependency chain with src/env.h. -namespace node { -class Environment; -} // namespace node - namespace v8_inspector { class StringBuffer; class StringView; } // namespace v8_inspector namespace node { +// Forward declaration to break recursive dependency chain with src/env.h. +class Environment; namespace inspector { std::string FormatWsAddress(const std::string& host, int port, @@ -34,143 +29,64 @@ std::string FormatWsAddress(const std::string& host, int port, bool include_protocol); class InspectorIoDelegate; - -enum class InspectorAction { - kStartSession, - kStartSessionUnconditionally, // First attach with --inspect-brk - kEndSession, - kSendMessage -}; +class MainThreadHandle; +class RequestQueue; // kKill closes connections and stops the server, kStop only stops the server enum class TransportAction { kKill, kSendMessage, - kStop, - kAcceptSession, - kDeclineSession + kStop }; class InspectorIo { public: - InspectorIo(node::Environment* env, const std::string& path, - const DebugOptions& options, bool wait_for_connect); - + // Start the inspector agent thread, waiting for it to initialize + // bool Start(); + // Returns empty pointer if thread was not started + static std::unique_ptr Start( + std::shared_ptr main_thread, const std::string& path, + const DebugOptions& options); + + // Will block till the transport thread shuts down ~InspectorIo(); - // Start the inspector agent thread, waiting for it to initialize, - // and waiting as well for a connection if wait_for_connect. - bool Start(); - // Stop the inspector agent thread. - void Stop(); - - bool IsStarted(); - - void WaitForDisconnect(); - // Called from thread to queue an incoming message and trigger - // DispatchMessages() on the main thread. - void PostIncomingMessage(InspectorAction action, int session_id, - const std::string& message); - void ResumeStartup() { - uv_sem_post(&thread_start_sem_); - } - void ServerDone() { - uv_close(reinterpret_cast(&thread_req_), nullptr); - } - bool WaitForFrontendEvent(); - int port() const { return port_; } + void StopAcceptingNewConnections(); std::string host() const { return options_.host_name(); } + int port() const { return port_; } std::vector GetTargetIds() const; private: - template - using MessageQueue = - std::deque>>; - enum class State { - kNew, - kAccepting, - kDone, - kError, - kShutDown - }; - - // Callback for main_thread_req_'s uv_async_t - static void MainThreadReqAsyncCb(uv_async_t* req); + InspectorIo(std::shared_ptr handle, + const std::string& path, const DebugOptions& options); // Wrapper for agent->ThreadMain() static void ThreadMain(void* agent); // Runs a uv_loop_t - template void ThreadMain(); - // Called by ThreadMain's loop when triggered by thread_req_, writes - // messages from outgoing_message_queue to the InspectorSockerServer - template static void IoThreadAsyncCb(uv_async_t* async); - - void DispatchMessages(); - // Write action to outgoing_message_queue, and wake the thread - void Write(TransportAction action, int session_id, - const v8_inspector::StringView& message); - // Thread-safe append of message to a queue. Return true if the queue - // used to be empty. - template - bool AppendMessage(MessageQueue* vector, ActionType action, - int session_id, - std::unique_ptr buffer); - // Used as equivalent of a thread-safe "pop" of an entire queue's content. - template - void SwapBehindLock(MessageQueue* vector1, - MessageQueue* vector2); - // Attach session to an inspector. Either kAcceptSession or kDeclineSession - TransportAction Attach(int session_id); - + void ThreadMain(); + + // This is a thread-safe object that will post async tasks. It lives as long + // as an Inspector object lives (almost as long as an Isolate). + std::shared_ptr main_thread_; + // Used to post on a frontend interface thread, lives while the server is + // running + std::shared_ptr request_queue_; const DebugOptions options_; // The IO thread runs its own uv_loop to implement the TCP server off // the main thread. uv_thread_t thread_; - // Used by Start() to wait for thread to initialize, or for it to initialize - // and receive a connection if wait_for_connect was requested. - uv_sem_t thread_start_sem_; - - State state_; - node::Environment* parent_env_; - - // Attached to the uv_loop in ThreadMain() - uv_async_t thread_req_; - // Note that this will live while the async is being closed - likely, past - // the parent object lifespan - std::pair* main_thread_req_; - // Will be used to post tasks from another thread - v8::Platform* const platform_; - - // Message queues - ConditionVariable incoming_message_cond_; - Mutex state_lock_; // Locked before mutating either queue. - MessageQueue incoming_message_queue_; - MessageQueue outgoing_message_queue_; - // This queue is to maintain the order of the messages for the cases - // when we reenter the DispatchMessages function. - MessageQueue dispatching_message_queue_; - - bool dispatching_messages_; + // For setting up interthread communications + Mutex thread_start_lock_; + ConditionVariable thread_start_condition_; std::string script_name_; - std::string script_path_; - const bool wait_for_connect_; - int port_; - std::unordered_map> sessions_; + int port_ = -1; // May be accessed from any thread const std::string id_; - - friend class DispatchMessagesTask; - friend class IoSessionDelegate; - friend void InterruptCallback(v8::Isolate*, void* agent); }; -std::unique_ptr Utf8ToStringView( - const std::string& message); - } // namespace inspector } // namespace node diff --git a/src/inspector_js_api.cc b/src/inspector_js_api.cc index 104cd93b3cd9e0..a8e2e8ecafeefe 100644 --- a/src/inspector_js_api.cc +++ b/src/inspector_js_api.cc @@ -66,7 +66,7 @@ class JSBindingsConnection : public AsyncWrap { callback_(env->isolate(), callback) { Agent* inspector = env->inspector_agent(); session_ = inspector->Connect(std::unique_ptr( - new JSBindingsSessionDelegate(env, this))); + new JSBindingsSessionDelegate(env, this)), false); } void OnMessage(Local value) { @@ -103,7 +103,11 @@ class JSBindingsConnection : public AsyncWrap { } } - size_t self_size() const override { return sizeof(*this); } + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + tracker->TrackField("callback", callback_); + tracker->TrackFieldWithSize("session", sizeof(*session_)); + } private: std::unique_ptr session_; @@ -112,7 +116,7 @@ class JSBindingsConnection : public AsyncWrap { static bool InspectorEnabled(Environment* env) { Agent* agent = env->inspector_agent(); - return agent->io() != nullptr || agent->HasConnectedSessions(); + return agent->IsActive(); } void AddCommandLineAPI(const FunctionCallbackInfo& info) { @@ -247,8 +251,9 @@ void Open(const FunctionCallbackInfo& args) { if (args.Length() > 2 && args[2]->IsBoolean()) { wait_for_connect = args[2]->BooleanValue(env->context()).FromJust(); } - - agent->StartIoThread(wait_for_connect); + agent->StartIoThread(); + if (wait_for_connect) + agent->WaitForConnect(); } void Url(const FunctionCallbackInfo& args) { @@ -279,10 +284,10 @@ void Initialize(Local target, Local unused, Agent* agent = env->inspector_agent(); env->SetMethod(target, "consoleCall", InspectorConsoleCall); env->SetMethod(target, "addCommandLineAPI", AddCommandLineAPI); - if (agent->IsWaitingForConnect()) + if (agent->WillWaitForConnect()) env->SetMethod(target, "callAndPauseOnStart", CallAndPauseOnStart); env->SetMethod(target, "open", Open); - env->SetMethod(target, "url", Url); + env->SetMethodNoSideEffect(target, "url", Url); env->SetMethod(target, "asyncTaskScheduled", AsyncTaskScheduledWrapper); env->SetMethod(target, "asyncTaskCanceled", @@ -293,7 +298,7 @@ void Initialize(Local target, Local unused, InvokeAsyncTaskFnWithId<&Agent::AsyncTaskFinished>); env->SetMethod(target, "registerAsyncHook", RegisterAsyncHookWrapper); - env->SetMethod(target, "isEnabled", IsEnabled); + env->SetMethodNoSideEffect(target, "isEnabled", IsEnabled); auto conn_str = FIXED_ONE_BYTE_STRING(env->isolate(), "Connection"); Local tmpl = diff --git a/src/inspector_socket_server.cc b/src/inspector_socket_server.cc index 174dc7c726f21c..1621b408b43274 100644 --- a/src/inspector_socket_server.cc +++ b/src/inspector_socket_server.cc @@ -173,11 +173,8 @@ class SocketSession { InspectorSocket* ws_socket() { return ws_socket_.get(); } - void set_ws_key(const std::string& ws_key) { - ws_key_ = ws_key; - } - void Accept() { - ws_socket_->AcceptUpgrade(ws_key_); + void Accept(const std::string& ws_key) { + ws_socket_->AcceptUpgrade(ws_key); } void Decline() { ws_socket_->CancelHandshake(); @@ -208,7 +205,6 @@ class SocketSession { const int id_; InspectorSocket::Pointer ws_socket_; const int server_port_; - std::string ws_key_; }; class ServerSocket { @@ -260,11 +256,11 @@ void InspectorSocketServer::SessionStarted(int session_id, const std::string& ws_key) { SocketSession* session = Session(session_id); if (!TargetExists(id)) { - Session(session_id)->Decline(); + session->Decline(); return; } connected_sessions_[session_id].first = id; - session->set_ws_key(ws_key); + session->Accept(ws_key); delegate_->StartSession(session_id, id); } @@ -404,6 +400,8 @@ bool InspectorSocketServer::Start() { } void InspectorSocketServer::Stop() { + if (state_ == ServerState::kStopped) + return; CHECK_EQ(state_, ServerState::kRunning); state_ = ServerState::kStopped; server_sockets_.clear(); @@ -446,23 +444,6 @@ void InspectorSocketServer::Accept(int server_port, } } -void InspectorSocketServer::AcceptSession(int session_id) { - SocketSession* session = Session(session_id); - if (session == nullptr) { - delegate_->EndSession(session_id); - } else { - session->Accept(); - } -} - -void InspectorSocketServer::DeclineSession(int session_id) { - auto it = connected_sessions_.find(session_id); - if (it != connected_sessions_.end()) { - it->second.first.clear(); - it->second.second->Decline(); - } -} - void InspectorSocketServer::Send(int session_id, const std::string& message) { SocketSession* session = Session(session_id); if (session != nullptr) { diff --git a/src/inspector_socket_server.h b/src/inspector_socket_server.h index bbc2195095772f..271be6ec555bf1 100644 --- a/src/inspector_socket_server.h +++ b/src/inspector_socket_server.h @@ -54,10 +54,6 @@ class InspectorSocketServer { void Send(int session_id, const std::string& message); // kKill void TerminateConnections(); - // kAcceptSession - void AcceptSession(int session_id); - // kDeclineSession - void DeclineSession(int session_id); int Port() const; // Session connection lifecycle diff --git a/src/js_stream.h b/src/js_stream.h index b47a91a653ba7e..f3406ae83ee560 100644 --- a/src/js_stream.h +++ b/src/js_stream.h @@ -27,7 +27,9 @@ class JSStream : public AsyncWrap, public StreamBase { size_t count, uv_stream_t* send_handle) override; - size_t self_size() const override { return sizeof(*this); } + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + } protected: JSStream(Environment* env, v8::Local obj); diff --git a/src/memory_tracker-inl.h b/src/memory_tracker-inl.h new file mode 100644 index 00000000000000..568a4364f9c64d --- /dev/null +++ b/src/memory_tracker-inl.h @@ -0,0 +1,198 @@ +#ifndef SRC_MEMORY_TRACKER_INL_H_ +#define SRC_MEMORY_TRACKER_INL_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include "memory_tracker.h" + +namespace node { + +class MemoryRetainerNode : public v8::EmbedderGraph::Node { + public: + explicit inline MemoryRetainerNode(MemoryTracker* tracker, + const MemoryRetainer* retainer, + const char* name) + : retainer_(retainer) { + if (retainer_ != nullptr) { + v8::HandleScope handle_scope(tracker->isolate()); + v8::Local obj = retainer_->WrappedObject(); + if (!obj.IsEmpty()) + wrapper_node_ = tracker->graph()->V8Node(obj); + + name_ = retainer_->MemoryInfoName(); + } + if (name_.empty() && name != nullptr) { + name_ = name; + } + } + + const char* Name() override { return name_.c_str(); } + const char* NamePrefix() override { return "Node /"; } + size_t SizeInBytes() override { return size_; } + // TODO(addaleax): Merging this with the "official" WrapperNode() method + // seems to lose accuracy, e.g. SizeInBytes() is disregarded. + // Figure out whether to do anything about that. + Node* JSWrapperNode() { return wrapper_node_; } + + bool IsRootNode() override { + return retainer_ != nullptr && retainer_->IsRootNode(); + } + + private: + friend class MemoryTracker; + + Node* wrapper_node_ = nullptr; + const MemoryRetainer* retainer_; + std::string name_; + size_t size_ = 0; +}; + +template +void MemoryTracker::TrackThis(const T* obj) { + CurrentNode()->size_ = sizeof(T); +} + +void MemoryTracker::TrackFieldWithSize(const char* name, size_t size) { + if (size > 0) + AddNode(name)->size_ = size; +} + +void MemoryTracker::TrackField(const char* name, const MemoryRetainer& value) { + TrackField(name, &value); +} + +void MemoryTracker::TrackField(const char* name, const MemoryRetainer* value) { + if (track_only_self_ || value == nullptr) return; + auto it = seen_.find(value); + if (it != seen_.end()) { + graph_->AddEdge(CurrentNode(), it->second); + } else { + Track(value, name); + } +} + +template +void MemoryTracker::TrackField(const char* name, + const std::unique_ptr& value) { + TrackField(name, value.get()); +} + +template +void MemoryTracker::TrackField(const char* name, const T& value) { + if (value.begin() == value.end()) return; + size_t index = 0; + PushNode(name); + for (Iterator it = value.begin(); it != value.end(); ++it) + TrackField(std::to_string(index++).c_str(), *it); + PopNode(); +} + +template +void MemoryTracker::TrackField(const char* name, const std::queue& value) { + struct ContainerGetter : public std::queue { + static const typename std::queue::container_type& Get( + const std::queue& value) { + return value.*&ContainerGetter::c; + } + }; + + const auto& container = ContainerGetter::Get(value); + TrackField(name, container); +} + +template +void MemoryTracker::TrackField(const char* name, const T& value) { + // For numbers, creating new nodes is not worth the overhead. + CurrentNode()->size_ += sizeof(T); +} + +template +void MemoryTracker::TrackField(const char* name, const std::pair& value) { + PushNode(name); + TrackField("first", value.first); + TrackField("second", value.second); + PopNode(); +} + +template +void MemoryTracker::TrackField(const char* name, + const std::basic_string& value) { + TrackFieldWithSize(name, value.size() * sizeof(T)); +} + +template +void MemoryTracker::TrackField(const char* name, + const v8::Persistent& value) { + TrackField(name, value.Get(isolate_)); +} + +template +void MemoryTracker::TrackField(const char* name, const v8::Local& value) { + if (!value.IsEmpty()) + graph_->AddEdge(CurrentNode(), graph_->V8Node(value)); +} + +template +void MemoryTracker::TrackField(const char* name, + const MallocedBuffer& value) { + TrackFieldWithSize(name, value.size); +} + +void MemoryTracker::TrackField(const char* name, const uv_buf_t& value) { + TrackFieldWithSize(name, value.len); +} + +template +void MemoryTracker::TrackField(const char* name, + const AliasedBuffer& value) { + TrackField(name, value.GetJSArray()); +} + +void MemoryTracker::Track(const MemoryRetainer* value, const char* name) { + v8::HandleScope handle_scope(isolate_); + MemoryRetainerNode* n = PushNode(name, value); + value->MemoryInfo(this); + CHECK_EQ(CurrentNode(), n); + CHECK_NE(n->size_, 0); + PopNode(); +} + +MemoryRetainerNode* MemoryTracker::CurrentNode() const { + if (node_stack_.empty()) return nullptr; + return node_stack_.top(); +} + +MemoryRetainerNode* MemoryTracker::AddNode( + const char* name, const MemoryRetainer* retainer) { + MemoryRetainerNode* n = new MemoryRetainerNode(this, retainer, name); + graph_->AddNode(std::unique_ptr(n)); + if (retainer != nullptr) + seen_[retainer] = n; + + if (CurrentNode() != nullptr) + graph_->AddEdge(CurrentNode(), n); + + if (n->JSWrapperNode() != nullptr) { + graph_->AddEdge(n, n->JSWrapperNode()); + graph_->AddEdge(n->JSWrapperNode(), n); + } + + return n; +} + +MemoryRetainerNode* MemoryTracker::PushNode( + const char* name, const MemoryRetainer* retainer) { + MemoryRetainerNode* n = AddNode(name, retainer); + node_stack_.push(n); + return n; +} + +void MemoryTracker::PopNode() { + node_stack_.pop(); +} + +} // namespace node + +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#endif // SRC_MEMORY_TRACKER_INL_H_ diff --git a/src/memory_tracker.h b/src/memory_tracker.h new file mode 100644 index 00000000000000..d0f9e0dcad8f1e --- /dev/null +++ b/src/memory_tracker.h @@ -0,0 +1,108 @@ +#ifndef SRC_MEMORY_TRACKER_H_ +#define SRC_MEMORY_TRACKER_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include +#include +#include +#include +#include +#include +#include "aliased_buffer.h" +#include "v8-profiler.h" + +namespace node { + +class MemoryTracker; +class MemoryRetainerNode; + +namespace crypto { +class NodeBIO; +} + +class MemoryRetainer { + public: + virtual ~MemoryRetainer() {} + + // Subclasses should implement this to provide information for heap snapshots. + virtual void MemoryInfo(MemoryTracker* tracker) const = 0; + + virtual v8::Local WrappedObject() const { + return v8::Local(); + } + + virtual bool IsRootNode() const { return false; } + + virtual std::string MemoryInfoName() const { return std::string(); } +}; + +class MemoryTracker { + public: + template + inline void TrackThis(const T* obj); + + inline void TrackFieldWithSize(const char* name, size_t size); + + inline void TrackField(const char* name, const MemoryRetainer& value); + inline void TrackField(const char* name, const MemoryRetainer* value); + template + inline void TrackField(const char* name, const std::unique_ptr& value); + template + inline void TrackField(const char* name, const T& value); + template + inline void TrackField(const char* name, const std::queue& value); + template + inline void TrackField(const char* name, const std::basic_string& value); + template ::is_specialized, bool>::type, + typename dummy = bool> + inline void TrackField(const char* name, const T& value); + template + inline void TrackField(const char* name, const std::pair& value); + template + inline void TrackField(const char* name, + const v8::Persistent& value); + template + inline void TrackField(const char* name, const v8::Local& value); + template + inline void TrackField(const char* name, const MallocedBuffer& value); + inline void TrackField(const char* name, const uv_buf_t& value); + template + inline void TrackField(const char* name, + const AliasedBuffer& value); + + inline void Track(const MemoryRetainer* value, const char* name = nullptr); + + inline void set_track_only_self(bool value) { track_only_self_ = value; } + inline v8::EmbedderGraph* graph() { return graph_; } + inline v8::Isolate* isolate() { return isolate_; } + + inline explicit MemoryTracker(v8::Isolate* isolate, + v8::EmbedderGraph* graph) + : isolate_(isolate), graph_(graph) {} + + private: + typedef std::unordered_map + NodeMap; + + inline MemoryRetainerNode* CurrentNode() const; + inline MemoryRetainerNode* AddNode(const char* name, + const MemoryRetainer* retainer = nullptr); + inline MemoryRetainerNode* PushNode(const char* name, + const MemoryRetainer* retainer = nullptr); + inline void PopNode(); + + bool track_only_self_ = false; + v8::Isolate* isolate_; + v8::EmbedderGraph* graph_; + std::stack node_stack_; + NodeMap seen_; +}; + +} // namespace node + +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#endif // SRC_MEMORY_TRACKER_H_ diff --git a/src/module_wrap.cc b/src/module_wrap.cc index 7569ef106688e2..3aba52dcde7780 100644 --- a/src/module_wrap.cc +++ b/src/module_wrap.cc @@ -789,11 +789,11 @@ void ModuleWrap::Initialize(Local target, env->SetProtoMethod(tpl, "link", Link); env->SetProtoMethod(tpl, "instantiate", Instantiate); env->SetProtoMethod(tpl, "evaluate", Evaluate); - env->SetProtoMethod(tpl, "namespace", Namespace); - env->SetProtoMethod(tpl, "getStatus", GetStatus); - env->SetProtoMethod(tpl, "getError", GetError); - env->SetProtoMethod(tpl, "getStaticDependencySpecifiers", - GetStaticDependencySpecifiers); + env->SetProtoMethodNoSideEffect(tpl, "namespace", Namespace); + env->SetProtoMethodNoSideEffect(tpl, "getStatus", GetStatus); + env->SetProtoMethodNoSideEffect(tpl, "getError", GetError); + env->SetProtoMethodNoSideEffect(tpl, "getStaticDependencySpecifiers", + GetStaticDependencySpecifiers); target->Set(FIXED_ONE_BYTE_STRING(isolate, "ModuleWrap"), tpl->GetFunction()); env->SetMethod(target, "resolve", Resolve); diff --git a/src/module_wrap.h b/src/module_wrap.h index 3969e2a37878fc..2d6f5c49d88988 100644 --- a/src/module_wrap.h +++ b/src/module_wrap.h @@ -33,6 +33,12 @@ class ModuleWrap : public BaseObject { v8::Local module, v8::Local meta); + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + tracker->TrackField("url", url_); + tracker->TrackField("resolve_cache", resolve_cache_); + } + private: ModuleWrap(Environment* env, v8::Local object, diff --git a/src/node.cc b/src/node.cc index 7ed25bd2aedcc4..627f4958e0278c 100644 --- a/src/node.cc +++ b/src/node.cc @@ -138,11 +138,9 @@ using v8::Boolean; using v8::Context; using v8::EscapableHandleScope; using v8::Exception; -using v8::Float64Array; using v8::Function; using v8::FunctionCallbackInfo; using v8::HandleScope; -using v8::HeapStatistics; using v8::Integer; using v8::Isolate; using v8::Just; @@ -162,9 +160,9 @@ using v8::Promise; using v8::PropertyCallbackInfo; using v8::ScriptOrigin; using v8::SealHandleScope; +using v8::SideEffectType; using v8::String; using v8::TryCatch; -using v8::Uint32Array; using v8::Undefined; using v8::V8; using v8::Value; @@ -277,12 +275,14 @@ std::string config_warning_file; // NOLINT(runtime/string) // that is used by lib/internal/bootstrap/node.js bool config_expose_internals = false; +std::string config_process_title; // NOLINT(runtime/string) + bool v8_initialized = false; bool linux_at_secure = false; // process-relative uptime base, initialized at start-up -static double prog_start_time; +double prog_start_time; static Mutex node_isolate_mutex; static v8::Isolate* node_isolate; @@ -321,11 +321,12 @@ static struct { // Inspector agent can't fail to start, but if it was configured to listen // right away on the websocket port and fails to bind/etc, this will return // false. - return env->inspector_agent()->Start(script_path, options); + return env->inspector_agent()->Start( + script_path == nullptr ? "" : script_path, options); } bool InspectorStarted(Environment* env) { - return env->inspector_agent()->IsStarted(); + return env->inspector_agent()->IsListening(); } #endif // HAVE_INSPECTOR @@ -385,7 +386,7 @@ static struct { static const unsigned kMaxSignal = 32; #endif -static void PrintErrorString(const char* format, ...) { +void PrintErrorString(const char* format, ...) { va_list ap; va_start(ap, format); #ifdef _WIN32 @@ -1101,414 +1102,9 @@ NO_RETURN void Assert(const char* const (*args)[4]) { Abort(); } - -static void Abort(const FunctionCallbackInfo& args) { - Abort(); -} - - -void Chdir(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - CHECK(env->is_main_thread()); - - if (args.Length() != 1 || !args[0]->IsString()) { - return env->ThrowTypeError("Bad argument."); - } - - node::Utf8Value path(args.GetIsolate(), args[0]); - int err = uv_chdir(*path); - if (err) { - return env->ThrowUVException(err, "chdir", nullptr, *path, nullptr); - } -} - - -static void Cwd(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); -#ifdef _WIN32 - /* MAX_PATH is in characters, not bytes. Make sure we have enough headroom. */ - char buf[MAX_PATH * 4]; -#else - char buf[PATH_MAX]; -#endif - - size_t cwd_len = sizeof(buf); - int err = uv_cwd(buf, &cwd_len); - if (err) { - return env->ThrowUVException(err, "uv_cwd"); - } - - Local cwd = String::NewFromUtf8(env->isolate(), - buf, - String::kNormalString, - cwd_len); - args.GetReturnValue().Set(cwd); -} - - -void Umask(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - uint32_t old; - - if (args.Length() < 1 || args[0]->IsUndefined()) { - old = umask(0); - umask(static_cast(old)); - } else if (!args[0]->IsInt32() && !args[0]->IsString()) { - return env->ThrowTypeError("argument must be an integer or octal string."); - } else { - int oct; - if (args[0]->IsInt32()) { - oct = args[0]->Uint32Value(); - } else { - oct = 0; - node::Utf8Value str(env->isolate(), args[0]); - - // Parse the octal string. - for (size_t i = 0; i < str.length(); i++) { - char c = (*str)[i]; - if (c > '7' || c < '0') { - return env->ThrowTypeError("invalid octal string"); - } - oct *= 8; - oct += c - '0'; - } - } - old = umask(static_cast(oct)); - } - - args.GetReturnValue().Set(old); -} - - -#if defined(__POSIX__) && !defined(__ANDROID__) && !defined(__CloudABI__) - -static const uid_t uid_not_found = static_cast(-1); -static const gid_t gid_not_found = static_cast(-1); - - -static uid_t uid_by_name(const char* name) { - struct passwd pwd; - struct passwd* pp; - char buf[8192]; - - errno = 0; - pp = nullptr; - - if (getpwnam_r(name, &pwd, buf, sizeof(buf), &pp) == 0 && pp != nullptr) { - return pp->pw_uid; - } - - return uid_not_found; -} - - -static char* name_by_uid(uid_t uid) { - struct passwd pwd; - struct passwd* pp; - char buf[8192]; - int rc; - - errno = 0; - pp = nullptr; - - if ((rc = getpwuid_r(uid, &pwd, buf, sizeof(buf), &pp)) == 0 && - pp != nullptr) { - return strdup(pp->pw_name); - } - - if (rc == 0) { - errno = ENOENT; - } - - return nullptr; -} - - -static gid_t gid_by_name(const char* name) { - struct group pwd; - struct group* pp; - char buf[8192]; - - errno = 0; - pp = nullptr; - - if (getgrnam_r(name, &pwd, buf, sizeof(buf), &pp) == 0 && pp != nullptr) { - return pp->gr_gid; - } - - return gid_not_found; -} - - -#if 0 // For future use. -static const char* name_by_gid(gid_t gid) { - struct group pwd; - struct group* pp; - char buf[8192]; - int rc; - - errno = 0; - pp = nullptr; - - if ((rc = getgrgid_r(gid, &pwd, buf, sizeof(buf), &pp)) == 0 && - pp != nullptr) { - return strdup(pp->gr_name); - } - - if (rc == 0) { - errno = ENOENT; - } - - return nullptr; -} -#endif - - -static uid_t uid_by_name(Isolate* isolate, Local value) { - if (value->IsUint32()) { - return static_cast(value->Uint32Value()); - } else { - node::Utf8Value name(isolate, value); - return uid_by_name(*name); - } -} - - -static gid_t gid_by_name(Isolate* isolate, Local value) { - if (value->IsUint32()) { - return static_cast(value->Uint32Value()); - } else { - node::Utf8Value name(isolate, value); - return gid_by_name(*name); - } -} - -static void GetUid(const FunctionCallbackInfo& args) { - // uid_t is an uint32_t on all supported platforms. - args.GetReturnValue().Set(static_cast(getuid())); -} - - -static void GetGid(const FunctionCallbackInfo& args) { - // gid_t is an uint32_t on all supported platforms. - args.GetReturnValue().Set(static_cast(getgid())); -} - - -static void GetEUid(const FunctionCallbackInfo& args) { - // uid_t is an uint32_t on all supported platforms. - args.GetReturnValue().Set(static_cast(geteuid())); -} - - -static void GetEGid(const FunctionCallbackInfo& args) { - // gid_t is an uint32_t on all supported platforms. - args.GetReturnValue().Set(static_cast(getegid())); -} - - -void SetGid(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - CHECK(env->is_main_thread()); - - if (!args[0]->IsUint32() && !args[0]->IsString()) { - return env->ThrowTypeError("setgid argument must be a number or a string"); - } - - gid_t gid = gid_by_name(env->isolate(), args[0]); - - if (gid == gid_not_found) { - return env->ThrowError("setgid group id does not exist"); - } - - if (setgid(gid)) { - return env->ThrowErrnoException(errno, "setgid"); - } -} - - -void SetEGid(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - CHECK(env->is_main_thread()); - - if (!args[0]->IsUint32() && !args[0]->IsString()) { - return env->ThrowTypeError("setegid argument must be a number or string"); - } - - gid_t gid = gid_by_name(env->isolate(), args[0]); - - if (gid == gid_not_found) { - return env->ThrowError("setegid group id does not exist"); - } - - if (setegid(gid)) { - return env->ThrowErrnoException(errno, "setegid"); - } -} - - -void SetUid(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - CHECK(env->is_main_thread()); - - if (!args[0]->IsUint32() && !args[0]->IsString()) { - return env->ThrowTypeError("setuid argument must be a number or a string"); - } - - uid_t uid = uid_by_name(env->isolate(), args[0]); - - if (uid == uid_not_found) { - return env->ThrowError("setuid user id does not exist"); - } - - if (setuid(uid)) { - return env->ThrowErrnoException(errno, "setuid"); - } -} - - -void SetEUid(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - CHECK(env->is_main_thread()); - - if (!args[0]->IsUint32() && !args[0]->IsString()) { - return env->ThrowTypeError("seteuid argument must be a number or string"); - } - - uid_t uid = uid_by_name(env->isolate(), args[0]); - - if (uid == uid_not_found) { - return env->ThrowError("seteuid user id does not exist"); - } - - if (seteuid(uid)) { - return env->ThrowErrnoException(errno, "seteuid"); - } -} - - -static void GetGroups(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - - int ngroups = getgroups(0, nullptr); - - if (ngroups == -1) { - return env->ThrowErrnoException(errno, "getgroups"); - } - - gid_t* groups = new gid_t[ngroups]; - - ngroups = getgroups(ngroups, groups); - - if (ngroups == -1) { - delete[] groups; - return env->ThrowErrnoException(errno, "getgroups"); - } - - Local groups_list = Array::New(env->isolate(), ngroups); - bool seen_egid = false; - gid_t egid = getegid(); - - for (int i = 0; i < ngroups; i++) { - groups_list->Set(i, Integer::New(env->isolate(), groups[i])); - if (groups[i] == egid) - seen_egid = true; - } - - delete[] groups; - - if (seen_egid == false) { - groups_list->Set(ngroups, Integer::New(env->isolate(), egid)); - } - - args.GetReturnValue().Set(groups_list); -} - - -void SetGroups(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - - if (!args[0]->IsArray()) { - return env->ThrowTypeError("argument 1 must be an array"); - } - - Local groups_list = args[0].As(); - size_t size = groups_list->Length(); - gid_t* groups = new gid_t[size]; - - for (size_t i = 0; i < size; i++) { - gid_t gid = gid_by_name(env->isolate(), groups_list->Get(i)); - - if (gid == gid_not_found) { - delete[] groups; - return env->ThrowError("group name not found"); - } - - groups[i] = gid; - } - - int rc = setgroups(size, groups); - delete[] groups; - - if (rc == -1) { - return env->ThrowErrnoException(errno, "setgroups"); - } -} - - -void InitGroups(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - - if (!args[0]->IsUint32() && !args[0]->IsString()) { - return env->ThrowTypeError("argument 1 must be a number or a string"); - } - - if (!args[1]->IsUint32() && !args[1]->IsString()) { - return env->ThrowTypeError("argument 2 must be a number or a string"); - } - - node::Utf8Value arg0(env->isolate(), args[0]); - gid_t extra_group; - bool must_free; - char* user; - - if (args[0]->IsUint32()) { - user = name_by_uid(args[0]->Uint32Value()); - must_free = true; - } else { - user = *arg0; - must_free = false; - } - - if (user == nullptr) { - return env->ThrowError("initgroups user not found"); - } - - extra_group = gid_by_name(env->isolate(), args[1]); - - if (extra_group == gid_not_found) { - if (must_free) - free(user); - return env->ThrowError("initgroups extra group not found"); - } - - int rc = initgroups(user, extra_group); - - if (must_free) { - free(user); - } - - if (rc) { - return env->ThrowErrnoException(errno, "initgroups"); - } -} - -#endif // __POSIX__ && !defined(__ANDROID__) && !defined(__CloudABI__) - - static void WaitForInspectorDisconnect(Environment* env) { #if HAVE_INSPECTOR - if (env->inspector_agent()->HasConnectedSessions()) { + if (env->inspector_agent()->IsActive()) { // Restore signal dispositions, the app is done and is no longer // capable of handling signals. #if defined(__POSIX__) && !defined(NODE_SHARED_MODE) @@ -1534,113 +1130,6 @@ static void Exit(const FunctionCallbackInfo& args) { env->Exit(args[0]->Int32Value()); } - -static void Uptime(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - double uptime; - - uv_update_time(env->event_loop()); - uptime = uv_now(env->event_loop()) - prog_start_time; - - args.GetReturnValue().Set(Number::New(env->isolate(), uptime / 1000)); -} - - -void MemoryUsage(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - - size_t rss; - int err = uv_resident_set_memory(&rss); - if (err) { - return env->ThrowUVException(err, "uv_resident_set_memory"); - } - - Isolate* isolate = env->isolate(); - // V8 memory usage - HeapStatistics v8_heap_stats; - isolate->GetHeapStatistics(&v8_heap_stats); - - // Get the double array pointer from the Float64Array argument. - CHECK(args[0]->IsFloat64Array()); - Local array = args[0].As(); - CHECK_EQ(array->Length(), 4); - Local ab = array->Buffer(); - double* fields = static_cast(ab->GetContents().Data()); - - fields[0] = rss; - fields[1] = v8_heap_stats.total_heap_size(); - fields[2] = v8_heap_stats.used_heap_size(); - fields[3] = isolate->AdjustAmountOfExternalAllocatedMemory(0); -} - - -static void Kill(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - - if (args.Length() != 2) { - return env->ThrowError("Bad argument."); - } - - int pid = args[0]->Int32Value(); - int sig = args[1]->Int32Value(); - int err = uv_kill(pid, sig); - args.GetReturnValue().Set(err); -} - -// used in Hrtime() below -#define NANOS_PER_SEC 1000000000 - -// Hrtime exposes libuv's uv_hrtime() high-resolution timer. -// The value returned by uv_hrtime() is a 64-bit int representing nanoseconds, -// so this function instead fills in an Uint32Array with 3 entries, -// to avoid any integer overflow possibility. -// The first two entries contain the second part of the value -// broken into the upper/lower 32 bits to be converted back in JS, -// because there is no Uint64Array in JS. -// The third entry contains the remaining nanosecond part of the value. -void Hrtime(const FunctionCallbackInfo& args) { - uint64_t t = uv_hrtime(); - - Local ab = args[0].As()->Buffer(); - uint32_t* fields = static_cast(ab->GetContents().Data()); - - fields[0] = (t / NANOS_PER_SEC) >> 32; - fields[1] = (t / NANOS_PER_SEC) & 0xffffffff; - fields[2] = t % NANOS_PER_SEC; -} - -// Microseconds in a second, as a float, used in CPUUsage() below -#define MICROS_PER_SEC 1e6 - -// CPUUsage use libuv's uv_getrusage() this-process resource usage accessor, -// to access ru_utime (user CPU time used) and ru_stime (system CPU time used), -// which are uv_timeval_t structs (long tv_sec, long tv_usec). -// Returns those values as Float64 microseconds in the elements of the array -// passed to the function. -void CPUUsage(const FunctionCallbackInfo& args) { - uv_rusage_t rusage; - - // Call libuv to get the values we'll return. - int err = uv_getrusage(&rusage); - if (err) { - // On error, return the strerror version of the error code. - Local errmsg = OneByteString(args.GetIsolate(), uv_strerror(err)); - args.GetReturnValue().Set(errmsg); - return; - } - - // Get the double array pointer from the Float64Array argument. - CHECK(args[0]->IsFloat64Array()); - Local array = args[0].As(); - CHECK_EQ(array->Length(), 2); - Local ab = array->Buffer(); - double* fields = static_cast(ab->GetContents().Data()); - - // Set the Float64Array elements to be user / system values in microseconds. - fields[0] = MICROS_PER_SEC * rusage.ru_utime.tv_sec + rusage.ru_utime.tv_usec; - fields[1] = MICROS_PER_SEC * rusage.ru_stime.tv_sec + rusage.ru_stime.tv_usec; -} - extern "C" void node_module_register(void* m) { struct node_module* mp = reinterpret_cast(m); @@ -1949,7 +1438,16 @@ void FatalException(Isolate* isolate, exit(7); } else if (caught->IsFalse()) { ReportException(env, error, message); - exit(1); + + // fatal_exception_function call before may have set a new exit code -> + // read it again, otherwise use default for uncaughtException 1 + Local exit_code = env->exit_code_string(); + Local code; + if (!process_object->Get(env->context(), exit_code).ToLocal(&code) || + !code->IsInt32()) { + exit(1); + } + exit(code.As()->Value()); } } } @@ -2178,6 +1676,8 @@ static void ProcessTitleSetter(Local property, Local value, const PropertyCallbackInfo& info) { node::Utf8Value title(info.GetIsolate(), value); + TRACE_EVENT_METADATA1("__metadata", "process_name", "name", + TRACE_STR_COPY(*title)); uv_set_process_title(*title); } @@ -2435,18 +1935,6 @@ static void DebugEnd(const FunctionCallbackInfo& args); namespace { -void StartProfilerIdleNotifier(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - env->StartProfilerIdleNotifier(); -} - - -void StopProfilerIdleNotifier(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - env->StopProfilerIdleNotifier(); -} - - #define READONLY_PROPERTY(obj, str, var) \ do { \ obj->DefineOwnProperty(env->context(), \ @@ -2482,7 +1970,10 @@ void SetupProcessObject(Environment* env, title_string, ProcessTitleGetter, env->is_main_thread() ? ProcessTitleSetter : nullptr, - env->as_external()).FromJust()); + env->as_external(), + v8::DEFAULT, + v8::None, + SideEffectType::kHasNoSideEffect).FromJust()); // process.version READONLY_PROPERTY(process, @@ -2776,6 +2267,8 @@ void SetupProcessObject(Environment* env, // define various internal methods if (env->is_main_thread()) { + env->SetMethod(process, "_debugProcess", DebugProcess); + env->SetMethod(process, "_debugEnd", DebugEnd); env->SetMethod(process, "_startProfilerIdleNotifier", StartProfilerIdleNotifier); @@ -2786,34 +2279,22 @@ void SetupProcessObject(Environment* env, env->SetMethod(process, "chdir", Chdir); env->SetMethod(process, "umask", Umask); } - env->SetMethod(process, "_getActiveRequests", GetActiveRequests); env->SetMethod(process, "_getActiveHandles", GetActiveHandles); + env->SetMethod(process, "_kill", Kill); + + env->SetMethodNoSideEffect(process, "cwd", Cwd); + env->SetMethod(process, "dlopen", DLOpen); env->SetMethod(process, "reallyExit", Exit); - env->SetMethod(process, "cwd", Cwd); + env->SetMethodNoSideEffect(process, "uptime", Uptime); #if defined(__POSIX__) && !defined(__ANDROID__) && !defined(__CloudABI__) - env->SetMethod(process, "getuid", GetUid); - env->SetMethod(process, "geteuid", GetEUid); - env->SetMethod(process, "getgid", GetGid); - env->SetMethod(process, "getegid", GetEGid); - env->SetMethod(process, "getgroups", GetGroups); + env->SetMethodNoSideEffect(process, "getuid", GetUid); + env->SetMethodNoSideEffect(process, "geteuid", GetEUid); + env->SetMethodNoSideEffect(process, "getgid", GetGid); + env->SetMethodNoSideEffect(process, "getegid", GetEGid); + env->SetMethodNoSideEffect(process, "getgroups", GetGroups); #endif // __POSIX__ && !defined(__ANDROID__) && !defined(__CloudABI__) - - env->SetMethod(process, "_kill", Kill); - env->SetMethod(process, "dlopen", DLOpen); - - if (env->is_main_thread()) { - env->SetMethod(process, "_debugProcess", DebugProcess); - env->SetMethod(process, "_debugEnd", DebugEnd); - } - - env->SetMethod(process, "hrtime", Hrtime); - - env->SetMethod(process, "cpuUsage", CPUUsage); - - env->SetMethod(process, "uptime", Uptime); - env->SetMethod(process, "memoryUsage", MemoryUsage); } @@ -2834,19 +2315,6 @@ void SignalExit(int signo) { } -// Most of the time, it's best to use `console.error` to write -// to the process.stderr stream. However, in some cases, such as -// when debugging the stream.Writable class or the process.nextTick -// function, it is useful to bypass JavaScript entirely. -void RawDebug(const FunctionCallbackInfo& args) { - CHECK(args.Length() == 1 && args[0]->IsString() && - "must be called with a single string"); - node::Utf8Value message(args.GetIsolate(), args[0]); - PrintErrorString("%s\n", *message); - fflush(stderr); -} - - static MaybeLocal GetBootstrapper( Environment* env, Local source, @@ -3064,6 +2532,7 @@ static void PrintHelp() { " write warnings to file instead of\n" " stderr\n" " --throw-deprecation throw an exception on deprecations\n" + " --title=title the process title to use on start up\n" #if HAVE_OPENSSL " --tls-cipher-list=val use an alternative default TLS cipher " "list\n" @@ -3202,6 +2671,7 @@ static void CheckIfAllowedInEnv(const char* exe, bool is_env, "--redirect-warnings", "--require", "--throw-deprecation", + "--title", "--tls-cipher-list", "--trace-deprecation", "--trace-event-categories", @@ -3372,6 +2842,8 @@ static void ParseArgs(int* argc, } else if (strncmp(arg, "--security-revert=", 18) == 0) { const char* cve = arg + 18; Revert(cve); + } else if (strncmp(arg, "--title=", 8) == 0) { + config_process_title = arg + 8; } else if (strcmp(arg, "--preserve-symlinks") == 0) { config_preserve_symlinks = true; } else if (strcmp(arg, "--preserve-symlinks-main") == 0) { @@ -3509,7 +2981,7 @@ static void ParseArgs(int* argc, static void StartInspector(Environment* env, const char* path, DebugOptions debug_options) { #if HAVE_INSPECTOR - CHECK(!env->inspector_agent()->IsStarted()); + CHECK(!env->inspector_agent()->IsListening()); v8_platform.StartInspector(env, path, debug_options); #endif // HAVE_INSPECTOR } @@ -3652,7 +3124,7 @@ static void DebugProcess(const FunctionCallbackInfo& args) { static void DebugEnd(const FunctionCallbackInfo& args) { #if HAVE_INSPECTOR Environment* env = Environment::GetCurrent(args); - if (env->inspector_agent()->IsStarted()) { + if (env->inspector_agent()->IsListening()) { env->inspector_agent()->Stop(); } #endif @@ -3866,6 +3338,10 @@ void Init(int* argc, ProcessArgv(argc, argv, exec_argc, exec_argv); + // Set the process.title immediately after processing argv if --title is set. + if (!config_process_title.empty()) + uv_set_process_title(config_process_title.c_str()); + #if defined(NODE_HAVE_I18N_SUPPORT) // If the parameter isn't given, use the env variable. if (icu_data_dir.empty()) @@ -4074,6 +3550,13 @@ inline int Start(Isolate* isolate, IsolateData* isolate_data, Environment env(isolate_data, context, v8_platform.GetTracingAgent()); env.Start(argc, argv, exec_argc, exec_argv, v8_is_profiling); + char name_buffer[512]; + if (uv_get_process_title(name_buffer, sizeof(name_buffer)) == 0) { + // Only emit the metadata event if the title can be retrieved successfully. + // Ignore it otherwise. + TRACE_EVENT_METADATA1("__metadata", "process_name", "name", + TRACE_STR_COPY(name_buffer)); + } TRACE_EVENT_METADATA1("__metadata", "version", "node", NODE_VERSION_STRING); TRACE_EVENT_METADATA1("__metadata", "thread_name", "name", "JavaScriptMainThread"); diff --git a/src/node.h b/src/node.h index b551c8bb09baf9..305d605ef4a33a 100644 --- a/src/node.h +++ b/src/node.h @@ -579,6 +579,28 @@ extern "C" NODE_EXTERN void node_module_register(void* mod); */ #define NODE_MODULE_DECL /* nothing */ +#define NODE_MODULE_INITIALIZER_BASE node_register_module_v + +#define NODE_MODULE_INITIALIZER_X(base, version) \ + NODE_MODULE_INITIALIZER_X_HELPER(base, version) + +#define NODE_MODULE_INITIALIZER_X_HELPER(base, version) base##version + +#define NODE_MODULE_INITIALIZER \ + NODE_MODULE_INITIALIZER_X(NODE_MODULE_INITIALIZER_BASE, \ + NODE_MODULE_VERSION) + +#define NODE_MODULE_INIT() \ + extern "C" NODE_MODULE_EXPORT void \ + NODE_MODULE_INITIALIZER(v8::Local exports, \ + v8::Local module, \ + v8::Local context); \ + NODE_MODULE_CONTEXT_AWARE(NODE_GYP_MODULE_NAME, \ + NODE_MODULE_INITIALIZER) \ + void NODE_MODULE_INITIALIZER(v8::Local exports, \ + v8::Local module, \ + v8::Local context) + /* Called after the event loop exits but before the VM is disposed. * Callbacks are run in reverse order of registration, i.e. newest first. */ @@ -661,6 +683,10 @@ class InternalCallbackScope; * * This object should be stack-allocated to ensure that it is contained in a * valid HandleScope. + * + * Exceptions happening within this scope will be treated like uncaught + * exceptions. If this behaviour is undesirable, a new `v8::TryCatch` scope + * needs to be created inside of this scope. */ class NODE_EXTERN CallbackScope { public: diff --git a/src/node_api.cc b/src/node_api.cc index 2b14617175c167..ad0796570a4c14 100644 --- a/src/node_api.cc +++ b/src/node_api.cc @@ -926,7 +926,8 @@ const char* error_messages[] = {nullptr, "Invalid handle scope usage", "Invalid callback scope usage", "Thread-safe function queue is full", - "Thread-safe function handle is closing" + "Thread-safe function handle is closing", + "A bigint was expected", }; static inline napi_status napi_clear_last_error(napi_env env) { @@ -958,7 +959,7 @@ napi_status napi_get_last_error_info(napi_env env, // We don't have a napi_status_last as this would result in an ABI // change each time a message was added. static_assert( - node::arraysize(error_messages) == napi_closing + 1, + node::arraysize(error_messages) == napi_bigint_expected + 1, "Count of error messages must match count of error values"); CHECK_LE(env->last_error.error_code, napi_callback_scope_mismatch); @@ -1025,11 +1026,11 @@ napi_status napi_create_function(napi_env env, RETURN_STATUS_IF_FALSE(env, !cbdata.IsEmpty(), napi_generic_failure); - v8::Local tpl = v8::FunctionTemplate::New( - isolate, v8impl::FunctionCallbackWrapper::Invoke, cbdata); - v8::Local context = isolate->GetCurrentContext(); - v8::MaybeLocal maybe_function = tpl->GetFunction(context); + v8::MaybeLocal maybe_function = + v8::Function::New(context, + v8impl::FunctionCallbackWrapper::Invoke, + cbdata); CHECK_MAYBE_EMPTY(env, maybe_function, napi_generic_failure); return_value = scope.Escape(maybe_function.ToLocalChecked()); @@ -1491,13 +1492,17 @@ napi_status napi_define_properties(napi_env env, v8::Local cbdata = v8impl::CreateFunctionCallbackData(env, p->method, p->data); - RETURN_STATUS_IF_FALSE(env, !cbdata.IsEmpty(), napi_generic_failure); + CHECK_MAYBE_EMPTY(env, cbdata, napi_generic_failure); + + v8::MaybeLocal maybe_fn = + v8::Function::New(context, + v8impl::FunctionCallbackWrapper::Invoke, + cbdata); - v8::Local t = v8::FunctionTemplate::New( - isolate, v8impl::FunctionCallbackWrapper::Invoke, cbdata); + CHECK_MAYBE_EMPTY(env, maybe_fn, napi_generic_failure); auto define_maybe = obj->DefineOwnProperty( - context, property_name, t->GetFunction(), attributes); + context, property_name, maybe_fn.ToLocalChecked(), attributes); if (!define_maybe.FromMaybe(false)) { return napi_set_last_error(env, napi_generic_failure); @@ -1709,6 +1714,58 @@ napi_status napi_create_int64(napi_env env, return napi_clear_last_error(env); } +napi_status napi_create_bigint_int64(napi_env env, + int64_t value, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + *result = v8impl::JsValueFromV8LocalValue( + v8::BigInt::New(env->isolate, value)); + + return napi_clear_last_error(env); +} + +napi_status napi_create_bigint_uint64(napi_env env, + uint64_t value, + napi_value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + *result = v8impl::JsValueFromV8LocalValue( + v8::BigInt::NewFromUnsigned(env->isolate, value)); + + return napi_clear_last_error(env); +} + +napi_status napi_create_bigint_words(napi_env env, + int sign_bit, + size_t word_count, + const uint64_t* words, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, words); + CHECK_ARG(env, result); + + v8::Local context = env->isolate->GetCurrentContext(); + + if (word_count > INT_MAX) { + napi_throw_range_error(env, nullptr, "Maximum BigInt size exceeded"); + return napi_set_last_error(env, napi_pending_exception); + } + + v8::MaybeLocal b = v8::BigInt::NewFromWords( + context, sign_bit, word_count, words); + + if (try_catch.HasCaught()) { + return napi_set_last_error(env, napi_pending_exception); + } else { + CHECK_MAYBE_EMPTY(env, b, napi_generic_failure); + *result = v8impl::JsValueFromV8LocalValue(b.ToLocalChecked()); + return napi_clear_last_error(env); + } +} + napi_status napi_get_boolean(napi_env env, bool value, napi_value* result) { CHECK_ENV(env); CHECK_ARG(env, result); @@ -1874,6 +1931,8 @@ napi_status napi_typeof(napi_env env, if (v->IsNumber()) { *result = napi_number; + } else if (v->IsBigInt()) { + *result = napi_bigint; } else if (v->IsString()) { *result = napi_string; } else if (v->IsFunction()) { @@ -2197,6 +2256,72 @@ napi_status napi_get_value_int64(napi_env env, return napi_clear_last_error(env); } +napi_status napi_get_value_bigint_int64(napi_env env, + napi_value value, + int64_t* result, + bool* lossless) { + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + CHECK_ARG(env, lossless); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + + RETURN_STATUS_IF_FALSE(env, val->IsBigInt(), napi_bigint_expected); + + *result = val.As()->Int64Value(lossless); + + return napi_clear_last_error(env); +} + +napi_status napi_get_value_bigint_uint64(napi_env env, + napi_value value, + uint64_t* result, + bool* lossless) { + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + CHECK_ARG(env, lossless); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + + RETURN_STATUS_IF_FALSE(env, val->IsBigInt(), napi_bigint_expected); + + *result = val.As()->Uint64Value(lossless); + + return napi_clear_last_error(env); +} + +napi_status napi_get_value_bigint_words(napi_env env, + napi_value value, + int* sign_bit, + size_t* word_count, + uint64_t* words) { + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, word_count); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + + RETURN_STATUS_IF_FALSE(env, val->IsBigInt(), napi_bigint_expected); + + v8::Local big = val.As(); + + int word_count_int = *word_count; + + if (sign_bit == nullptr && words == nullptr) { + word_count_int = big->WordCount(); + } else { + CHECK_ARG(env, sign_bit); + CHECK_ARG(env, words); + big->ToWordsArray(sign_bit, &word_count_int, words); + } + + *word_count = word_count_int; + + return napi_clear_last_error(env); +} + napi_status napi_get_value_bool(napi_env env, napi_value value, bool* result) { // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. @@ -3135,6 +3260,14 @@ napi_status napi_create_typedarray(napi_env env, CREATE_TYPED_ARRAY( env, Float64Array, 8, buffer, byte_offset, length, typedArray); break; + case napi_bigint64_array: + CREATE_TYPED_ARRAY( + env, BigInt64Array, 8, buffer, byte_offset, length, typedArray); + break; + case napi_biguint64_array: + CREATE_TYPED_ARRAY( + env, BigUint64Array, 8, buffer, byte_offset, length, typedArray); + break; default: return napi_set_last_error(env, napi_invalid_arg); } @@ -3177,6 +3310,10 @@ napi_status napi_get_typedarray_info(napi_env env, *type = napi_float32_array; } else if (value->IsFloat64Array()) { *type = napi_float64_array; + } else if (value->IsBigInt64Array()) { + *type = napi_bigint64_array; + } else if (value->IsBigUint64Array()) { + *type = napi_biguint64_array; } } diff --git a/src/node_api.h b/src/node_api.h index 84706ac3ed6769..1b2a392a0a0b1c 100644 --- a/src/node_api.h +++ b/src/node_api.h @@ -7,6 +7,16 @@ struct uv_loop_s; // Forward declaration. +#ifndef NAPI_VERSION +#ifdef NAPI_EXPERIMENTAL +// Use INT_MAX, this should only be consumed by the pre-processor anyway. +#define NAPI_VERSION 2147483647 +#else +// The baseline version for N-API +#define NAPI_VERSION 3 +#endif +#endif + #ifdef _WIN32 #ifdef BUILDING_NODE_EXTENSION #ifdef EXTERNAL_NAPI @@ -118,19 +128,10 @@ EXTERN_C_START NAPI_EXTERN void napi_module_register(napi_module* mod); -NAPI_EXTERN napi_status napi_add_env_cleanup_hook(napi_env env, - void (*fun)(void* arg), - void* arg); -NAPI_EXTERN napi_status napi_remove_env_cleanup_hook(napi_env env, - void (*fun)(void* arg), - void* arg); - NAPI_EXTERN napi_status napi_get_last_error_info(napi_env env, const napi_extended_error_info** result); -NAPI_EXTERN napi_status napi_fatal_exception(napi_env env, napi_value err); - NAPI_EXTERN NAPI_NO_RETURN void napi_fatal_error(const char* location, size_t location_len, const char* message, @@ -443,14 +444,6 @@ NAPI_EXTERN napi_status napi_escape_handle(napi_env env, napi_value escapee, napi_value* result); -NAPI_EXTERN napi_status napi_open_callback_scope(napi_env env, - napi_value resource_object, - napi_async_context context, - napi_callback_scope* result); - -NAPI_EXTERN napi_status napi_close_callback_scope(napi_env env, - napi_callback_scope scope); - // Methods to support error handling NAPI_EXTERN napi_status napi_throw(napi_env env, napi_value error); NAPI_EXTERN napi_status napi_throw_error(napi_env env, @@ -610,11 +603,38 @@ NAPI_EXTERN napi_status napi_run_script(napi_env env, napi_value script, napi_value* result); +#if NAPI_VERSION >= 2 + // Return the current libuv event loop for a given environment NAPI_EXTERN napi_status napi_get_uv_event_loop(napi_env env, struct uv_loop_s** loop); +#endif // NAPI_VERSION >= 2 + +#if NAPI_VERSION >= 3 + +NAPI_EXTERN napi_status napi_open_callback_scope(napi_env env, + napi_value resource_object, + napi_async_context context, + napi_callback_scope* result); + +NAPI_EXTERN napi_status napi_close_callback_scope(napi_env env, + napi_callback_scope scope); + +NAPI_EXTERN napi_status napi_fatal_exception(napi_env env, napi_value err); + +NAPI_EXTERN napi_status napi_add_env_cleanup_hook(napi_env env, + void (*fun)(void* arg), + void* arg); + +NAPI_EXTERN napi_status napi_remove_env_cleanup_hook(napi_env env, + void (*fun)(void* arg), + void* arg); + +#endif // NAPI_VERSION >= 3 + #ifdef NAPI_EXPERIMENTAL + // Calling into JS from other threads NAPI_EXTERN napi_status napi_create_threadsafe_function(napi_env env, @@ -651,7 +671,32 @@ napi_unref_threadsafe_function(napi_env env, napi_threadsafe_function func); NAPI_EXTERN napi_status napi_ref_threadsafe_function(napi_env env, napi_threadsafe_function func); +NAPI_EXTERN napi_status napi_create_bigint_int64(napi_env env, + int64_t value, + napi_value* result); +NAPI_EXTERN napi_status napi_create_bigint_uint64(napi_env env, + uint64_t value, + napi_value* result); +NAPI_EXTERN napi_status napi_create_bigint_words(napi_env env, + int sign_bit, + size_t word_count, + const uint64_t* words, + napi_value* result); +NAPI_EXTERN napi_status napi_get_value_bigint_int64(napi_env env, + napi_value value, + int64_t* result, + bool* lossless); +NAPI_EXTERN napi_status napi_get_value_bigint_uint64(napi_env env, + napi_value value, + uint64_t* result, + bool* lossless); +NAPI_EXTERN napi_status napi_get_value_bigint_words(napi_env env, + napi_value value, + int* sign_bit, + size_t* word_count, + uint64_t* words); #endif // NAPI_EXPERIMENTAL + EXTERN_C_END #endif // SRC_NODE_API_H_ diff --git a/src/node_api_types.h b/src/node_api_types.h index af7d7c7f95331d..10215d9aa3a0ff 100644 --- a/src/node_api_types.h +++ b/src/node_api_types.h @@ -46,6 +46,7 @@ typedef enum { napi_object, napi_function, napi_external, + napi_bigint, } napi_valuetype; typedef enum { @@ -58,6 +59,8 @@ typedef enum { napi_uint32_array, napi_float32_array, napi_float64_array, + napi_bigint64_array, + napi_biguint64_array, } napi_typedarray_type; typedef enum { @@ -76,10 +79,9 @@ typedef enum { napi_escape_called_twice, napi_handle_scope_mismatch, napi_callback_scope_mismatch, -#ifdef NAPI_EXPERIMENTAL napi_queue_full, napi_closing, -#endif // NAPI_EXPERIMENTAL + napi_bigint_expected, } napi_status; #ifdef NAPI_EXPERIMENTAL diff --git a/src/node_buffer.cc b/src/node_buffer.cc index 9465145ac37eb9..858852e748fe03 100644 --- a/src/node_buffer.cc +++ b/src/node_buffer.cc @@ -483,7 +483,7 @@ void StringSlice(const FunctionCallbackInfo& args) { bool release = false; // Node's "ucs2" encoding expects LE character data inside a Buffer, so we - // need to reorder on BE platforms. See http://nodejs.org/api/buffer.html + // need to reorder on BE platforms. See https://nodejs.org/api/buffer.html // regarding Node's "ucs2" encoding specification. const bool aligned = (reinterpret_cast(data) % sizeof(*buf) == 0); if (IsLittleEndian() && !aligned) { @@ -612,7 +612,6 @@ void Fill(const FunctionCallbackInfo& args) { memcpy(ts_obj_data + start, *str, MIN(str_length, fill_length)); } else { - str_length = str_obj->Length(); // Write initial String to Buffer, then use that memory to copy remainder // of string. Correct the string length for cases like HEX where less than // the total string length is written. @@ -1083,12 +1082,12 @@ void SetupBufferJS(const FunctionCallbackInfo& args) { Local proto = args[0].As(); env->set_buffer_prototype_object(proto); - env->SetMethod(proto, "asciiSlice", StringSlice); - env->SetMethod(proto, "base64Slice", StringSlice); - env->SetMethod(proto, "latin1Slice", StringSlice); - env->SetMethod(proto, "hexSlice", StringSlice); - env->SetMethod(proto, "ucs2Slice", StringSlice); - env->SetMethod(proto, "utf8Slice", StringSlice); + env->SetMethodNoSideEffect(proto, "asciiSlice", StringSlice); + env->SetMethodNoSideEffect(proto, "base64Slice", StringSlice); + env->SetMethodNoSideEffect(proto, "latin1Slice", StringSlice); + env->SetMethodNoSideEffect(proto, "hexSlice", StringSlice); + env->SetMethodNoSideEffect(proto, "ucs2Slice", StringSlice); + env->SetMethodNoSideEffect(proto, "utf8Slice", StringSlice); env->SetMethod(proto, "asciiWrite", StringWrite); env->SetMethod(proto, "base64Write", StringWrite); @@ -1116,22 +1115,22 @@ void Initialize(Local target, Environment* env = Environment::GetCurrent(context); env->SetMethod(target, "setupBufferJS", SetupBufferJS); - env->SetMethod(target, "createFromString", CreateFromString); + env->SetMethodNoSideEffect(target, "createFromString", CreateFromString); - env->SetMethod(target, "byteLengthUtf8", ByteLengthUtf8); + env->SetMethodNoSideEffect(target, "byteLengthUtf8", ByteLengthUtf8); env->SetMethod(target, "copy", Copy); - env->SetMethod(target, "compare", Compare); - env->SetMethod(target, "compareOffset", CompareOffset); + env->SetMethodNoSideEffect(target, "compare", Compare); + env->SetMethodNoSideEffect(target, "compareOffset", CompareOffset); env->SetMethod(target, "fill", Fill); - env->SetMethod(target, "indexOfBuffer", IndexOfBuffer); - env->SetMethod(target, "indexOfNumber", IndexOfNumber); - env->SetMethod(target, "indexOfString", IndexOfString); + env->SetMethodNoSideEffect(target, "indexOfBuffer", IndexOfBuffer); + env->SetMethodNoSideEffect(target, "indexOfNumber", IndexOfNumber); + env->SetMethodNoSideEffect(target, "indexOfString", IndexOfString); env->SetMethod(target, "swap16", Swap16); env->SetMethod(target, "swap32", Swap32); env->SetMethod(target, "swap64", Swap64); - env->SetMethod(target, "encodeUtf8String", EncodeUtf8String); + env->SetMethodNoSideEffect(target, "encodeUtf8String", EncodeUtf8String); target->Set(env->context(), FIXED_ONE_BYTE_STRING(env->isolate(), "kMaxLength"), diff --git a/src/node_context_data.h b/src/node_context_data.h index 522ce292d21684..3892b31354027d 100644 --- a/src/node_context_data.h +++ b/src/node_context_data.h @@ -19,10 +19,20 @@ namespace node { #define NODE_CONTEXT_ALLOW_WASM_CODE_GENERATION_INDEX 34 #endif +#ifndef NODE_CONTEXT_TAG +#define NODE_CONTEXT_TAG 35 +#endif + +#ifndef NODE_CONTEXT_TAG_BOUNDARY +#define NODE_CONTEXT_TAG_BOUNDARY 36 +#endif + enum ContextEmbedderIndex { kEnvironment = NODE_CONTEXT_EMBEDDER_DATA_INDEX, kSandboxObject = NODE_CONTEXT_SANDBOX_OBJECT_INDEX, kAllowWasmCodeGeneration = NODE_CONTEXT_ALLOW_WASM_CODE_GENERATION_INDEX, + kContextTag = NODE_CONTEXT_TAG, + kContextTagBoundary = NODE_CONTEXT_TAG_BOUNDARY, }; } // namespace node diff --git a/src/node_contextify.cc b/src/node_contextify.cc index c8155c33ee3d48..b6ef5a37b2c47d 100644 --- a/src/node_contextify.cc +++ b/src/node_contextify.cc @@ -587,6 +587,10 @@ class ContextifyScript : public BaseObject { private: Persistent script_; + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + } + public: static void Init(Environment* env, Local target) { HandleScope scope(env->isolate()); diff --git a/src/node_contextify.h b/src/node_contextify.h index 2ebfef7f27374d..3d94fbc5c47947 100644 --- a/src/node_contextify.h +++ b/src/node_contextify.h @@ -49,6 +49,7 @@ class ContextifyContext { context()->GetEmbedderData(ContextEmbedderIndex::kSandboxObject)); } + template static ContextifyContext* Get(const v8::PropertyCallbackInfo& args); diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 20975513f53911..3380961c8ebdfb 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -62,6 +62,7 @@ namespace crypto { using v8::Array; using v8::Boolean; +using v8::ConstructorBehavior; using v8::Context; using v8::DontDelete; using v8::EscapableHandleScope; @@ -83,6 +84,7 @@ using v8::Null; using v8::Object; using v8::PropertyAttribute; using v8::ReadOnly; +using v8::SideEffectType; using v8::Signature; using v8::String; using v8::Uint32; @@ -345,12 +347,12 @@ void SecureContext::Initialize(Environment* env, Local target) { #ifndef OPENSSL_NO_ENGINE env->SetProtoMethod(t, "setClientCertEngine", SetClientCertEngine); #endif // !OPENSSL_NO_ENGINE - env->SetProtoMethod(t, "getTicketKeys", GetTicketKeys); + env->SetProtoMethodNoSideEffect(t, "getTicketKeys", GetTicketKeys); env->SetProtoMethod(t, "setTicketKeys", SetTicketKeys); env->SetProtoMethod(t, "setFreeListLength", SetFreeListLength); env->SetProtoMethod(t, "enableTicketKeyCallback", EnableTicketKeyCallback); - env->SetProtoMethod(t, "getCertificate", GetCertificate); - env->SetProtoMethod(t, "getIssuer", GetCertificate); + env->SetProtoMethodNoSideEffect(t, "getCertificate", GetCertificate); + env->SetProtoMethodNoSideEffect(t, "getIssuer", GetCertificate); t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kTicketKeyReturnIndex"), Integer::NewFromUnsigned(env->isolate(), kTicketKeyReturnIndex)); @@ -1366,32 +1368,34 @@ template void SSLWrap::AddMethods(Environment* env, Local t) { HandleScope scope(env->isolate()); - env->SetProtoMethod(t, "getPeerCertificate", GetPeerCertificate); - env->SetProtoMethod(t, "getFinished", GetFinished); - env->SetProtoMethod(t, "getPeerFinished", GetPeerFinished); - env->SetProtoMethod(t, "getSession", GetSession); + env->SetProtoMethodNoSideEffect(t, "getPeerCertificate", GetPeerCertificate); + env->SetProtoMethodNoSideEffect(t, "getFinished", GetFinished); + env->SetProtoMethodNoSideEffect(t, "getPeerFinished", GetPeerFinished); + env->SetProtoMethodNoSideEffect(t, "getSession", GetSession); env->SetProtoMethod(t, "setSession", SetSession); env->SetProtoMethod(t, "loadSession", LoadSession); - env->SetProtoMethod(t, "isSessionReused", IsSessionReused); - env->SetProtoMethod(t, "isInitFinished", IsInitFinished); - env->SetProtoMethod(t, "verifyError", VerifyError); - env->SetProtoMethod(t, "getCurrentCipher", GetCurrentCipher); + env->SetProtoMethodNoSideEffect(t, "isSessionReused", IsSessionReused); + env->SetProtoMethodNoSideEffect(t, "isInitFinished", IsInitFinished); + env->SetProtoMethodNoSideEffect(t, "verifyError", VerifyError); + env->SetProtoMethodNoSideEffect(t, "getCurrentCipher", GetCurrentCipher); env->SetProtoMethod(t, "endParser", EndParser); env->SetProtoMethod(t, "certCbDone", CertCbDone); env->SetProtoMethod(t, "renegotiate", Renegotiate); env->SetProtoMethod(t, "shutdownSSL", Shutdown); - env->SetProtoMethod(t, "getTLSTicket", GetTLSTicket); + env->SetProtoMethodNoSideEffect(t, "getTLSTicket", GetTLSTicket); env->SetProtoMethod(t, "newSessionDone", NewSessionDone); env->SetProtoMethod(t, "setOCSPResponse", SetOCSPResponse); env->SetProtoMethod(t, "requestOCSP", RequestOCSP); - env->SetProtoMethod(t, "getEphemeralKeyInfo", GetEphemeralKeyInfo); - env->SetProtoMethod(t, "getProtocol", GetProtocol); + env->SetProtoMethodNoSideEffect(t, "getEphemeralKeyInfo", + GetEphemeralKeyInfo); + env->SetProtoMethodNoSideEffect(t, "getProtocol", GetProtocol); #ifdef SSL_set_max_send_fragment env->SetProtoMethod(t, "setMaxSendFragment", SetMaxSendFragment); #endif // SSL_set_max_send_fragment - env->SetProtoMethod(t, "getALPNNegotiatedProtocol", GetALPNNegotiatedProto); + env->SetProtoMethodNoSideEffect(t, "getALPNNegotiatedProtocol", + GetALPNNegotiatedProto); env->SetProtoMethod(t, "setALPNProtocols", SetALPNProtocols); } @@ -2559,7 +2563,7 @@ void CipherBase::Initialize(Environment* env, Local target) { env->SetProtoMethod(t, "update", Update); env->SetProtoMethod(t, "final", Final); env->SetProtoMethod(t, "setAutoPadding", SetAutoPadding); - env->SetProtoMethod(t, "getAuthTag", GetAuthTag); + env->SetProtoMethodNoSideEffect(t, "getAuthTag", GetAuthTag); env->SetProtoMethod(t, "setAuthTag", SetAuthTag); env->SetProtoMethod(t, "setAAD", SetAAD); @@ -2581,6 +2585,7 @@ void CipherBase::Init(const char* cipher_type, int key_buf_len, unsigned int auth_tag_len) { HandleScope scope(env()->isolate()); + MarkPopErrorOnReturn mark_pop_error_on_return; #ifdef NODE_FIPS_MODE if (FIPS_mode()) { @@ -2605,6 +2610,7 @@ void CipherBase::Init(const char* cipher_type, 1, key, iv); + CHECK_NE(key_len, 0); ctx_.reset(EVP_CIPHER_CTX_new()); @@ -2613,7 +2619,11 @@ void CipherBase::Init(const char* cipher_type, EVP_CIPHER_CTX_set_flags(ctx_.get(), EVP_CIPHER_CTX_FLAG_WRAP_ALLOW); const bool encrypt = (kind_ == kCipher); - EVP_CipherInit_ex(ctx_.get(), cipher, nullptr, nullptr, nullptr, encrypt); + if (1 != EVP_CipherInit_ex(ctx_.get(), cipher, nullptr, + nullptr, nullptr, encrypt)) { + return ThrowCryptoError(env(), ERR_get_error(), + "Failed to initialize cipher"); + } if (encrypt && (mode == EVP_CIPH_CTR_MODE || mode == EVP_CIPH_GCM_MODE || mode == EVP_CIPH_CCM_MODE)) { @@ -2632,12 +2642,15 @@ void CipherBase::Init(const char* cipher_type, CHECK_EQ(1, EVP_CIPHER_CTX_set_key_length(ctx_.get(), key_len)); - EVP_CipherInit_ex(ctx_.get(), - nullptr, - nullptr, - reinterpret_cast(key), - reinterpret_cast(iv), - encrypt); + if (1 != EVP_CipherInit_ex(ctx_.get(), + nullptr, + nullptr, + reinterpret_cast(key), + reinterpret_cast(iv), + encrypt)) { + return ThrowCryptoError(env(), ERR_get_error(), + "Failed to initialize cipher"); + } } @@ -2672,6 +2685,7 @@ void CipherBase::InitIv(const char* cipher_type, int iv_len, unsigned int auth_tag_len) { HandleScope scope(env()->isolate()); + MarkPopErrorOnReturn mark_pop_error_on_return; const EVP_CIPHER* const cipher = EVP_get_cipherbyname(cipher_type); if (cipher == nullptr) { @@ -2702,7 +2716,11 @@ void CipherBase::InitIv(const char* cipher_type, EVP_CIPHER_CTX_set_flags(ctx_.get(), EVP_CIPHER_CTX_FLAG_WRAP_ALLOW); const bool encrypt = (kind_ == kCipher); - EVP_CipherInit_ex(ctx_.get(), cipher, nullptr, nullptr, nullptr, encrypt); + if (1 != EVP_CipherInit_ex(ctx_.get(), cipher, nullptr, + nullptr, nullptr, encrypt)) { + return ThrowCryptoError(env(), ERR_get_error(), + "Failed to initialize cipher"); + } if (IsAuthenticatedMode()) { CHECK(has_iv); @@ -2715,12 +2733,15 @@ void CipherBase::InitIv(const char* cipher_type, return env()->ThrowError("Invalid key length"); } - EVP_CipherInit_ex(ctx_.get(), - nullptr, - nullptr, - reinterpret_cast(key), - reinterpret_cast(iv), - encrypt); + if (1 != EVP_CipherInit_ex(ctx_.get(), + nullptr, + nullptr, + reinterpret_cast(key), + reinterpret_cast(iv), + encrypt)) { + return ThrowCryptoError(env(), ERR_get_error(), + "Failed to initialize cipher"); + } } @@ -2765,6 +2786,7 @@ static bool IsValidGCMTagLength(unsigned int tag_len) { bool CipherBase::InitAuthenticated(const char* cipher_type, int iv_len, unsigned int auth_tag_len) { CHECK(IsAuthenticatedMode()); + MarkPopErrorOnReturn mark_pop_error_on_return; if (!EVP_CIPHER_CTX_ctrl(ctx_.get(), EVP_CTRL_AEAD_SET_IVLEN, @@ -2910,6 +2932,7 @@ void CipherBase::SetAuthTag(const FunctionCallbackInfo& args) { bool CipherBase::SetAAD(const char* data, unsigned int len, int plaintext_len) { if (!ctx_ || !IsAuthenticatedMode()) return false; + MarkPopErrorOnReturn mark_pop_error_on_return; int outlen; const int mode = EVP_CIPHER_CTX_mode(ctx_.get()); @@ -2969,6 +2992,7 @@ CipherBase::UpdateResult CipherBase::Update(const char* data, int* out_len) { if (!ctx_) return kErrorState; + MarkPopErrorOnReturn mark_pop_error_on_return; const int mode = EVP_CIPHER_CTX_mode(ctx_.get()); @@ -2980,10 +3004,10 @@ CipherBase::UpdateResult CipherBase::Update(const char* data, // on first update: if (kind_ == kDecipher && IsAuthenticatedMode() && auth_tag_len_ > 0 && auth_tag_len_ != kNoAuthTagLength && !auth_tag_set_) { - EVP_CIPHER_CTX_ctrl(ctx_.get(), - EVP_CTRL_GCM_SET_TAG, - auth_tag_len_, - reinterpret_cast(auth_tag_)); + CHECK(EVP_CIPHER_CTX_ctrl(ctx_.get(), + EVP_CTRL_GCM_SET_TAG, + auth_tag_len_, + reinterpret_cast(auth_tag_))); auth_tag_set_ = true; } @@ -3061,6 +3085,7 @@ void CipherBase::Update(const FunctionCallbackInfo& args) { bool CipherBase::SetAutoPadding(bool auto_padding) { if (!ctx_) return false; + MarkPopErrorOnReturn mark_pop_error_on_return; return EVP_CIPHER_CTX_set_padding(ctx_.get(), auto_padding); } @@ -3895,10 +3920,10 @@ void DiffieHellman::Initialize(Environment* env, Local target) { env->SetProtoMethod(t, "generateKeys", GenerateKeys); env->SetProtoMethod(t, "computeSecret", ComputeSecret); - env->SetProtoMethod(t, "getPrime", GetPrime); - env->SetProtoMethod(t, "getGenerator", GetGenerator); - env->SetProtoMethod(t, "getPublicKey", GetPublicKey); - env->SetProtoMethod(t, "getPrivateKey", GetPrivateKey); + env->SetProtoMethodNoSideEffect(t, "getPrime", GetPrime); + env->SetProtoMethodNoSideEffect(t, "getGenerator", GetGenerator); + env->SetProtoMethodNoSideEffect(t, "getPublicKey", GetPublicKey); + env->SetProtoMethodNoSideEffect(t, "getPrivateKey", GetPrivateKey); env->SetProtoMethod(t, "setPublicKey", SetPublicKey); env->SetProtoMethod(t, "setPrivateKey", SetPrivateKey); @@ -3906,7 +3931,11 @@ void DiffieHellman::Initialize(Environment* env, Local target) { FunctionTemplate::New(env->isolate(), DiffieHellman::VerifyErrorGetter, env->as_external(), - Signature::New(env->isolate(), t)); + Signature::New(env->isolate(), t), + /* length */ 0, + // TODO(TimothyGu): should be deny + ConstructorBehavior::kAllow, + SideEffectType::kHasNoSideEffect); t->InstanceTemplate()->SetAccessorProperty( env->verify_error_string(), @@ -3922,16 +3951,20 @@ void DiffieHellman::Initialize(Environment* env, Local target) { env->SetProtoMethod(t2, "generateKeys", GenerateKeys); env->SetProtoMethod(t2, "computeSecret", ComputeSecret); - env->SetProtoMethod(t2, "getPrime", GetPrime); - env->SetProtoMethod(t2, "getGenerator", GetGenerator); - env->SetProtoMethod(t2, "getPublicKey", GetPublicKey); - env->SetProtoMethod(t2, "getPrivateKey", GetPrivateKey); + env->SetProtoMethodNoSideEffect(t2, "getPrime", GetPrime); + env->SetProtoMethodNoSideEffect(t2, "getGenerator", GetGenerator); + env->SetProtoMethodNoSideEffect(t2, "getPublicKey", GetPublicKey); + env->SetProtoMethodNoSideEffect(t2, "getPrivateKey", GetPrivateKey); Local verify_error_getter_templ2 = FunctionTemplate::New(env->isolate(), DiffieHellman::VerifyErrorGetter, env->as_external(), - Signature::New(env->isolate(), t2)); + Signature::New(env->isolate(), t2), + /* length */ 0, + // TODO(TimothyGu): should be deny + ConstructorBehavior::kAllow, + SideEffectType::kHasNoSideEffect); t2->InstanceTemplate()->SetAccessorProperty( env->verify_error_string(), @@ -4283,8 +4316,8 @@ void ECDH::Initialize(Environment* env, Local target) { env->SetProtoMethod(t, "generateKeys", GenerateKeys); env->SetProtoMethod(t, "computeSecret", ComputeSecret); - env->SetProtoMethod(t, "getPublicKey", GetPublicKey); - env->SetProtoMethod(t, "getPrivateKey", GetPrivateKey); + env->SetProtoMethodNoSideEffect(t, "getPublicKey", GetPublicKey); + env->SetProtoMethodNoSideEffect(t, "getPrivateKey", GetPrivateKey); env->SetProtoMethod(t, "setPublicKey", SetPublicKey); env->SetProtoMethod(t, "setPrivateKey", SetPrivateKey); @@ -4546,6 +4579,8 @@ bool ECDH::IsKeyPairValid() { } +// TODO(addaleax): If there is an `AsyncWrap`, it currently has no access to +// this object. This makes proper reporting of memory usage impossible. struct CryptoJob : public ThreadPoolWork { Environment* const env; std::unique_ptr async_wrap; @@ -5175,27 +5210,27 @@ void Initialize(Local target, Sign::Initialize(env, target); Verify::Initialize(env, target); - env->SetMethod(target, "certVerifySpkac", VerifySpkac); - env->SetMethod(target, "certExportPublicKey", ExportPublicKey); - env->SetMethod(target, "certExportChallenge", ExportChallenge); + env->SetMethodNoSideEffect(target, "certVerifySpkac", VerifySpkac); + env->SetMethodNoSideEffect(target, "certExportPublicKey", ExportPublicKey); + env->SetMethodNoSideEffect(target, "certExportChallenge", ExportChallenge); - env->SetMethod(target, "ECDHConvertKey", ConvertKey); + env->SetMethodNoSideEffect(target, "ECDHConvertKey", ConvertKey); #ifndef OPENSSL_NO_ENGINE env->SetMethod(target, "setEngine", SetEngine); #endif // !OPENSSL_NO_ENGINE #ifdef NODE_FIPS_MODE - env->SetMethod(target, "getFipsCrypto", GetFipsCrypto); + env->SetMethodNoSideEffect(target, "getFipsCrypto", GetFipsCrypto); env->SetMethod(target, "setFipsCrypto", SetFipsCrypto); #endif env->SetMethod(target, "pbkdf2", PBKDF2); env->SetMethod(target, "randomBytes", RandomBytes); - env->SetMethod(target, "timingSafeEqual", TimingSafeEqual); - env->SetMethod(target, "getSSLCiphers", GetSSLCiphers); - env->SetMethod(target, "getCiphers", GetCiphers); - env->SetMethod(target, "getHashes", GetHashes); - env->SetMethod(target, "getCurves", GetCurves); + env->SetMethodNoSideEffect(target, "timingSafeEqual", TimingSafeEqual); + env->SetMethodNoSideEffect(target, "getSSLCiphers", GetSSLCiphers); + env->SetMethodNoSideEffect(target, "getCiphers", GetCiphers); + env->SetMethodNoSideEffect(target, "getHashes", GetHashes); + env->SetMethodNoSideEffect(target, "getCurves", GetCurves); env->SetMethod(target, "publicEncrypt", PublicKeyCipher::Cipher target); + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + } + SSLCtxPointer ctx_; X509Pointer cert_; X509Pointer issuer_; @@ -337,6 +341,10 @@ class CipherBase : public BaseObject { public: static void Initialize(Environment* env, v8::Local target); + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + } + protected: enum CipherKind { kCipher, @@ -407,6 +415,10 @@ class Hmac : public BaseObject { public: static void Initialize(Environment* env, v8::Local target); + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + } + protected: void HmacInit(const char* hash_type, const char* key, int key_len); bool HmacUpdate(const char* data, int len); @@ -430,6 +442,10 @@ class Hash : public BaseObject { public: static void Initialize(Environment* env, v8::Local target); + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + } + bool HashInit(const char* hash_type); bool HashUpdate(const char* data, int len); @@ -469,6 +485,10 @@ class SignBase : public BaseObject { Error Init(const char* sign_type); Error Update(const char* data, int len); + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + } + protected: void CheckThrow(Error error); @@ -581,6 +601,10 @@ class DiffieHellman : public BaseObject { MakeWeak(); } + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + } + private: static void GetField(const v8::FunctionCallbackInfo& args, const BIGNUM* (*get_field)(const DH*), @@ -606,6 +630,10 @@ class ECDH : public BaseObject { char* data, size_t len); + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + } + protected: ECDH(Environment* env, v8::Local wrap, ECKeyPointer&& key) : BaseObject(env, wrap), diff --git a/src/node_crypto_bio.h b/src/node_crypto_bio.h index 380a3a6b4c64f5..dea010fa0158b4 100644 --- a/src/node_crypto_bio.h +++ b/src/node_crypto_bio.h @@ -32,7 +32,7 @@ namespace node { namespace crypto { -class NodeBIO { +class NodeBIO : public MemoryRetainer { public: NodeBIO() : env_(nullptr), initial_(kInitialBufferLength), @@ -110,6 +110,11 @@ class NodeBIO { static NodeBIO* FromBIO(BIO* bio); + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + tracker->TrackFieldWithSize("buffer", length_); + } + private: static int New(BIO* bio); static int Free(BIO* bio); diff --git a/src/node_encoding.cc b/src/node_encoding.cc index 467f04e24568cb..d5c6bcab488dd2 100644 --- a/src/node_encoding.cc +++ b/src/node_encoding.cc @@ -1,8 +1,6 @@ #include "node.h" -#include "env.h" #include "env-inl.h" #include "string_bytes.h" -#include "util.h" #include "util-inl.h" #include "v8.h" diff --git a/src/node_file.h b/src/node_file.h index a14a1b0f85e108..6b45dc881750a7 100644 --- a/src/node_file.h +++ b/src/node_file.h @@ -66,7 +66,6 @@ class FSReqBase : public ReqWrap { const char* data() const { return has_data_ ? *buffer_ : nullptr; } enum encoding encoding() const { return encoding_; } - size_t self_size() const override { return sizeof(*this); } bool use_bigint() const { return use_bigint_; } private: @@ -92,6 +91,10 @@ class FSReqWrap : public FSReqBase { void ResolveStat(const uv_stat_t* stat) override; void SetReturnValue(const FunctionCallbackInfo& args) override; + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + } + private: DISALLOW_COPY_AND_ASSIGN(FSReqWrap); }; @@ -150,6 +153,11 @@ class FSReqPromise : public FSReqBase { args.GetReturnValue().Set(resolver->GetPromise()); } + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + tracker->TrackField("stats_field_array", stats_field_array_); + } + private: bool finished_ = false; AliasedBuffer stats_field_array_; @@ -184,7 +192,10 @@ class FileHandleReadWrap : public ReqWrap { return static_cast(ReqWrap::from_req(req)); } - size_t self_size() const override { return sizeof(*this); } + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + tracker->TrackField("buffer", buffer_); + } private: FileHandle* file_handle_; @@ -205,7 +216,6 @@ class FileHandle : public AsyncWrap, public StreamBase { static void New(const v8::FunctionCallbackInfo& args); int fd() const { return fd_; } - size_t self_size() const override { return sizeof(*this); } // Will asynchronously close the FD and return a Promise that will // be resolved once closing is complete. @@ -233,6 +243,11 @@ class FileHandle : public AsyncWrap, public StreamBase { return UV_ENOSYS; // Not implemented (yet). } + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + tracker->TrackField("current_read", current_read_); + } + private: // Synchronous close that emits a warning void Close(); @@ -259,7 +274,11 @@ class FileHandle : public AsyncWrap, public StreamBase { FileHandle* file_handle(); - size_t self_size() const override { return sizeof(*this); } + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + tracker->TrackField("promise", promise_); + tracker->TrackField("ref", ref_); + } void Resolve(); diff --git a/src/node_http2.cc b/src/node_http2.cc index e6aeb80a91add9..d1319c9d82fd97 100644 --- a/src/node_http2.cc +++ b/src/node_http2.cc @@ -737,7 +737,7 @@ inline void Http2Session::AddStream(Http2Stream* stream) { size_t size = streams_.size(); if (size > statistics_.max_concurrent_streams) statistics_.max_concurrent_streams = size; - IncrementCurrentSessionMemory(stream->self_size()); + IncrementCurrentSessionMemory(sizeof(*stream)); } @@ -745,7 +745,7 @@ inline void Http2Session::RemoveStream(Http2Stream* stream) { if (streams_.empty() || stream == nullptr) return; // Nothing to remove, item was never added? streams_.erase(stream->id()); - DecrementCurrentSessionMemory(stream->self_size()); + DecrementCurrentSessionMemory(sizeof(*stream)); } // Used as one of the Padding Strategy functions. Will attempt to ensure @@ -2060,10 +2060,9 @@ int Http2Stream::DoWrite(WriteWrap* req_wrap, uv_buf_t* bufs, size_t nbufs, uv_stream_t* send_handle) { - CHECK(!this->IsDestroyed()); CHECK_NULL(send_handle); Http2Scope h2scope(this); - if (!IsWritable()) { + if (!IsWritable() || IsDestroyed()) { req_wrap->Done(UV_EOF); return 0; } @@ -2694,7 +2693,7 @@ Http2Session::Http2Ping* Http2Session::PopPing() { if (!outstanding_pings_.empty()) { ping = outstanding_pings_.front(); outstanding_pings_.pop(); - DecrementCurrentSessionMemory(ping->self_size()); + DecrementCurrentSessionMemory(sizeof(*ping)); } return ping; } @@ -2703,7 +2702,7 @@ bool Http2Session::AddPing(Http2Session::Http2Ping* ping) { if (outstanding_pings_.size() == max_outstanding_pings_) return false; outstanding_pings_.push(ping); - IncrementCurrentSessionMemory(ping->self_size()); + IncrementCurrentSessionMemory(sizeof(*ping)); return true; } @@ -2712,7 +2711,7 @@ Http2Session::Http2Settings* Http2Session::PopSettings() { if (!outstanding_settings_.empty()) { settings = outstanding_settings_.front(); outstanding_settings_.pop(); - DecrementCurrentSessionMemory(settings->self_size()); + DecrementCurrentSessionMemory(sizeof(*settings)); } return settings; } @@ -2721,7 +2720,7 @@ bool Http2Session::AddSettings(Http2Session::Http2Settings* settings) { if (outstanding_settings_.size() == max_outstanding_settings_) return false; outstanding_settings_.push(settings); - IncrementCurrentSessionMemory(settings->self_size()); + IncrementCurrentSessionMemory(sizeof(*settings)); return true; } @@ -2766,6 +2765,21 @@ void Http2Session::Http2Ping::Done(bool ack, const uint8_t* payload) { } +void nghttp2_stream_write::MemoryInfo(MemoryTracker* tracker) const { + tracker->TrackThis(this); + if (req_wrap != nullptr) + tracker->TrackField("req_wrap", req_wrap->GetAsyncWrap()); + tracker->TrackField("buf", buf); +} + + +void nghttp2_header::MemoryInfo(MemoryTracker* tracker) const { + tracker->TrackThis(this); + tracker->TrackFieldWithSize("name", nghttp2_rcbuf_get_buf(name).len); + tracker->TrackFieldWithSize("value", nghttp2_rcbuf_get_buf(value).len); +} + + // Set up the process.binding('http2') binding. void Initialize(Local target, Local unused, diff --git a/src/node_http2.h b/src/node_http2.h index d90c3aed66b0a0..cbed2a267d171e 100644 --- a/src/node_http2.h +++ b/src/node_http2.h @@ -83,19 +83,23 @@ enum nghttp2_stream_options { STREAM_OPTION_GET_TRAILERS = 0x2, }; -struct nghttp2_stream_write { +struct nghttp2_stream_write : public MemoryRetainer { WriteWrap* req_wrap = nullptr; uv_buf_t buf; inline explicit nghttp2_stream_write(uv_buf_t buf_) : buf(buf_) {} inline nghttp2_stream_write(WriteWrap* req, uv_buf_t buf_) : req_wrap(req), buf(buf_) {} + + void MemoryInfo(MemoryTracker* tracker) const override; }; -struct nghttp2_header { +struct nghttp2_header : public MemoryRetainer { nghttp2_rcbuf* name = nullptr; nghttp2_rcbuf* value = nullptr; uint8_t flags = 0; + + void MemoryInfo(MemoryTracker* tracker) const override; }; @@ -617,7 +621,12 @@ class Http2Stream : public AsyncWrap, int DoWrite(WriteWrap* w, uv_buf_t* bufs, size_t count, uv_stream_t* send_handle) override; - size_t self_size() const override { return sizeof(*this); } + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + tracker->TrackField("current_headers", current_headers_); + tracker->TrackField("queue", queue_); + } + std::string diagnostic_name() const override; // JavaScript API @@ -793,7 +802,17 @@ class Http2Session : public AsyncWrap, public StreamListener { // Write data to the session ssize_t Write(const uv_buf_t* bufs, size_t nbufs); - size_t self_size() const override { return sizeof(*this); } + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + tracker->TrackField("streams", streams_); + tracker->TrackField("outstanding_pings", outstanding_pings_); + tracker->TrackField("outstanding_settings", outstanding_settings_); + tracker->TrackField("outgoing_buffers", outgoing_buffers_); + tracker->TrackFieldWithSize("outgoing_storage", outgoing_storage_.size()); + tracker->TrackFieldWithSize("pending_rst_streams", + pending_rst_streams_.size() * sizeof(int32_t)); + } + std::string diagnostic_name() const override; // Schedule an RstStream for after the current write finishes. @@ -1109,7 +1128,10 @@ class Http2Session::Http2Ping : public AsyncWrap { public: explicit Http2Ping(Http2Session* session); - size_t self_size() const override { return sizeof(*this); } + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + tracker->TrackField("session", session_); + } void Send(uint8_t* payload); void Done(bool ack, const uint8_t* payload = nullptr); @@ -1129,7 +1151,10 @@ class Http2Session::Http2Settings : public AsyncWrap { explicit Http2Settings(Environment* env); explicit Http2Settings(Http2Session* session); - size_t self_size() const override { return sizeof(*this); } + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + tracker->TrackField("session", session_); + } void Send(); void Done(bool ack); diff --git a/src/node_http_parser.cc b/src/node_http_parser.cc index 7d96466c3933fc..e8fc1f22e4938a 100644 --- a/src/node_http_parser.cc +++ b/src/node_http_parser.cc @@ -155,8 +155,9 @@ class Parser : public AsyncWrap, public StreamListener { } - size_t self_size() const override { - return sizeof(*this); + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + tracker->TrackField("current_buffer", current_buffer_); } diff --git a/src/node_i18n.cc b/src/node_i18n.cc index 4ace513810f1b8..288ad77f619188 100644 --- a/src/node_i18n.cc +++ b/src/node_i18n.cc @@ -250,6 +250,10 @@ class ConverterObject : public BaseObject, Converter { } } + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + } + protected: ConverterObject(Environment* env, v8::Local wrap, diff --git a/src/node_internals.h b/src/node_internals.h index 042b685c5939ae..d29400d4594569 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -111,6 +111,7 @@ struct sockaddr; V(domain) \ V(fs) \ V(fs_event_wrap) \ + V(heap_utils) \ V(http2) \ V(http_parser) \ V(inspector) \ @@ -931,12 +932,22 @@ static inline const char* errno_string(int errorno) { // Functions defined in node.cc that are exposed via the bootstrapper object +extern double prog_start_time; +void PrintErrorString(const char* format, ...); + +void Abort(const v8::FunctionCallbackInfo& args); void Chdir(const v8::FunctionCallbackInfo& args); void CPUUsage(const v8::FunctionCallbackInfo& args); +void Cwd(const v8::FunctionCallbackInfo& args); void Hrtime(const v8::FunctionCallbackInfo& args); +void HrtimeBigInt(const v8::FunctionCallbackInfo& args); +void Kill(const v8::FunctionCallbackInfo& args); void MemoryUsage(const v8::FunctionCallbackInfo& args); void RawDebug(const v8::FunctionCallbackInfo& args); +void StartProfilerIdleNotifier(const v8::FunctionCallbackInfo& args); +void StopProfilerIdleNotifier(const v8::FunctionCallbackInfo& args); void Umask(const v8::FunctionCallbackInfo& args); +void Uptime(const v8::FunctionCallbackInfo& args); #if defined(__POSIX__) && !defined(__ANDROID__) && !defined(__CloudABI__) void SetGid(const v8::FunctionCallbackInfo& args); @@ -945,6 +956,11 @@ void SetUid(const v8::FunctionCallbackInfo& args); void SetEUid(const v8::FunctionCallbackInfo& args); void SetGroups(const v8::FunctionCallbackInfo& args); void InitGroups(const v8::FunctionCallbackInfo& args); +void GetUid(const v8::FunctionCallbackInfo& args); +void GetGid(const v8::FunctionCallbackInfo& args); +void GetEUid(const v8::FunctionCallbackInfo& args); +void GetEGid(const v8::FunctionCallbackInfo& args); +void GetGroups(const v8::FunctionCallbackInfo& args); #endif // __POSIX__ && !defined(__ANDROID__) && !defined(__CloudABI__) } // namespace node diff --git a/src/node_messaging.cc b/src/node_messaging.cc index be37cd39c366d0..20e0c7673b8fa9 100644 --- a/src/node_messaging.cc +++ b/src/node_messaging.cc @@ -144,6 +144,21 @@ void Message::AddMessagePort(std::unique_ptr&& data) { namespace { +void ThrowDataCloneException(Environment* env, Local message) { + Local argv[] = { + message, + FIXED_ONE_BYTE_STRING(env->isolate(), "DataCloneError") + }; + Local exception; + Local domexception_ctor = env->domexception_function(); + CHECK(!domexception_ctor.IsEmpty()); + if (!domexception_ctor->NewInstance(env->context(), arraysize(argv), argv) + .ToLocal(&exception)) { + return; + } + env->isolate()->ThrowException(exception); +} + // This tells V8 how to serialize objects that it does not understand // (e.g. C++ objects) into the output buffer, in a way that our own // DeserializerDelegate understands how to unpack. @@ -153,7 +168,7 @@ class SerializerDelegate : public ValueSerializer::Delegate { : env_(env), context_(context), msg_(m) {} void ThrowDataCloneError(Local message) override { - env_->isolate()->ThrowException(Exception::Error(message)); + ThrowDataCloneException(env_, message); } Maybe WriteHostObject(Isolate* isolate, Local object) override { @@ -224,7 +239,8 @@ class SerializerDelegate : public ValueSerializer::Delegate { Maybe Message::Serialize(Environment* env, Local context, Local input, - Local transfer_list_v) { + Local transfer_list_v, + Local source_port) { HandleScope handle_scope(env->isolate()); Context::Scope context_scope(context); @@ -258,8 +274,23 @@ Maybe Message::Serialize(Environment* env, continue; } else if (env->message_port_constructor_template() ->HasInstance(entry)) { + // Check if the source MessagePort is being transferred. + if (!source_port.IsEmpty() && entry == source_port) { + ThrowDataCloneException( + env, + FIXED_ONE_BYTE_STRING(env->isolate(), + "Transfer list contains source port")); + return Nothing(); + } MessagePort* port = Unwrap(entry.As()); - CHECK_NE(port, nullptr); + if (port == nullptr || port->IsDetached()) { + ThrowDataCloneException( + env, + FIXED_ONE_BYTE_STRING( + env->isolate(), + "MessagePort in transfer list is already detached")); + return Nothing(); + } delegate.ports_.push_back(port); continue; } @@ -294,6 +325,14 @@ Maybe Message::Serialize(Environment* env, return Just(true); } +void Message::MemoryInfo(MemoryTracker* tracker) const { + tracker->TrackThis(this); + tracker->TrackField("array_buffer_contents", array_buffer_contents_); + tracker->TrackFieldWithSize("shared_array_buffers", + shared_array_buffers_.size() * sizeof(shared_array_buffers_[0])); + tracker->TrackField("message_ports", message_ports_); +} + MessagePortData::MessagePortData(MessagePort* owner) : owner_(owner) { } MessagePortData::~MessagePortData() { @@ -301,6 +340,12 @@ MessagePortData::~MessagePortData() { Disentangle(); } +void MessagePortData::MemoryInfo(MemoryTracker* tracker) const { + Mutex::ScopedLock lock(mutex_); + tracker->TrackThis(this); + tracker->TrackField("incoming_messages", incoming_messages_); +} + void MessagePortData::AddToIncomingQueue(Message&& message) { // This function will be called by other threads. Mutex::ScopedLock lock(mutex_); @@ -395,6 +440,10 @@ uv_async_t* MessagePort::async() { return reinterpret_cast(GetHandle()); } +bool MessagePort::IsDetached() const { + return data_ == nullptr || IsHandleClosing(); +} + void MessagePort::TriggerAsync() { if (IsHandleClosing()) return; CHECK_EQ(uv_async_send(async()), 0); @@ -537,36 +586,69 @@ std::unique_ptr MessagePort::Detach() { } -void MessagePort::Send(Message&& message) { - Mutex::ScopedLock lock(*data_->sibling_mutex_); - if (data_->sibling_ == nullptr) - return; - data_->sibling_->AddToIncomingQueue(std::move(message)); -} +Maybe MessagePort::PostMessage(Environment* env, + Local message_v, + Local transfer_v) { + Isolate* isolate = env->isolate(); + Local obj = object(isolate); + Local context = obj->CreationContext(); -void MessagePort::Send(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - Local context = object(env->isolate())->CreationContext(); Message msg; - if (msg.Serialize(env, context, args[0], args[1]) - .IsNothing()) { - return; + + // Per spec, we need to both check if transfer list has the source port, and + // serialize the input message, even if the MessagePort is closed or detached. + + Maybe serialization_maybe = + msg.Serialize(env, context, message_v, transfer_v, obj); + if (data_ == nullptr) { + return serialization_maybe; + } + if (serialization_maybe.IsNothing()) { + return Nothing(); } - Send(std::move(msg)); + + Mutex::ScopedLock lock(*data_->sibling_mutex_); + bool doomed = false; + + // Check if the target port is posted to itself. + if (data_->sibling_ != nullptr) { + for (const auto& port_data : msg.message_ports()) { + if (data_->sibling_ == port_data.get()) { + doomed = true; + ProcessEmitWarning(env, "The target port was posted to itself, and " + "the communication channel was lost"); + break; + } + } + } + + if (data_->sibling_ == nullptr || doomed) + return Just(true); + + data_->sibling_->AddToIncomingQueue(std::move(msg)); + return Just(true); } void MessagePort::PostMessage(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - MessagePort* port; - ASSIGN_OR_RETURN_UNWRAP(&port, args.This()); - if (!port->data_) { - return THROW_ERR_CLOSED_MESSAGE_PORT(env); - } if (args.Length() == 0) { return THROW_ERR_MISSING_ARGS(env, "Not enough arguments to " "MessagePort.postMessage"); } - port->Send(args); + + MessagePort* port = Unwrap(args.This()); + // Even if the backing MessagePort object has already been deleted, we still + // want to serialize the message to ensure spec-compliant behavior w.r.t. + // transfers. + if (port == nullptr) { + Message msg; + Local obj = args.This(); + Local context = obj->CreationContext(); + USE(msg.Serialize(env, context, args[0], args[1], obj)); + return; + } + + port->PostMessage(env, args[0], args[1]); } void MessagePort::Start() { @@ -620,14 +702,6 @@ void MessagePort::Drain(const FunctionCallbackInfo& args) { port->OnMessage(); } -size_t MessagePort::self_size() const { - Mutex::ScopedLock lock(data_->mutex_); - size_t sz = sizeof(*this) + sizeof(*data_); - for (const Message& msg : data_->incoming_messages_) - sz += sizeof(msg) + msg.main_message_buf_.size; - return sz; -} - void MessagePort::Entangle(MessagePort* a, MessagePort* b) { Entangle(a, b->data_.get()); } @@ -650,15 +724,12 @@ MaybeLocal GetMessagePortConstructor( m->InstanceTemplate()->SetInternalFieldCount(1); AsyncWrap::AddWrapMethods(env, m); + HandleWrap::AddWrapMethods(env, m); env->SetProtoMethod(m, "postMessage", MessagePort::PostMessage); env->SetProtoMethod(m, "start", MessagePort::Start); env->SetProtoMethod(m, "stop", MessagePort::Stop); env->SetProtoMethod(m, "drain", MessagePort::Drain); - env->SetProtoMethod(m, "close", HandleWrap::Close); - env->SetProtoMethod(m, "unref", HandleWrap::Unref); - env->SetProtoMethod(m, "ref", HandleWrap::Ref); - env->SetProtoMethod(m, "hasRef", HandleWrap::HasRef); env->set_message_port_constructor_template(m); } @@ -688,6 +759,13 @@ static void MessageChannel(const FunctionCallbackInfo& args) { .FromJust(); } +static void RegisterDOMException(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + CHECK_EQ(args.Length(), 1); + CHECK(args[0]->IsFunction()); + env->set_domexception_function(args[0].As()); +} + static void InitMessaging(Local target, Local unused, Local context, @@ -708,6 +786,8 @@ static void InitMessaging(Local target, env->message_port_constructor_string(), GetMessagePortConstructor(env, context).ToLocalChecked()) .FromJust(); + + env->SetMethod(target, "registerDOMException", RegisterDOMException); } } // anonymous namespace diff --git a/src/node_messaging.h b/src/node_messaging.h index 28122c526cccea..da10300aedbd42 100644 --- a/src/node_messaging.h +++ b/src/node_messaging.h @@ -15,7 +15,7 @@ class MessagePortData; class MessagePort; // Represents a single communication message. -class Message { +class Message : public MemoryRetainer { public: explicit Message(MallocedBuffer&& payload = MallocedBuffer()); @@ -32,10 +32,14 @@ class Message { // Serialize a JS value, and optionally transfer objects, into this message. // The Message object retains ownership of all transferred objects until // deserialization. + // The source_port parameter, if provided, will make Serialize() throw a + // "DataCloneError" DOMException if source_port is found in transfer_list. v8::Maybe Serialize(Environment* env, v8::Local context, v8::Local input, - v8::Local transfer_list); + v8::Local transfer_list, + v8::Local source_port = + v8::Local()); // Internal method of Message that is called when a new SharedArrayBuffer // object is encountered in the incoming value's structure. @@ -44,6 +48,15 @@ class Message { // and that transfers ownership of `data` to this message. void AddMessagePort(std::unique_ptr&& data); + // The MessagePorts that will be transferred, as recorded by Serialize(). + // Used for warning user about posting the target MessagePort to itself, + // which will as a side effect destroy the communication channel. + const std::vector>& message_ports() const { + return message_ports_; + } + + void MemoryInfo(MemoryTracker* tracker) const override; + private: MallocedBuffer main_message_buf_; std::vector> array_buffer_contents_; @@ -55,7 +68,7 @@ class Message { // This contains all data for a `MessagePort` instance that is not tied to // a specific Environment/Isolate/event loop, for easier transfer between those. -class MessagePortData { +class MessagePortData : public MemoryRetainer { public: explicit MessagePortData(MessagePort* owner); ~MessagePortData(); @@ -83,6 +96,8 @@ class MessagePortData { // which can happen on either side of a worker. void Disentangle(); + void MemoryInfo(MemoryTracker* tracker) const override; + private: // After disentangling this message port, the owner handle (if any) // is asynchronously triggered, so that it can close down naturally. @@ -122,10 +137,11 @@ class MessagePort : public HandleWrap { std::unique_ptr data = nullptr); // Send a message, i.e. deliver it into the sibling's incoming queue. - // If there is no sibling, i.e. this port is closed, - // this message is silently discarded. - void Send(Message&& message); - void Send(const v8::FunctionCallbackInfo& args); + // If this port is closed, or if there is no sibling, this message is + // serialized with transfers, then silently discarded. + v8::Maybe PostMessage(Environment* env, + v8::Local message, + v8::Local transfer); // Deliver a single message into this port's incoming queue. void AddToIncomingQueue(Message&& message); @@ -157,7 +173,19 @@ class MessagePort : public HandleWrap { void Close( v8::Local close_callback = v8::Local()) override; - size_t self_size() const override; + // Returns true if either data_ has been freed, or if the handle is being + // closed. Equivalent to the [[Detached]] internal slot in the HTML Standard. + // + // If checking if a JavaScript MessagePort object is detached, this method + // alone is often not enough, since the backing C++ MessagePort object may + // have been deleted already. For all intents and purposes, an object with a + // NULL pointer to the C++ MessagePort object is also detached. + inline bool IsDetached() const; + + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + tracker->TrackField("data", data_); + } private: void OnClose() override; diff --git a/src/node_platform.cc b/src/node_platform.cc index fdca115e5f8f03..947622a219cdef 100644 --- a/src/node_platform.cc +++ b/src/node_platform.cc @@ -1,7 +1,6 @@ #include "node_platform.h" #include "node_internals.h" -#include "env.h" #include "env-inl.h" #include "util.h" #include diff --git a/src/node_process.cc b/src/node_process.cc new file mode 100644 index 00000000000000..43982e6f39f769 --- /dev/null +++ b/src/node_process.cc @@ -0,0 +1,551 @@ +#include "node.h" +#include "node_internals.h" +#include "env-inl.h" +#include "util-inl.h" +#include "uv.h" +#include "v8.h" + +#include // PATH_MAX +#include + +#if defined(_MSC_VER) +#include +#include +#define umask _umask +typedef int mode_t; +#else +#include +#include // getrlimit, setrlimit +#include // tcgetattr, tcsetattr +#include // setuid, getuid +#endif + +#if defined(__POSIX__) && !defined(__ANDROID__) && !defined(__CloudABI__) +#include // getpwnam() +#include // getgrnam() +#endif + +namespace node { + +using v8::Array; +using v8::ArrayBuffer; +using v8::BigUint64Array; +using v8::Float64Array; +using v8::FunctionCallbackInfo; +using v8::HeapStatistics; +using v8::Integer; +using v8::Isolate; +using v8::Local; +using v8::Number; +using v8::String; +using v8::Uint32; +using v8::Uint32Array; +using v8::Value; + +// Microseconds in a second, as a float, used in CPUUsage() below +#define MICROS_PER_SEC 1e6 +// used in Hrtime() below +#define NANOS_PER_SEC 1000000000 + +void Abort(const FunctionCallbackInfo& args) { + Abort(); +} + +void Chdir(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + CHECK(env->is_main_thread()); + + if (args.Length() != 1 || !args[0]->IsString()) + return env->ThrowTypeError("Bad argument."); + + Utf8Value path(args.GetIsolate(), args[0]); + int err = uv_chdir(*path); + if (err) + return env->ThrowUVException(err, "chdir", nullptr, *path, nullptr); +} + +// CPUUsage use libuv's uv_getrusage() this-process resource usage accessor, +// to access ru_utime (user CPU time used) and ru_stime (system CPU time used), +// which are uv_timeval_t structs (long tv_sec, long tv_usec). +// Returns those values as Float64 microseconds in the elements of the array +// passed to the function. +void CPUUsage(const FunctionCallbackInfo& args) { + uv_rusage_t rusage; + + // Call libuv to get the values we'll return. + int err = uv_getrusage(&rusage); + if (err) { + // On error, return the strerror version of the error code. + Local errmsg = OneByteString(args.GetIsolate(), uv_strerror(err)); + return args.GetReturnValue().Set(errmsg); + } + + // Get the double array pointer from the Float64Array argument. + CHECK(args[0]->IsFloat64Array()); + Local array = args[0].As(); + CHECK_EQ(array->Length(), 2); + Local ab = array->Buffer(); + double* fields = static_cast(ab->GetContents().Data()); + + // Set the Float64Array elements to be user / system values in microseconds. + fields[0] = MICROS_PER_SEC * rusage.ru_utime.tv_sec + rusage.ru_utime.tv_usec; + fields[1] = MICROS_PER_SEC * rusage.ru_stime.tv_sec + rusage.ru_stime.tv_usec; +} + +void Cwd(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); +#ifdef _WIN32 + /* MAX_PATH is in characters, not bytes. Make sure we have enough headroom. */ + char buf[MAX_PATH * 4]; +#else + char buf[PATH_MAX]; +#endif + + size_t cwd_len = sizeof(buf); + int err = uv_cwd(buf, &cwd_len); + if (err) + return env->ThrowUVException(err, "uv_cwd"); + + Local cwd = String::NewFromUtf8(env->isolate(), + buf, + String::kNormalString, + cwd_len); + args.GetReturnValue().Set(cwd); +} + + +// Hrtime exposes libuv's uv_hrtime() high-resolution timer. + +// This is the legacy version of hrtime before BigInt was introduced in +// JavaScript. +// The value returned by uv_hrtime() is a 64-bit int representing nanoseconds, +// so this function instead fills in an Uint32Array with 3 entries, +// to avoid any integer overflow possibility. +// The first two entries contain the second part of the value +// broken into the upper/lower 32 bits to be converted back in JS, +// because there is no Uint64Array in JS. +// The third entry contains the remaining nanosecond part of the value. +void Hrtime(const FunctionCallbackInfo& args) { + uint64_t t = uv_hrtime(); + + Local ab = args[0].As()->Buffer(); + uint32_t* fields = static_cast(ab->GetContents().Data()); + + fields[0] = (t / NANOS_PER_SEC) >> 32; + fields[1] = (t / NANOS_PER_SEC) & 0xffffffff; + fields[2] = t % NANOS_PER_SEC; +} + +void HrtimeBigInt(const FunctionCallbackInfo& args) { + Local ab = args[0].As()->Buffer(); + uint64_t* fields = static_cast(ab->GetContents().Data()); + fields[0] = uv_hrtime(); +} + +void Kill(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + + if (args.Length() != 2) + return env->ThrowError("Bad argument."); + + int pid = args[0]->Int32Value(); + int sig = args[1]->Int32Value(); + int err = uv_kill(pid, sig); + args.GetReturnValue().Set(err); +} + + +void MemoryUsage(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + + size_t rss; + int err = uv_resident_set_memory(&rss); + if (err) + return env->ThrowUVException(err, "uv_resident_set_memory"); + + Isolate* isolate = env->isolate(); + // V8 memory usage + HeapStatistics v8_heap_stats; + isolate->GetHeapStatistics(&v8_heap_stats); + + // Get the double array pointer from the Float64Array argument. + CHECK(args[0]->IsFloat64Array()); + Local array = args[0].As(); + CHECK_EQ(array->Length(), 4); + Local ab = array->Buffer(); + double* fields = static_cast(ab->GetContents().Data()); + + fields[0] = rss; + fields[1] = v8_heap_stats.total_heap_size(); + fields[2] = v8_heap_stats.used_heap_size(); + fields[3] = isolate->AdjustAmountOfExternalAllocatedMemory(0); +} + +// Most of the time, it's best to use `console.error` to write +// to the process.stderr stream. However, in some cases, such as +// when debugging the stream.Writable class or the process.nextTick +// function, it is useful to bypass JavaScript entirely. +void RawDebug(const FunctionCallbackInfo& args) { + CHECK(args.Length() == 1 && args[0]->IsString() && + "must be called with a single string"); + Utf8Value message(args.GetIsolate(), args[0]); + PrintErrorString("%s\n", *message); + fflush(stderr); +} + +void StartProfilerIdleNotifier(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + env->StartProfilerIdleNotifier(); +} + + +void StopProfilerIdleNotifier(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + env->StopProfilerIdleNotifier(); +} + +void Umask(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + uint32_t old; + + if (args.Length() < 1 || args[0]->IsUndefined()) { + old = umask(0); + umask(static_cast(old)); + } else if (!args[0]->IsInt32() && !args[0]->IsString()) { + return env->ThrowTypeError("argument must be an integer or octal string."); + } else { + int oct; + if (args[0]->IsInt32()) { + oct = args[0]->Uint32Value(); + } else { + oct = 0; + Utf8Value str(env->isolate(), args[0]); + + // Parse the octal string. + for (size_t i = 0; i < str.length(); i++) { + char c = (*str)[i]; + if (c > '7' || c < '0') + return env->ThrowTypeError("invalid octal string"); + oct *= 8; + oct += c - '0'; + } + } + old = umask(static_cast(oct)); + } + + args.GetReturnValue().Set(old); +} + +void Uptime(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + double uptime; + + uv_update_time(env->event_loop()); + uptime = uv_now(env->event_loop()) - prog_start_time; + + args.GetReturnValue().Set(Number::New(env->isolate(), uptime / 1000)); +} + + +#if defined(__POSIX__) && !defined(__ANDROID__) && !defined(__CloudABI__) + +static const uid_t uid_not_found = static_cast(-1); +static const gid_t gid_not_found = static_cast(-1); + + +static uid_t uid_by_name(const char* name) { + struct passwd pwd; + struct passwd* pp; + char buf[8192]; + + errno = 0; + pp = nullptr; + + if (getpwnam_r(name, &pwd, buf, sizeof(buf), &pp) == 0 && pp != nullptr) + return pp->pw_uid; + + return uid_not_found; +} + + +static char* name_by_uid(uid_t uid) { + struct passwd pwd; + struct passwd* pp; + char buf[8192]; + int rc; + + errno = 0; + pp = nullptr; + + if ((rc = getpwuid_r(uid, &pwd, buf, sizeof(buf), &pp)) == 0 && + pp != nullptr) { + return strdup(pp->pw_name); + } + + if (rc == 0) + errno = ENOENT; + + return nullptr; +} + + +static gid_t gid_by_name(const char* name) { + struct group pwd; + struct group* pp; + char buf[8192]; + + errno = 0; + pp = nullptr; + + if (getgrnam_r(name, &pwd, buf, sizeof(buf), &pp) == 0 && pp != nullptr) + return pp->gr_gid; + + return gid_not_found; +} + + +#if 0 // For future use. +static const char* name_by_gid(gid_t gid) { + struct group pwd; + struct group* pp; + char buf[8192]; + int rc; + + errno = 0; + pp = nullptr; + + if ((rc = getgrgid_r(gid, &pwd, buf, sizeof(buf), &pp)) == 0 && + pp != nullptr) { + return strdup(pp->gr_name); + } + + if (rc == 0) { + errno = ENOENT; + } + + return nullptr; +} +#endif + + +static uid_t uid_by_name(Isolate* isolate, Local value) { + if (value->IsUint32()) { + return static_cast(value->Uint32Value()); + } else { + Utf8Value name(isolate, value); + return uid_by_name(*name); + } +} + + +static gid_t gid_by_name(Isolate* isolate, Local value) { + if (value->IsUint32()) { + return static_cast(value->Uint32Value()); + } else { + Utf8Value name(isolate, value); + return gid_by_name(*name); + } +} + +void GetUid(const FunctionCallbackInfo& args) { + // uid_t is an uint32_t on all supported platforms. + args.GetReturnValue().Set(static_cast(getuid())); +} + + +void GetGid(const FunctionCallbackInfo& args) { + // gid_t is an uint32_t on all supported platforms. + args.GetReturnValue().Set(static_cast(getgid())); +} + + +void GetEUid(const FunctionCallbackInfo& args) { + // uid_t is an uint32_t on all supported platforms. + args.GetReturnValue().Set(static_cast(geteuid())); +} + + +void GetEGid(const FunctionCallbackInfo& args) { + // gid_t is an uint32_t on all supported platforms. + args.GetReturnValue().Set(static_cast(getegid())); +} + + +void SetGid(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + CHECK(env->is_main_thread()); + + if (!args[0]->IsUint32() && !args[0]->IsString()) + return env->ThrowTypeError("setgid argument must be a number or a string"); + + gid_t gid = gid_by_name(env->isolate(), args[0]); + + if (gid == gid_not_found) + return env->ThrowError("setgid group id does not exist"); + + if (setgid(gid)) + return env->ThrowErrnoException(errno, "setgid"); +} + + +void SetEGid(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + CHECK(env->is_main_thread()); + + if (!args[0]->IsUint32() && !args[0]->IsString()) + return env->ThrowTypeError("setegid argument must be a number or string"); + + gid_t gid = gid_by_name(env->isolate(), args[0]); + + if (gid == gid_not_found) + return env->ThrowError("setegid group id does not exist"); + + if (setegid(gid)) + return env->ThrowErrnoException(errno, "setegid"); +} + + +void SetUid(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + CHECK(env->is_main_thread()); + + if (!args[0]->IsUint32() && !args[0]->IsString()) + return env->ThrowTypeError("setuid argument must be a number or a string"); + + uid_t uid = uid_by_name(env->isolate(), args[0]); + + if (uid == uid_not_found) + return env->ThrowError("setuid user id does not exist"); + + if (setuid(uid)) + return env->ThrowErrnoException(errno, "setuid"); +} + + +void SetEUid(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + CHECK(env->is_main_thread()); + + if (!args[0]->IsUint32() && !args[0]->IsString()) + return env->ThrowTypeError("seteuid argument must be a number or string"); + + uid_t uid = uid_by_name(env->isolate(), args[0]); + + if (uid == uid_not_found) + return env->ThrowError("seteuid user id does not exist"); + + if (seteuid(uid)) + return env->ThrowErrnoException(errno, "seteuid"); +} + + +void GetGroups(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + + int ngroups = getgroups(0, nullptr); + + if (ngroups == -1) + return env->ThrowErrnoException(errno, "getgroups"); + + gid_t* groups = new gid_t[ngroups]; + + ngroups = getgroups(ngroups, groups); + + if (ngroups == -1) { + delete[] groups; + return env->ThrowErrnoException(errno, "getgroups"); + } + + Local groups_list = Array::New(env->isolate(), ngroups); + bool seen_egid = false; + gid_t egid = getegid(); + + for (int i = 0; i < ngroups; i++) { + groups_list->Set(i, Integer::New(env->isolate(), groups[i])); + if (groups[i] == egid) + seen_egid = true; + } + + delete[] groups; + + if (seen_egid == false) + groups_list->Set(ngroups, Integer::New(env->isolate(), egid)); + + args.GetReturnValue().Set(groups_list); +} + + +void SetGroups(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + + if (!args[0]->IsArray()) + return env->ThrowTypeError("argument 1 must be an array"); + + Local groups_list = args[0].As(); + size_t size = groups_list->Length(); + gid_t* groups = new gid_t[size]; + + for (size_t i = 0; i < size; i++) { + gid_t gid = gid_by_name(env->isolate(), groups_list->Get(i)); + + if (gid == gid_not_found) { + delete[] groups; + return env->ThrowError("group name not found"); + } + + groups[i] = gid; + } + + int rc = setgroups(size, groups); + delete[] groups; + + if (rc == -1) + return env->ThrowErrnoException(errno, "setgroups"); +} + + +void InitGroups(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + + if (!args[0]->IsUint32() && !args[0]->IsString()) + return env->ThrowTypeError("argument 1 must be a number or a string"); + + if (!args[1]->IsUint32() && !args[1]->IsString()) + return env->ThrowTypeError("argument 2 must be a number or a string"); + + Utf8Value arg0(env->isolate(), args[0]); + gid_t extra_group; + bool must_free; + char* user; + + if (args[0]->IsUint32()) { + user = name_by_uid(args[0]->Uint32Value()); + must_free = true; + } else { + user = *arg0; + must_free = false; + } + + if (user == nullptr) + return env->ThrowError("initgroups user not found"); + + extra_group = gid_by_name(env->isolate(), args[1]); + + if (extra_group == gid_not_found) { + if (must_free) + free(user); + return env->ThrowError("initgroups extra group not found"); + } + + int rc = initgroups(user, extra_group); + + if (must_free) + free(user); + + if (rc) + return env->ThrowErrnoException(errno, "initgroups"); +} + +#endif // __POSIX__ && !defined(__ANDROID__) && !defined(__CloudABI__) + +} // namespace node diff --git a/src/node_serdes.cc b/src/node_serdes.cc index 520b350199245a..4b2cc60b3f7584 100644 --- a/src/node_serdes.cc +++ b/src/node_serdes.cc @@ -52,6 +52,11 @@ class SerializerContext : public BaseObject, static void WriteUint64(const FunctionCallbackInfo& args); static void WriteDouble(const FunctionCallbackInfo& args); static void WriteRawBytes(const FunctionCallbackInfo& args); + + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + } + private: ValueSerializer serializer_; }; @@ -76,6 +81,11 @@ class DeserializerContext : public BaseObject, static void ReadUint64(const FunctionCallbackInfo& args); static void ReadDouble(const FunctionCallbackInfo& args); static void ReadRawBytes(const FunctionCallbackInfo& args); + + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + } + private: const uint8_t* data_; const size_t length_; diff --git a/src/node_stat_watcher.cc b/src/node_stat_watcher.cc index 767bc024e46be1..5e47476fd97a0c 100644 --- a/src/node_stat_watcher.cc +++ b/src/node_stat_watcher.cc @@ -52,11 +52,9 @@ void StatWatcher::Initialize(Environment* env, Local target) { t->SetClassName(statWatcherString); AsyncWrap::AddWrapMethods(env, t); + HandleWrap::AddWrapMethods(env, t); + env->SetProtoMethod(t, "start", StatWatcher::Start); - env->SetProtoMethod(t, "close", HandleWrap::Close); - env->SetProtoMethod(t, "ref", HandleWrap::Ref); - env->SetProtoMethod(t, "unref", HandleWrap::Unref); - env->SetProtoMethod(t, "hasRef", HandleWrap::HasRef); target->Set(statWatcherString, t->GetFunction()); } diff --git a/src/node_stat_watcher.h b/src/node_stat_watcher.h index 45150de785f9d1..baf6bdc14ee317 100644 --- a/src/node_stat_watcher.h +++ b/src/node_stat_watcher.h @@ -44,7 +44,9 @@ class StatWatcher : public HandleWrap { static void New(const v8::FunctionCallbackInfo& args); static void Start(const v8::FunctionCallbackInfo& args); - size_t self_size() const override { return sizeof(*this); } + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + } private: static void Callback(uv_fs_poll_t* handle, diff --git a/src/node_trace_events.cc b/src/node_trace_events.cc index 985c706dc4a2f1..0904311dad865b 100644 --- a/src/node_trace_events.cc +++ b/src/node_trace_events.cc @@ -27,6 +27,11 @@ class NodeCategorySet : public BaseObject { const std::set& GetCategories() { return categories_; } + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + tracker->TrackField("categories", categories_); + } + private: NodeCategorySet(Environment* env, Local wrap, diff --git a/src/node_types.cc b/src/node_types.cc index 5dac1f6d275b63..4872491c924989 100644 --- a/src/node_types.cc +++ b/src/node_types.cc @@ -56,13 +56,13 @@ void InitializeTypes(Local target, Local context) { Environment* env = Environment::GetCurrent(context); -#define V(type) env->SetMethod(target, \ - "is" #type, \ - Is##type); +#define V(type) env->SetMethodNoSideEffect(target, \ + "is" #type, \ + Is##type); VALUE_METHOD_MAP(V) #undef V - env->SetMethod(target, "isAnyArrayBuffer", IsAnyArrayBuffer); + env->SetMethodNoSideEffect(target, "isAnyArrayBuffer", IsAnyArrayBuffer); } } // anonymous namespace diff --git a/src/node_url.cc b/src/node_url.cc index 82c093d516bc4a..1cdb179ed254f5 100644 --- a/src/node_url.cc +++ b/src/node_url.cc @@ -2334,10 +2334,10 @@ static void Initialize(Local target, void* priv) { Environment* env = Environment::GetCurrent(context); env->SetMethod(target, "parse", Parse); - env->SetMethod(target, "encodeAuth", EncodeAuthSet); - env->SetMethod(target, "toUSVString", ToUSVString); - env->SetMethod(target, "domainToASCII", DomainToASCII); - env->SetMethod(target, "domainToUnicode", DomainToUnicode); + env->SetMethodNoSideEffect(target, "encodeAuth", EncodeAuthSet); + env->SetMethodNoSideEffect(target, "toUSVString", ToUSVString); + env->SetMethodNoSideEffect(target, "domainToASCII", DomainToASCII); + env->SetMethodNoSideEffect(target, "domainToUnicode", DomainToUnicode); env->SetMethod(target, "setURLConstructor", SetURLConstructor); #define XX(name, _) NODE_DEFINE_CONSTANT(target, name); diff --git a/src/node_util.cc b/src/node_util.cc index 2db68586459ab2..ef7dc8a818bf11 100644 --- a/src/node_util.cc +++ b/src/node_util.cc @@ -53,29 +53,16 @@ static void PreviewEntries(const FunctionCallbackInfo& args) { if (!args[0]->IsObject()) return; + Environment* env = Environment::GetCurrent(args); bool is_key_value; Local entries; if (!args[0].As()->PreviewEntries(&is_key_value).ToLocal(&entries)) return; - if (!is_key_value) - return args.GetReturnValue().Set(entries); - - uint32_t length = entries->Length(); - CHECK_EQ(length % 2, 0); - - Environment* env = Environment::GetCurrent(args); - Local context = env->context(); - - Local pairs = Array::New(env->isolate(), length / 2); - for (uint32_t i = 0; i < length / 2; i++) { - Local pair = Array::New(env->isolate(), 2); - pair->Set(context, 0, entries->Get(context, i * 2).ToLocalChecked()) - .FromJust(); - pair->Set(context, 1, entries->Get(context, i * 2 + 1).ToLocalChecked()) - .FromJust(); - pairs->Set(context, i, pair).FromJust(); - } - args.GetReturnValue().Set(pairs); + Local ret = Array::New(env->isolate(), 2); + ret->Set(env->context(), 0, entries).FromJust(); + ret->Set(env->context(), 1, v8::Boolean::New(env->isolate(), is_key_value)) + .FromJust(); + return args.GetReturnValue().Set(ret); } // Side effect-free stringification that will never throw exceptions. @@ -212,18 +199,19 @@ void Initialize(Local target, V(kRejected); #undef V - env->SetMethod(target, "getHiddenValue", GetHiddenValue); + env->SetMethodNoSideEffect(target, "getHiddenValue", GetHiddenValue); env->SetMethod(target, "setHiddenValue", SetHiddenValue); - env->SetMethod(target, "getPromiseDetails", GetPromiseDetails); - env->SetMethod(target, "getProxyDetails", GetProxyDetails); - env->SetMethod(target, "safeToString", SafeToString); - env->SetMethod(target, "previewEntries", PreviewEntries); + env->SetMethodNoSideEffect(target, "getPromiseDetails", GetPromiseDetails); + env->SetMethodNoSideEffect(target, "getProxyDetails", GetProxyDetails); + env->SetMethodNoSideEffect(target, "safeToString", SafeToString); + env->SetMethodNoSideEffect(target, "previewEntries", PreviewEntries); env->SetMethod(target, "startSigintWatchdog", StartSigintWatchdog); env->SetMethod(target, "stopSigintWatchdog", StopSigintWatchdog); - env->SetMethod(target, "watchdogHasPendingSigint", WatchdogHasPendingSigint); + env->SetMethodNoSideEffect(target, "watchdogHasPendingSigint", + WatchdogHasPendingSigint); - env->SetMethod(target, "createPromise", CreatePromise); + env->SetMethodNoSideEffect(target, "createPromise", CreatePromise); env->SetMethod(target, "promiseResolve", PromiseResolve); env->SetMethod(target, "promiseReject", PromiseReject); diff --git a/src/node_v8.cc b/src/node_v8.cc index d546eeba93f4d9..fb0a9fea1e5d27 100644 --- a/src/node_v8.cc +++ b/src/node_v8.cc @@ -122,7 +122,8 @@ void Initialize(Local target, Local context) { Environment* env = Environment::GetCurrent(context); - env->SetMethod(target, "cachedDataVersionTag", CachedDataVersionTag); + env->SetMethodNoSideEffect(target, "cachedDataVersionTag", + CachedDataVersionTag); env->SetMethod(target, "updateHeapStatisticsArrayBuffer", diff --git a/src/node_version.h b/src/node_version.h index 0a7d134d9e25e4..8370b4c86f44db 100644 --- a/src/node_version.h +++ b/src/node_version.h @@ -23,13 +23,13 @@ #define SRC_NODE_VERSION_H_ #define NODE_MAJOR_VERSION 10 -#define NODE_MINOR_VERSION 6 -#define NODE_PATCH_VERSION 1 +#define NODE_MINOR_VERSION 7 +#define NODE_PATCH_VERSION 0 #define NODE_VERSION_IS_LTS 0 #define NODE_VERSION_LTS_CODENAME "" -#define NODE_VERSION_IS_RELEASE 0 +#define NODE_VERSION_IS_RELEASE 1 #ifndef NODE_STRINGIFY #define NODE_STRINGIFY(n) NODE_STRINGIFY_HELPER(n) diff --git a/src/node_worker.cc b/src/node_worker.cc index 6f325668b86921..3768d80a9c58d4 100644 --- a/src/node_worker.cc +++ b/src/node_worker.cc @@ -403,10 +403,6 @@ void Worker::Exit(int code) { } } -size_t Worker::self_size() const { - return sizeof(*this); -} - namespace { // Return the MessagePort that is global for this Environment and communicates diff --git a/src/node_worker.h b/src/node_worker.h index d802b5cfefdf77..bd737d4800fd79 100644 --- a/src/node_worker.h +++ b/src/node_worker.h @@ -25,7 +25,14 @@ class Worker : public AsyncWrap { // Wait for the worker thread to stop (in a blocking manner). void JoinThread(); - size_t self_size() const override; + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + tracker->TrackFieldWithSize("isolate_data", sizeof(IsolateData)); + tracker->TrackFieldWithSize("env", sizeof(Environment)); + tracker->TrackFieldWithSize("thread_exit_async", sizeof(uv_async_t)); + tracker->TrackField("parent_port", parent_port_); + } + bool is_stopped() const; static void New(const v8::FunctionCallbackInfo& args); diff --git a/src/node_zlib.cc b/src/node_zlib.cc index de8b0335892f65..031666e19ad29c 100644 --- a/src/node_zlib.cc +++ b/src/node_zlib.cc @@ -34,6 +34,7 @@ #include #include #include +#include namespace node { @@ -90,12 +91,15 @@ class ZCtx : public AsyncWrap, public ThreadPoolWork { refs_(0), gzip_id_bytes_read_(0), write_result_(nullptr) { + MakeWeak(); } ~ZCtx() override { CHECK_EQ(false, write_in_progress_ && "write in progress"); Close(); + CHECK_EQ(zlib_memory_, 0); + CHECK_EQ(unreported_allocations_, 0); } void Close() { @@ -108,17 +112,15 @@ class ZCtx : public AsyncWrap, public ThreadPoolWork { CHECK(init_done_ && "close before init"); CHECK_LE(mode_, UNZIP); + AllocScope alloc_scope(this); int status = Z_OK; if (mode_ == DEFLATE || mode_ == GZIP || mode_ == DEFLATERAW) { status = deflateEnd(&strm_); - int64_t change_in_bytes = -static_cast(kDeflateContextSize); - env()->isolate()->AdjustAmountOfExternalAllocatedMemory(change_in_bytes); } else if (mode_ == INFLATE || mode_ == GUNZIP || mode_ == INFLATERAW || mode_ == UNZIP) { status = inflateEnd(&strm_); - int64_t change_in_bytes = -static_cast(kInflateContextSize); - env()->isolate()->AdjustAmountOfExternalAllocatedMemory(change_in_bytes); } + CHECK(status == Z_OK || status == Z_DATA_ERROR); mode_ = NONE; @@ -164,6 +166,8 @@ class ZCtx : public AsyncWrap, public ThreadPoolWork { CHECK(0 && "Invalid flush value"); } + AllocScope alloc_scope(ctx); + Bytef* in; Bytef* out; size_t in_off, in_len, out_off, out_len; @@ -354,6 +358,8 @@ class ZCtx : public AsyncWrap, public ThreadPoolWork { // v8 land! void AfterThreadPoolWork(int status) override { + AllocScope alloc_scope(this); + write_in_progress_ = false; if (status == UV_ECANCELED) { @@ -480,7 +486,7 @@ class ZCtx : public AsyncWrap, public ThreadPoolWork { write_js_callback, dictionary, dictionary_len); if (!ret) goto end; - SetDictionary(ctx); + ctx->SetDictionary(); end: return args.GetReturnValue().Set(ret); @@ -490,28 +496,29 @@ class ZCtx : public AsyncWrap, public ThreadPoolWork { CHECK(args.Length() == 2 && "params(level, strategy)"); ZCtx* ctx; ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Holder()); - Params(ctx, args[0]->Int32Value(), args[1]->Int32Value()); + ctx->Params(args[0]->Int32Value(), args[1]->Int32Value()); } static void Reset(const FunctionCallbackInfo &args) { ZCtx* ctx; ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Holder()); ctx->Reset(); - SetDictionary(ctx); + ctx->SetDictionary(); } static bool Init(ZCtx* ctx, int level, int windowBits, int memLevel, int strategy, uint32_t* write_result, Local write_js_callback, char* dictionary, size_t dictionary_len) { + AllocScope alloc_scope(ctx); ctx->level_ = level; ctx->windowBits_ = windowBits; ctx->memLevel_ = memLevel; ctx->strategy_ = strategy; - ctx->strm_.zalloc = Z_NULL; - ctx->strm_.zfree = Z_NULL; - ctx->strm_.opaque = Z_NULL; + ctx->strm_.zalloc = AllocForZlib; + ctx->strm_.zfree = FreeForZlib; + ctx->strm_.opaque = static_cast(ctx); ctx->flush_ = Z_NO_FLUSH; @@ -539,16 +546,12 @@ class ZCtx : public AsyncWrap, public ThreadPoolWork { ctx->windowBits_, ctx->memLevel_, ctx->strategy_); - ctx->env()->isolate() - ->AdjustAmountOfExternalAllocatedMemory(kDeflateContextSize); break; case INFLATE: case GUNZIP: case INFLATERAW: case UNZIP: ctx->err_ = inflateInit2(&ctx->strm_, ctx->windowBits_); - ctx->env()->isolate() - ->AdjustAmountOfExternalAllocatedMemory(kInflateContextSize); break; default: UNREACHABLE(); @@ -574,53 +577,53 @@ class ZCtx : public AsyncWrap, public ThreadPoolWork { return true; } - static void SetDictionary(ZCtx* ctx) { - if (ctx->dictionary_ == nullptr) + void SetDictionary() { + if (dictionary_ == nullptr) return; - ctx->err_ = Z_OK; + err_ = Z_OK; - switch (ctx->mode_) { + switch (mode_) { case DEFLATE: case DEFLATERAW: - ctx->err_ = deflateSetDictionary(&ctx->strm_, - ctx->dictionary_, - ctx->dictionary_len_); + err_ = deflateSetDictionary(&strm_, dictionary_, dictionary_len_); break; case INFLATERAW: // The other inflate cases will have the dictionary set when inflate() // returns Z_NEED_DICT in Process() - ctx->err_ = inflateSetDictionary(&ctx->strm_, - ctx->dictionary_, - ctx->dictionary_len_); + err_ = inflateSetDictionary(&strm_, dictionary_, dictionary_len_); break; default: break; } - if (ctx->err_ != Z_OK) { - ctx->Error("Failed to set dictionary"); + if (err_ != Z_OK) { + Error("Failed to set dictionary"); } } - static void Params(ZCtx* ctx, int level, int strategy) { - ctx->err_ = Z_OK; + void Params(int level, int strategy) { + AllocScope alloc_scope(this); - switch (ctx->mode_) { + err_ = Z_OK; + + switch (mode_) { case DEFLATE: case DEFLATERAW: - ctx->err_ = deflateParams(&ctx->strm_, level, strategy); + err_ = deflateParams(&strm_, level, strategy); break; default: break; } - if (ctx->err_ != Z_OK && ctx->err_ != Z_BUF_ERROR) { - ctx->Error("Failed to set parameters"); + if (err_ != Z_OK && err_ != Z_BUF_ERROR) { + Error("Failed to set parameters"); } } void Reset() { + AllocScope alloc_scope(this); + err_ = Z_OK; switch (mode_) { @@ -643,7 +646,12 @@ class ZCtx : public AsyncWrap, public ThreadPoolWork { } } - size_t self_size() const override { return sizeof(*this); } + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + tracker->TrackFieldWithSize("dictionary", dictionary_len_); + tracker->TrackFieldWithSize("zlib memory", + zlib_memory_ + unreported_allocations_); + } private: void Ref() { @@ -659,8 +667,51 @@ class ZCtx : public AsyncWrap, public ThreadPoolWork { } } - static const int kDeflateContextSize = 16384; // approximate - static const int kInflateContextSize = 10240; // approximate + // Allocation functions provided to zlib itself. We store the real size of + // the allocated memory chunk just before the "payload" memory we return + // to zlib. + // Because we use zlib off the thread pool, we can not report memory directly + // to V8; rather, we first store it as "unreported" memory in a separate + // field and later report it back from the main thread. + static void* AllocForZlib(void* data, uInt items, uInt size) { + ZCtx* ctx = static_cast(data); + size_t real_size = + MultiplyWithOverflowCheck(static_cast(items), + static_cast(size)) + sizeof(size_t); + char* memory = UncheckedMalloc(real_size); + if (UNLIKELY(memory == nullptr)) return nullptr; + *reinterpret_cast(memory) = real_size; + ctx->unreported_allocations_.fetch_add(real_size, + std::memory_order_relaxed); + return memory + sizeof(size_t); + } + + static void FreeForZlib(void* data, void* pointer) { + if (UNLIKELY(pointer == nullptr)) return; + ZCtx* ctx = static_cast(data); + char* real_pointer = static_cast(pointer) - sizeof(size_t); + size_t real_size = *reinterpret_cast(real_pointer); + ctx->unreported_allocations_.fetch_sub(real_size, + std::memory_order_relaxed); + free(real_pointer); + } + + // This is called on the main thread after zlib may have allocated something + // in order to report it back to V8. + void AdjustAmountOfExternalAllocatedMemory() { + ssize_t report = + unreported_allocations_.exchange(0, std::memory_order_relaxed); + if (report == 0) return; + CHECK_IMPLIES(report < 0, zlib_memory_ >= static_cast(-report)); + zlib_memory_ += report; + env()->isolate()->AdjustAmountOfExternalAllocatedMemory(report); + } + + struct AllocScope { + explicit AllocScope(ZCtx* ctx) : ctx(ctx) {} + ~AllocScope() { ctx->AdjustAmountOfExternalAllocatedMemory(); } + ZCtx* ctx; + }; Bytef* dictionary_; size_t dictionary_len_; @@ -679,6 +730,8 @@ class ZCtx : public AsyncWrap, public ThreadPoolWork { unsigned int gzip_id_bytes_read_; uint32_t* write_result_; Persistent write_js_callback_; + std::atomic unreported_allocations_{0}; + size_t zlib_memory_ = 0; }; diff --git a/src/pipe_wrap.cc b/src/pipe_wrap.cc index e2cc114479d5a1..a6044b6b267e47 100644 --- a/src/pipe_wrap.cc +++ b/src/pipe_wrap.cc @@ -77,12 +77,7 @@ void PipeWrap::Initialize(Local target, t->InstanceTemplate()->SetInternalFieldCount(1); AsyncWrap::AddWrapMethods(env, t); - - env->SetProtoMethod(t, "close", HandleWrap::Close); - env->SetProtoMethod(t, "unref", HandleWrap::Unref); - env->SetProtoMethod(t, "ref", HandleWrap::Ref); - env->SetProtoMethod(t, "hasRef", HandleWrap::HasRef); - + HandleWrap::AddWrapMethods(env, t); LibuvStreamWrap::AddMethods(env, t); env->SetProtoMethod(t, "bind", Bind); diff --git a/src/pipe_wrap.h b/src/pipe_wrap.h index d6e45c28ff7822..9ed4f153ae5c4c 100644 --- a/src/pipe_wrap.h +++ b/src/pipe_wrap.h @@ -45,7 +45,9 @@ class PipeWrap : public ConnectionWrap { v8::Local unused, v8::Local context); - size_t self_size() const override { return sizeof(*this); } + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + } private: PipeWrap(Environment* env, diff --git a/src/process_wrap.cc b/src/process_wrap.cc index 54345b231bccce..b9a20c34a770ba 100644 --- a/src/process_wrap.cc +++ b/src/process_wrap.cc @@ -58,20 +58,17 @@ class ProcessWrap : public HandleWrap { constructor->SetClassName(processString); AsyncWrap::AddWrapMethods(env, constructor); - - env->SetProtoMethod(constructor, "close", HandleWrap::Close); + HandleWrap::AddWrapMethods(env, constructor); env->SetProtoMethod(constructor, "spawn", Spawn); env->SetProtoMethod(constructor, "kill", Kill); - env->SetProtoMethod(constructor, "ref", HandleWrap::Ref); - env->SetProtoMethod(constructor, "unref", HandleWrap::Unref); - env->SetProtoMethod(constructor, "hasRef", HandleWrap::HasRef); - target->Set(processString, constructor->GetFunction()); } - size_t self_size() const override { return sizeof(*this); } + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + } private: static void New(const FunctionCallbackInfo& args) { diff --git a/src/sharedarraybuffer_metadata.cc b/src/sharedarraybuffer_metadata.cc index 86476a9f12c38b..95fed87c8d4ea0 100644 --- a/src/sharedarraybuffer_metadata.cc +++ b/src/sharedarraybuffer_metadata.cc @@ -47,6 +47,10 @@ class SABLifetimePartner : public BaseObject { MakeWeak(); } + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + } + SharedArrayBufferMetadataReference reference; }; diff --git a/src/signal_wrap.cc b/src/signal_wrap.cc index 5117d3ab1d1988..5568d22e4c6228 100644 --- a/src/signal_wrap.cc +++ b/src/signal_wrap.cc @@ -52,17 +52,17 @@ class SignalWrap : public HandleWrap { constructor->SetClassName(signalString); AsyncWrap::AddWrapMethods(env, constructor); - env->SetProtoMethod(constructor, "close", HandleWrap::Close); - env->SetProtoMethod(constructor, "ref", HandleWrap::Ref); - env->SetProtoMethod(constructor, "unref", HandleWrap::Unref); - env->SetProtoMethod(constructor, "hasRef", HandleWrap::HasRef); + HandleWrap::AddWrapMethods(env, constructor); + env->SetProtoMethod(constructor, "start", Start); env->SetProtoMethod(constructor, "stop", Stop); target->Set(signalString, constructor->GetFunction()); } - size_t self_size() const override { return sizeof(*this); } + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + } private: static void New(const FunctionCallbackInfo& args) { @@ -90,7 +90,7 @@ class SignalWrap : public HandleWrap { #if defined(__POSIX__) && HAVE_INSPECTOR if (signum == SIGPROF) { Environment* env = Environment::GetCurrent(args); - if (env->inspector_agent()->IsStarted()) { + if (env->inspector_agent()->IsListening()) { ProcessEmitWarning(env, "process.on(SIGPROF) is reserved while debugging"); return; diff --git a/src/spawn_sync.h b/src/spawn_sync.h index 806431846691db..fed14532960326 100644 --- a/src/spawn_sync.h +++ b/src/spawn_sync.h @@ -29,18 +29,6 @@ namespace node { -using v8::Array; -using v8::Context; -using v8::FunctionCallbackInfo; -using v8::HandleScope; -using v8::Integer; -using v8::Isolate; -using v8::Local; -using v8::Null; -using v8::Number; -using v8::Object; -using v8::String; -using v8::Value; class SyncProcessOutputBuffer; @@ -94,7 +82,7 @@ class SyncProcessStdioPipe { int Start(); void Close(); - Local GetOutputAsBuffer(Environment* env) const; + v8::Local GetOutputAsBuffer(Environment* env) const; inline bool readable() const; inline bool writable() const; @@ -151,10 +139,10 @@ class SyncProcessRunner { }; public: - static void Initialize(Local target, - Local unused, - Local context); - static void Spawn(const FunctionCallbackInfo& args); + static void Initialize(v8::Local target, + v8::Local unused, + v8::Local context); + static void Spawn(const v8::FunctionCallbackInfo& args); private: friend class SyncProcessStdioPipe; @@ -164,8 +152,8 @@ class SyncProcessRunner { inline Environment* env() const; - Local Run(Local options); - void TryInitializeAndRunLoop(Local options); + v8::Local Run(v8::Local options); + void TryInitializeAndRunLoop(v8::Local options); void CloseHandlesAndDeleteLoop(); void CloseStdioPipes(); @@ -181,12 +169,12 @@ class SyncProcessRunner { void SetError(int error); void SetPipeError(int pipe_error); - Local BuildResultObject(); - Local BuildOutputArray(); + v8::Local BuildResultObject(); + v8::Local BuildOutputArray(); - int ParseOptions(Local js_value); - int ParseStdioOptions(Local js_value); - int ParseStdioOption(int child_fd, Local js_stdio_option); + int ParseOptions(v8::Local js_value); + int ParseStdioOptions(v8::Local js_value); + int ParseStdioOption(int child_fd, v8::Local js_stdio_option); inline int AddStdioIgnore(uint32_t child_fd); inline int AddStdioPipe(uint32_t child_fd, @@ -195,9 +183,9 @@ class SyncProcessRunner { uv_buf_t input_buffer); inline int AddStdioInheritFD(uint32_t child_fd, int inherit_fd); - static bool IsSet(Local value); - int CopyJsString(Local js_value, const char** target); - int CopyJsStringArray(Local js_value, char** target); + static bool IsSet(v8::Local value); + int CopyJsString(v8::Local js_value, const char** target); + int CopyJsStringArray(v8::Local js_value, char** target); static void ExitCallback(uv_process_t* handle, int64_t exit_status, diff --git a/src/stream_base-inl.h b/src/stream_base-inl.h index b53d3e5979c2a5..027b938d30df1c 100644 --- a/src/stream_base-inl.h +++ b/src/stream_base-inl.h @@ -275,29 +275,30 @@ void StreamBase::AddMethods(Environment* env, Local t) { Local signature = Signature::New(env->isolate(), t); + // TODO(TimothyGu): None of these should have ConstructorBehavior::kAllow. Local get_fd_templ = - FunctionTemplate::New(env->isolate(), - GetFD, - env->as_external(), - signature); + env->NewFunctionTemplate(GetFD, + signature, + v8::ConstructorBehavior::kAllow, + v8::SideEffectType::kHasNoSideEffect); Local get_external_templ = - FunctionTemplate::New(env->isolate(), - GetExternal, - env->as_external(), - signature); + env->NewFunctionTemplate(GetExternal, + signature, + v8::ConstructorBehavior::kAllow, + v8::SideEffectType::kHasNoSideEffect); Local get_bytes_read_templ = - FunctionTemplate::New(env->isolate(), - GetBytesRead, - env->as_external(), - signature); + env->NewFunctionTemplate(GetBytesRead, + signature, + v8::ConstructorBehavior::kAllow, + v8::SideEffectType::kHasNoSideEffect); Local get_bytes_written_templ = - FunctionTemplate::New(env->isolate(), - GetBytesWritten, - env->as_external(), - signature); + env->NewFunctionTemplate(GetBytesWritten, + signature, + v8::ConstructorBehavior::kAllow, + v8::SideEffectType::kHasNoSideEffect); t->PrototypeTemplate()->SetAccessorProperty(env->fd_string(), get_fd_templ, diff --git a/src/stream_base.h b/src/stream_base.h index 405780619856ba..bbb20e52e1a8de 100644 --- a/src/stream_base.h +++ b/src/stream_base.h @@ -346,7 +346,10 @@ class SimpleShutdownWrap : public ShutdownWrap, public OtherBase { v8::Local req_wrap_obj); AsyncWrap* GetAsyncWrap() override { return this; } - size_t self_size() const override { return sizeof(*this); } + + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + } }; template @@ -356,7 +359,11 @@ class SimpleWriteWrap : public WriteWrap, public OtherBase { v8::Local req_wrap_obj); AsyncWrap* GetAsyncWrap() override { return this; } - size_t self_size() const override { return sizeof(*this) + StorageSize(); } + + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + tracker->TrackFieldWithSize("storage", StorageSize()); + } }; } // namespace node diff --git a/src/stream_pipe.cc b/src/stream_pipe.cc index bfe7d4297257a0..e19f98e35d2821 100644 --- a/src/stream_pipe.cc +++ b/src/stream_pipe.cc @@ -57,9 +57,11 @@ void StreamPipe::Unpipe() { if (is_closed_) return; - // Note that we cannot use virtual methods on `source` and `sink` here, - // because this function can be called from their destructors via + // Note that we possibly cannot use virtual methods on `source` and `sink` + // here, because this function can be called from their destructors via // `OnStreamDestroy()`. + if (!source_destroyed_) + source()->ReadStop(); is_closed_ = true; is_reading_ = false; @@ -144,7 +146,8 @@ void StreamPipe::ProcessData(size_t nread, const uv_buf_t& buf) { is_writing_ = true; is_reading_ = false; res.wrap->SetAllocatedStorage(buf.base, buf.len); - source()->ReadStop(); + if (source() != nullptr) + source()->ReadStop(); } } @@ -183,6 +186,7 @@ void StreamPipe::WritableListener::OnStreamAfterShutdown(ShutdownWrap* w, void StreamPipe::ReadableListener::OnStreamDestroy() { StreamPipe* pipe = ContainerOf(&StreamPipe::readable_listener_, this); + pipe->source_destroyed_ = true; if (!pipe->is_eof_) { OnStreamRead(UV_EPIPE, uv_buf_init(nullptr, 0)); } @@ -190,6 +194,7 @@ void StreamPipe::ReadableListener::OnStreamDestroy() { void StreamPipe::WritableListener::OnStreamDestroy() { StreamPipe* pipe = ContainerOf(&StreamPipe::writable_listener_, this); + pipe->sink_destroyed_ = true; pipe->is_eof_ = true; pipe->Unpipe(); } diff --git a/src/stream_pipe.h b/src/stream_pipe.h index 98d6dae11be841..36a0b1dc08106b 100644 --- a/src/stream_pipe.h +++ b/src/stream_pipe.h @@ -18,19 +18,23 @@ class StreamPipe : public AsyncWrap { static void Start(const v8::FunctionCallbackInfo& args); static void Unpipe(const v8::FunctionCallbackInfo& args); - size_t self_size() const override { return sizeof(*this); } + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + } private: - StreamBase* source(); - StreamBase* sink(); + inline StreamBase* source(); + inline StreamBase* sink(); - void ShutdownWritable(); - void FlushToWritable(); + inline void ShutdownWritable(); + inline void FlushToWritable(); bool is_reading_ = false; bool is_writing_ = false; bool is_eof_ = false; bool is_closed_ = true; + bool sink_destroyed_ = false; + bool source_destroyed_ = false; // Set a default value so that when we’re coming from Start(), we know // that we don’t want to read just yet. diff --git a/src/string_bytes.cc b/src/string_bytes.cc index 0bc2ec044ebb55..83c74d2f182021 100644 --- a/src/string_bytes.cc +++ b/src/string_bytes.cc @@ -343,7 +343,7 @@ size_t StringBytes::Write(Isolate* isolate, // Node's "ucs2" encoding wants LE character data stored in // the Buffer, so we need to reorder on BE platforms. See - // http://nodejs.org/api/buffer.html regarding Node's "ucs2" + // https://nodejs.org/api/buffer.html regarding Node's "ucs2" // encoding specification if (IsBigEndian()) SwapBytes16(buf, nbytes); @@ -709,7 +709,7 @@ MaybeLocal StringBytes::Encode(Isolate* isolate, // Node's "ucs2" encoding expects LE character data inside a // Buffer, so we need to reorder on BE platforms. See - // http://nodejs.org/api/buffer.html regarding Node's "ucs2" + // https://nodejs.org/api/buffer.html regarding Node's "ucs2" // encoding specification if (IsBigEndian()) { uint16_t* dst = node::UncheckedMalloc(buflen); diff --git a/src/tcp_wrap.cc b/src/tcp_wrap.cc index aa130d22e02689..805e566bfab08b 100644 --- a/src/tcp_wrap.cc +++ b/src/tcp_wrap.cc @@ -86,13 +86,7 @@ void TCPWrap::Initialize(Local target, t->InstanceTemplate()->Set(env->onconnection_string(), Null(env->isolate())); AsyncWrap::AddWrapMethods(env, t, AsyncWrap::kFlagHasReset); - - env->SetProtoMethod(t, "close", HandleWrap::Close); - - env->SetProtoMethod(t, "ref", HandleWrap::Ref); - env->SetProtoMethod(t, "unref", HandleWrap::Unref); - env->SetProtoMethod(t, "hasRef", HandleWrap::HasRef); - + HandleWrap::AddWrapMethods(env, t); LibuvStreamWrap::AddMethods(env, t); env->SetProtoMethod(t, "open", Open); diff --git a/src/tcp_wrap.h b/src/tcp_wrap.h index 2ab50f1fdcdfab..d6ca9306099e37 100644 --- a/src/tcp_wrap.h +++ b/src/tcp_wrap.h @@ -44,7 +44,9 @@ class TCPWrap : public ConnectionWrap { v8::Local unused, v8::Local context); - size_t self_size() const override { return sizeof(*this); } + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + } private: typedef uv_tcp_t HandleType; diff --git a/src/timer_wrap.cc b/src/timer_wrap.cc index 50541acc9b8a0d..7af6d13c61f7d2 100644 --- a/src/timer_wrap.cc +++ b/src/timer_wrap.cc @@ -55,11 +55,7 @@ class TimerWrap : public HandleWrap { env->SetTemplateMethod(constructor, "now", Now); AsyncWrap::AddWrapMethods(env, constructor); - - env->SetProtoMethod(constructor, "close", HandleWrap::Close); - env->SetProtoMethod(constructor, "ref", HandleWrap::Ref); - env->SetProtoMethod(constructor, "unref", HandleWrap::Unref); - env->SetProtoMethod(constructor, "hasRef", HandleWrap::HasRef); + HandleWrap::AddWrapMethods(env, constructor); env->SetProtoMethod(constructor, "start", Start); env->SetProtoMethod(constructor, "stop", Stop); @@ -72,7 +68,9 @@ class TimerWrap : public HandleWrap { ->GetFunction(env->context()).ToLocalChecked()).FromJust(); } - size_t self_size() const override { return sizeof(*this); } + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + } private: static void SetupTimers(const FunctionCallbackInfo& args) { diff --git a/src/tls_wrap.cc b/src/tls_wrap.cc index e731c0c130216b..3efa6adb4edb0e 100644 --- a/src/tls_wrap.cc +++ b/src/tls_wrap.cc @@ -758,6 +758,8 @@ void TLSWrap::DestroySSL(const FunctionCallbackInfo& args) { // Destroy the SSL structure and friends wrap->SSLWrap::DestroySSL(); + wrap->enc_in_ = nullptr; + wrap->enc_out_ = nullptr; if (wrap->stream_ != nullptr) wrap->stream_->RemoveStreamListener(wrap); @@ -864,6 +866,17 @@ void TLSWrap::GetWriteQueueSize(const FunctionCallbackInfo& info) { } +void TLSWrap::MemoryInfo(MemoryTracker* tracker) const { + tracker->TrackThis(this); + tracker->TrackField("error", error_); + tracker->TrackField("pending_cleartext_input", pending_cleartext_input_); + if (enc_in_ != nullptr) + tracker->TrackField("enc_in", crypto::NodeBIO::FromBIO(enc_in_)); + if (enc_out_ != nullptr) + tracker->TrackField("enc_out", crypto::NodeBIO::FromBIO(enc_out_)); +} + + void TLSWrap::Initialize(Local target, Local unused, Local context) { diff --git a/src/tls_wrap.h b/src/tls_wrap.h index 1603d8919a472c..5f4fd3f7073305 100644 --- a/src/tls_wrap.h +++ b/src/tls_wrap.h @@ -76,7 +76,7 @@ class TLSWrap : public AsyncWrap, void NewSessionDoneCb(); - size_t self_size() const override { return sizeof(*this); } + void MemoryInfo(MemoryTracker* tracker) const override; protected: inline StreamBase* underlying_stream() { @@ -143,8 +143,8 @@ class TLSWrap : public AsyncWrap, static int SelectSNIContextCallback(SSL* s, int* ad, void* arg); crypto::SecureContext* sc_; - BIO* enc_in_; - BIO* enc_out_; + BIO* enc_in_ = nullptr; + BIO* enc_out_ = nullptr; std::vector pending_cleartext_input_; size_t write_size_; WriteWrap* current_write_ = nullptr; diff --git a/src/tracing/traced_value.cc b/src/tracing/traced_value.cc new file mode 100644 index 00000000000000..e256df267eb5a9 --- /dev/null +++ b/src/tracing/traced_value.cc @@ -0,0 +1,224 @@ +// Copyright 2016 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "tracing/traced_value.h" + +#include +#include +#include +#include + +#if defined(NODE_HAVE_I18N_SUPPORT) +#include +#include +#endif + +#if defined(_STLP_VENDOR_CSTD) +// STLPort doesn't import fpclassify into the std namespace. +#define FPCLASSIFY_NAMESPACE +#else +#define FPCLASSIFY_NAMESPACE std +#endif + +namespace node { +namespace tracing { + +namespace { + +std::string EscapeString(const char* value) { + std::string result; + result += '"'; + char number_buffer[10]; +#if defined(NODE_HAVE_I18N_SUPPORT) + int32_t len = strlen(value); + int32_t p = 0; + int32_t i = 0; + for (; i < len; p = i) { + UChar32 c; + U8_NEXT_OR_FFFD(value, i, len, c); + switch (c) { + case '\b': result += "\\b"; break; + case '\f': result += "\\f"; break; + case '\n': result += "\\n"; break; + case '\r': result += "\\r"; break; + case '\t': result += "\\t"; break; + case '\\': result += "\\\\"; break; + case '"': result += "\\\""; break; + default: + if (c < 32 || c > 126) { + snprintf( + number_buffer, arraysize(number_buffer), "\\u%04X", + static_cast(static_cast(c))); + result += number_buffer; + } else { + result.append(value + p, i - p); + } + } + } +#else + // If we do not have ICU, use a modified version of the non-UTF8 aware + // code from V8's own TracedValue implementation. Note, however, This + // will not produce correctly serialized results for UTF8 values. + while (*value) { + char c = *value++; + switch (c) { + case '\b': result += "\\b"; break; + case '\f': result += "\\f"; break; + case '\n': result += "\\n"; break; + case '\r': result += "\\r"; break; + case '\t': result += "\\t"; break; + case '\\': result += "\\\\"; break; + case '"': result += "\\\""; break; + default: + if (c < '\x20') { + snprintf( + number_buffer, arraysize(number_buffer), "\\u%04X", + static_cast(static_cast(c))); + result += number_buffer; + } else { + result += c; + } + } + } +#endif // defined(NODE_HAVE_I18N_SUPPORT) + result += '"'; + return result; +} + +std::string DoubleToCString(double v) { + switch (FPCLASSIFY_NAMESPACE::fpclassify(v)) { + case FP_NAN: return "\"NaN\""; + case FP_INFINITE: return (v < 0.0 ? "\"-Infinity\"" : "\"Infinity\""); + case FP_ZERO: return "0"; + default: + // This is a far less sophisticated version than the one used inside v8. + std::ostringstream stream; + stream.imbue(std::locale("C")); // Ignore locale + stream << v; + return stream.str(); + } +} + +} // namespace + +std::unique_ptr TracedValue::Create() { + return std::unique_ptr(new TracedValue(false)); +} + +std::unique_ptr TracedValue::CreateArray() { + return std::unique_ptr(new TracedValue(true)); +} + +TracedValue::TracedValue(bool root_is_array) : + first_item_(true), root_is_array_(root_is_array) {} + +TracedValue::~TracedValue() {} + +void TracedValue::SetInteger(const char* name, int value) { + WriteName(name); + data_ += std::to_string(value); +} + +void TracedValue::SetDouble(const char* name, double value) { + WriteName(name); + data_ += DoubleToCString(value); +} + +void TracedValue::SetBoolean(const char* name, bool value) { + WriteName(name); + data_ += value ? "true" : "false"; +} + +void TracedValue::SetNull(const char* name) { + WriteName(name); + data_ += "null"; +} + +void TracedValue::SetString(const char* name, const char* value) { + WriteName(name); + data_ += EscapeString(value); +} + +void TracedValue::BeginDictionary(const char* name) { + WriteName(name); + data_ += '{'; + first_item_ = true; +} + +void TracedValue::BeginArray(const char* name) { + WriteName(name); + data_ += '['; + first_item_ = true; +} + +void TracedValue::AppendInteger(int value) { + WriteComma(); + data_ += std::to_string(value); +} + +void TracedValue::AppendDouble(double value) { + WriteComma(); + data_ += DoubleToCString(value); +} + +void TracedValue::AppendBoolean(bool value) { + WriteComma(); + data_ += value ? "true" : "false"; +} + +void TracedValue::AppendNull() { + WriteComma(); + data_ += "null"; +} + +void TracedValue::AppendString(const char* value) { + WriteComma(); + data_ += EscapeString(value); +} + +void TracedValue::BeginDictionary() { + WriteComma(); + data_ += '{'; + first_item_ = true; +} + +void TracedValue::BeginArray() { + WriteComma(); + data_ += '['; + first_item_ = true; +} + +void TracedValue::EndDictionary() { + data_ += '}'; + first_item_ = false; +} + +void TracedValue::EndArray() { + data_ += ']'; + first_item_ = false; +} + +void TracedValue::WriteComma() { + if (first_item_) { + first_item_ = false; + } else { + data_ += ','; + } +} + +void TracedValue::WriteName(const char* name) { + WriteComma(); + data_ += '"'; + data_ += name; + data_ += "\":"; +} + +void TracedValue::AppendAsTraceFormat(std::string* out) const { + *out += root_is_array_ ? '[' : '{'; + *out += data_; + *out += root_is_array_ ? ']' : '}'; +} + +} // namespace tracing +} // namespace node diff --git a/src/tracing/traced_value.h b/src/tracing/traced_value.h new file mode 100644 index 00000000000000..84e24c952528f5 --- /dev/null +++ b/src/tracing/traced_value.h @@ -0,0 +1,68 @@ +// Copyright 2016 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SRC_TRACING_TRACED_VALUE_H_ +#define SRC_TRACING_TRACED_VALUE_H_ + +#include "node_internals.h" +#include "v8.h" + +#include +#include +#include + +namespace node { +namespace tracing { + +class TracedValue : public v8::ConvertableToTraceFormat { + public: + ~TracedValue() override; + + static std::unique_ptr Create(); + static std::unique_ptr CreateArray(); + + void EndDictionary(); + void EndArray(); + + // These methods assume that |name| is a long lived "quoted" string. + void SetInteger(const char* name, int value); + void SetDouble(const char* name, double value); + void SetBoolean(const char* name, bool value); + void SetNull(const char* name); + void SetString(const char* name, const char* value); + void SetString(const char* name, const std::string& value) { + SetString(name, value.c_str()); + } + void BeginDictionary(const char* name); + void BeginArray(const char* name); + + void AppendInteger(int); + void AppendDouble(double); + void AppendBoolean(bool); + void AppendNull(); + void AppendString(const char*); + void AppendString(const std::string& value) { AppendString(value.c_str()); } + void BeginArray(); + void BeginDictionary(); + + // ConvertableToTraceFormat implementation. + void AppendAsTraceFormat(std::string* out) const override; + + private: + explicit TracedValue(bool root_is_array = false); + + void WriteComma(); + void WriteName(const char* name); + + std::string data_; + bool first_item_; + bool root_is_array_; + + DISALLOW_COPY_AND_ASSIGN(TracedValue); +}; + +} // namespace tracing +} // namespace node + +#endif // SRC_TRACING_TRACED_VALUE_H_ diff --git a/src/tty_wrap.cc b/src/tty_wrap.cc index 175b32879bbb83..39d7ca1474ff8a 100644 --- a/src/tty_wrap.cc +++ b/src/tty_wrap.cc @@ -54,19 +54,14 @@ void TTYWrap::Initialize(Local target, t->InstanceTemplate()->SetInternalFieldCount(1); AsyncWrap::AddWrapMethods(env, t); - - env->SetProtoMethod(t, "close", HandleWrap::Close); - env->SetProtoMethod(t, "unref", HandleWrap::Unref); - env->SetProtoMethod(t, "ref", HandleWrap::Ref); - env->SetProtoMethod(t, "hasRef", HandleWrap::HasRef); - + HandleWrap::AddWrapMethods(env, t); LibuvStreamWrap::AddMethods(env, t); - env->SetProtoMethod(t, "getWindowSize", TTYWrap::GetWindowSize); + env->SetProtoMethodNoSideEffect(t, "getWindowSize", TTYWrap::GetWindowSize); env->SetProtoMethod(t, "setRawMode", SetRawMode); - env->SetMethod(target, "isTTY", IsTTY); - env->SetMethod(target, "guessHandleType", GuessHandleType); + env->SetMethodNoSideEffect(target, "isTTY", IsTTY); + env->SetMethodNoSideEffect(target, "guessHandleType", GuessHandleType); target->Set(ttyString, t->GetFunction()); env->set_tty_constructor_template(t); diff --git a/src/tty_wrap.h b/src/tty_wrap.h index 91b07a570e9b50..cca5650ddb3964 100644 --- a/src/tty_wrap.h +++ b/src/tty_wrap.h @@ -38,7 +38,9 @@ class TTYWrap : public LibuvStreamWrap { uv_tty_t* UVHandle(); - size_t self_size() const override { return sizeof(*this); } + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + } private: TTYWrap(Environment* env, diff --git a/src/udp_wrap.cc b/src/udp_wrap.cc index 1d1ded449bd221..e5243319a553e7 100644 --- a/src/udp_wrap.cc +++ b/src/udp_wrap.cc @@ -55,7 +55,11 @@ class SendWrap : public ReqWrap { SendWrap(Environment* env, Local req_wrap_obj, bool have_callback); inline bool have_callback() const; size_t msg_size; - size_t self_size() const override { return sizeof(*this); } + + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + } + private: const bool have_callback_; }; @@ -115,7 +119,6 @@ void UDPWrap::Initialize(Local target, env->SetProtoMethod(t, "send", Send); env->SetProtoMethod(t, "bind6", Bind6); env->SetProtoMethod(t, "send6", Send6); - env->SetProtoMethod(t, "close", Close); env->SetProtoMethod(t, "recvStart", RecvStart); env->SetProtoMethod(t, "recvStop", RecvStop); env->SetProtoMethod(t, "getsockname", @@ -129,11 +132,8 @@ void UDPWrap::Initialize(Local target, env->SetProtoMethod(t, "setTTL", SetTTL); env->SetProtoMethod(t, "bufferSize", BufferSize); - env->SetProtoMethod(t, "ref", HandleWrap::Ref); - env->SetProtoMethod(t, "unref", HandleWrap::Unref); - env->SetProtoMethod(t, "hasRef", HandleWrap::HasRef); - AsyncWrap::AddWrapMethods(env, t); + HandleWrap::AddWrapMethods(env, t); target->Set(udpString, t->GetFunction()); env->set_udp_constructor_function(t->GetFunction()); diff --git a/src/udp_wrap.h b/src/udp_wrap.h index 01eb8b961f0cf2..3792bcc459da23 100644 --- a/src/udp_wrap.h +++ b/src/udp_wrap.h @@ -64,7 +64,9 @@ class UDPWrap: public HandleWrap { SocketType type); uv_udp_t* UVHandle(); - size_t self_size() const override { return sizeof(*this); } + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackThis(this); + } private: typedef uv_udp_t HandleType; diff --git a/test/.eslintrc.yaml b/test/.eslintrc.yaml index c57d7505c984ff..25026fec5a103a 100644 --- a/test/.eslintrc.yaml +++ b/test/.eslintrc.yaml @@ -18,6 +18,7 @@ rules: node-core/number-isnan: error ## common module is mandatory in tests node-core/required-modules: [error, common] + node-core/no-duplicate-requires: off no-restricted-syntax: # Config copied from .eslintrc.js diff --git a/test/addons-napi/test_bigint/binding.gyp b/test/addons-napi/test_bigint/binding.gyp new file mode 100644 index 00000000000000..1b9f75bab4f737 --- /dev/null +++ b/test/addons-napi/test_bigint/binding.gyp @@ -0,0 +1,8 @@ +{ + "targets": [ + { + "target_name": "test_bigint", + "sources": [ "test_bigint.c" ] + } + ] +} diff --git a/test/addons-napi/test_bigint/test.js b/test/addons-napi/test_bigint/test.js new file mode 100644 index 00000000000000..b92c810459321b --- /dev/null +++ b/test/addons-napi/test_bigint/test.js @@ -0,0 +1,45 @@ +'use strict'; +const common = require('../../common'); +const assert = require('assert'); +const { + IsLossless, + TestInt64, + TestUint64, + TestWords, + CreateTooBigBigInt, +} = require(`./build/${common.buildType}/test_bigint`); + +[ + 0n, + -0n, + 1n, + -1n, + 100n, + 2121n, + -1233n, + 986583n, + -976675n, + 98765432213456789876546896323445679887645323232436587988766545658n, + -4350987086545760976737453646576078997096876957864353245245769809n, +].forEach((num) => { + if (num > -(2n ** 63n) && num < 2n ** 63n) { + assert.strictEqual(TestInt64(num), num); + assert.strictEqual(IsLossless(num, true), true); + } else { + assert.strictEqual(IsLossless(num, true), false); + } + + if (num >= 0 && num < 2n ** 64n) { + assert.strictEqual(TestUint64(num), num); + assert.strictEqual(IsLossless(num, false), true); + } else { + assert.strictEqual(IsLossless(num, false), false); + } + + assert.strictEqual(num, TestWords(num)); +}); + +assert.throws(CreateTooBigBigInt, { + name: 'RangeError', + message: 'Maximum BigInt size exceeded', +}); diff --git a/test/addons-napi/test_bigint/test_bigint.c b/test/addons-napi/test_bigint/test_bigint.c new file mode 100644 index 00000000000000..b1e6c359db6add --- /dev/null +++ b/test/addons-napi/test_bigint/test_bigint.c @@ -0,0 +1,142 @@ +#define NAPI_EXPERIMENTAL + +#include +#include +#include +#include "../common.h" + +static napi_value IsLossless(napi_env env, napi_callback_info info) { + size_t argc = 2; + napi_value args[2]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + bool is_signed; + NAPI_CALL(env, napi_get_value_bool(env, args[1], &is_signed)); + + bool lossless; + + if (is_signed) { + int64_t input; + NAPI_CALL(env, napi_get_value_bigint_int64(env, args[0], &input, &lossless)); + } else { + uint64_t input; + NAPI_CALL(env, napi_get_value_bigint_uint64(env, args[0], &input, &lossless)); + } + + napi_value output; + NAPI_CALL(env, napi_get_boolean(env, lossless, &output)); + + return output; +} + +static napi_value TestInt64(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments"); + + napi_valuetype valuetype0; + NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0)); + + NAPI_ASSERT(env, valuetype0 == napi_bigint, + "Wrong type of arguments. Expects a bigint as first argument."); + + int64_t input; + bool lossless; + NAPI_CALL(env, napi_get_value_bigint_int64(env, args[0], &input, &lossless)); + + napi_value output; + NAPI_CALL(env, napi_create_bigint_int64(env, input, &output)); + + return output; +} + +static napi_value TestUint64(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments"); + + napi_valuetype valuetype0; + NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0)); + + NAPI_ASSERT(env, valuetype0 == napi_bigint, + "Wrong type of arguments. Expects a bigint as first argument."); + + uint64_t input; + bool lossless; + NAPI_CALL(env, napi_get_value_bigint_uint64( + env, args[0], &input, &lossless)); + + napi_value output; + NAPI_CALL(env, napi_create_bigint_uint64(env, input, &output)); + + return output; +} + +static napi_value TestWords(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments"); + + napi_valuetype valuetype0; + NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0)); + + NAPI_ASSERT(env, valuetype0 == napi_bigint, + "Wrong type of arguments. Expects a bigint as first argument."); + + size_t expected_word_count; + NAPI_CALL(env, napi_get_value_bigint_words( + env, args[0], NULL, &expected_word_count, NULL)); + + int sign_bit; + size_t word_count = 10; + uint64_t words[10]; + + NAPI_CALL(env, napi_get_value_bigint_words( + env, args[0], &sign_bit, &word_count, words)); + + NAPI_ASSERT(env, word_count == expected_word_count, + "word counts do not match"); + + napi_value output; + NAPI_CALL(env, napi_create_bigint_words( + env, sign_bit, word_count, words, &output)); + + return output; +} + +// throws RangeError +static napi_value CreateTooBigBigInt(napi_env env, napi_callback_info info) { + int sign_bit = 0; + size_t word_count = SIZE_MAX; + uint64_t words[10]; + + napi_value output; + + NAPI_CALL(env, napi_create_bigint_words( + env, sign_bit, word_count, words, &output)); + + return output; +} + +static napi_value Init(napi_env env, napi_value exports) { + napi_property_descriptor descriptors[] = { + DECLARE_NAPI_PROPERTY("IsLossless", IsLossless), + DECLARE_NAPI_PROPERTY("TestInt64", TestInt64), + DECLARE_NAPI_PROPERTY("TestUint64", TestUint64), + DECLARE_NAPI_PROPERTY("TestWords", TestWords), + DECLARE_NAPI_PROPERTY("CreateTooBigBigInt", CreateTooBigBigInt), + }; + + NAPI_CALL(env, napi_define_properties( + env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors)); + + return exports; +} + +NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) diff --git a/test/addons-napi/test_function/test.js b/test/addons-napi/test_function/test.js index 752e9965b23039..6f0f1681752a2c 100644 --- a/test/addons-napi/test_function/test.js +++ b/test/addons-napi/test_function/test.js @@ -1,11 +1,12 @@ 'use strict'; +// Flags: --expose-gc + const common = require('../../common'); const assert = require('assert'); // testing api calls for function const test_function = require(`./build/${common.buildType}/test_function`); - function func1() { return 1; } @@ -29,3 +30,8 @@ assert.strictEqual(test_function.TestCall(func4, 1), 2); assert.strictEqual(test_function.TestName.name, 'Name'); assert.strictEqual(test_function.TestNameShort.name, 'Name_'); + +let tracked_function = test_function.MakeTrackedFunction(common.mustCall()); +assert(!!tracked_function); +tracked_function = null; +global.gc(); diff --git a/test/addons-napi/test_function/test_function.c b/test/addons-napi/test_function/test_function.c index 2c361933cfa071..068999a6e5bc5c 100644 --- a/test/addons-napi/test_function/test_function.c +++ b/test/addons-napi/test_function/test_function.c @@ -30,6 +30,78 @@ static napi_value TestFunctionName(napi_env env, napi_callback_info info) { return NULL; } +static void finalize_function(napi_env env, void* data, void* hint) { + napi_ref ref = data; + + // Retrieve the JavaScript undefined value. + napi_value undefined; + NAPI_CALL_RETURN_VOID(env, napi_get_undefined(env, &undefined)); + + // Retrieve the JavaScript function we must call. + napi_value js_function; + NAPI_CALL_RETURN_VOID(env, napi_get_reference_value(env, ref, &js_function)); + + // Call the JavaScript function to indicate that the generated JavaScript + // function is about to be gc-ed. + NAPI_CALL_RETURN_VOID(env, napi_call_function(env, + undefined, + js_function, + 0, + NULL, + NULL)); + + // Destroy the persistent reference to the function we just called so as to + // properly clean up. + NAPI_CALL_RETURN_VOID(env, napi_delete_reference(env, ref)); +} + +static napi_value MakeTrackedFunction(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value js_finalize_cb; + napi_valuetype arg_type; + + // Retrieve and validate from the arguments the function we will use to + // indicate to JavaScript that the function we are about to create is about to + // be gc-ed. + NAPI_CALL(env, napi_get_cb_info(env, + info, + &argc, + &js_finalize_cb, + NULL, + NULL)); + NAPI_ASSERT(env, argc == 1, "Wrong number of arguments"); + NAPI_CALL(env, napi_typeof(env, js_finalize_cb, &arg_type)); + NAPI_ASSERT(env, arg_type == napi_function, "Argument must be a function"); + + // Dynamically create a function. + napi_value result; + NAPI_CALL(env, napi_create_function(env, + "TrackedFunction", + NAPI_AUTO_LENGTH, + TestFunctionName, + NULL, + &result)); + + // Create a strong reference to the function we will call when the tracked + // function is about to be gc-ed. + napi_ref js_finalize_cb_ref; + NAPI_CALL(env, napi_create_reference(env, + js_finalize_cb, + 1, + &js_finalize_cb_ref)); + + // Attach a finalizer to the dynamically created function and pass it the + // strong reference we created in the previous step. + NAPI_CALL(env, napi_wrap(env, + result, + js_finalize_cb_ref, + finalize_function, + NULL, + NULL)); + + return result; +} + static napi_value Init(napi_env env, napi_value exports) { napi_value fn1; NAPI_CALL(env, napi_create_function( @@ -43,9 +115,21 @@ static napi_value Init(napi_env env, napi_value exports) { NAPI_CALL(env, napi_create_function( env, "Name_extra", 5, TestFunctionName, NULL, &fn3)); + napi_value fn4; + NAPI_CALL(env, napi_create_function(env, + "MakeTrackedFunction", + NAPI_AUTO_LENGTH, + MakeTrackedFunction, + NULL, + &fn4)); + NAPI_CALL(env, napi_set_named_property(env, exports, "TestCall", fn1)); NAPI_CALL(env, napi_set_named_property(env, exports, "TestName", fn2)); NAPI_CALL(env, napi_set_named_property(env, exports, "TestNameShort", fn3)); + NAPI_CALL(env, napi_set_named_property(env, + exports, + "MakeTrackedFunction", + fn4)); return exports; } diff --git a/test/addons-napi/test_number/test.js b/test/addons-napi/test_number/test.js index 6c04a222cb4eb6..34f48aee578ac6 100644 --- a/test/addons-napi/test_number/test.js +++ b/test/addons-napi/test_number/test.js @@ -35,6 +35,20 @@ testNumber(Number.POSITIVE_INFINITY); testNumber(Number.NEGATIVE_INFINITY); testNumber(Number.NaN); +function testUint32(input, expected = input) { + assert.strictEqual(expected, test_number.TestUint32Truncation(input)); +} + +// Test zero +testUint32(0.0, 0); +testUint32(-0.0, 0); + +// Test overflow scenarios +testUint32(4294967295); +testUint32(4294967296, 0); +testUint32(4294967297, 1); +testUint32(17 * 4294967296 + 1, 1); + // validate documented behavior when value is retrieved as 32-bit integer with // `napi_get_value_int32` function testInt32(input, expected = input) { diff --git a/test/addons-napi/test_number/test_number.c b/test/addons-napi/test_number/test_number.c index 19b0ae83f051df..63290bf6d64d7e 100644 --- a/test/addons-napi/test_number/test_number.c +++ b/test/addons-napi/test_number/test_number.c @@ -23,6 +23,28 @@ static napi_value Test(napi_env env, napi_callback_info info) { return output; } +static napi_value TestUint32Truncation(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments"); + + napi_valuetype valuetype0; + NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0)); + + NAPI_ASSERT(env, valuetype0 == napi_number, + "Wrong type of arguments. Expects a number as first argument."); + + uint32_t input; + NAPI_CALL(env, napi_get_value_uint32(env, args[0], &input)); + + napi_value output; + NAPI_CALL(env, napi_create_uint32(env, input, &output)); + + return output; +} + static napi_value TestInt32Truncation(napi_env env, napi_callback_info info) { size_t argc = 1; napi_value args[1]; @@ -71,6 +93,7 @@ static napi_value Init(napi_env env, napi_value exports) { napi_property_descriptor descriptors[] = { DECLARE_NAPI_PROPERTY("Test", Test), DECLARE_NAPI_PROPERTY("TestInt32Truncation", TestInt32Truncation), + DECLARE_NAPI_PROPERTY("TestUint32Truncation", TestUint32Truncation), DECLARE_NAPI_PROPERTY("TestInt64Truncation", TestInt64Truncation), }; diff --git a/test/addons-napi/test_typedarray/test.js b/test/addons-napi/test_typedarray/test.js index 41915b380be8e8..2a8cf18feb866c 100644 --- a/test/addons-napi/test_typedarray/test.js +++ b/test/addons-napi/test_typedarray/test.js @@ -42,7 +42,7 @@ assert.strictEqual(externalResult[2], 2); const buffer = new ArrayBuffer(128); const arrayTypes = [ Int8Array, Uint8Array, Uint8ClampedArray, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, - Float64Array ]; + Float64Array, BigInt64Array, BigUint64Array ]; arrayTypes.forEach((currentType) => { const template = Reflect.construct(currentType, buffer); @@ -64,7 +64,8 @@ arrayTypes.forEach((currentType) => { }); const nonByteArrayTypes = [ Int16Array, Uint16Array, Int32Array, Uint32Array, - Float32Array, Float64Array ]; + Float32Array, Float64Array, + BigInt64Array, BigUint64Array ]; nonByteArrayTypes.forEach((currentType) => { const template = Reflect.construct(currentType, buffer); assert.throws(() => { diff --git a/test/addons/hello-world/binding.cc b/test/addons/hello-world/binding.cc index e267a3b2a7629a..341b58f9a640d8 100644 --- a/test/addons/hello-world/binding.cc +++ b/test/addons/hello-world/binding.cc @@ -6,13 +6,12 @@ void Method(const v8::FunctionCallbackInfo& args) { args.GetReturnValue().Set(v8::String::NewFromUtf8(isolate, "world")); } -#define CONCAT(a, b) CONCAT_HELPER(a, b) -#define CONCAT_HELPER(a, b) a##b -#define INITIALIZER CONCAT(node_register_module_v, NODE_MODULE_VERSION) - -extern "C" NODE_MODULE_EXPORT void INITIALIZER(v8::Local exports, - v8::Local module, - v8::Local context) { +// Not using the full NODE_MODULE_INIT() macro here because we want to test the +// addon loader's reaction to the FakeInit() entry point below. +extern "C" NODE_MODULE_EXPORT void +NODE_MODULE_INITIALIZER(v8::Local exports, + v8::Local module, + v8::Local context) { NODE_SET_METHOD(exports, "hello", Method); } diff --git a/test/cctest/test_inspector_socket_server.cc b/test/cctest/test_inspector_socket_server.cc index 60b7eefc5e997f..349356ef56c9fc 100644 --- a/test/cctest/test_inspector_socket_server.cc +++ b/test/cctest/test_inspector_socket_server.cc @@ -14,7 +14,6 @@ static const char CLIENT_CLOSE_FRAME[] = "\x88\x80\x2D\x0E\x1E\xFA"; static const char SERVER_CLOSE_FRAME[] = "\x88\x00"; static const char MAIN_TARGET_ID[] = "main-target"; -static const char UNCONNECTABLE_TARGET_ID[] = "unconnectable-target"; static const char WS_HANDSHAKE_RESPONSE[] = "HTTP/1.1 101 Switching Protocols\r\n" @@ -258,10 +257,6 @@ class ServerHolder { return server_->done(); } - void Connected() { - connected++; - } - void Disconnected() { disconnected++; } @@ -270,9 +265,10 @@ class ServerHolder { delegate_done = true; } - void PrepareSession(int id) { + void Connected(int id) { buffer_.clear(); session_id_ = id; + connected++; } void Received(const std::string& message) { @@ -319,15 +315,9 @@ class TestSocketServerDelegate : public SocketServerDelegate { void StartSession(int session_id, const std::string& target_id) override { session_id_ = session_id; - harness_->PrepareSession(session_id_); CHECK_NE(targets_.end(), std::find(targets_.begin(), targets_.end(), target_id)); - if (target_id == UNCONNECTABLE_TARGET_ID) { - server_->DeclineSession(session_id); - return; - } - harness_->Connected(); - server_->AcceptSession(session_id); + harness_->Connected(session_id_); } void MessageReceived(int session_id, const std::string& message) override { @@ -363,7 +353,7 @@ ServerHolder::ServerHolder(bool has_targets, uv_loop_t* loop, const std::string host, int port, FILE* out) { std::vector targets; if (has_targets) - targets = { MAIN_TARGET_ID, UNCONNECTABLE_TARGET_ID }; + targets = { MAIN_TARGET_ID }; std::unique_ptr delegate( new TestSocketServerDelegate(this, targets)); server_.reset( @@ -414,15 +404,6 @@ TEST_F(InspectorSocketServerTest, InspectorSessions) { well_behaved_socket.Close(); - // Declined connection - SocketWrapper declined_target_socket(&loop); - declined_target_socket.Connect(HOST, server.port()); - declined_target_socket.Write(WsHandshakeRequest(UNCONNECTABLE_TARGET_ID)); - declined_target_socket.Expect("HTTP/1.0 400 Bad Request"); - declined_target_socket.ExpectEOF(); - EXPECT_EQ(1, server.connected); - EXPECT_EQ(1, server.disconnected); - // Bogus target - start session callback should not even be invoked SocketWrapper bogus_target_socket(&loop); bogus_target_socket.Connect(HOST, server.port()); @@ -491,7 +472,7 @@ TEST_F(InspectorSocketServerTest, ServerWithoutTargets) { // Declined connection SocketWrapper socket(&loop); socket.Connect(HOST, server.port()); - socket.Write(WsHandshakeRequest(UNCONNECTABLE_TARGET_ID)); + socket.Write(WsHandshakeRequest("any target id")); socket.Expect("HTTP/1.0 400 Bad Request"); socket.ExpectEOF(); server->Stop(); diff --git a/test/cctest/test_node_postmortem_metadata.cc b/test/cctest/test_node_postmortem_metadata.cc index f69df3ed227717..6f5db0fdf9272b 100644 --- a/test/cctest/test_node_postmortem_metadata.cc +++ b/test/cctest/test_node_postmortem_metadata.cc @@ -34,7 +34,9 @@ class DebugSymbolsTest : public EnvironmentTestFixture {}; class TestHandleWrap : public node::HandleWrap { public: - size_t self_size() const override { return sizeof(*this); } + void MemoryInfo(node::MemoryTracker* tracker) const override { + tracker->TrackThis(this); + } TestHandleWrap(node::Environment* env, v8::Local object, @@ -48,7 +50,9 @@ class TestHandleWrap : public node::HandleWrap { class TestReqWrap : public node::ReqWrap { public: - size_t self_size() const override { return sizeof(*this); } + void MemoryInfo(node::MemoryTracker* tracker) const override { + tracker->TrackThis(this); + } TestReqWrap(node::Environment* env, v8::Local object) : node::ReqWrap(env, @@ -67,6 +71,16 @@ TEST_F(DebugSymbolsTest, ExternalStringDataOffset) { NODE_OFF_EXTSTR_DATA); } +class DummyBaseObject : public node::BaseObject { + public: + DummyBaseObject(node::Environment* env, v8::Local obj) : + BaseObject(env, obj) {} + + void MemoryInfo(node::MemoryTracker* tracker) const override { + tracker->TrackThis(this); + } +}; + TEST_F(DebugSymbolsTest, BaseObjectPersistentHandle) { const v8::HandleScope handle_scope(isolate_); const Argv argv; @@ -77,7 +91,7 @@ TEST_F(DebugSymbolsTest, BaseObjectPersistentHandle) { v8::Local object = obj_templ->NewInstance(env.context()).ToLocalChecked(); - node::BaseObject obj(*env, object); + DummyBaseObject obj(*env, object); auto expected = reinterpret_cast(&obj.persistent()); auto calculated = reinterpret_cast(&obj) + diff --git a/test/cctest/test_traced_value.cc b/test/cctest/test_traced_value.cc new file mode 100644 index 00000000000000..5329c78446ca6f --- /dev/null +++ b/test/cctest/test_traced_value.cc @@ -0,0 +1,96 @@ +#include "tracing/traced_value.h" + +#include +#include +#include + +#include "gtest/gtest.h" + +using node::tracing::TracedValue; + +TEST(TracedValue, Object) { + auto traced_value = TracedValue::Create(); + traced_value->SetString("a", "b"); + traced_value->SetInteger("b", 1); + traced_value->SetDouble("c", 1.234); + traced_value->SetDouble("d", NAN); + traced_value->SetDouble("e", INFINITY); + traced_value->SetDouble("f", -INFINITY); + traced_value->SetDouble("g", 1.23e7); + traced_value->SetBoolean("h", false); + traced_value->SetBoolean("i", true); + traced_value->SetNull("j"); + traced_value->BeginDictionary("k"); + traced_value->SetString("l", "m"); + traced_value->EndDictionary(); + + std::string string; + traced_value->AppendAsTraceFormat(&string); + + static const char* check = "{\"a\":\"b\",\"b\":1,\"c\":1.234,\"d\":\"NaN\"," + "\"e\":\"Infinity\",\"f\":\"-Infinity\",\"g\":" + "1.23e+07,\"h\":false,\"i\":true,\"j\":null,\"k\":" + "{\"l\":\"m\"}}"; + + EXPECT_EQ(check, string); +} + +TEST(TracedValue, Array) { + auto traced_value = TracedValue::CreateArray(); + traced_value->AppendString("a"); + traced_value->AppendInteger(1); + traced_value->AppendDouble(1.234); + traced_value->AppendDouble(NAN); + traced_value->AppendDouble(INFINITY); + traced_value->AppendDouble(-INFINITY); + traced_value->AppendDouble(1.23e7); + traced_value->AppendBoolean(false); + traced_value->AppendBoolean(true); + traced_value->AppendNull(); + traced_value->BeginDictionary(); + traced_value->BeginArray("foo"); + traced_value->EndArray(); + traced_value->EndDictionary(); + + std::string string; + traced_value->AppendAsTraceFormat(&string); + + static const char* check = "[\"a\",1,1.234,\"NaN\",\"Infinity\"," + "\"-Infinity\",1.23e+07,false,true,null," + "{\"foo\":[]}]"; + + EXPECT_EQ(check, string); +} + +#define UTF8_SEQUENCE "1" "\xE2\x82\xAC" "23\"\x01\b\f\n\r\t\\" +#if defined(NODE_HAVE_I18N_SUPPORT) +# define UTF8_RESULT \ + "\"1\\u20AC23\\\"\\u0001\\b\\f\\n\\r\\t\\\\\"" +#else +# define UTF8_RESULT \ + "\"1\\u00E2\\u0082\\u00AC23\\\"\\u0001\\b\\f\\n\\r\\t\\\\\"" +#endif + +TEST(TracedValue, EscapingObject) { + auto traced_value = TracedValue::Create(); + traced_value->SetString("a", UTF8_SEQUENCE); + + std::string string; + traced_value->AppendAsTraceFormat(&string); + + static const char* check = "{\"a\":" UTF8_RESULT "}"; + + EXPECT_EQ(check, string); +} + +TEST(TracedValue, EscapingArray) { + auto traced_value = TracedValue::CreateArray(); + traced_value->AppendString(UTF8_SEQUENCE); + + std::string string; + traced_value->AppendAsTraceFormat(&string); + + static const char* check = "[" UTF8_RESULT "]"; + + EXPECT_EQ(check, string); +} diff --git a/test/common/README.md b/test/common/README.md index 111ce45b00360c..41cc88aca10f59 100644 --- a/test/common/README.md +++ b/test/common/README.md @@ -10,6 +10,7 @@ This directory contains modules used to test the Node.js implementation. * [DNS module](#dns-module) * [Duplex pair helper](#duplex-pair-helper) * [Fixtures module](#fixtures-module) +* [Heap dump checker module](#heap-dump-checker-module) * [HTTP2 module](#http2-module) * [Internet module](#internet-module) * [tmpdir module](#tmpdir-module) @@ -321,6 +322,21 @@ otherwise. ### noWarnCode See `common.expectWarning()` for usage. +### onGC(target, listener) +* `target` [<Object>] +* `listener` [<Object>] + * `ongc` [<Function>] + +Installs a GC listener for the collection of `target`. + +This uses `async_hooks` for GC tracking. This means that it enables +`async_hooks` tracking, which may affect the test functionality. It also +means that between a `global.gc()` call and the listener being invoked +a full `setImmediate()` invocation passes. + +`listener` is an object to make it easier to use a closure; the target object +should not be in scope when `listener.ongc()` is created. + ### opensslCli * [<boolean>] @@ -538,6 +554,42 @@ Returns the result of Returns the result of `fs.readFileSync(path.join(fixtures.fixturesDir, 'keys', arg), 'enc')`. +## Heap dump checker module + +This provides utilities for checking the validity of heap dumps. +This requires the usage of `--expose-internals`. + +### heap.recordState() + +Create a heap dump and an embedder graph copy for inspection. +The returned object has a `validateSnapshotNodes` function similar to the +one listed below. (`heap.validateSnapshotNodes(...)` is a shortcut for +`heap.recordState().validateSnapshotNodes(...)`.) + +### heap.validateSnapshotNodes(name, expected, options) + +* `name` [<string>] Look for this string as the name of heap dump nodes. +* `expected` [<Array>] A list of objects, possibly with an `children` + property that points to expected other adjacent nodes. +* `options` [<Array>] + * `loose` [<boolean>] Do not expect an exact listing of occurrences + of nodes with name `name` in `expected`. + +Create a heap dump and an embedder graph copy and validate occurrences. + + +```js +validateSnapshotNodes('TLSWRAP', [ + { + children: [ + { name: 'enc_out' }, + { name: 'enc_in' }, + { name: 'TLSWrap' } + ] + } +]); +``` + ## HTTP/2 Module The http2.js module provides a handful of utilities for creating mock HTTP/2 diff --git a/test/common/heap.js b/test/common/heap.js new file mode 100644 index 00000000000000..a02de9a60651f4 --- /dev/null +++ b/test/common/heap.js @@ -0,0 +1,80 @@ +/* eslint-disable node-core/required-modules */ +'use strict'; +const assert = require('assert'); +const util = require('util'); + +let internalTestHeap; +try { + internalTestHeap = require('internal/test/heap'); +} catch (e) { + console.log('using `test/common/heap.js` requires `--expose-internals`'); + throw e; +} +const { createJSHeapDump, buildEmbedderGraph } = internalTestHeap; + +class State { + constructor() { + this.snapshot = createJSHeapDump(); + this.embedderGraph = buildEmbedderGraph(); + } + + validateSnapshotNodes(name, expected, { loose = false } = {}) { + const snapshot = this.snapshot.filter( + (node) => node.name === 'Node / ' + name && node.type !== 'string'); + if (loose) + assert(snapshot.length >= expected.length); + else + assert.strictEqual(snapshot.length, expected.length); + for (const expectedNode of expected) { + if (expectedNode.children) { + for (const expectedChild of expectedNode.children) { + const check = typeof expectedChild === 'function' ? + expectedChild : + (node) => [expectedChild.name, 'Node / ' + expectedChild.name] + .includes(node.name); + + assert(snapshot.some((node) => { + return node.outgoingEdges.map((edge) => edge.toNode).some(check); + }), `expected to find child ${util.inspect(expectedChild)} ` + + `in ${util.inspect(snapshot)}`); + } + } + } + + const graph = this.embedderGraph.filter((node) => node.name === name); + if (loose) + assert(graph.length >= expected.length); + else + assert.strictEqual(graph.length, expected.length); + for (const expectedNode of expected) { + if (expectedNode.edges) { + for (const expectedChild of expectedNode.children) { + const check = typeof expectedChild === 'function' ? + expectedChild : (node) => { + return node.name === expectedChild.name || + (node.value && + node.value.constructor && + node.value.constructor.name === expectedChild.name); + }; + + assert(graph.some((node) => node.edges.some(check)), + `expected to find child ${util.inspect(expectedChild)} ` + + `in ${util.inspect(snapshot)}`); + } + } + } + } +} + +function recordState() { + return new State(); +} + +function validateSnapshotNodes(...args) { + return recordState().validateSnapshotNodes(...args); +} + +module.exports = { + recordState, + validateSnapshotNodes +}; diff --git a/test/common/index.js b/test/common/index.js index bf6b1077d1859b..cca289337ef4a2 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -883,3 +883,30 @@ exports.isCPPSymbolsNotMapped = exports.isWindows || exports.isAIX || exports.isLinuxPPCBE || exports.isFreeBSD; + +const gcTrackerMap = new WeakMap(); +const gcTrackerTag = 'NODE_TEST_COMMON_GC_TRACKER'; + +exports.onGC = function(obj, gcListener) { + const async_hooks = require('async_hooks'); + + const onGcAsyncHook = async_hooks.createHook({ + init: exports.mustCallAtLeast(function(id, type, trigger, resource) { + if (this.trackedId === undefined) { + assert.strictEqual(type, gcTrackerTag); + this.trackedId = id; + } + }), + destroy(id) { + assert.notStrictEqual(this.trackedId, -1); + if (id === this.trackedId) { + this.gcListener.ongc(); + onGcAsyncHook.disable(); + } + } + }).enable(); + onGcAsyncHook.gcListener = gcListener; + + gcTrackerMap.set(obj, new async_hooks.AsyncResource(gcTrackerTag)); + obj = null; +}; diff --git a/test/common/inspector-helper.js b/test/common/inspector-helper.js index e590349f9c2b71..13726049798f0e 100644 --- a/test/common/inspector-helper.js +++ b/test/common/inspector-helper.js @@ -7,6 +7,7 @@ const fixtures = require('../common/fixtures'); const { spawn } = require('child_process'); const { parse: parseURL } = require('url'); const { getURLFromFilePath } = require('internal/url'); +const { EventEmitter } = require('events'); const _MAINSCRIPT = fixtures.path('loop.js'); const DEBUG = false; @@ -311,10 +312,12 @@ class InspectorSession { } } -class NodeInstance { +class NodeInstance extends EventEmitter { constructor(inspectorFlags = ['--inspect-brk=0'], scriptContents = '', scriptFile = _MAINSCRIPT) { + super(); + this._scriptPath = scriptFile; this._script = scriptFile ? null : scriptContents; this._portCallback = null; @@ -326,7 +329,10 @@ class NodeInstance { this._unprocessedStderrLines = []; this._process.stdout.on('data', makeBufferingDataCallback( - (line) => console.log('[out]', line))); + (line) => { + this.emit('stdout', line); + console.log('[out]', line); + })); this._process.stderr.on('data', makeBufferingDataCallback( (message) => this.onStderrLine(message))); diff --git a/test/doctool/test-doctool-html.js b/test/doctool/test-doctool-html.js index 0ca46c0adedfc1..a6119c16903a74 100644 --- a/test/doctool/test-doctool-html.js +++ b/test/doctool/test-doctool-html.js @@ -11,7 +11,6 @@ try { const assert = require('assert'); const { readFile } = require('fs'); const fixtures = require('../common/fixtures'); -const processIncludes = require('../../tools/doc/preprocess.js'); const toHTML = require('../../tools/doc/html.js'); // Test data is a list of objects with two properties. @@ -64,15 +63,6 @@ const testData = [ ' ' + '

Describe Something in more detail here.

' }, - { - file: fixtures.path('doc_with_includes.md'), - html: '' + - '

Look here!

' + - '' + - '

foobar#

' + - '

I exist and am being linked to.

' - }, { file: fixtures.path('sample_document.md'), html: '
  1. fish
  2. fish

  3. Redfish

  4. ' + @@ -90,36 +80,34 @@ testData.forEach(({ file, html, analyticsId }) => { readFile(file, 'utf8', common.mustCall((err, input) => { assert.ifError(err); - processIncludes(file, input, common.mustCall((err, preprocessed) => { - assert.ifError(err); - toHTML( - { - input: preprocessed, - filename: 'foo', - nodeVersion: process.version, - analytics: analyticsId, - }, - common.mustCall((err, output) => { - assert.ifError(err); + toHTML( + { + input: input, + filename: 'foo', + nodeVersion: process.version, + analytics: analyticsId, + }, + common.mustCall((err, output) => { + assert.ifError(err); - const actual = output.replace(spaces, ''); - // Assert that the input stripped of all whitespace contains the - // expected markup. - assert(actual.includes(expected)); + const actual = output.replace(spaces, ''); + // Assert that the input stripped of all whitespace contains the + // expected markup. + assert(actual.includes(expected)); - // Testing the insertion of Google Analytics script when - // an analytics id is provided. Should not be present by default. - const scriptDomain = 'google-analytics.com'; - if (includeAnalytics) { - assert(actual.includes(scriptDomain), - `Google Analytics script was not present in "${actual}"`); - } else { - assert.strictEqual(actual.includes(scriptDomain), false, - 'Google Analytics script was present in ' + - `"${actual}"`); - } - })); - })); + // Testing the insertion of Google Analytics script when + // an analytics id is provided. Should not be present by default. + const scriptDomain = 'google-analytics.com'; + if (includeAnalytics) { + assert(actual.includes(scriptDomain), + `Google Analytics script was not present in "${actual}"`); + } else { + assert.strictEqual(actual.includes(scriptDomain), false, + 'Google Analytics script was present in ' + + `"${actual}"`); + } + }) + ); })); }); diff --git a/test/doctool/test-make-doc.js b/test/doctool/test-make-doc.js index e019288f75cc1e..a11da0e97d8355 100644 --- a/test/doctool/test-make-doc.js +++ b/test/doctool/test-make-doc.js @@ -13,17 +13,16 @@ const path = require('path'); const apiPath = path.resolve(__dirname, '..', '..', 'out', 'doc', 'api'); const allDocs = fs.readdirSync(apiPath); -assert.ok(allDocs.includes('_toc.html')); +assert.ok(allDocs.includes('index.html')); const actualDocs = allDocs.filter( (name) => { const extension = path.extname(name); - return (extension === '.html' || extension === '.json') && - name !== '_toc.html'; + return extension === '.html' || extension === '.json'; } ); -const toc = fs.readFileSync(path.resolve(apiPath, '_toc.html'), 'utf8'); +const toc = fs.readFileSync(path.resolve(apiPath, 'index.html'), 'utf8'); const re = /href="([^/]+\.html)"/; const globalRe = new RegExp(re, 'g'); const links = toc.match(globalRe); @@ -32,8 +31,7 @@ assert.notStrictEqual(links, null); // Filter out duplicate links, leave just filenames, add expected JSON files. const linkedHtmls = [...new Set(links)].map((link) => link.match(re)[1]); const expectedJsons = linkedHtmls - .map((name) => name.replace('.html', '.json')) - .concat('_toc.json'); + .map((name) => name.replace('.html', '.json')); const expectedDocs = linkedHtmls.concat(expectedJsons); // Test that all the relative links in the TOC match to the actual documents. diff --git a/test/es-module/test-esm-loader-missing-dynamic-instantiate-hook.mjs b/test/es-module/test-esm-loader-missing-dynamic-instantiate-hook.mjs new file mode 100644 index 00000000000000..27447b3e436afe --- /dev/null +++ b/test/es-module/test-esm-loader-missing-dynamic-instantiate-hook.mjs @@ -0,0 +1,14 @@ +// Flags: --experimental-modules --loader ./test/fixtures/es-module-loaders/missing-dynamic-instantiate-hook.mjs + +import { + crashOnUnhandledRejection, + expectsError +} from '../common'; + +crashOnUnhandledRejection(); + +import('test').catch(expectsError({ + code: 'ERR_MISSING_DYNAMIC_INSTANTIATE_HOOK', + message: 'The ES Module loader may not return a format of \'dynamic\' ' + + 'when no dynamicInstantiate function was provided' +})); diff --git a/test/fixtures/assert-first-line.js b/test/fixtures/assert-first-line.js new file mode 100644 index 00000000000000..8a65113559c981 --- /dev/null +++ b/test/fixtures/assert-first-line.js @@ -0,0 +1,2 @@ +'use strict'; const ässört = require('assert'); ässört(true); ässört.ok(''); ässört(null); +// aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(false); diff --git a/test/fixtures/assert-long-line.js b/test/fixtures/assert-long-line.js new file mode 100644 index 00000000000000..cab3507ac8d779 --- /dev/null +++ b/test/fixtures/assert-long-line.js @@ -0,0 +1 @@ +'use strict'; /* aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa */ const assert = require('assert'); assert(true); assert.ok(''); assert(null); diff --git a/test/fixtures/doc_with_includes.md b/test/fixtures/doc_with_includes.md deleted file mode 100644 index 901bf0f1b0bf3b..00000000000000 --- a/test/fixtures/doc_with_includes.md +++ /dev/null @@ -1,2 +0,0 @@ -@include doc_inc_1 -@include doc_inc_2.md diff --git a/test/fixtures/es-module-loaders/missing-dynamic-instantiate-hook.mjs b/test/fixtures/es-module-loaders/missing-dynamic-instantiate-hook.mjs new file mode 100644 index 00000000000000..6993747fcc0142 --- /dev/null +++ b/test/fixtures/es-module-loaders/missing-dynamic-instantiate-hook.mjs @@ -0,0 +1,6 @@ +export function resolve(specifier, parentModule, defaultResolver) { + if (specifier !== 'test') { + return defaultResolver(specifier, parentModule); + } + return { url: 'file://', format: 'dynamic' }; +} diff --git a/test/fixtures/process-exit-code-cases.js b/test/fixtures/process-exit-code-cases.js new file mode 100644 index 00000000000000..c8c4a2ebe6c7ff --- /dev/null +++ b/test/fixtures/process-exit-code-cases.js @@ -0,0 +1,113 @@ +'use strict'; + +const assert = require('assert'); + +const cases = []; +module.exports = cases; + +function exitsOnExitCodeSet() { + process.exitCode = 42; + process.on('exit', (code) => { + assert.strictEqual(process.exitCode, 42); + assert.strictEqual(code, 42); + }); +} +cases.push({ func: exitsOnExitCodeSet, result: 42 }); + +function changesCodeViaExit() { + process.exitCode = 99; + process.on('exit', (code) => { + assert.strictEqual(process.exitCode, 42); + assert.strictEqual(code, 42); + }); + process.exit(42); +} +cases.push({ func: changesCodeViaExit, result: 42 }); + +function changesCodeZeroExit() { + process.exitCode = 99; + process.on('exit', (code) => { + assert.strictEqual(process.exitCode, 0); + assert.strictEqual(code, 0); + }); + process.exit(0); +} +cases.push({ func: changesCodeZeroExit, result: 0 }); + +function exitWithOneOnUncaught() { + process.exitCode = 99; + process.on('exit', (code) => { + // cannot use assert because it will be uncaughtException -> 1 exit code + // that will render this test useless + if (code !== 1 || process.exitCode !== 1) { + console.log('wrong code! expected 1 for uncaughtException'); + process.exit(99); + } + }); + throw new Error('ok'); +} +cases.push({ + func: exitWithOneOnUncaught, + result: 1, + error: /^Error: ok$/, +}); + +function changeCodeInsideExit() { + process.exitCode = 95; + process.on('exit', (code) => { + assert.strictEqual(process.exitCode, 95); + assert.strictEqual(code, 95); + process.exitCode = 99; + }); +} +cases.push({ func: changeCodeInsideExit, result: 99 }); + +function zeroExitWithUncaughtHandler() { + process.on('exit', (code) => { + assert.strictEqual(process.exitCode, 0); + assert.strictEqual(code, 0); + }); + process.on('uncaughtException', () => {}); + throw new Error('ok'); +} +cases.push({ func: zeroExitWithUncaughtHandler, result: 0 }); + +function changeCodeInUncaughtHandler() { + process.on('exit', (code) => { + assert.strictEqual(process.exitCode, 97); + assert.strictEqual(code, 97); + }); + process.on('uncaughtException', () => { + process.exitCode = 97; + }); + throw new Error('ok'); +} +cases.push({ func: changeCodeInUncaughtHandler, result: 97 }); + +function changeCodeInExitWithUncaught() { + process.on('exit', (code) => { + assert.strictEqual(process.exitCode, 1); + assert.strictEqual(code, 1); + process.exitCode = 98; + }); + throw new Error('ok'); +} +cases.push({ + func: changeCodeInExitWithUncaught, + result: 98, + error: /^Error: ok$/, +}); + +function exitWithZeroInExitWithUncaught() { + process.on('exit', (code) => { + assert.strictEqual(process.exitCode, 1); + assert.strictEqual(code, 1); + process.exitCode = 0; + }); + throw new Error('ok'); +} +cases.push({ + func: exitWithZeroInExitWithUncaught, + result: 0, + error: /^Error: ok$/, +}); diff --git a/test/parallel/test-assert-first-line.js b/test/parallel/test-assert-first-line.js new file mode 100644 index 00000000000000..32eadbf74156c3 --- /dev/null +++ b/test/parallel/test-assert-first-line.js @@ -0,0 +1,23 @@ +'use strict'; + +// Verify that asserting in the very first line produces the expected result. + +require('../common'); +const assert = require('assert'); +const { path } = require('../common/fixtures'); + +assert.throws( + () => require(path('assert-first-line')), + { + name: 'AssertionError [ERR_ASSERTION]', + message: "The expression evaluated to a falsy value:\n\n ässört.ok('')\n" + } +); + +assert.throws( + () => require(path('assert-long-line')), + { + name: 'AssertionError [ERR_ASSERTION]', + message: "The expression evaluated to a falsy value:\n\n assert.ok('')\n" + } +); diff --git a/test/parallel/test-assert.js b/test/parallel/test-assert.js index 4b4b802701b778..ec7fe58d05c6c4 100644 --- a/test/parallel/test-assert.js +++ b/test/parallel/test-assert.js @@ -457,7 +457,7 @@ assert.throws( code: 'ERR_ASSERTION', type: assert.AssertionError, message: 'The expression evaluated to a falsy value:\n\n ' + - "assert.ok(typeof 123 === 'string')\n" + "assert.ok(\n typeof 123 === 'string'\n )\n" } ); Error.stackTraceLimit = tmpLimit; @@ -661,7 +661,7 @@ common.expectsError( code: 'ERR_ASSERTION', type: assert.AssertionError, message: 'The expression evaluated to a falsy value:\n\n ' + - "assert(Buffer.from('test') instanceof Error)\n" + "assert(\n (Buffer.from('test') instanceof Error)\n )\n" } ); common.expectsError( @@ -670,7 +670,7 @@ common.expectsError( code: 'ERR_ASSERTION', type: assert.AssertionError, message: 'The expression evaluated to a falsy value:\n\n ' + - "assert(Buffer.from('test') instanceof Error)\n" + "assert(\n (Buffer.from('test') instanceof Error)\n )\n" } ); fs.close = tmp; @@ -690,11 +690,13 @@ common.expectsError( code: 'ERR_ASSERTION', type: assert.AssertionError, message: 'The expression evaluated to a falsy value:\n\n' + - ' assert((() => \'string\')()\n' + + ' a(\n' + + ' (() => \'string\')()\n' + ' // eslint-disable-next-line\n' + ' ===\n' + ' 123 instanceof\n' + - ' Buffer)\n' + ' Buffer\n' + + ' )\n' } ); @@ -712,11 +714,13 @@ common.expectsError( code: 'ERR_ASSERTION', type: assert.AssertionError, message: 'The expression evaluated to a falsy value:\n\n' + - ' assert((() => \'string\')()\n' + + ' a(\n' + + ' (() => \'string\')()\n' + ' // eslint-disable-next-line\n' + ' ===\n' + ' 123 instanceof\n' + - ' Buffer)\n' + ' Buffer\n' + + ' )\n' } ); @@ -731,16 +735,19 @@ Buffer code: 'ERR_ASSERTION', type: assert.AssertionError, message: 'The expression evaluated to a falsy value:\n\n' + - ' assert((\n' + + ' a((\n' + ' () => \'string\')() ===\n' + ' 123 instanceof\n' + - ' Buffer)\n' + ' Buffer\n' + + ' )\n' } ); /* eslint-enable indent */ common.expectsError( - () => assert(null, undefined), + () => { + assert(true); assert(null, undefined); + }, { code: 'ERR_ASSERTION', type: assert.AssertionError, @@ -750,11 +757,38 @@ common.expectsError( ); common.expectsError( - () => assert.ok.apply(null, [0]), + () => { + assert + .ok(null, undefined); + }, { code: 'ERR_ASSERTION', type: assert.AssertionError, - message: '0 == true' + message: 'The expression evaluated to a falsy value:\n\n ' + + 'ok(null, undefined)\n' + } +); + +common.expectsError( + // eslint-disable-next-line dot-notation, quotes + () => assert['ok']["apply"](null, [0]), + { + code: 'ERR_ASSERTION', + type: assert.AssertionError, + message: 'The expression evaluated to a falsy value:\n\n ' + + 'assert[\'ok\']["apply"](null, [0])\n' + } +); + +common.expectsError( + () => { + const wrapper = (fn, value) => fn(value); + wrapper(assert, false); + }, + { + code: 'ERR_ASSERTION', + type: assert.AssertionError, + message: 'The expression evaluated to a falsy value:\n\n fn(value)\n' } ); @@ -763,7 +797,8 @@ common.expectsError( { code: 'ERR_ASSERTION', type: assert.AssertionError, - message: '0 == true', + message: 'The expression evaluated to a falsy value:\n\n ' + + 'assert.ok.call(null, 0)\n', generatedMessage: true } ); @@ -778,7 +813,7 @@ common.expectsError( } ); -// works in eval +// Works in eval. common.expectsError( () => new Function('assert', 'assert(1 === 2);')(assert), { diff --git a/test/parallel/test-common-gc.js b/test/parallel/test-common-gc.js new file mode 100644 index 00000000000000..210b1d6d5f49ea --- /dev/null +++ b/test/parallel/test-common-gc.js @@ -0,0 +1,15 @@ +'use strict'; +// Flags: --expose-gc +const common = require('../common'); + +{ + const gcListener = { ongc: common.mustCall() }; + common.onGC({}, gcListener); + global.gc(); +} + +{ + const gcListener = { ongc: common.mustNotCall() }; + common.onGC(process, gcListener); + global.gc(); +} diff --git a/test/parallel/test-console-table.js b/test/parallel/test-console-table.js index 2b63556acaa478..844d2e01e4180b 100644 --- a/test/parallel/test-console-table.js +++ b/test/parallel/test-console-table.js @@ -17,7 +17,10 @@ function test(data, only, expected) { only = undefined; } console.table(data, only); - assert.strictEqual(queue.shift(), expected.trimLeft()); + assert.deepStrictEqual( + queue.shift().split('\n'), + expected.trimLeft().split('\n') + ); } common.expectsError(() => console.table([], false), { @@ -117,6 +120,26 @@ test(new Map([[1, 1], [2, 2], [3, 3]]).entries(), ` └───────────────────┴─────┴────────┘ `); +test(new Map([[1, 1], [2, 2], [3, 3]]).values(), ` +┌───────────────────┬────────┐ +│ (iteration index) │ Values │ +├───────────────────┼────────┤ +│ 0 │ 1 │ +│ 1 │ 2 │ +│ 2 │ 3 │ +└───────────────────┴────────┘ +`); + +test(new Map([[1, 1], [2, 2], [3, 3]]).keys(), ` +┌───────────────────┬────────┐ +│ (iteration index) │ Values │ +├───────────────────┼────────┤ +│ 0 │ 1 │ +│ 1 │ 2 │ +│ 2 │ 3 │ +└───────────────────┴────────┘ +`); + test(new Set([1, 2, 3]).values(), ` ┌───────────────────┬────────┐ │ (iteration index) │ Values │ diff --git a/test/parallel/test-console.js b/test/parallel/test-console.js index 1b44ca9c4dac65..a0e6e322a5e42a 100644 --- a/test/parallel/test-console.js +++ b/test/parallel/test-console.js @@ -22,6 +22,7 @@ 'use strict'; const common = require('../common'); const assert = require('assert'); +const util = require('util'); assert.ok(process.stdout.writable); assert.ok(process.stderr.writable); @@ -30,11 +31,17 @@ if (common.isMainThread) { assert.strictEqual(typeof process.stdout.fd, 'number'); assert.strictEqual(typeof process.stderr.fd, 'number'); } -process.once('warning', common.mustCall((warning) => { - assert(/no such label/.test(warning.message)); -})); -console.timeEnd('no such label'); +common.expectWarning( + 'Warning', + [ + ['No such label \'nolabel\' for console.timeEnd()', common.noWarnCode], + ['No such label \'nolabel\' for console.timeLog()', common.noWarnCode] + ] +); + +console.timeEnd('nolabel'); +console.timeLog('nolabel'); console.time('label'); console.timeEnd('label'); @@ -47,8 +54,8 @@ assert.throws(() => console.timeEnd(Symbol('test')), TypeError); -// an Object with a custom .inspect() function -const custom_inspect = { foo: 'bar', inspect: () => 'inspect' }; +// An Object with a custom inspect function. +const custom_inspect = { foo: 'bar', [util.inspect.custom]: () => 'inspect' }; const strings = []; const errStrings = []; @@ -125,11 +132,14 @@ console.timeEnd('constructor'); console.time('hasOwnProperty'); console.timeEnd('hasOwnProperty'); -// verify that values are coerced to strings +// Verify that values are coerced to strings. console.time([]); console.timeEnd([]); console.time({}); console.timeEnd({}); +// Repeat the object call to verify that everything really worked. +console.time({}); +console.timeEnd({}); console.time(null); console.timeEnd(null); console.time(undefined); @@ -139,6 +149,12 @@ console.timeEnd(); console.time(NaN); console.timeEnd(NaN); +console.time('log1'); +console.timeLog('log1'); +console.timeLog('log1', 'test'); +console.timeLog('log1', {}, [1, 2, 3]); +console.timeEnd('log1'); + console.assert(false, '%s should', 'console.assert', 'not throw'); assert.strictEqual(errStrings[errStrings.length - 1], 'Assertion failed: console.assert should not throw\n'); @@ -179,9 +195,11 @@ for (const expected of expectedStrings) { } assert.strictEqual(strings.shift(), - "{ foo: 'bar', inspect: [Function: inspect] }\n"); + "{ foo: 'bar',\n [Symbol(util.inspect.custom)]: " + + '[Function: [util.inspect.custom]] }\n'); assert.strictEqual(strings.shift(), - "{ foo: 'bar', inspect: [Function: inspect] }\n"); + "{ foo: 'bar',\n [Symbol(util.inspect.custom)]: " + + '[Function: [util.inspect.custom]] }\n'); assert.ok(strings.shift().includes('foo: [Object]')); assert.strictEqual(strings.shift().includes('baz'), false); assert.strictEqual(strings.shift(), 'inspect inspect\n'); @@ -197,11 +215,20 @@ assert.ok(/^hasOwnProperty: \d+\.\d{3}ms$/.test(strings.shift().trim())); // verify that console.time() coerces label values to strings as expected assert.ok(/^: \d+\.\d{3}ms$/.test(strings.shift().trim())); assert.ok(/^\[object Object\]: \d+\.\d{3}ms$/.test(strings.shift().trim())); +assert.ok(/^\[object Object\]: \d+\.\d{3}ms$/.test(strings.shift().trim())); assert.ok(/^null: \d+\.\d{3}ms$/.test(strings.shift().trim())); assert.ok(/^default: \d+\.\d{3}ms$/.test(strings.shift().trim())); assert.ok(/^default: \d+\.\d{3}ms$/.test(strings.shift().trim())); assert.ok(/^NaN: \d+\.\d{3}ms$/.test(strings.shift().trim())); +assert.ok(/^log1: \d+\.\d{3}ms$/.test(strings.shift().trim())); +assert.ok(/^log1: \d+\.\d{3}ms test$/.test(strings.shift().trim())); +assert.ok(/^log1: \d+\.\d{3}ms {} \[ 1, 2, 3 ]$/.test(strings.shift().trim())); +assert.ok(/^log1: \d+\.\d{3}ms$/.test(strings.shift().trim())); + +// Make sure that we checked all strings +assert.strictEqual(strings.length, 0); + assert.strictEqual(errStrings.shift().split('\n').shift(), 'Trace: This is a {"formatted":"trace"} 10 foo'); @@ -212,6 +239,6 @@ common.hijackStderr(common.mustCall(function(data) { // stderr.write will catch sync error, so use `process.nextTick` here process.nextTick(function() { - assert.strictEqual(data.includes('no such label'), true); + assert.strictEqual(data.includes('nolabel'), true); }); })); diff --git a/test/parallel/test-dns-promises-resolve.js b/test/parallel/test-dns-promises-resolve.js new file mode 100644 index 00000000000000..fcaa8977a5ba18 --- /dev/null +++ b/test/parallel/test-dns-promises-resolve.js @@ -0,0 +1,44 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const dnsPromises = require('dns').promises; + +common.crashOnUnhandledRejection(); + +// Error when rrtype is invalid. +{ + const rrtype = 'DUMMY'; + common.expectsError( + () => dnsPromises.resolve('example.org', rrtype), + { + code: 'ERR_INVALID_OPT_VALUE', + type: TypeError, + message: `The value "${rrtype}" is invalid for option "rrtype"` + } + ); +} + +// Error when rrtype is a number. +{ + const rrtype = 0; + common.expectsError( + () => dnsPromises.resolve('example.org', rrtype), + { + code: 'ERR_INVALID_ARG_TYPE', + type: TypeError, + message: 'The "rrtype" argument must be of type string. ' + + `Received type ${typeof rrtype}` + } + ); +} + +// rrtype is undefined, it's same as resolve4 +{ + (async function() { + const rrtype = undefined; + const result = await dnsPromises.resolve('example.org', rrtype); + assert.ok(result !== undefined); + assert.ok(result.length > 0); + })(); +} diff --git a/test/parallel/test-eslint-duplicate-requires.js b/test/parallel/test-eslint-duplicate-requires.js new file mode 100644 index 00000000000000..c7edaeed2b6b6e --- /dev/null +++ b/test/parallel/test-eslint-duplicate-requires.js @@ -0,0 +1,25 @@ +'use strict'; + +const common = require('../common'); + +common.skipIfEslintMissing(); + +const { RuleTester } = require('../../tools/node_modules/eslint'); +const rule = require('../../tools/eslint-rules/no-duplicate-requires'); + +new RuleTester().run('no-duplicate-requires', rule, { + valid: [ + { + code: 'require("a"); require("b"); (function() { require("a"); });', + }, + { + code: 'require(a); require(a);', + }, + ], + invalid: [ + { + code: 'require("a"); require("a");', + errors: [{ message: '\'a\' require is duplicated.' }], + }, + ], +}); diff --git a/test/parallel/test-fs-copyfile.js b/test/parallel/test-fs-copyfile.js index 6e5d6c9403e795..f001535598bbeb 100644 --- a/test/parallel/test-fs-copyfile.js +++ b/test/parallel/test-fs-copyfile.js @@ -64,7 +64,7 @@ try { } catch (err) { assert.strictEqual(err.syscall, 'copyfile'); assert(err.code === 'ENOTSUP' || err.code === 'ENOTTY' || - err.code === 'ENOSYS'); + err.code === 'ENOSYS' || err.code === 'EXDEV'); assert.strictEqual(err.path, src); assert.strictEqual(err.dest, dest); } diff --git a/test/parallel/test-fs-error-messages.js b/test/parallel/test-fs-error-messages.js index f406e8743ed120..8ef009e8128b0d 100644 --- a/test/parallel/test-fs-error-messages.js +++ b/test/parallel/test-fs-error-messages.js @@ -303,6 +303,11 @@ function re(literals, ...values) { `ENOTEMPTY: directory not empty, rename '${existingDir}' -> ` + `'${existingDir2}'`); assert.strictEqual(err.errno, uv.UV_ENOTEMPTY); + } else if (err.code === 'EXDEV') { // not on the same mounted filesystem + assert.strictEqual( + err.message, + `EXDEV: cross-device link not permitted, rename '${existingDir}' -> ` + + `'${existingDir2}'`); } else if (err.code === 'EEXIST') { // smartos and aix assert.strictEqual( err.message, diff --git a/test/parallel/test-fs-promises-file-handle-readFile.js b/test/parallel/test-fs-promises-file-handle-readFile.js index 316fd6581fa446..9e6fcc2784c49d 100644 --- a/test/parallel/test-fs-promises-file-handle-readFile.js +++ b/test/parallel/test-fs-promises-file-handle-readFile.js @@ -28,5 +28,22 @@ async function validateReadFile() { assert.deepStrictEqual(buffer, readFileData); } +async function validateReadFileProc() { + // Test to make sure reading a file under the /proc directory works. Adapted + // from test-fs-read-file-sync-hostname.js. + // Refs: + // - https://groups.google.com/forum/#!topic/nodejs-dev/rxZ_RoH1Gn0 + // - https://github.com/nodejs/node/issues/21331 + + // Test is Linux-specific. + if (!common.isLinux) + return; + + const fileHandle = await open('/proc/sys/kernel/hostname', 'r'); + const hostname = await fileHandle.readFile(); + assert.ok(hostname.length > 0); +} + validateReadFile() + .then(() => validateReadFileProc()) .then(common.mustCall()); diff --git a/test/parallel/test-fs-promises-file-handle-sync.js b/test/parallel/test-fs-promises-file-handle-sync.js new file mode 100644 index 00000000000000..cf28df31cb2e0f --- /dev/null +++ b/test/parallel/test-fs-promises-file-handle-sync.js @@ -0,0 +1,27 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const fixtures = require('../common/fixtures'); +const tmpdir = require('../common/tmpdir'); + +const { access, copyFile, open } = require('fs').promises; +const path = require('path'); + +common.crashOnUnhandledRejection(); + +async function validateSync() { + tmpdir.refresh(); + const dest = path.resolve(tmpdir.path, 'baz.js'); + await copyFile(fixtures.path('baz.js'), dest); + await access(dest, 'r'); + const handle = await open(dest, 'r+'); + await handle.datasync(); + await handle.sync(); + const buf = Buffer.from('hello world'); + await handle.write(buf); + const ret = await handle.read(Buffer.alloc(11), 0, 11, 0); + assert.strictEqual(ret.bytesRead, 11); + assert.deepStrictEqual(ret.buffer, buf); +} + +validateSync(); diff --git a/test/parallel/test-fs-promises-file-handle-write.js b/test/parallel/test-fs-promises-file-handle-write.js index d7812745c5439e..49df2bf54f45ed 100644 --- a/test/parallel/test-fs-promises-file-handle-write.js +++ b/test/parallel/test-fs-promises-file-handle-write.js @@ -3,7 +3,7 @@ const common = require('../common'); // The following tests validate base functionality for the fs.promises -// FileHandle.read method. +// FileHandle.write method. const fs = require('fs'); const { open } = fs.promises; @@ -45,8 +45,22 @@ async function validateNonUint8ArrayWrite() { assert.deepStrictEqual(Buffer.from(buffer, 'utf8'), readFileData); } +async function validateNonStringValuesWrite() { + const filePathForHandle = path.resolve(tmpDir, 'tmp-non-string-write.txt'); + const fileHandle = await open(filePathForHandle, 'w+'); + const nonStringValues = [123, {}, new Map()]; + for (const nonStringValue of nonStringValues) { + await fileHandle.write(nonStringValue); + } + + const readFileData = fs.readFileSync(filePathForHandle); + const expected = ['123', '[object Object]', '[object Map]'].join(''); + assert.deepStrictEqual(Buffer.from(expected, 'utf8'), readFileData); +} + Promise.all([ validateWrite(), validateEmptyWrite(), - validateNonUint8ArrayWrite() + validateNonUint8ArrayWrite(), + validateNonStringValuesWrite() ]).then(common.mustCall()); diff --git a/test/parallel/test-fs-promises-readfile.js b/test/parallel/test-fs-promises-readfile.js index 4334673c30f5fb..b1186ad2172507 100644 --- a/test/parallel/test-fs-promises-readfile.js +++ b/test/parallel/test-fs-promises-readfile.js @@ -12,17 +12,35 @@ const fn = path.join(tmpdir.path, 'large-file'); common.crashOnUnhandledRejection(); -// Creating large buffer with random content -const buffer = Buffer.from( - Array.apply(null, { length: 16834 * 2 }) - .map(Math.random) - .map((number) => (number * (1 << 8))) -); - -// Writing buffer to a file then try to read it -writeFile(fn, buffer) - .then(() => readFile(fn)) - .then((readBuffer) => { - assert.strictEqual(readBuffer.equals(buffer), true); - }) +async function validateReadFile() { + // Creating large buffer with random content + const buffer = Buffer.from( + Array.apply(null, { length: 16834 * 2 }) + .map(Math.random) + .map((number) => (number * (1 << 8))) + ); + + // Writing buffer to a file then try to read it + await writeFile(fn, buffer); + const readBuffer = await readFile(fn); + assert.strictEqual(readBuffer.equals(buffer), true); +} + +async function validateReadFileProc() { + // Test to make sure reading a file under the /proc directory works. Adapted + // from test-fs-read-file-sync-hostname.js. + // Refs: + // - https://groups.google.com/forum/#!topic/nodejs-dev/rxZ_RoH1Gn0 + // - https://github.com/nodejs/node/issues/21331 + + // Test is Linux-specific. + if (!common.isLinux) + return; + + const hostname = await readFile('/proc/sys/kernel/hostname'); + assert.ok(hostname.length > 0); +} + +validateReadFile() + .then(() => validateReadFileProc()) .then(common.mustCall()); diff --git a/test/parallel/test-fs-promises.js b/test/parallel/test-fs-promises.js index 6871a6762b68e2..602f1191b7aef5 100644 --- a/test/parallel/test-fs-promises.js +++ b/test/parallel/test-fs-promises.js @@ -140,15 +140,26 @@ function verifyStatObject(stat) { (await realpath(newLink)).toLowerCase()); assert.strictEqual(newPath.toLowerCase(), (await readlink(newLink)).toLowerCase()); + + const newMode = 0o666; if (common.isOSX) { // lchmod is only available on macOS - const newMode = 0o666; await lchmod(newLink, newMode); stats = await lstat(newLink); assert.strictEqual(stats.mode & 0o777, newMode); + } else { + await Promise.all([ + assert.rejects( + lchmod(newLink, newMode), + common.expectsError({ + code: 'ERR_METHOD_NOT_IMPLEMENTED', + type: Error, + message: 'The lchmod() method is not implemented' + }) + ) + ]); } - await unlink(newLink); } diff --git a/test/sequential/test-fs-watch-file-enoent-after-deletion.js b/test/parallel/test-fs-watch-file-enoent-after-deletion.js similarity index 91% rename from test/sequential/test-fs-watch-file-enoent-after-deletion.js rename to test/parallel/test-fs-watch-file-enoent-after-deletion.js index f5b9012acabe58..6eaa4c68c58bc8 100644 --- a/test/sequential/test-fs-watch-file-enoent-after-deletion.js +++ b/test/parallel/test-fs-watch-file-enoent-after-deletion.js @@ -32,7 +32,6 @@ const common = require('../common'); // stopped it from getting emitted. // https://github.com/nodejs/node-v0.x-archive/issues/4027 -const assert = require('assert'); const path = require('path'); const fs = require('fs'); @@ -43,9 +42,7 @@ const filename = path.join(tmpdir.path, 'watched'); fs.writeFileSync(filename, 'quis custodiet ipsos custodes'); fs.watchFile(filename, { interval: 50 }, common.mustCall(function(curr, prev) { - assert.strictEqual(prev.nlink, 1); - assert.strictEqual(curr.nlink, 0); fs.unwatchFile(filename); })); -setTimeout(fs.unlinkSync, common.platformTimeout(300), filename); +fs.unlinkSync(filename); diff --git a/test/parallel/test-heapdump-dns.js b/test/parallel/test-heapdump-dns.js new file mode 100644 index 00000000000000..011503f5874d5a --- /dev/null +++ b/test/parallel/test-heapdump-dns.js @@ -0,0 +1,17 @@ +// Flags: --expose-internals +'use strict'; +require('../common'); +const { validateSnapshotNodes } = require('../common/heap'); + +validateSnapshotNodes('DNSCHANNEL', []); +const dns = require('dns'); +validateSnapshotNodes('DNSCHANNEL', [{}]); +dns.resolve('localhost', () => {}); +validateSnapshotNodes('DNSCHANNEL', [ + { + children: [ + { name: 'task list' }, + { name: 'ChannelWrap' } + ] + } +]); diff --git a/test/parallel/test-heapdump-fs-promise.js b/test/parallel/test-heapdump-fs-promise.js new file mode 100644 index 00000000000000..be44b3d8731bc1 --- /dev/null +++ b/test/parallel/test-heapdump-fs-promise.js @@ -0,0 +1,16 @@ +// Flags: --expose-internals +'use strict'; +require('../common'); +const { validateSnapshotNodes } = require('../common/heap'); +const fs = require('fs').promises; + +validateSnapshotNodes('FSREQPROMISE', []); +fs.stat(__filename); +validateSnapshotNodes('FSREQPROMISE', [ + { + children: [ + { name: 'FSReqPromise' }, + { name: 'Float64Array' } // Stat array + ] + } +]); diff --git a/test/parallel/test-heapdump-http2.js b/test/parallel/test-heapdump-http2.js new file mode 100644 index 00000000000000..19a70d8c44b15d --- /dev/null +++ b/test/parallel/test-heapdump-http2.js @@ -0,0 +1,76 @@ +// Flags: --expose-internals +'use strict'; +const common = require('../common'); +const { recordState } = require('../common/heap'); +const http2 = require('http2'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +{ + const state = recordState(); + state.validateSnapshotNodes('HTTP2SESSION', []); + state.validateSnapshotNodes('HTTP2STREAM', []); +} + +const server = http2.createServer(); +server.on('stream', (stream) => { + stream.respondWithFile(__filename); +}); +server.listen(0, () => { + const client = http2.connect(`http://localhost:${server.address().port}`); + const req = client.request(); + + req.on('response', common.mustCall(() => { + const state = recordState(); + state.validateSnapshotNodes('HTTP2STREAM', [ + { + children: [ + { name: 'Http2Stream' } + ] + }, + ], { loose: true }); + state.validateSnapshotNodes('FILEHANDLE', [ + { + children: [ + { name: 'FileHandle' } + ] + } + ]); + state.validateSnapshotNodes('TCPWRAP', [ + { + children: [ + { name: 'TCP' } + ] + } + ], { loose: true }); + state.validateSnapshotNodes('TCPSERVERWRAP', [ + { + children: [ + { name: 'TCP' } + ] + } + ], { loose: true }); + state.validateSnapshotNodes('STREAMPIPE', [ + { + children: [ + { name: 'StreamPipe' } + ] + } + ]); + state.validateSnapshotNodes('HTTP2SESSION', [ + { + children: [ + { name: 'Http2Session' }, + { name: 'streams' } + ] + } + ], { loose: true }); + })); + + req.resume(); + req.on('end', common.mustCall(() => { + client.close(); + server.close(); + })); + req.end(); +}); diff --git a/test/parallel/test-heapdump-inspector.js b/test/parallel/test-heapdump-inspector.js new file mode 100644 index 00000000000000..355b8d0d0a1d51 --- /dev/null +++ b/test/parallel/test-heapdump-inspector.js @@ -0,0 +1,21 @@ +// Flags: --expose-internals +'use strict'; +const common = require('../common'); + +common.skipIfInspectorDisabled(); + +const { validateSnapshotNodes } = require('../common/heap'); +const inspector = require('inspector'); + +const session = new inspector.Session(); +validateSnapshotNodes('INSPECTORJSBINDING', []); +session.connect(); +validateSnapshotNodes('INSPECTORJSBINDING', [ + { + children: [ + { name: 'session' }, + { name: 'Connection' }, + (node) => node.type === 'closure' || typeof node.value === 'function' + ] + } +]); diff --git a/test/parallel/test-heapdump-tls.js b/test/parallel/test-heapdump-tls.js new file mode 100644 index 00000000000000..be14b7b5f7ca64 --- /dev/null +++ b/test/parallel/test-heapdump-tls.js @@ -0,0 +1,33 @@ +// Flags: --expose-internals +'use strict'; +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const { validateSnapshotNodes } = require('../common/heap'); +const net = require('net'); +const tls = require('tls'); + +validateSnapshotNodes('TLSWRAP', []); + +const server = net.createServer(common.mustCall((c) => { + c.end(); +})).listen(0, common.mustCall(() => { + const c = tls.connect({ port: server.address().port }); + + c.on('error', common.mustCall(() => { + server.close(); + })); + c.write('hello'); + + validateSnapshotNodes('TLSWRAP', [ + { + children: [ + { name: 'enc_out' }, + { name: 'enc_in' }, + { name: 'TLSWrap' } + ] + } + ]); +})); diff --git a/test/parallel/test-heapdump-worker.js b/test/parallel/test-heapdump-worker.js new file mode 100644 index 00000000000000..68d2ccd1abbc29 --- /dev/null +++ b/test/parallel/test-heapdump-worker.js @@ -0,0 +1,27 @@ +// Flags: --expose-internals --experimental-worker +'use strict'; +require('../common'); +const { validateSnapshotNodes } = require('../common/heap'); +const { Worker } = require('worker_threads'); + +validateSnapshotNodes('WORKER', []); +const worker = new Worker('setInterval(() => {}, 100);', { eval: true }); +validateSnapshotNodes('WORKER', [ + { + children: [ + { name: 'thread_exit_async' }, + { name: 'env' }, + { name: 'MESSAGEPORT' }, + { name: 'Worker' } + ] + } +]); +validateSnapshotNodes('MESSAGEPORT', [ + { + children: [ + { name: 'data' }, + { name: 'MessagePort' } + ] + } +], { loose: true }); +worker.terminate(); diff --git a/test/parallel/test-heapdump-zlib.js b/test/parallel/test-heapdump-zlib.js new file mode 100644 index 00000000000000..7a749902f5aaf6 --- /dev/null +++ b/test/parallel/test-heapdump-zlib.js @@ -0,0 +1,17 @@ +// Flags: --expose-internals +'use strict'; +require('../common'); +const { validateSnapshotNodes } = require('../common/heap'); +const zlib = require('zlib'); + +validateSnapshotNodes('ZLIB', []); +// eslint-disable-next-line no-unused-vars +const gunzip = zlib.createGunzip(); +validateSnapshotNodes('ZLIB', [ + { + children: [ + { name: 'Zlib' }, + { name: 'zlib memory' } + ] + } +]); diff --git a/test/parallel/test-http-client-set-timeout.js b/test/parallel/test-http-client-set-timeout.js new file mode 100644 index 00000000000000..51284b42765493 --- /dev/null +++ b/test/parallel/test-http-client-set-timeout.js @@ -0,0 +1,46 @@ +'use strict'; +const common = require('../common'); + +// Test that `req.setTimeout` will fired exactly once. + +const assert = require('assert'); +const http = require('http'); + +const HTTP_CLIENT_TIMEOUT = 2000; + +const options = { + method: 'GET', + port: undefined, + host: '127.0.0.1', + path: '/', + timeout: HTTP_CLIENT_TIMEOUT, +}; + +const server = http.createServer(() => { + // Never respond. +}); + +server.listen(0, options.host, function() { + doRequest(); +}); + +function doRequest() { + options.port = server.address().port; + const req = http.request(options); + req.setTimeout(HTTP_CLIENT_TIMEOUT / 2); + req.on('error', () => { + // This space is intentionally left blank. + }); + req.on('close', common.mustCall(() => server.close())); + + let timeout_events = 0; + req.on('timeout', common.mustCall(() => { + timeout_events += 1; + })); + req.end(); + + setTimeout(function() { + req.destroy(); + assert.strictEqual(timeout_events, 1); + }, common.platformTimeout(HTTP_CLIENT_TIMEOUT)); +} diff --git a/test/parallel/test-http-client-timeout-option-with-agent.js b/test/parallel/test-http-client-timeout-option-with-agent.js new file mode 100644 index 00000000000000..a7f750a42e557b --- /dev/null +++ b/test/parallel/test-http-client-timeout-option-with-agent.js @@ -0,0 +1,55 @@ +'use strict'; +const common = require('../common'); + +// Test that when http request uses both timeout and agent, +// timeout will work as expected. + +const assert = require('assert'); +const http = require('http'); + +const HTTP_AGENT_TIMEOUT = 1000; +const HTTP_CLIENT_TIMEOUT = 3000; + +const agent = new http.Agent({ timeout: HTTP_AGENT_TIMEOUT }); +const options = { + method: 'GET', + port: undefined, + host: '127.0.0.1', + path: '/', + timeout: HTTP_CLIENT_TIMEOUT, + agent, +}; + +const server = http.createServer(() => { + // Never respond. +}); + +server.listen(0, options.host, function() { + doRequest(); +}); + +function doRequest() { + options.port = server.address().port; + const req = http.request(options); + const start = Date.now(); + req.on('error', () => { + // This space is intentionally left blank. + }); + req.on('close', common.mustCall(() => server.close())); + + let timeout_events = 0; + req.on('timeout', common.mustCall(() => { + timeout_events += 1; + const duration = Date.now() - start; + // The timeout event cannot be precisely timed. It will delay + // some number of milliseconds, so test it in second units. + assert.strictEqual(duration / 1000 | 0, HTTP_CLIENT_TIMEOUT / 1000); + })); + req.end(); + + setTimeout(function() { + req.destroy(); + assert.strictEqual(timeout_events, 1); + // Ensure the `timeout` event fired only once. + }, common.platformTimeout(HTTP_CLIENT_TIMEOUT * 2)); +} diff --git a/test/parallel/test-http2-compat-serverresponse-end-after-statuses-without-body.js b/test/parallel/test-http2-compat-serverresponse-end-after-statuses-without-body.js new file mode 100644 index 00000000000000..83d5521bf2473f --- /dev/null +++ b/test/parallel/test-http2-compat-serverresponse-end-after-statuses-without-body.js @@ -0,0 +1,47 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const h2 = require('http2'); + +// This test case ensures that calling of res.end after sending +// 204, 205 and 304 HTTP statuses will not cause an error +// See issue: https://github.com/nodejs/node/issues/21740 + +const { + HTTP_STATUS_NO_CONTENT, + HTTP_STATUS_RESET_CONTENT, + HTTP_STATUS_NOT_MODIFIED +} = h2.constants; + +const statusWithouBody = [ + HTTP_STATUS_NO_CONTENT, + HTTP_STATUS_RESET_CONTENT, + HTTP_STATUS_NOT_MODIFIED, +]; +const STATUS_CODES_COUNT = statusWithouBody.length; + +const server = h2.createServer(common.mustCall(function(req, res) { + res.writeHead(statusWithouBody.pop()); + res.end(); +}, STATUS_CODES_COUNT)); + +server.listen(0, common.mustCall(function() { + const url = `http://localhost:${server.address().port}`; + const client = h2.connect(url, common.mustCall(() => { + let responseCount = 0; + const closeAfterResponse = () => { + if (STATUS_CODES_COUNT === ++responseCount) { + client.destroy(); + server.close(); + } + }; + + for (let i = 0; i < STATUS_CODES_COUNT; i++) { + const request = client.request(); + request.on('response', common.mustCall(closeAfterResponse)); + } + + })); +})); diff --git a/test/parallel/test-http2-respond-with-file-connection-abort.js b/test/parallel/test-http2-respond-with-file-connection-abort.js new file mode 100644 index 00000000000000..25926b2c9805a3 --- /dev/null +++ b/test/parallel/test-http2-respond-with-file-connection-abort.js @@ -0,0 +1,37 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const http2 = require('http2'); +const net = require('net'); + +const { + HTTP2_HEADER_CONTENT_TYPE +} = http2.constants; + +const server = http2.createServer(); +server.on('stream', common.mustCall((stream) => { + stream.respondWithFile(process.execPath, { + [HTTP2_HEADER_CONTENT_TYPE]: 'application/octet-stream' + }); +})); + +server.listen(0, common.mustCall(() => { + const client = http2.connect(`http://localhost:${server.address().port}`); + const req = client.request(); + + req.on('response', common.mustCall(() => {})); + req.on('data', common.mustCall(() => { + net.Socket.prototype.destroy.call(client.socket); + server.close(); + })); + req.end(); +})); + +// TODO(addaleax): This is a *hack*. HTTP/2 needs to have a proper way of +// dealing with this kind of issue. +process.once('uncaughtException', (err) => { + if (err.code === 'ECONNRESET') return; + throw err; +}); diff --git a/test/parallel/test-http2-util-headers-list.js b/test/parallel/test-http2-util-headers-list.js index 772e9c8679feef..7b838835531977 100644 --- a/test/parallel/test-http2-util-headers-list.js +++ b/test/parallel/test-http2-util-headers-list.js @@ -1,14 +1,14 @@ // Flags: --expose-internals 'use strict'; -// Tests the internal utility function that is used to prepare headers -// to pass to the internal binding layer. +// Tests the internal utility functions that are used to prepare headers +// to pass to the internal binding layer and to build a header object. const common = require('../common'); if (!common.hasCrypto) common.skip('missing crypto'); const assert = require('assert'); -const { mapToHeaders } = require('internal/http2/util'); +const { mapToHeaders, toHeaderObject } = require('internal/http2/util'); const { HTTP2_HEADER_STATUS, @@ -305,3 +305,41 @@ common.expectsError({ assert(!(mapToHeaders({ te: 'trailers' }) instanceof Error)); assert(!(mapToHeaders({ te: ['trailers'] }) instanceof Error)); + + +{ + const rawHeaders = [ + ':status', '200', + 'cookie', 'foo', + 'set-cookie', 'sc1', + 'age', '10', + 'x-multi', 'first' + ]; + const headers = toHeaderObject(rawHeaders); + assert.strictEqual(headers[':status'], 200); + assert.strictEqual(headers.cookie, 'foo'); + assert.deepStrictEqual(headers['set-cookie'], ['sc1']); + assert.strictEqual(headers.age, '10'); + assert.strictEqual(headers['x-multi'], 'first'); +} + +{ + const rawHeaders = [ + ':status', '200', + ':status', '400', + 'cookie', 'foo', + 'cookie', 'bar', + 'set-cookie', 'sc1', + 'set-cookie', 'sc2', + 'age', '10', + 'age', '20', + 'x-multi', 'first', + 'x-multi', 'second' + ]; + const headers = toHeaderObject(rawHeaders); + assert.strictEqual(headers[':status'], 200); + assert.strictEqual(headers.cookie, 'foo; bar'); + assert.deepStrictEqual(headers['set-cookie'], ['sc1', 'sc2']); + assert.strictEqual(headers.age, '10'); + assert.strictEqual(headers['x-multi'], 'first, second'); +} diff --git a/test/sequential/test-inspector-port-zero-cluster.js b/test/parallel/test-inspector-port-zero-cluster.js similarity index 100% rename from test/sequential/test-inspector-port-zero-cluster.js rename to test/parallel/test-inspector-port-zero-cluster.js diff --git a/test/parallel/test-internal-util-normalizeencoding.js b/test/parallel/test-internal-util-normalizeencoding.js index 081255cef02f2a..2f16d55fad247c 100644 --- a/test/parallel/test-internal-util-normalizeencoding.js +++ b/test/parallel/test-internal-util-normalizeencoding.js @@ -18,10 +18,16 @@ const tests = [ ['utF-8', 'utf8'], ['ucs2', 'utf16le'], ['UCS2', 'utf16le'], + ['ucs-2', 'utf16le'], + ['UCS-2', 'utf16le'], + ['UcS-2', 'utf16le'], ['utf16le', 'utf16le'], ['utf-16le', 'utf16le'], ['UTF-16LE', 'utf16le'], ['UTF16LE', 'utf16le'], + ['usc-2', 'utf16le'], + ['USC-2', 'utf16le'], + ['uSc-2', 'utf16le'], ['binary', 'latin1'], ['BINARY', 'latin1'], ['latin1', 'latin1'], @@ -36,6 +42,7 @@ const tests = [ [NaN, undefined], [0, undefined], [[], undefined], + [{}, undefined] ]; tests.forEach((e, i) => { diff --git a/test/pummel/test-net-connect-memleak.js b/test/parallel/test-net-connect-memleak.js similarity index 64% rename from test/pummel/test-net-connect-memleak.js rename to test/parallel/test-net-connect-memleak.js index b6a31daed230d5..afcc61f173509f 100644 --- a/test/pummel/test-net-connect-memleak.js +++ b/test/parallel/test-net-connect-memleak.js @@ -26,33 +26,32 @@ const common = require('../common'); const assert = require('assert'); const net = require('net'); -console.log('Run this test with --expose-gc'); -assert.strictEqual( - typeof global.gc, - 'function' -); -net.createServer(function() {}).listen(common.PORT); - -let before = 0; +// Test that the implicit listener for an 'connect' event on net.Sockets is +// added using `once()`, i.e. can be gc'ed once that event has occurred. + +const server = net.createServer(common.mustCall()).listen(0); + +let collected = false; +const gcListener = { ongc() { collected = true; } }; + { - // 2**26 == 64M entries - global.gc(); - let junk = [0]; - for (let i = 0; i < 26; ++i) junk = junk.concat(junk); - before = process.memoryUsage().rss; - - net.createConnection(common.PORT, '127.0.0.1', function() { - assert.notStrictEqual(junk.length, 0); // keep reference alive - setTimeout(done, 10); - global.gc(); - }); + const gcObject = {}; + common.onGC(gcObject, gcListener); + + const sock = net.createConnection( + server.address().port, + common.mustCall(() => { + assert.strictEqual(gcObject, gcObject); // keep reference alive + assert.strictEqual(collected, false); + setImmediate(done, sock); + })); } -function done() { +function done(sock) { global.gc(); - const after = process.memoryUsage().rss; - const reclaimed = (before - after) / 1024; - console.log('%d kB reclaimed', reclaimed); - assert(reclaimed > 128 * 1024); // It's around 256 MB on x64. - process.exit(); + setImmediate(() => { + assert.strictEqual(collected, true); + sock.end(); + server.close(); + }); } diff --git a/test/parallel/test-process-exit-code.js b/test/parallel/test-process-exit-code.js index f5f8099c8d2439..9059b0b5c22487 100644 --- a/test/parallel/test-process-exit-code.js +++ b/test/parallel/test-process-exit-code.js @@ -23,63 +23,18 @@ require('../common'); const assert = require('assert'); -switch (process.argv[2]) { - case 'child1': - return child1(); - case 'child2': - return child2(); - case 'child3': - return child3(); - case 'child4': - return child4(); - case 'child5': - return child5(); - case undefined: - return parent(); - default: - throw new Error('invalid'); -} - -function child1() { - process.exitCode = 42; - process.on('exit', function(code) { - assert.strictEqual(code, 42); - }); -} - -function child2() { - process.exitCode = 99; - process.on('exit', function(code) { - assert.strictEqual(code, 42); - }); - process.exit(42); -} - -function child3() { - process.exitCode = 99; - process.on('exit', function(code) { - assert.strictEqual(code, 0); - }); - process.exit(0); -} - -function child4() { - process.exitCode = 99; - process.on('exit', function(code) { - if (code !== 1) { - console.log('wrong code! expected 1 for uncaughtException'); - process.exit(99); - } - }); - throw new Error('ok'); -} - -function child5() { - process.exitCode = 95; - process.on('exit', function(code) { - assert.strictEqual(code, 95); - process.exitCode = 99; - }); +const testCases = require('../fixtures/process-exit-code-cases'); + +if (!process.argv[2]) { + parent(); +} else { + const i = parseInt(process.argv[2]); + if (Number.isNaN(i)) { + console.log('Invalid test case index'); + process.exit(100); + return; + } + testCases[i].func(); } function parent() { @@ -88,18 +43,14 @@ function parent() { const f = __filename; const option = { stdio: [ 0, 1, 'ignore' ] }; - const test = (arg, exit) => { + const test = (arg, name = 'child', exit) => { spawn(node, [f, arg], option).on('exit', (code) => { assert.strictEqual( code, exit, - `wrong exit for ${arg}\nexpected:${exit} but got:${code}`); + `wrong exit for ${arg}-${name}\nexpected:${exit} but got:${code}`); console.log(`ok - ${arg} exited with ${exit}`); }); }; - test('child1', 42); - test('child2', 42); - test('child3', 0); - test('child4', 1); - test('child5', 99); + testCases.forEach((tc, i) => test(i, tc.func.name, tc.result)); } diff --git a/test/parallel/test-process-hrtime-bigint.js b/test/parallel/test-process-hrtime-bigint.js new file mode 100644 index 00000000000000..e5ce40a994d815 --- /dev/null +++ b/test/parallel/test-process-hrtime-bigint.js @@ -0,0 +1,14 @@ +'use strict'; + +// Tests that process.hrtime.bigint() works. + +require('../common'); +const assert = require('assert'); + +const start = process.hrtime.bigint(); +assert.strictEqual(typeof start, 'bigint'); + +const end = process.hrtime.bigint(); +assert.strictEqual(typeof end, 'bigint'); + +assert(end - start >= 0n); diff --git a/test/parallel/test-process-title-cli.js b/test/parallel/test-process-title-cli.js new file mode 100644 index 00000000000000..b6160fed1d0390 --- /dev/null +++ b/test/parallel/test-process-title-cli.js @@ -0,0 +1,13 @@ +// Flags: --title=foo +'use strict'; + +const common = require('../common'); + +if (common.isSunOS) + common.skip(`Unsupported platform [${process.platform}]`); + +const assert = require('assert'); + +// Verifies that the --title=foo command line flag set the process +// title on startup. +assert.strictEqual(process.title, 'foo'); diff --git a/test/parallel/test-repl-tab-complete.js b/test/parallel/test-repl-tab-complete.js index 0e6c4bb654910e..9d6ecf4e284811 100644 --- a/test/parallel/test-repl-tab-complete.js +++ b/test/parallel/test-repl-tab-complete.js @@ -149,7 +149,10 @@ putIn.run([ ' one:1', '};' ]); -testMe.complete('inner.o', getNoResultsFunction()); +// See: https://github.com/nodejs/node/issues/21586 +// testMe.complete('inner.o', getNoResultsFunction()); +testMe.complete('inner.o', common.mustCall(function(error, data) { +})); putIn.run(['.clear']); @@ -206,6 +209,20 @@ testMe.complete('toSt', common.mustCall(function(error, data) { assert.deepStrictEqual(data, [['toString'], 'toSt']); })); +// own properties should shadow properties on the prototype +putIn.run(['.clear']); +putIn.run([ + 'var x = Object.create(null);', + 'x.a = 1;', + 'x.b = 2;', + 'var y = Object.create(x);', + 'y.a = 3;', + 'y.c = 4;' +]); +testMe.complete('y.', common.mustCall(function(error, data) { + assert.deepStrictEqual(data, [['y.b', '', 'y.a', 'y.c'], 'y.']); +})); + // Tab complete provides built in libs for require() putIn.run(['.clear']); diff --git a/test/parallel/test-stdio-pipe-access.js b/test/parallel/test-stdio-pipe-access.js index d32c5f0ba9c2e7..084fa4c038e7da 100644 --- a/test/parallel/test-stdio-pipe-access.js +++ b/test/parallel/test-stdio-pipe-access.js @@ -1,7 +1,7 @@ 'use strict'; const common = require('../common'); if (!common.isMainThread) - common.skip('Workers don’t have process-like stdio'); + common.skip("Workers don't have process-like stdio"); // Test if Node handles acessing process.stdin if it is a redirected // pipe without deadlocking diff --git a/test/parallel/test-stdio-pipe-redirect.js b/test/parallel/test-stdio-pipe-redirect.js index 60f16b5cb2f6df..fbde6ef8085457 100644 --- a/test/parallel/test-stdio-pipe-redirect.js +++ b/test/parallel/test-stdio-pipe-redirect.js @@ -1,7 +1,7 @@ 'use strict'; const common = require('../common'); if (!common.isMainThread) - common.skip('Workers don’t have process-like stdio'); + common.skip("Workers don't have process-like stdio"); // Test if Node handles redirecting one child process stdout to another // process stdin without crashing. diff --git a/test/parallel/test-timers-immediate-queue.js b/test/parallel/test-timers-immediate-queue.js index 511a3adf864859..ba9ba87c4070bd 100644 --- a/test/parallel/test-timers-immediate-queue.js +++ b/test/parallel/test-timers-immediate-queue.js @@ -50,5 +50,5 @@ for (let i = 0; i < QUEUE; i++) process.on('exit', function() { console.log('hit', hit); - assert.strictEqual(hit, QUEUE, 'We ticked between the immediate queue'); + assert.strictEqual(hit, QUEUE); }); diff --git a/test/pummel/test-tls-connect-memleak.js b/test/parallel/test-tls-connect-memleak.js similarity index 64% rename from test/pummel/test-tls-connect-memleak.js rename to test/parallel/test-tls-connect-memleak.js index 1ef131d6876ac4..95f71acdc3b57b 100644 --- a/test/pummel/test-tls-connect-memleak.js +++ b/test/parallel/test-tls-connect-memleak.js @@ -30,38 +30,36 @@ const assert = require('assert'); const tls = require('tls'); const fixtures = require('../common/fixtures'); -assert.strictEqual( - typeof global.gc, - 'function', - `Type of global.gc is not a function. Type: ${typeof global.gc}.` + - ' Run this test with --expose-gc' -); +// Test that the implicit listener for an 'connect' event on tls.Sockets is +// added using `once()`, i.e. can be gc'ed once that event has occurred. -tls.createServer({ +const server = tls.createServer({ cert: fixtures.readSync('test_cert.pem'), key: fixtures.readSync('test_key.pem') -}).listen(common.PORT); +}).listen(0); -{ - // 2**26 == 64M entries - let junk = [0]; +let collected = false; +const gcListener = { ongc() { collected = true; } }; - for (let i = 0; i < 26; ++i) junk = junk.concat(junk); +{ + const gcObject = {}; + common.onGC(gcObject, gcListener); - const options = { rejectUnauthorized: false }; - tls.connect(common.PORT, '127.0.0.1', options, function() { - assert.notStrictEqual(junk.length, 0); // keep reference alive - setTimeout(done, 10); - global.gc(); - }); + const sock = tls.connect( + server.address().port, + { rejectUnauthorized: false }, + common.mustCall(() => { + assert.strictEqual(gcObject, gcObject); // keep reference alive + assert.strictEqual(collected, false); + setImmediate(done, sock); + })); } -function done() { - const before = process.memoryUsage().rss; +function done(sock) { global.gc(); - const after = process.memoryUsage().rss; - const reclaimed = (before - after) / 1024; - console.log('%d kB reclaimed', reclaimed); - assert(reclaimed > 256 * 1024); // it's more like 512M on x64 - process.exit(); + setImmediate(() => { + assert.strictEqual(collected, true); + sock.end(); + server.close(); + }); } diff --git a/test/parallel/test-tls-env-bad-extra-ca.js b/test/parallel/test-tls-env-bad-extra-ca.js index e141de34a9fe1b..5167f586079187 100644 --- a/test/parallel/test-tls-env-bad-extra-ca.js +++ b/test/parallel/test-tls-env-bad-extra-ca.js @@ -29,7 +29,8 @@ let stderr = ''; fork(__filename, opts) .on('exit', common.mustCall(function(status) { - assert.strictEqual(status, 0, 'client did not succeed in connecting'); + // Check that client succeeded in connecting. + assert.strictEqual(status, 0); })) .on('close', common.mustCall(function() { // TODO(addaleax): Make `SafeGetenv` work like `process.env` diff --git a/test/parallel/test-tls-env-extra-ca.js b/test/parallel/test-tls-env-extra-ca.js index c4f647a735d72f..b8ff1d3be237eb 100644 --- a/test/parallel/test-tls-env-extra-ca.js +++ b/test/parallel/test-tls-env-extra-ca.js @@ -39,6 +39,7 @@ const server = tls.createServer(options, common.mustCall(function(s) { }); fork(__filename, { env }).on('exit', common.mustCall(function(status) { - assert.strictEqual(status, 0, 'client did not succeed in connecting'); + // client did not succeed in connecting + assert.strictEqual(status, 0); })); })); diff --git a/test/parallel/test-trace-events-metadata.js b/test/parallel/test-trace-events-metadata.js index 440aa00a9c216c..7f9ccc3c7378d5 100644 --- a/test/parallel/test-trace-events-metadata.js +++ b/test/parallel/test-trace-events-metadata.js @@ -8,7 +8,8 @@ if (!common.isMainThread) common.skip('process.chdir is not available in Workers'); const CODE = - 'setTimeout(() => { for (var i = 0; i < 100000; i++) { "test" + i } }, 1)'; + 'setTimeout(() => { for (var i = 0; i < 100000; i++) { "test" + i } }, 1);' + + 'process.title = "foo"'; const FILE_NAME = 'node_trace.1.log'; const tmpdir = require('../common/tmpdir'); @@ -17,6 +18,7 @@ process.chdir(tmpdir.path); const proc = cp.spawn(process.execPath, [ '--trace-event-categories', 'node.perf.usertiming', + '--title=bar', '-e', CODE ]); proc.once('exit', common.mustCall(() => { assert(common.fileExists(FILE_NAME)); @@ -32,5 +34,14 @@ proc.once('exit', common.mustCall(() => { assert(traces.some((trace) => trace.cat === '__metadata' && trace.name === 'version' && trace.args.node === process.versions.node)); + if (!common.isSunOS) { + // Changing process.title is currently unsupported on SunOS/SmartOS + assert(traces.some((trace) => + trace.cat === '__metadata' && trace.name === 'process_name' && + trace.args.name === 'foo')); + assert(traces.some((trace) => + trace.cat === '__metadata' && trace.name === 'process_name' && + trace.args.name === 'bar')); + } })); })); diff --git a/test/parallel/test-util-inspect.js b/test/parallel/test-util-inspect.js index 874797665dc415..826e965fd0044d 100644 --- a/test/parallel/test-util-inspect.js +++ b/test/parallel/test-util-inspect.js @@ -447,13 +447,13 @@ assert.strictEqual(util.inspect(-5e-324), '-5e-324'); { const map = new Map(); map.set(1, 2); - const vals = previewEntries(map.entries()); + const [ vals ] = previewEntries(map.entries()); const valsOutput = []; for (const o of vals) { valsOutput.push(o); } - assert.strictEqual(util.inspect(valsOutput), '[ [ 1, 2 ] ]'); + assert.strictEqual(util.inspect(valsOutput), '[ 1, 2 ]'); } // Test for other constructors in different context. diff --git a/test/parallel/test-vm-function-declaration.js b/test/parallel/test-vm-function-declaration.js index 0f6f19b13ebcf1..6405babfa78148 100644 --- a/test/parallel/test-vm-function-declaration.js +++ b/test/parallel/test-vm-function-declaration.js @@ -37,8 +37,8 @@ code += '(function(){return this})().b;\n'; const res = vm.runInContext(code, o, 'test'); -assert.strictEqual(typeof res, 'function', 'result should be function'); -assert.strictEqual(res.name, 'b', 'res should be named b'); -assert.strictEqual(typeof o.a, 'function', 'a should be function'); -assert.strictEqual(typeof o.b, 'function', 'b should be function'); -assert.strictEqual(res, o.b, 'result should be global b function'); +assert.strictEqual(typeof res, 'function'); +assert.strictEqual(res.name, 'b'); +assert.strictEqual(typeof o.a, 'function'); +assert.strictEqual(typeof o.b, 'function'); +assert.strictEqual(res, o.b); diff --git a/test/parallel/test-warn-sigprof.js b/test/parallel/test-warn-sigprof.js index 71ac25443bc266..e5335215d743b6 100644 --- a/test/parallel/test-warn-sigprof.js +++ b/test/parallel/test-warn-sigprof.js @@ -1,3 +1,4 @@ +// Flags: --inspect=0 'use strict'; const common = require('../common'); diff --git a/test/parallel/test-worker-exit-code.js b/test/parallel/test-worker-exit-code.js new file mode 100644 index 00000000000000..f09b834f853b12 --- /dev/null +++ b/test/parallel/test-worker-exit-code.js @@ -0,0 +1,47 @@ +// Flags: --experimental-worker +'use strict'; +const common = require('../common'); + +// This test checks that Worker has correct exit codes on parent side +// in multiple situations. + +const assert = require('assert'); +const worker = require('worker_threads'); +const { Worker, parentPort } = worker; + +const testCases = require('../fixtures/process-exit-code-cases'); + +// Do not use isMainThread so that this test itself can be run inside a Worker. +if (!process.env.HAS_STARTED_WORKER) { + process.env.HAS_STARTED_WORKER = 1; + parent(); +} else { + if (!parentPort) { + console.error('Parent port must not be null'); + process.exit(100); + return; + } + parentPort.once('message', (msg) => testCases[msg].func()); +} + +function parent() { + const test = (arg, name = 'worker', exit, error = null) => { + const w = new Worker(__filename); + w.on('exit', common.mustCall((code) => { + assert.strictEqual( + code, exit, + `wrong exit for ${arg}-${name}\nexpected:${exit} but got:${code}`); + console.log(`ok - ${arg} exited with ${exit}`); + })); + if (error) { + w.on('error', common.mustCall((err) => { + console.log(err); + assert(error.test(err), + `wrong error for ${arg}\nexpected:${error} but got:${err}`); + })); + } + w.postMessage(arg); + }; + + testCases.forEach((tc, i) => test(i, tc.func.name, tc.result, tc.error)); +} diff --git a/test/parallel/test-worker-message-port-transfer-closed.js b/test/parallel/test-worker-message-port-transfer-closed.js new file mode 100644 index 00000000000000..435a3789fca724 --- /dev/null +++ b/test/parallel/test-worker-message-port-transfer-closed.js @@ -0,0 +1,54 @@ +// Flags: --experimental-worker +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { MessageChannel } = require('worker_threads'); + +// This tests various behaviors around transferring MessagePorts with closing +// or closed handles. + +const { port1, port2 } = new MessageChannel(); + +const arrayBuf = new ArrayBuffer(10); +port1.onmessage = common.mustNotCall(); +port2.onmessage = common.mustNotCall(); + +function testSingle(closedPort, potentiallyOpenPort) { + assert.throws(common.mustCall(() => { + potentiallyOpenPort.postMessage(null, [arrayBuf, closedPort]); + }), common.mustCall((err) => { + assert.strictEqual(err.name, 'DataCloneError'); + assert.strictEqual(err.message, + 'MessagePort in transfer list is already detached'); + assert.strictEqual(err.code, 25); + assert.ok(err instanceof Error); + + const DOMException = err.constructor; + assert.ok(err instanceof DOMException); + assert.strictEqual(DOMException.name, 'DOMException'); + + return true; + })); + + // arrayBuf must not be transferred, even though it is present earlier in the + // transfer list than the closedPort. + assert.strictEqual(arrayBuf.byteLength, 10); +} + +function testBothClosed() { + testSingle(port1, port2); + testSingle(port2, port1); +} + +// Even though the port handles may not be completely closed in C++ land, the +// observable behavior must be that the closing/detachment is synchronous and +// instant. + +port1.close(common.mustCall(testBothClosed)); +testSingle(port1, port2); +port2.close(common.mustCall(testBothClosed)); +testBothClosed(); + +setTimeout(common.mustNotCall('The communication channel is still open'), + common.platformTimeout(1000)).unref(); diff --git a/test/parallel/test-worker-message-port-transfer-self.js b/test/parallel/test-worker-message-port-transfer-self.js new file mode 100644 index 00000000000000..1855023cdfae04 --- /dev/null +++ b/test/parallel/test-worker-message-port-transfer-self.js @@ -0,0 +1,33 @@ +// Flags: --experimental-worker +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { MessageChannel } = require('worker_threads'); + +const { port1, port2 } = new MessageChannel(); + +assert.throws(common.mustCall(() => { + port1.postMessage(null, [port1]); +}), common.mustCall((err) => { + assert.strictEqual(err.name, 'DataCloneError'); + assert.strictEqual(err.message, 'Transfer list contains source port'); + assert.strictEqual(err.code, 25); + assert.ok(err instanceof Error); + + const DOMException = err.constructor; + assert.ok(err instanceof DOMException); + assert.strictEqual(DOMException.name, 'DOMException'); + + return true; +})); + +// The failed transfer should not affect the ports in anyway. +port2.onmessage = common.mustCall((message) => { + assert.strictEqual(message, 2); + port1.close(); + + setTimeout(common.mustNotCall('The communication channel is still open'), + common.platformTimeout(1000)).unref(); +}); +port1.postMessage(2); diff --git a/test/parallel/test-worker-message-port-transfer-target.js b/test/parallel/test-worker-message-port-transfer-target.js new file mode 100644 index 00000000000000..8e6354d8269771 --- /dev/null +++ b/test/parallel/test-worker-message-port-transfer-target.js @@ -0,0 +1,24 @@ +// Flags: --experimental-worker +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { MessageChannel } = require('worker_threads'); + +const { port1, port2 } = new MessageChannel(); + +const arrayBuf = new ArrayBuffer(10); + +common.expectWarning('Warning', + 'The target port was posted to itself, and the ' + + 'communication channel was lost', + common.noWarnCode); +port2.onmessage = common.mustNotCall(); +port2.postMessage(null, [port1, arrayBuf]); + +// arrayBuf must be transferred, despite the fact that port2 never received the +// message. +assert.strictEqual(arrayBuf.byteLength, 0); + +setTimeout(common.mustNotCall('The communication channel is still open'), + common.platformTimeout(1000)).unref(); diff --git a/test/parallel/test-worker-onmessage.js b/test/parallel/test-worker-onmessage.js index 895536c15038be..2ae3d90f852ba9 100644 --- a/test/parallel/test-worker-onmessage.js +++ b/test/parallel/test-worker-onmessage.js @@ -2,9 +2,11 @@ 'use strict'; const common = require('../common'); const assert = require('assert'); -const { Worker, isMainThread, parentPort } = require('worker_threads'); +const { Worker, parentPort } = require('worker_threads'); -if (isMainThread) { +// Do not use isMainThread so that this test itself can be run inside a Worker. +if (!process.env.HAS_STARTED_WORKER) { + process.env.HAS_STARTED_WORKER = 1; const w = new Worker(__filename); w.on('message', common.mustCall((message) => { assert.strictEqual(message, 4); diff --git a/test/parallel/test-worker-type-check.js b/test/parallel/test-worker-type-check.js new file mode 100644 index 00000000000000..43013bc538712a --- /dev/null +++ b/test/parallel/test-worker-type-check.js @@ -0,0 +1,28 @@ +// Flags: --experimental-worker +'use strict'; + +const common = require('../common'); +const { Worker } = require('worker_threads'); + +{ + [ + undefined, + null, + false, + 0, + Symbol('test'), + {}, + [], + () => {} + ].forEach((val) => { + common.expectsError( + () => new Worker(val), + { + code: 'ERR_INVALID_ARG_TYPE', + type: TypeError, + message: 'The "filename" argument must be of type string. ' + + `Received type ${typeof val}` + } + ); + }); +} diff --git a/test/parallel/test-worker-uncaught-exception-async.js b/test/parallel/test-worker-uncaught-exception-async.js index c3f0c8dec59f09..862b1a66d619c3 100644 --- a/test/parallel/test-worker-uncaught-exception-async.js +++ b/test/parallel/test-worker-uncaught-exception-async.js @@ -10,9 +10,27 @@ if (!process.env.HAS_STARTED_WORKER) { const w = new Worker(__filename); w.on('message', common.mustNotCall()); w.on('error', common.mustCall((err) => { + console.log(err.message); assert(/^Error: foo$/.test(err)); })); + w.on('exit', common.mustCall((code) => { + // uncaughtException is code 1 + assert.strictEqual(code, 1); + })); } else { + // cannot use common.mustCall as it cannot catch this + let called = false; + process.on('exit', (code) => { + if (!called) { + called = true; + } else { + assert.fail('Exit callback called twice in worker'); + } + }); + + setTimeout(() => assert.fail('Timeout executed after uncaughtException'), + 2000); + setImmediate(() => { throw new Error('foo'); }); diff --git a/test/parallel/test-worker-uncaught-exception.js b/test/parallel/test-worker-uncaught-exception.js index b7d9f8a2928ec1..95c142b6c70d64 100644 --- a/test/parallel/test-worker-uncaught-exception.js +++ b/test/parallel/test-worker-uncaught-exception.js @@ -10,8 +10,26 @@ if (!process.env.HAS_STARTED_WORKER) { const w = new Worker(__filename); w.on('message', common.mustNotCall()); w.on('error', common.mustCall((err) => { + console.log(err.message); assert(/^Error: foo$/.test(err)); })); + w.on('exit', common.mustCall((code) => { + // uncaughtException is code 1 + assert.strictEqual(code, 1); + })); } else { + // cannot use common.mustCall as it cannot catch this + let called = false; + process.on('exit', (code) => { + if (!called) { + called = true; + } else { + assert.fail('Exit callback called twice in worker'); + } + }); + + setTimeout(() => assert.fail('Timeout executed after uncaughtException'), + 2000); + throw new Error('foo'); } diff --git a/test/parallel/test-zlib-unused-weak.js b/test/parallel/test-zlib-unused-weak.js new file mode 100644 index 00000000000000..7a5a6728533e18 --- /dev/null +++ b/test/parallel/test-zlib-unused-weak.js @@ -0,0 +1,18 @@ +'use strict'; +// Flags: --expose-gc +require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + +// Tests that native zlib handles start out their life as weak handles. + +const before = process.memoryUsage().external; +for (let i = 0; i < 100; ++i) + zlib.createGzip(); +const afterCreation = process.memoryUsage().external; +global.gc(); +const afterGC = process.memoryUsage().external; + +assert((afterGC - before) / (afterCreation - before) <= 0.05, + `Expected after-GC delta ${afterGC - before} to be less than 5 %` + + ` of before-GC delta ${afterCreation - before}`); diff --git a/test/pummel/test-timers.js b/test/pummel/test-timers.js index 7e0492a6425b7d..a6766b7a33c11d 100644 --- a/test/pummel/test-timers.js +++ b/test/pummel/test-timers.js @@ -31,7 +31,7 @@ let interval_count = 0; clearTimeout(null); clearInterval(null); -assert.strictEqual(true, setTimeout instanceof Function); +assert.strictEqual(setTimeout instanceof Function, true); const starttime = new Date(); setTimeout(common.mustCall(function() { const endtime = new Date(); @@ -40,7 +40,7 @@ setTimeout(common.mustCall(function() { assert.ok(diff > 0); console.error(`diff: ${diff}`); - assert.strictEqual(true, 1000 - WINDOW < diff && diff < 1000 + WINDOW); + assert.strictEqual(1000 - WINDOW < diff && diff < 1000 + WINDOW, true); }), 1000); // this timer shouldn't execute @@ -57,9 +57,9 @@ setInterval(function() { const t = interval_count * 1000; - assert.strictEqual(true, t - WINDOW < diff && diff < t + WINDOW); + assert.strictEqual(t - WINDOW < diff && diff < t + WINDOW, true); - assert.strictEqual(true, interval_count <= 3); + assert.strictEqual(interval_count <= 3, true); if (interval_count === 3) clearInterval(this); }, 1000); @@ -67,13 +67,13 @@ setInterval(function() { // Single param: setTimeout(function(param) { - assert.strictEqual('test param', param); + assert.strictEqual(param, 'test param'); }, 1000, 'test param'); let interval_count2 = 0; setInterval(function(param) { ++interval_count2; - assert.strictEqual('test param', param); + assert.strictEqual(param, 'test param'); if (interval_count2 === 3) clearInterval(this); @@ -82,15 +82,15 @@ setInterval(function(param) { // Multiple param setTimeout(function(param1, param2) { - assert.strictEqual('param1', param1); - assert.strictEqual('param2', param2); + assert.strictEqual(param1, 'param1'); + assert.strictEqual(param2, 'param2'); }, 1000, 'param1', 'param2'); let interval_count3 = 0; setInterval(function(param1, param2) { ++interval_count3; - assert.strictEqual('param1', param1); - assert.strictEqual('param2', param2); + assert.strictEqual(param1, 'param1'); + assert.strictEqual(param2, 'param2'); if (interval_count3 === 3) clearInterval(this); @@ -120,8 +120,9 @@ clearTimeout(y); process.on('exit', function() { - assert.strictEqual(3, interval_count); - assert.strictEqual(11, count4); - assert.strictEqual(0, expectedTimeouts, - 'clearTimeout cleared too many timeouts'); + assert.strictEqual(interval_count, 3); + assert.strictEqual(count4, 11); + + // Check that the correct number of timers ran. + assert.strictEqual(expectedTimeouts, 0); }); diff --git a/test/sequential/sequential.status b/test/sequential/sequential.status index 59cc2bc7f77881..c5873b9060f8fe 100644 --- a/test/sequential/sequential.status +++ b/test/sequential/sequential.status @@ -16,7 +16,6 @@ test-inspector-bindings : PASS, FLAKY test-inspector-debug-end : PASS, FLAKY [$system==linux] -test-inspector-port-zero-cluster : PASS, FLAKY [$system==macos] diff --git a/test/sequential/test-debug-prompt.js b/test/sequential/test-debug-prompt.js index 902bce34fc46f4..e32f4646900536 100644 --- a/test/sequential/test-debug-prompt.js +++ b/test/sequential/test-debug-prompt.js @@ -7,9 +7,12 @@ const spawn = require('child_process').spawn; const proc = spawn(process.execPath, ['inspect', 'foo']); proc.stdout.setEncoding('utf8'); +let needToSendExit = true; let output = ''; proc.stdout.on('data', (data) => { output += data; - if (output.includes('debug> ')) + if (output.includes('debug> ') && needToSendExit) { proc.stdin.write('.exit\n'); + needToSendExit = false; + } }); diff --git a/test/sequential/test-inspector-console.js b/test/sequential/test-inspector-console.js new file mode 100644 index 00000000000000..6a06c798881654 --- /dev/null +++ b/test/sequential/test-inspector-console.js @@ -0,0 +1,39 @@ +// Flags: --expose-internals +'use strict'; + +const common = require('../common'); +common.skipIfInspectorDisabled(); + +const { NodeInstance } = require('../common/inspector-helper.js'); +const assert = require('assert'); + +async function runTest() { + const script = 'require(\'inspector\').console.log(\'hello world\');'; + const child = new NodeInstance('--inspect-brk=0', script, ''); + + let out = ''; + child.on('stdout', (line) => out += line); + + const session = await child.connectInspectorSession(); + + const commands = [ + { 'method': 'Runtime.enable' }, + { 'method': 'Runtime.runIfWaitingForDebugger' } + ]; + + session.send(commands); + + const msg = await session.waitForNotification('Runtime.consoleAPICalled'); + + assert.strictEqual(msg.params.type, 'log'); + assert.deepStrictEqual(msg.params.args, [{ + type: 'string', + value: 'hello world' + }]); + assert.strictEqual(out, ''); + + session.disconnect(); +} + +common.crashOnUnhandledRejection(); +runTest(); diff --git a/tools/doc/allhtml.js b/tools/doc/allhtml.js index fad530f1c44245..d185538ab683b6 100644 --- a/tools/doc/allhtml.js +++ b/tools/doc/allhtml.js @@ -12,7 +12,7 @@ const htmlFiles = fs.readdirSync(source, 'utf8') .filter((name) => name.includes('.html') && name !== 'all.html'); // Read the table of contents. -const toc = fs.readFileSync(source + '/_toc.html', 'utf8'); +const toc = fs.readFileSync(source + '/index.html', 'utf8'); // Extract (and concatenate) the toc and apicontent from each document. let contents = ''; @@ -47,11 +47,13 @@ for (const link of toc.match(//g)) { seen[href] = true; } -// Replace various mentions of _toc with all. -let all = toc.replace(/_toc\.html/g, 'all.html') - .replace('_toc.json', 'all.json') - .replace('api-section-_toc', 'api-section-all') - .replace('data-id="_toc"', 'data-id="all"'); +// Replace various mentions of index with all. +let all = toc.replace(/index\.html/g, 'all.html') + .replace('', '') + .replace('index.json', 'all.json') + .replace('api-section-index', 'api-section-all') + .replace('data-id="index"', 'data-id="all"') + .replace(/
  5. .*?<\/li>/, ''); // Clean up the title. all = all.replace(/.*?\| /, '<title>'); diff --git a/tools/doc/alljson.js b/tools/doc/alljson.js new file mode 100644 index 00000000000000..7e027f764e7efd --- /dev/null +++ b/tools/doc/alljson.js @@ -0,0 +1,56 @@ +'use strict'; + +// Build all.json by combining the miscs, modules, classes, globals, and methods +// from the generated json files. + +const fs = require('fs'); + +const source = `${__dirname}/../../out/doc/api`; + +// Get a list of generated API documents. +const jsonFiles = fs.readdirSync(source, 'utf8') + .filter((name) => name.includes('.json') && name !== 'all.json'); + +// Read the table of contents. +const toc = fs.readFileSync(source + '/index.html', 'utf8'); + +// Initialize results. Only these four data values will be collected. +const results = { + miscs: [], + modules: [], + classes: [], + globals: [], + methods: [] +}; + +// Identify files that should be skipped. As files are processed, they +// are added to this list to prevent dupes. +const seen = { + 'all.json': true, + 'index.json': true +}; + +// Extract (and concatenate) the selected data from each document. +// Expand hrefs found in json to include source HTML file. +for (const link of toc.match(/<a.*?>/g)) { + const href = /href="(.*?)"/.exec(link)[1]; + const json = href.replace('.html', '.json'); + if (!jsonFiles.includes(json) || seen[json]) continue; + const data = JSON.parse( + fs.readFileSync(source + '/' + json, 'utf8') + .replace(/<a href=\\"#/g, `<a href=\\"${href}#`) + ); + + for (const property in data) { + if (results.hasOwnProperty(property)) { + results[property].push(...data[property]); + } + } + + // Mark source as seen. + seen[json] = true; +} + +// Write results. +fs.writeFileSync(source + '/all.json', + `${JSON.stringify(results, null, 2)}\n`, 'utf8'); diff --git a/tools/doc/common.js b/tools/doc/common.js index 813935f23b84f4..4dfadd353d9ec8 100644 --- a/tools/doc/common.js +++ b/tools/doc/common.js @@ -25,6 +25,10 @@ function extractAndParseYAML(text) { meta.added = arrify(meta.added); } + if (meta.napiVersion) { + meta.napiVersion = arrify(meta.napiVersion); + } + if (meta.deprecated) { // Treat deprecated like added for consistency. meta.deprecated = arrify(meta.deprecated); diff --git a/tools/doc/generate.js b/tools/doc/generate.js index 9f217b19c7225f..8ed97610cf3bc2 100644 --- a/tools/doc/generate.js +++ b/tools/doc/generate.js @@ -21,7 +21,6 @@ 'use strict'; -const processIncludes = require('./preprocess.js'); const fs = require('fs'); // Parse the args. @@ -52,12 +51,6 @@ if (!filename) { } fs.readFile(filename, 'utf8', (er, input) => { - if (er) throw er; - // Process the input for @include lines. - processIncludes(filename, input, next); -}); - -function next(er, input) { if (er) throw er; switch (format) { case 'json': @@ -78,4 +71,4 @@ function next(er, input) { default: throw new Error(`Invalid format: ${format}`); } -} +}); diff --git a/tools/doc/html.js b/tools/doc/html.js index ae2da58b7a0131..0f3293dadd51d1 100644 --- a/tools/doc/html.js +++ b/tools/doc/html.js @@ -36,8 +36,8 @@ marked.setOptions({ renderer }); const docPath = path.resolve(__dirname, '..', '..', 'doc'); -const gtocPath = path.join(docPath, 'api', '_toc.md'); -const gtocMD = fs.readFileSync(gtocPath, 'utf8').replace(/^@\/\/.*$/gm, ''); +const gtocPath = path.join(docPath, 'api', 'index.md'); +const gtocMD = fs.readFileSync(gtocPath, 'utf8').replace(/^<!--.*?-->/gms, ''); const gtocHTML = marked(gtocMD).replace( /<a href="(.*?)"/g, (all, href) => `<a class="nav-${href.replace('.html', '') @@ -96,6 +96,8 @@ function toHTML({ input, filename, nodeVersion, analytics }, cb) { HTML = HTML.replace('__ALTDOCS__', ''); } + HTML = HTML.replace('__EDIT_ON_GITHUB__', editOnGitHub(filename)); + // Content insertion has to be the last thing we do with the lexed tokens, // because it's destructive. HTML = HTML.replace('__CONTENT__', marked.parser(lexed)); @@ -261,6 +263,10 @@ function parseYAML(text) { html += `${added.description}${deprecated.description}\n`; } + if (meta.napiVersion) { + html += `<span>N-API version: ${meta.napiVersion.join(', ')}</span>\n`; + } + html += '</div>'; return html; } @@ -373,3 +379,9 @@ function altDocs(filename, docCreated) { </li> ` : ''; } + +// eslint-disable-next-line max-len +const githubLogo = '<span class="github_icon"><svg height="16" width="16" viewBox="0 0 16.1 16.1" fill="currentColor"><path d="M8 0a8 8 0 0 0-2.5 15.6c.4 0 .5-.2.5-.4v-1.5c-2 .4-2.5-.5-2.7-1 0-.1-.5-.9-.8-1-.3-.2-.7-.6 0-.6.6 0 1 .6 1.2.8.7 1.2 1.9 1 2.4.7 0-.5.2-.9.5-1-1.8-.3-3.7-1-3.7-4 0-.9.3-1.6.8-2.2 0-.2-.3-1 .1-2 0 0 .7-.3 2.2.7a7.4 7.4 0 0 1 4 0c1.5-1 2.2-.8 2.2-.8.5 1.1.2 2 .1 2.1.5.6.8 1.3.8 2.2 0 3-1.9 3.7-3.6 4 .3.2.5.7.5 1.4v2.2c0 .2.1.5.5.4A8 8 0 0 0 16 8a8 8 0 0 0-8-8z"/></svg></span>'; +function editOnGitHub(filename) { + return `<li class="edit_on_github"><a href="https://github.com/nodejs/node/edit/master/doc/api/${filename}.md">${githubLogo}Edit on GitHub</a></li>`; +} diff --git a/tools/doc/preprocess.js b/tools/doc/preprocess.js deleted file mode 100644 index 554af2ccb77ae0..00000000000000 --- a/tools/doc/preprocess.js +++ /dev/null @@ -1,38 +0,0 @@ -'use strict'; - -module.exports = processIncludes; - -const path = require('path'); -const fs = require('fs'); - -const includeExpr = /^@include\s+([\w-]+)(?:\.md)?$/gmi; -const commentExpr = /^@\/\/.*$/gm; - -function processIncludes(inputFile, input, cb) { - const includes = input.match(includeExpr); - if (includes === null) - return cb(null, input.replace(commentExpr, '')); - - let errState = null; - let incCount = includes.length; - - includes.forEach((include) => { - const fname = include.replace(includeExpr, '$1.md'); - const fullFname = path.resolve(path.dirname(inputFile), fname); - - fs.readFile(fullFname, 'utf8', function(er, inc) { - if (errState) return; - if (er) return cb(errState = er); - incCount--; - - // Add comments to let the HTML generator know - // how the anchors for headings should look like. - inc = `<!-- [start-include:${fname}] -->\n` + - `${inc}\n<!-- [end-include:${fname}] -->\n`; - input = input.split(`${include}\n`).join(`${inc}\n`); - - if (incCount === 0) - return cb(null, input.replace(commentExpr, '')); - }); - }); -} diff --git a/tools/eslint-rules/no-duplicate-requires.js b/tools/eslint-rules/no-duplicate-requires.js new file mode 100644 index 00000000000000..595c22360112ca --- /dev/null +++ b/tools/eslint-rules/no-duplicate-requires.js @@ -0,0 +1,70 @@ +/** + * @fileoverview Ensure modules are not required twice at top level of a module + * @author devsnek + */ +'use strict'; + +//------------------------------------------------------------------------------ +// Rule Definition +//------------------------------------------------------------------------------ + + +function isString(node) { + return node && node.type === 'Literal' && typeof node.value === 'string'; +} + +function isRequireCall(node) { + return node.callee.type === 'Identifier' && node.callee.name === 'require'; +} + +function isTopLevel(node) { + do { + if (node.type === 'FunctionDeclaration' || + node.type === 'FunctionExpression' || + node.type === 'ArrowFunctionExpression' || + node.type === 'ClassBody' || + node.type === 'MethodDefinition') { + return false; + } + } while (node = node.parent); + return true; +} + +module.exports = (context) => { + if (context.parserOptions.sourceType === 'module') { + return {}; + } + + function getRequiredModuleNameFromCall(node) { + // node has arguments and first argument is string + if (node.arguments.length && isString(node.arguments[0])) { + return node.arguments[0].value.trim(); + } + + return undefined; + } + + const required = new Set(); + + const rules = { + CallExpression: (node) => { + if (isRequireCall(node) && isTopLevel(node)) { + const moduleName = getRequiredModuleNameFromCall(node); + if (moduleName === undefined) { + return; + } + if (required.has(moduleName)) { + context.report( + node, + '\'{{moduleName}}\' require is duplicated.', + { moduleName } + ); + } else { + required.add(moduleName); + } + } + }, + }; + + return rules; +}; diff --git a/tools/msvs/msi/product.wxs b/tools/msvs/msi/product.wxs index c8a89d725509db..e9e4e33751d254 100755 --- a/tools/msvs/msi/product.wxs +++ b/tools/msvs/msi/product.wxs @@ -254,7 +254,7 @@ KeyPath="yes"/> <util:InternetShortcut Id="WebsiteShortcut" Name="Node.js website" - Target="http://nodejs.org" + Target="https://nodejs.org/" Type="url"/> <util:InternetShortcut Id="DocsShortcut" Name="Node.js documentation" diff --git a/tools/pkgsrc/description b/tools/pkgsrc/description index 1cf2a08512e67f..b070f52fb6b092 100644 --- a/tools/pkgsrc/description +++ b/tools/pkgsrc/description @@ -4,4 +4,4 @@ intended for writing scalable network programs such as web servers. Packaged by nodejs.org Homepage: -http://nodejs.org/ +https://nodejs.org/ diff --git a/tools/rpm/node.spec b/tools/rpm/node.spec index 39b98ec5541c53..83c7b67b2a39a5 100644 --- a/tools/rpm/node.spec +++ b/tools/rpm/node.spec @@ -22,7 +22,7 @@ Summary: Node.js is a platform for building fast, scalable network applications. Group: Development/Languages License: MIT URL: https://nodejs.org/ -Source0: http://nodejs.org/dist/v%{_version}/node-v%{_version}.tar.gz +Source0: https://nodejs.org/dist/v%{_version}/node-v%{_version}.tar.gz BuildRequires: gcc BuildRequires: gcc-c++ BuildRequires: glibc-devel diff --git a/tools/update-eslint.sh b/tools/update-eslint.sh index 44f43a05d31e1a..3663ecf74283c0 100755 --- a/tools/update-eslint.sh +++ b/tools/update-eslint.sh @@ -2,7 +2,7 @@ # Shell script to update ESLint in the source tree to the latest release. -# Depends on npm and node being in $PATH. +# Depends on npm, npx, and node being in $PATH. # This script must be be in the tools directory when it runs because it uses # $BASH_SOURCE[0] to determine directories to work in. @@ -19,11 +19,8 @@ cd node_modules/eslint npm install --no-bin-links --production --no-package-lock eslint-plugin-markdown@next cd ../.. -# Install dmn if it is not in path. -type -P dmn || npm install -g dmn - # Use dmn to remove some unneeded files. -dmn -f clean +npx dmn -f clean cd .. mv eslint-tmp/node_modules/eslint node_modules/eslint